"use client"; import type { FocusEventHandler } from "react"; import { Anchor, Avatar, CheckIcon, CloseButton, Combobox, Group, Pill, PillsInput, Stack, Text, useCombobox, } from "@mantine/core"; import type { IntegrationKind } from "@homarr/definitions"; import { getIconUrl } from "@homarr/definitions"; import { useI18n } from "@homarr/translation/client"; import { Link } from "@homarr/ui"; import classes from "./widget-integration-select.module.css"; interface WidgetIntegrationSelectProps { label: string; onChange: (value: string[]) => void; value?: string[]; error?: string; onFocus?: FocusEventHandler; onBlur?: FocusEventHandler; canSelectMultiple?: boolean; data: IntegrationSelectOption[]; withAsterisk?: boolean; onOpenNewIntegration?: () => void; } export const WidgetIntegrationSelect = ({ data, onChange, value: valueProp, label, canSelectMultiple = true, withAsterisk = false, onOpenNewIntegration, ...props }: WidgetIntegrationSelectProps) => { const tItem = useI18n("item.edit.field.integrations"); const tIntegration = useI18n("widget.common.integration"); const tCommon = useI18n("common"); const multiSelectValues = valueProp ?? []; const combobox = useCombobox({ onDropdownClose: () => combobox.resetSelectedOption(), onDropdownOpen: () => combobox.updateSelectedOptionIndex("active"), }); const handleValueSelect = (selectedValue: string) => { if (multiSelectValues.includes(selectedValue)) { onChange(multiSelectValues.filter((value) => value !== selectedValue)); } else if (canSelectMultiple) { onChange([...multiSelectValues, selectedValue]); } else { onChange([selectedValue]); } if (!canSelectMultiple) { combobox.closeDropdown(); } }; const handleValueRemove = (valueToRemove: string) => onChange(multiSelectValues.filter((value) => value !== valueToRemove)); const values = multiSelectValues.map((item) => { const option = data.find((integration) => integration.id === item); if (!option) { return null; } return ( handleValueRemove(item)} showRemoveButton={canSelectMultiple} removeLabel={tItem("removeLabel", { name: option.name })} /> ); }); const options = data.map((item) => { return ( {multiSelectValues.includes(item.id) ? : null} {item.name} {item.url} ); }); return ( {tCommon("action.create")} ) : ( {tIntegration("manage")} ) } pointer onClick={() => combobox.openDropdown()} label={label} withAsterisk={withAsterisk} {...props} > {values} combobox.openDropdown()} onBlur={() => combobox.closeDropdown()} onKeyDown={(event) => { if (["ArrowDown", "Enter", " "].includes(event.key)) { event.preventDefault(); combobox.openDropdown(); return; } if (event.key === "Escape") { combobox.closeDropdown(); return; } if (event.key === "Backspace" && canSelectMultiple && multiSelectValues.length > 0) { event.preventDefault(); const lastValue = multiSelectValues.at(-1); if (lastValue) handleValueRemove(lastValue); } }} /> {options.length >= 1 ? ( options ) : ( {tIntegration("noData")} )} ); }; export interface IntegrationSelectOption { id: string; name: string; url: string; kind: IntegrationKind; permissions?: { hasUseAccess: boolean; hasInteractAccess: boolean; hasFullAccess: boolean; }; } interface IntegrationPillProps { option: IntegrationSelectOption; onRemove: () => void; showRemoveButton: boolean; removeLabel: string; } const IntegrationPill = ({ option, onRemove, showRemoveButton, removeLabel }: IntegrationPillProps) => ( {option.name} {showRemoveButton && ( { event.stopPropagation(); onRemove(); }} aria-label={removeLabel} variant="transparent" color="gray" size={24} iconSize={14} /> )} );