@pipe0/react 1.0.36 → 1.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/components/compound/effect-catalog/category-filter.mjs +1 -1
  3. package/dist/components/compound/effect-catalog/category-filter.mjs.map +1 -1
  4. package/dist/components/compound/effect-catalog/search-filter.mjs +1 -1
  5. package/dist/components/compound/effect-catalog/search-filter.mjs.map +1 -1
  6. package/dist/components/compound/pipe-catalog/category-filter.mjs +1 -1
  7. package/dist/components/compound/pipe-catalog/category-filter.mjs.map +1 -1
  8. package/dist/components/compound/pipe-catalog/input-field-filter.mjs +1 -1
  9. package/dist/components/compound/pipe-catalog/input-field-filter.mjs.map +1 -1
  10. package/dist/components/compound/pipe-catalog/output-field-filter.mjs +1 -1
  11. package/dist/components/compound/pipe-catalog/output-field-filter.mjs.map +1 -1
  12. package/dist/components/compound/pipe-catalog/provider-filter.mjs +1 -1
  13. package/dist/components/compound/pipe-catalog/provider-filter.mjs.map +1 -1
  14. package/dist/components/compound/pipe-catalog/search-filter.mjs +1 -1
  15. package/dist/components/compound/pipe-catalog/search-filter.mjs.map +1 -1
  16. package/dist/components/compound/pipe-catalog/tag-filter.mjs +1 -1
  17. package/dist/components/compound/pipe-catalog/tag-filter.mjs.map +1 -1
  18. package/dist/components/compound/search-catalog/category-filter.mjs +1 -1
  19. package/dist/components/compound/search-catalog/category-filter.mjs.map +1 -1
  20. package/dist/components/compound/search-catalog/output-field-filter.mjs +1 -1
  21. package/dist/components/compound/search-catalog/output-field-filter.mjs.map +1 -1
  22. package/dist/components/compound/search-catalog/provider-filter.mjs +1 -1
  23. package/dist/components/compound/search-catalog/provider-filter.mjs.map +1 -1
  24. package/dist/components/compound/search-catalog/search-filter.mjs +1 -1
  25. package/dist/components/compound/search-catalog/search-filter.mjs.map +1 -1
  26. package/dist/components/compound/search-catalog/tag-filter.mjs +1 -1
  27. package/dist/components/compound/search-catalog/tag-filter.mjs.map +1 -1
  28. package/dist/components/defaults/adapters/json-extraction-input.mjs +1 -1
  29. package/dist/components/defaults/adapters/loose-object-input.mjs +1 -1
  30. package/dist/components/defaults/adapters/loose-object-input.mjs.map +1 -1
  31. package/dist/components/defaults/adapters/pipes-run-if-input.mjs +1 -1
  32. package/dist/components/defaults/adapters/prompt-input.mjs +1 -1
  33. package/dist/components/defaults/adapters/property-list-input.mjs +32 -16
  34. package/dist/components/defaults/adapters/property-list-input.mjs.map +1 -1
  35. package/dist/components/defaults/adapters/providers-input.mjs +1 -2
  36. package/dist/components/defaults/adapters/providers-input.mjs.map +1 -1
  37. package/dist/components/defaults/adapters/select-input.mjs +1 -1
  38. package/dist/components/defaults/adapters/select-input.mjs.map +1 -1
  39. package/dist/components/defaults/adapters/tagged-ordered-multi-create-input.mjs +2 -3
  40. package/dist/components/defaults/adapters/tagged-ordered-multi-create-input.mjs.map +1 -1
  41. package/dist/components/defaults/adapters/tagged-text-input.mjs +1 -1
  42. package/dist/components/defaults/adapters/tagged-text-input.mjs.map +1 -1
  43. package/dist/components/defaults/catalog/active-filter-pill.mjs +1 -1
  44. package/dist/components/defaults/catalog/active-filter-pill.mjs.map +1 -1
  45. package/dist/components/defaults/catalog/card-derived.d.mts.map +1 -1
  46. package/dist/components/defaults/catalog/card-derived.mjs +9 -6
  47. package/dist/components/defaults/catalog/card-derived.mjs.map +1 -1
  48. package/dist/components/defaults/catalog/category-section.d.mts +2 -2
  49. package/dist/components/defaults/catalog/category-section.d.mts.map +1 -1
  50. package/dist/components/defaults/catalog/category-section.mjs +10 -15
  51. package/dist/components/defaults/catalog/category-section.mjs.map +1 -1
  52. package/dist/components/defaults/catalog/provider-avatars.mjs +1 -1
  53. package/dist/components/defaults/catalog/provider-avatars.mjs.map +1 -1
  54. package/dist/components/defaults/layout/field-wrapper.mjs +2 -2
  55. package/dist/components/defaults/layout/field-wrapper.mjs.map +1 -1
  56. package/dist/components/defaults/layout/group.mjs +2 -1
  57. package/dist/components/defaults/layout/group.mjs.map +1 -1
  58. package/dist/components/defaults/layout/section.mjs +5 -8
  59. package/dist/components/defaults/layout/section.mjs.map +1 -1
  60. package/dist/components/internal/LiquidEditor/ChipEditPopover.mjs +1 -1
  61. package/dist/components/internal/LiquidEditor/ChipEditPopover.mjs.map +1 -1
  62. package/dist/components/internal/LiquidEditor/UnifiedReferencePicker.mjs +1 -1
  63. package/dist/components/internal/LiquidEditor/UnifiedReferencePicker.mjs.map +1 -1
  64. package/dist/components/internal/combobox/include-exclude-combobox.mjs +2 -3
  65. package/dist/components/internal/combobox/include-exclude-combobox.mjs.map +1 -1
  66. package/dist/components/internal/combobox/suggest-combobox.mjs +2 -3
  67. package/dist/components/internal/combobox/suggest-combobox.mjs.map +1 -1
  68. package/dist/components/internal/icons.d.mts +14 -0
  69. package/dist/components/internal/icons.d.mts.map +1 -0
  70. package/dist/components/internal/icons.mjs +137 -185
  71. package/dist/components/internal/icons.mjs.map +1 -1
  72. package/dist/components/ui/combobox.mjs +4 -4
  73. package/dist/components/ui/combobox.mjs.map +1 -1
  74. package/dist/components/ui/select.mjs +5 -5
  75. package/dist/components/ui/select.mjs.map +1 -1
  76. package/dist/hooks/use-pipe-catalog-table.d.mts +8 -8
  77. package/dist/hooks/use-search-catalog-table.d.mts +6 -6
  78. package/dist/styles/pipe0-form.css +1 -1
  79. package/dist/utils/catalog-helpers.d.mts +4 -5
  80. package/dist/utils/catalog-helpers.d.mts.map +1 -1
  81. package/dist/utils/catalog-helpers.mjs +4 -4
  82. package/dist/utils/catalog-helpers.mjs.map +1 -1
  83. package/dist/widgets/avatar-group.d.mts +12 -3
  84. package/dist/widgets/avatar-group.d.mts.map +1 -1
  85. package/dist/widgets/avatar-group.mjs +173 -50
  86. package/dist/widgets/avatar-group.mjs.map +1 -1
  87. package/dist/widgets/field-type-badge.d.mts.map +1 -1
  88. package/dist/widgets/field-type-badge.mjs +1 -1
  89. package/dist/widgets/field-type-badge.mjs.map +1 -1
  90. package/dist/widgets/icon-glyph.mjs +2 -3
  91. package/dist/widgets/icon-glyph.mjs.map +1 -1
  92. package/dist/widgets/token-pricing-badge.mjs +1 -1
  93. package/dist/widgets/token-pricing-badge.mjs.map +1 -1
  94. package/package.json +4 -3
  95. package/dist/widgets/glyphs/linkedin.mjs +0 -32
  96. package/dist/widgets/glyphs/linkedin.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"include-exclude-combobox.mjs","names":[],"sources":["../../../../src/components/internal/combobox/include-exclude-combobox.tsx"],"sourcesContent":["import type { WidgetsByKind } from \"@pipe0/base\";\nimport { XIcon } from \"lucide-react\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useDebouncedFn } from \"../../../hooks/use-debounced-fn.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { FieldTypeBadge } from \"../../../widgets/field-type-badge.js\";\nimport { WidgetStrip } from \"../../../widgets/widget-strip.js\";\nimport { Button } from \"../../ui/button.js\";\nimport {\n Combobox,\n ComboboxChips,\n ComboboxChipsInput,\n ComboboxContent,\n ComboboxEmpty,\n ComboboxItem,\n ComboboxList,\n ComboboxTrigger,\n useComboboxAnchor,\n} from \"../../ui/combobox.js\";\nimport { IconMinus, IconPlus } from \"../icons.js\";\nimport { filterOptions, type PayloadOption } from \"../multi-select-popover-trigger.js\";\n\nexport type IncludeExcludeMode = \"include\" | \"exclude\";\n\nexport interface IncludeExcludeValue {\n include: string[];\n exclude: string[];\n}\n\nexport interface IncludeExcludeComboboxProps {\n include: string[];\n exclude: string[];\n /**\n * Atomic update for the entire `{ include, exclude }` value. We avoid\n * splitting include/exclude into separate add/remove callbacks because\n * flipping a chip needs to mutate both sides in one shot — otherwise the\n * stale closure in the field-handle wiring drops one of the updates.\n */\n onChange: (next: IncludeExcludeValue) => void;\n options?: PayloadOption[];\n loadOptions?: (query: string) => Promise<PayloadOption[]>;\n allowCreate?: boolean;\n disableSearch?: boolean;\n maxItems?: number;\n ariaInvalid?: boolean;\n disabled?: boolean;\n debounceMs?: number;\n emptyLabel?: string;\n placeholder?: string;\n}\n\ntype SelectableItem = PayloadOption & {\n __mode?: IncludeExcludeMode;\n __create?: boolean;\n};\n\nconst includeChipClass =\n \"pz:flex pz:h-[calc(--spacing(5.25))] pz:w-fit pz:items-center pz:gap-0 pz:rounded-sm pz:border pz:border-border pz:bg-muted pz:pl-0 pz:pr-0 pz:text-xs pz:font-medium pz:whitespace-nowrap pz:text-foreground pz:overflow-hidden\";\n\nconst excludeChipClass =\n \"pz:flex pz:h-[calc(--spacing(5.25))] pz:w-fit pz:items-center pz:gap-0 pz:rounded-sm pz:border pz:border-destructive/30 pz:bg-destructive/10 pz:pl-0 pz:pr-0 pz:text-xs pz:font-medium pz:whitespace-nowrap pz:text-destructive pz:overflow-hidden\";\n\nconst includeSignClass =\n \"pz:flex pz:h-full pz:w-5 pz:cursor-pointer pz:items-center pz:justify-center pz:border-r pz:border-border pz:bg-foreground/[0.04] pz:text-muted-foreground pz:hover:bg-foreground/[0.08] pz:hover:text-foreground\";\n\nconst excludeSignClass =\n \"pz:flex pz:h-full pz:w-5 pz:cursor-pointer pz:items-center pz:justify-center pz:border-r pz:border-destructive/30 pz:bg-destructive/[0.06] pz:text-destructive/80 pz:hover:bg-destructive/15 pz:hover:text-destructive\";\n\n/**\n * Unified include/exclude combobox.\n *\n * Renders a single chip input where each chip carries an inline `+` / `−`\n * sign toggle that flips include ↔ exclude in place. Free-text-create inputs\n * (`allowCreate=true`) show a \"Type to see suggestions\" hint until the user\n * types; select-style inputs show their options on focus. Pure-select mode\n * (`disableSearch=true`) shows the chevron trigger and renders the input\n * read-only.\n */\nexport function IncludeExcludeCombobox({\n include,\n exclude,\n onChange,\n options,\n loadOptions,\n allowCreate = false,\n disableSearch = false,\n maxItems,\n ariaInvalid,\n disabled,\n debounceMs = 300,\n emptyLabel,\n placeholder,\n}: IncludeExcludeComboboxProps) {\n const [input, setInput] = useState(\"\");\n const [debouncedQuery, setDebouncedQuery] = useState(\"\");\n const [asyncResults, setAsyncResults] = useState<PayloadOption[] | null>(null);\n const [loading, setLoading] = useState(false);\n const [open, setOpen] = useState(false);\n const cacheRef = useRef<Map<string, PayloadOption[]>>(new Map());\n const reqIdRef = useRef(0);\n\n const isAsync = typeof loadOptions === \"function\";\n\n const debouncedSetQuery = useDebouncedFn((q: string) => setDebouncedQuery(q), debounceMs);\n\n const effectiveQuery = input.trim();\n\n useEffect(() => {\n if (!isAsync || !loadOptions) return;\n if (!open && debouncedQuery === \"\") return;\n const q = debouncedQuery.trim();\n const cached = cacheRef.current.get(q);\n if (cached) {\n setAsyncResults(cached);\n setLoading(false);\n return;\n }\n const reqId = ++reqIdRef.current;\n setLoading(true);\n loadOptions(q)\n .then((opts) => {\n if (reqId !== reqIdRef.current) return;\n const kept = opts.filter((o) => !!o.value);\n cacheRef.current.set(q, kept);\n setAsyncResults(kept);\n setLoading(false);\n })\n .catch(() => {\n if (reqId !== reqIdRef.current) return;\n setAsyncResults([]);\n setLoading(false);\n });\n }, [debouncedQuery, isAsync, loadOptions, open]);\n\n const includeSet = useMemo(() => new Set(include), [include]);\n const excludeSet = useMemo(() => new Set(exclude), [exclude]);\n const totalSelected = include.length + exclude.length;\n const atMax = maxItems !== undefined && totalSelected >= maxItems;\n\n // Persist widgets seen for any value across loadOptions cycles. Async\n // results get replaced with each query, so without this cache an icon\n // shown when picking a value would disappear on the next refetch.\n const widgetCacheRef = useRef<Map<string, WidgetsByKind>>(new Map());\n for (const opt of options ?? []) {\n if (opt.widgets) widgetCacheRef.current.set(opt.value, opt.widgets);\n }\n for (const opt of asyncResults ?? []) {\n if (opt.widgets) widgetCacheRef.current.set(opt.value, opt.widgets);\n }\n\n // Track chip ordering locally so flipping include ↔ exclude keeps the chip\n // in its original visual position even though the field-handle data model\n // is two separate arrays. Mutated only in the action handlers below;\n // anything that arrives via an external `onChange` (e.g. form reset) is\n // appended at the end by the render-time reconciliation in `chips`.\n const [chipOrder, setChipOrder] = useState<string[]>(() => [...include, ...exclude]);\n\n const anchor = useComboboxAnchor();\n\n const filteredSuggestions = useMemo<PayloadOption[]>(() => {\n const source = isAsync ? (asyncResults ?? []) : (options ?? []);\n return isAsync ? source : filterOptions(source, effectiveQuery);\n }, [isAsync, asyncResults, options, effectiveQuery]);\n\n const showCreateRow =\n allowCreate &&\n effectiveQuery.length > 0 &&\n !includeSet.has(effectiveQuery) &&\n !excludeSet.has(effectiveQuery) &&\n !filteredSuggestions.some((o) => o.value.toLowerCase() === effectiveQuery.toLowerCase());\n\n const resolveLabel = (v: string): string => {\n const fromStatic = options?.find((o) => o.value === v)?.label;\n if (fromStatic) return fromStatic;\n const fromAsync = asyncResults?.find((o) => o.value === v)?.label;\n return fromAsync ?? v;\n };\n\n const resolveWidgets = (v: string): WidgetsByKind | undefined =>\n widgetCacheRef.current.get(v) ??\n options?.find((o) => o.value === v)?.widgets ??\n asyncResults?.find((o) => o.value === v)?.widgets;\n\n type Chip = {\n value: string;\n label: string;\n mode: IncludeExcludeMode;\n widgets?: WidgetsByKind;\n };\n\n // Order chips by the locally tracked `chipOrder`, with any not-yet-tracked\n // values appended at the end. Action handlers (`addAs`, `removeChip`) keep\n // `chipOrder` in sync; this append branch only kicks in for values that\n // arrived via an external `onChange` (e.g. a form reset).\n const chips = useMemo<Chip[]>(() => {\n const known = new Set(chipOrder);\n const orderedValues = chipOrder.filter((v) => includeSet.has(v) || excludeSet.has(v));\n for (const v of include)\n if (!known.has(v)) {\n orderedValues.push(v);\n known.add(v);\n }\n for (const v of exclude)\n if (!known.has(v)) {\n orderedValues.push(v);\n known.add(v);\n }\n return orderedValues.map((v) => ({\n value: v,\n label: resolveLabel(v),\n mode: includeSet.has(v) ? \"include\" : \"exclude\",\n widgets: resolveWidgets(v),\n }));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [chipOrder, includeSet, excludeSet, options, asyncResults]);\n\n const items = useMemo<SelectableItem[]>(() => {\n const list: SelectableItem[] = [];\n if (showCreateRow) {\n list.push({\n value: effectiveQuery,\n label: `Add \"${effectiveQuery}\"`,\n __create: true,\n });\n }\n for (const o of filteredSuggestions) list.push(o);\n return list;\n }, [showCreateRow, effectiveQuery, filteredSuggestions]);\n\n const trackInsertion = (value: string) => {\n setChipOrder((prev) => (prev.includes(value) ? prev : [...prev, value]));\n };\n const trackRemoval = (value: string) => {\n setChipOrder((prev) => prev.filter((v) => v !== value));\n };\n\n const addAs = (value: string, mode: IncludeExcludeMode) => {\n if (!value) return;\n if (atMax && !includeSet.has(value) && !excludeSet.has(value)) return;\n if (mode === \"include\") {\n if (includeSet.has(value)) return;\n onChange({\n include: [...include, value],\n exclude: exclude.filter((v) => v !== value),\n });\n } else {\n if (excludeSet.has(value)) return;\n onChange({\n include: include.filter((v) => v !== value),\n exclude: [...exclude, value],\n });\n }\n trackInsertion(value);\n };\n\n // Flipping intentionally does NOT touch chipOrder — keeping the slot keeps\n // the chip in place visually as it changes mode.\n const flip = (value: string, currentMode: IncludeExcludeMode) => {\n if (currentMode === \"include\") {\n onChange({\n include: include.filter((v) => v !== value),\n exclude: [...exclude, value],\n });\n } else {\n onChange({\n include: [...include, value],\n exclude: exclude.filter((v) => v !== value),\n });\n }\n };\n\n const removeChip = (value: string, mode: IncludeExcludeMode) => {\n if (mode === \"include\") onChange({ include: include.filter((v) => v !== value), exclude });\n else onChange({ include, exclude: exclude.filter((v) => v !== value) });\n trackRemoval(value);\n };\n\n const clearInput = () => {\n setInput(\"\");\n if (isAsync) debouncedSetQuery(\"\");\n };\n\n return (\n <Combobox<SelectableItem, true>\n multiple\n items={items}\n filter={null}\n open={open}\n onOpenChange={setOpen}\n // We only use base-ui's value plumbing to capture pick events from list\n // rows; chips are rendered manually so we keep the include/exclude\n // grouping. Returning an empty array keeps base-ui from drawing chips.\n value={[] as SelectableItem[]}\n onValueChange={(next) => {\n if (!Array.isArray(next) || next.length === 0) return;\n const last = next[next.length - 1] as SelectableItem;\n const mode: IncludeExcludeMode = last.__mode ?? \"include\";\n addAs(last.value, mode);\n clearInput();\n }}\n isItemEqualToValue={(a, b) => (a as SelectableItem).value === (b as SelectableItem).value}\n itemToStringLabel={(item) => (item as SelectableItem).label}\n itemToStringValue={(item) => (item as SelectableItem).value}\n inputValue={input}\n onInputValueChange={(v) => {\n setInput(v);\n if (isAsync) debouncedSetQuery(v);\n }}\n disabled={disabled}\n >\n <ComboboxChips aria-invalid={ariaInvalid || undefined} ref={anchor}>\n {chips.map((chip) => {\n const isInc = chip.mode === \"include\";\n return (\n <span\n key={chip.value}\n className={cn(isInc ? includeChipClass : excludeChipClass)}\n data-mode={chip.mode}\n >\n <button\n type=\"button\"\n tabIndex={-1}\n onClick={(e) => {\n e.stopPropagation();\n flip(chip.value, chip.mode);\n }}\n aria-label={isInc ? \"Switch to exclude\" : \"Switch to include\"}\n title={isInc ? \"Switch to exclude\" : \"Switch to include\"}\n className={cn(isInc ? includeSignClass : excludeSignClass)}\n >\n {isInc ? <IconPlus width={10} height={10} /> : <IconMinus width={10} height={10} />}\n </button>\n <span className=\"pz:flex pz:items-center pz:gap-1 pz:px-1.5\">\n {chip.widgets && <WidgetStrip widgets={chip.widgets} size={14} />}\n {chip.label}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n tabIndex={-1}\n onClick={(e) => {\n e.stopPropagation();\n removeChip(chip.value, chip.mode);\n }}\n aria-label={`Remove ${chip.label}`}\n className={cn(\n \"pz:opacity-50 pz:hover:opacity-100\",\n isInc ? undefined : \"pz:text-destructive pz:hover:bg-destructive/15\",\n )}\n >\n <XIcon className=\"pz:pointer-events-none\" />\n </Button>\n </span>\n );\n })}\n <ComboboxChipsInput\n placeholder={chips.length === 0 ? placeholder : undefined}\n readOnly={disableSearch}\n aria-invalid={ariaInvalid || undefined}\n onKeyDown={(e) => {\n if (e.key === \"Backspace\" && input.length === 0) {\n if (chips.length === 0) return;\n e.preventDefault();\n const last = chips[chips.length - 1];\n removeChip(last.value, last.mode);\n return;\n }\n if (e.key !== \"Enter\" || e.nativeEvent.isComposing) return;\n if (effectiveQuery.length === 0) return;\n // Prevent the Enter from bubbling up and submitting an enclosing form.\n e.preventDefault();\n e.stopPropagation();\n if (atMax) return;\n const exact = filteredSuggestions.find(\n (o) => o.value.toLowerCase() === effectiveQuery.toLowerCase(),\n );\n const toAdd = exact?.value ?? (allowCreate ? effectiveQuery : null);\n if (!toAdd) return;\n addAs(toAdd, \"include\");\n clearInput();\n }}\n />\n <ComboboxTrigger className=\"pz:ml-auto pz:bg-transparent pz:hover:bg-transparent\" />\n </ComboboxChips>\n\n <ComboboxContent anchor={anchor}>\n {loading && items.length > 0 && (\n // Refetch: prior items stay visible, dimmed, with a small corner\n // spinner so the layout doesn't jump while the new page loads.\n <span\n aria-label=\"Loading\"\n className=\"pz:absolute pz:right-2 pz:top-2 pz:z-10 pz:inline-block pz:h-3 pz:w-3 pz:animate-spin pz:rounded-full pz:border-2 pz:border-muted-foreground/30 pz:border-t-muted-foreground\"\n />\n )}\n {loading && items.length === 0 && (\n // Initial load: nothing to dim, so render a visible spinner in the\n // popover body to confirm options are being fetched.\n <div\n role=\"status\"\n aria-label=\"Loading\"\n className=\"pz:flex pz:items-center pz:justify-center pz:py-6\"\n >\n <span className=\"pz:inline-block pz:h-5 pz:w-5 pz:animate-spin pz:rounded-full pz:border-2 pz:border-muted-foreground/30 pz:border-t-muted-foreground\" />\n </div>\n )}\n <ComboboxList\n className={cn(\n loading &&\n items.length > 0 &&\n \"pz:opacity-50 pz:transition-opacity pz:pointer-events-none\",\n )}\n >\n {atMax && (\n <div className=\"pz:px-2 pz:py-1.5 pz:text-xs pz:text-muted-foreground\">\n Maximum {maxItems} item{maxItems === 1 ? \"\" : \"s\"} reached.\n </div>\n )}\n\n {items.map((item) => {\n const fieldType = !item.__create ? item.widgets?.field_type : undefined;\n const inIncluded = includeSet.has(item.value);\n const inExcluded = excludeSet.has(item.value);\n const includeDisabled = inIncluded || (atMax && !inExcluded);\n const excludeDisabled = inExcluded || (atMax && !inIncluded);\n return (\n <ComboboxItem\n key={`${item.__create ? \"__create__:\" : \"\"}${item.value}`}\n value={{ ...item, __mode: \"include\" } as SelectableItem}\n className={cn(\"pz:pr-1\", item.__create ? \"pz:italic\" : undefined)}\n disabled={includeDisabled && excludeDisabled && !item.__create}\n >\n {!item.__create && item.widgets && (\n <WidgetStrip\n widgets={\n // Field-type renders trailing; everything else leading.\n Object.fromEntries(\n Object.entries(item.widgets).filter(([k]) => k !== \"field_type\"),\n )\n }\n size={14}\n />\n )}\n <span className=\"pz:flex-1 pz:truncate\">{item.label}</span>\n {fieldType && <FieldTypeBadge type={fieldType.type} format={fieldType.format} />}\n <span className=\"pz:flex pz:items-center pz:pl-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n tabIndex={-1}\n disabled={excludeDisabled}\n onClick={(e) => {\n e.stopPropagation();\n e.preventDefault();\n if (excludeDisabled) return;\n addAs(item.value, \"exclude\");\n clearInput();\n }}\n aria-label={`Exclude ${item.label}`}\n title=\"Exclude\"\n className=\"pz:text-muted-foreground\"\n >\n <IconMinus width={10} height={10} />\n </Button>\n </span>\n </ComboboxItem>\n );\n })}\n\n {!loading && items.length === 0 && (\n <ComboboxEmpty>\n {emptyLabel ??\n (allowCreate\n ? options || loadOptions\n ? \"No matches\"\n : \"Type to add\"\n : \"No options\")}\n </ComboboxEmpty>\n )}\n </ComboboxList>\n </ComboboxContent>\n </Combobox>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"include-exclude-combobox.mjs","names":["XIcon"],"sources":["../../../../src/components/internal/combobox/include-exclude-combobox.tsx"],"sourcesContent":["import type { WidgetsByKind } from \"@pipe0/base\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useDebouncedFn } from \"../../../hooks/use-debounced-fn.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { FieldTypeBadge } from \"../../../widgets/field-type-badge.js\";\nimport { WidgetStrip } from \"../../../widgets/widget-strip.js\";\nimport { Button } from \"../../ui/button.js\";\nimport {\n Combobox,\n ComboboxChips,\n ComboboxChipsInput,\n ComboboxContent,\n ComboboxEmpty,\n ComboboxItem,\n ComboboxList,\n ComboboxTrigger,\n useComboboxAnchor,\n} from \"../../ui/combobox.js\";\nimport { IconMinus, IconPlus, X as XIcon } from \"../icons.js\";\nimport { filterOptions, type PayloadOption } from \"../multi-select-popover-trigger.js\";\n\nexport type IncludeExcludeMode = \"include\" | \"exclude\";\n\nexport interface IncludeExcludeValue {\n include: string[];\n exclude: string[];\n}\n\nexport interface IncludeExcludeComboboxProps {\n include: string[];\n exclude: string[];\n /**\n * Atomic update for the entire `{ include, exclude }` value. We avoid\n * splitting include/exclude into separate add/remove callbacks because\n * flipping a chip needs to mutate both sides in one shot — otherwise the\n * stale closure in the field-handle wiring drops one of the updates.\n */\n onChange: (next: IncludeExcludeValue) => void;\n options?: PayloadOption[];\n loadOptions?: (query: string) => Promise<PayloadOption[]>;\n allowCreate?: boolean;\n disableSearch?: boolean;\n maxItems?: number;\n ariaInvalid?: boolean;\n disabled?: boolean;\n debounceMs?: number;\n emptyLabel?: string;\n placeholder?: string;\n}\n\ntype SelectableItem = PayloadOption & {\n __mode?: IncludeExcludeMode;\n __create?: boolean;\n};\n\nconst includeChipClass =\n \"pz:flex pz:h-[calc(--spacing(5.25))] pz:w-fit pz:items-center pz:gap-0 pz:rounded-sm pz:border pz:border-border pz:bg-muted pz:pl-0 pz:pr-0 pz:text-xs pz:font-medium pz:whitespace-nowrap pz:text-foreground pz:overflow-hidden\";\n\nconst excludeChipClass =\n \"pz:flex pz:h-[calc(--spacing(5.25))] pz:w-fit pz:items-center pz:gap-0 pz:rounded-sm pz:border pz:border-destructive/30 pz:bg-destructive/10 pz:pl-0 pz:pr-0 pz:text-xs pz:font-medium pz:whitespace-nowrap pz:text-destructive pz:overflow-hidden\";\n\nconst includeSignClass =\n \"pz:flex pz:h-full pz:w-5 pz:cursor-pointer pz:items-center pz:justify-center pz:border-r pz:border-border pz:bg-foreground/[0.04] pz:text-muted-foreground pz:hover:bg-foreground/[0.08] pz:hover:text-foreground\";\n\nconst excludeSignClass =\n \"pz:flex pz:h-full pz:w-5 pz:cursor-pointer pz:items-center pz:justify-center pz:border-r pz:border-destructive/30 pz:bg-destructive/[0.06] pz:text-destructive/80 pz:hover:bg-destructive/15 pz:hover:text-destructive\";\n\n/**\n * Unified include/exclude combobox.\n *\n * Renders a single chip input where each chip carries an inline `+` / `−`\n * sign toggle that flips include ↔ exclude in place. Free-text-create inputs\n * (`allowCreate=true`) show a \"Type to see suggestions\" hint until the user\n * types; select-style inputs show their options on focus. Pure-select mode\n * (`disableSearch=true`) shows the chevron trigger and renders the input\n * read-only.\n */\nexport function IncludeExcludeCombobox({\n include,\n exclude,\n onChange,\n options,\n loadOptions,\n allowCreate = false,\n disableSearch = false,\n maxItems,\n ariaInvalid,\n disabled,\n debounceMs = 300,\n emptyLabel,\n placeholder,\n}: IncludeExcludeComboboxProps) {\n const [input, setInput] = useState(\"\");\n const [debouncedQuery, setDebouncedQuery] = useState(\"\");\n const [asyncResults, setAsyncResults] = useState<PayloadOption[] | null>(null);\n const [loading, setLoading] = useState(false);\n const [open, setOpen] = useState(false);\n const cacheRef = useRef<Map<string, PayloadOption[]>>(new Map());\n const reqIdRef = useRef(0);\n\n const isAsync = typeof loadOptions === \"function\";\n\n const debouncedSetQuery = useDebouncedFn((q: string) => setDebouncedQuery(q), debounceMs);\n\n const effectiveQuery = input.trim();\n\n useEffect(() => {\n if (!isAsync || !loadOptions) return;\n if (!open && debouncedQuery === \"\") return;\n const q = debouncedQuery.trim();\n const cached = cacheRef.current.get(q);\n if (cached) {\n setAsyncResults(cached);\n setLoading(false);\n return;\n }\n const reqId = ++reqIdRef.current;\n setLoading(true);\n loadOptions(q)\n .then((opts) => {\n if (reqId !== reqIdRef.current) return;\n const kept = opts.filter((o) => !!o.value);\n cacheRef.current.set(q, kept);\n setAsyncResults(kept);\n setLoading(false);\n })\n .catch(() => {\n if (reqId !== reqIdRef.current) return;\n setAsyncResults([]);\n setLoading(false);\n });\n }, [debouncedQuery, isAsync, loadOptions, open]);\n\n const includeSet = useMemo(() => new Set(include), [include]);\n const excludeSet = useMemo(() => new Set(exclude), [exclude]);\n const totalSelected = include.length + exclude.length;\n const atMax = maxItems !== undefined && totalSelected >= maxItems;\n\n // Persist widgets seen for any value across loadOptions cycles. Async\n // results get replaced with each query, so without this cache an icon\n // shown when picking a value would disappear on the next refetch.\n const widgetCacheRef = useRef<Map<string, WidgetsByKind>>(new Map());\n for (const opt of options ?? []) {\n if (opt.widgets) widgetCacheRef.current.set(opt.value, opt.widgets);\n }\n for (const opt of asyncResults ?? []) {\n if (opt.widgets) widgetCacheRef.current.set(opt.value, opt.widgets);\n }\n\n // Track chip ordering locally so flipping include ↔ exclude keeps the chip\n // in its original visual position even though the field-handle data model\n // is two separate arrays. Mutated only in the action handlers below;\n // anything that arrives via an external `onChange` (e.g. form reset) is\n // appended at the end by the render-time reconciliation in `chips`.\n const [chipOrder, setChipOrder] = useState<string[]>(() => [...include, ...exclude]);\n\n const anchor = useComboboxAnchor();\n\n const filteredSuggestions = useMemo<PayloadOption[]>(() => {\n const source = isAsync ? (asyncResults ?? []) : (options ?? []);\n return isAsync ? source : filterOptions(source, effectiveQuery);\n }, [isAsync, asyncResults, options, effectiveQuery]);\n\n const showCreateRow =\n allowCreate &&\n effectiveQuery.length > 0 &&\n !includeSet.has(effectiveQuery) &&\n !excludeSet.has(effectiveQuery) &&\n !filteredSuggestions.some((o) => o.value.toLowerCase() === effectiveQuery.toLowerCase());\n\n const resolveLabel = (v: string): string => {\n const fromStatic = options?.find((o) => o.value === v)?.label;\n if (fromStatic) return fromStatic;\n const fromAsync = asyncResults?.find((o) => o.value === v)?.label;\n return fromAsync ?? v;\n };\n\n const resolveWidgets = (v: string): WidgetsByKind | undefined =>\n widgetCacheRef.current.get(v) ??\n options?.find((o) => o.value === v)?.widgets ??\n asyncResults?.find((o) => o.value === v)?.widgets;\n\n type Chip = {\n value: string;\n label: string;\n mode: IncludeExcludeMode;\n widgets?: WidgetsByKind;\n };\n\n // Order chips by the locally tracked `chipOrder`, with any not-yet-tracked\n // values appended at the end. Action handlers (`addAs`, `removeChip`) keep\n // `chipOrder` in sync; this append branch only kicks in for values that\n // arrived via an external `onChange` (e.g. a form reset).\n const chips = useMemo<Chip[]>(() => {\n const known = new Set(chipOrder);\n const orderedValues = chipOrder.filter((v) => includeSet.has(v) || excludeSet.has(v));\n for (const v of include)\n if (!known.has(v)) {\n orderedValues.push(v);\n known.add(v);\n }\n for (const v of exclude)\n if (!known.has(v)) {\n orderedValues.push(v);\n known.add(v);\n }\n return orderedValues.map((v) => ({\n value: v,\n label: resolveLabel(v),\n mode: includeSet.has(v) ? \"include\" : \"exclude\",\n widgets: resolveWidgets(v),\n }));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [chipOrder, includeSet, excludeSet, options, asyncResults]);\n\n const items = useMemo<SelectableItem[]>(() => {\n const list: SelectableItem[] = [];\n if (showCreateRow) {\n list.push({\n value: effectiveQuery,\n label: `Add \"${effectiveQuery}\"`,\n __create: true,\n });\n }\n for (const o of filteredSuggestions) list.push(o);\n return list;\n }, [showCreateRow, effectiveQuery, filteredSuggestions]);\n\n const trackInsertion = (value: string) => {\n setChipOrder((prev) => (prev.includes(value) ? prev : [...prev, value]));\n };\n const trackRemoval = (value: string) => {\n setChipOrder((prev) => prev.filter((v) => v !== value));\n };\n\n const addAs = (value: string, mode: IncludeExcludeMode) => {\n if (!value) return;\n if (atMax && !includeSet.has(value) && !excludeSet.has(value)) return;\n if (mode === \"include\") {\n if (includeSet.has(value)) return;\n onChange({\n include: [...include, value],\n exclude: exclude.filter((v) => v !== value),\n });\n } else {\n if (excludeSet.has(value)) return;\n onChange({\n include: include.filter((v) => v !== value),\n exclude: [...exclude, value],\n });\n }\n trackInsertion(value);\n };\n\n // Flipping intentionally does NOT touch chipOrder — keeping the slot keeps\n // the chip in place visually as it changes mode.\n const flip = (value: string, currentMode: IncludeExcludeMode) => {\n if (currentMode === \"include\") {\n onChange({\n include: include.filter((v) => v !== value),\n exclude: [...exclude, value],\n });\n } else {\n onChange({\n include: [...include, value],\n exclude: exclude.filter((v) => v !== value),\n });\n }\n };\n\n const removeChip = (value: string, mode: IncludeExcludeMode) => {\n if (mode === \"include\") onChange({ include: include.filter((v) => v !== value), exclude });\n else onChange({ include, exclude: exclude.filter((v) => v !== value) });\n trackRemoval(value);\n };\n\n const clearInput = () => {\n setInput(\"\");\n if (isAsync) debouncedSetQuery(\"\");\n };\n\n return (\n <Combobox<SelectableItem, true>\n multiple\n items={items}\n filter={null}\n open={open}\n onOpenChange={setOpen}\n // We only use base-ui's value plumbing to capture pick events from list\n // rows; chips are rendered manually so we keep the include/exclude\n // grouping. Returning an empty array keeps base-ui from drawing chips.\n value={[] as SelectableItem[]}\n onValueChange={(next) => {\n if (!Array.isArray(next) || next.length === 0) return;\n const last = next[next.length - 1] as SelectableItem;\n const mode: IncludeExcludeMode = last.__mode ?? \"include\";\n addAs(last.value, mode);\n clearInput();\n }}\n isItemEqualToValue={(a, b) => (a as SelectableItem).value === (b as SelectableItem).value}\n itemToStringLabel={(item) => (item as SelectableItem).label}\n itemToStringValue={(item) => (item as SelectableItem).value}\n inputValue={input}\n onInputValueChange={(v) => {\n setInput(v);\n if (isAsync) debouncedSetQuery(v);\n }}\n disabled={disabled}\n >\n <ComboboxChips aria-invalid={ariaInvalid || undefined} ref={anchor}>\n {chips.map((chip) => {\n const isInc = chip.mode === \"include\";\n return (\n <span\n key={chip.value}\n className={cn(isInc ? includeChipClass : excludeChipClass)}\n data-mode={chip.mode}\n >\n <button\n type=\"button\"\n tabIndex={-1}\n onClick={(e) => {\n e.stopPropagation();\n flip(chip.value, chip.mode);\n }}\n aria-label={isInc ? \"Switch to exclude\" : \"Switch to include\"}\n title={isInc ? \"Switch to exclude\" : \"Switch to include\"}\n className={cn(isInc ? includeSignClass : excludeSignClass)}\n >\n {isInc ? <IconPlus width={10} height={10} /> : <IconMinus width={10} height={10} />}\n </button>\n <span className=\"pz:flex pz:items-center pz:gap-1 pz:px-1.5\">\n {chip.widgets && <WidgetStrip widgets={chip.widgets} size={14} />}\n {chip.label}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n tabIndex={-1}\n onClick={(e) => {\n e.stopPropagation();\n removeChip(chip.value, chip.mode);\n }}\n aria-label={`Remove ${chip.label}`}\n className={cn(\n \"pz:opacity-50 pz:hover:opacity-100\",\n isInc ? undefined : \"pz:text-destructive pz:hover:bg-destructive/15\",\n )}\n >\n <XIcon className=\"pz:pointer-events-none\" />\n </Button>\n </span>\n );\n })}\n <ComboboxChipsInput\n placeholder={chips.length === 0 ? placeholder : undefined}\n readOnly={disableSearch}\n aria-invalid={ariaInvalid || undefined}\n onKeyDown={(e) => {\n if (e.key === \"Backspace\" && input.length === 0) {\n if (chips.length === 0) return;\n e.preventDefault();\n const last = chips[chips.length - 1];\n removeChip(last.value, last.mode);\n return;\n }\n if (e.key !== \"Enter\" || e.nativeEvent.isComposing) return;\n if (effectiveQuery.length === 0) return;\n // Prevent the Enter from bubbling up and submitting an enclosing form.\n e.preventDefault();\n e.stopPropagation();\n if (atMax) return;\n const exact = filteredSuggestions.find(\n (o) => o.value.toLowerCase() === effectiveQuery.toLowerCase(),\n );\n const toAdd = exact?.value ?? (allowCreate ? effectiveQuery : null);\n if (!toAdd) return;\n addAs(toAdd, \"include\");\n clearInput();\n }}\n />\n <ComboboxTrigger className=\"pz:ml-auto pz:bg-transparent pz:hover:bg-transparent\" />\n </ComboboxChips>\n\n <ComboboxContent anchor={anchor}>\n {loading && items.length > 0 && (\n // Refetch: prior items stay visible, dimmed, with a small corner\n // spinner so the layout doesn't jump while the new page loads.\n <span\n aria-label=\"Loading\"\n className=\"pz:absolute pz:right-2 pz:top-2 pz:z-10 pz:inline-block pz:h-3 pz:w-3 pz:animate-spin pz:rounded-full pz:border-2 pz:border-muted-foreground/30 pz:border-t-muted-foreground\"\n />\n )}\n {loading && items.length === 0 && (\n // Initial load: nothing to dim, so render a visible spinner in the\n // popover body to confirm options are being fetched.\n <div\n role=\"status\"\n aria-label=\"Loading\"\n className=\"pz:flex pz:items-center pz:justify-center pz:py-6\"\n >\n <span className=\"pz:inline-block pz:h-5 pz:w-5 pz:animate-spin pz:rounded-full pz:border-2 pz:border-muted-foreground/30 pz:border-t-muted-foreground\" />\n </div>\n )}\n <ComboboxList\n className={cn(\n loading &&\n items.length > 0 &&\n \"pz:opacity-50 pz:transition-opacity pz:pointer-events-none\",\n )}\n >\n {atMax && (\n <div className=\"pz:px-2 pz:py-1.5 pz:text-xs pz:text-muted-foreground\">\n Maximum {maxItems} item{maxItems === 1 ? \"\" : \"s\"} reached.\n </div>\n )}\n\n {items.map((item) => {\n const fieldType = !item.__create ? item.widgets?.field_type : undefined;\n const inIncluded = includeSet.has(item.value);\n const inExcluded = excludeSet.has(item.value);\n const includeDisabled = inIncluded || (atMax && !inExcluded);\n const excludeDisabled = inExcluded || (atMax && !inIncluded);\n return (\n <ComboboxItem\n key={`${item.__create ? \"__create__:\" : \"\"}${item.value}`}\n value={{ ...item, __mode: \"include\" } as SelectableItem}\n className={cn(\"pz:pr-1\", item.__create ? \"pz:italic\" : undefined)}\n disabled={includeDisabled && excludeDisabled && !item.__create}\n >\n {!item.__create && item.widgets && (\n <WidgetStrip\n widgets={\n // Field-type renders trailing; everything else leading.\n Object.fromEntries(\n Object.entries(item.widgets).filter(([k]) => k !== \"field_type\"),\n )\n }\n size={14}\n />\n )}\n <span className=\"pz:flex-1 pz:truncate\">{item.label}</span>\n {fieldType && <FieldTypeBadge type={fieldType.type} format={fieldType.format} />}\n <span className=\"pz:flex pz:items-center pz:pl-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n tabIndex={-1}\n disabled={excludeDisabled}\n onClick={(e) => {\n e.stopPropagation();\n e.preventDefault();\n if (excludeDisabled) return;\n addAs(item.value, \"exclude\");\n clearInput();\n }}\n aria-label={`Exclude ${item.label}`}\n title=\"Exclude\"\n className=\"pz:text-muted-foreground\"\n >\n <IconMinus width={10} height={10} />\n </Button>\n </span>\n </ComboboxItem>\n );\n })}\n\n {!loading && items.length === 0 && (\n <ComboboxEmpty>\n {emptyLabel ??\n (allowCreate\n ? options || loadOptions\n ? \"No matches\"\n : \"Type to add\"\n : \"No options\")}\n </ComboboxEmpty>\n )}\n </ComboboxList>\n </ComboboxContent>\n </Combobox>\n );\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { cn } from "../../../lib/utils.mjs";
2
2
  import { Button } from "../../ui/button.mjs";
3
- import { IconGripVertical } from "../icons.mjs";
3
+ import { IconGripVertical, X } from "../icons.mjs";
4
4
  import { useDebouncedFn } from "../../../hooks/use-debounced-fn.mjs";
5
5
  import { FieldTypeBadge } from "../../../widgets/field-type-badge.mjs";
6
6
  import { WidgetStrip } from "../../../widgets/widget-strip.mjs";
@@ -8,7 +8,6 @@ import { Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxCont
8
8
  import { filterOptions } from "../multi-select-popover-trigger.mjs";
9
9
  import { useEffect, useId, useMemo, useRef, useState } from "react";
10
10
  import { jsx, jsxs } from "react/jsx-runtime";
11
- import { XIcon } from "lucide-react";
12
11
  import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
13
12
  import { SortableContext, horizontalListSortingStrategy, useSortable } from "@dnd-kit/sortable";
14
13
  import useSWR from "swr";
@@ -55,7 +54,7 @@ function SortableChip({ id, onRemove, children }) {
55
54
  onRemove();
56
55
  },
57
56
  "aria-label": "Remove",
58
- children: /* @__PURE__ */ jsx(XIcon, { className: "pz:pointer-events-none" })
57
+ children: /* @__PURE__ */ jsx(X, { className: "pz:pointer-events-none" })
59
58
  })
60
59
  ]
61
60
  });
@@ -1 +1 @@
1
- {"version":3,"file":"suggest-combobox.mjs","names":[],"sources":["../../../../src/components/internal/combobox/suggest-combobox.tsx"],"sourcesContent":["import {\n closestCenter,\n DndContext,\n type DragEndEvent,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n} from \"@dnd-kit/core\";\nimport { horizontalListSortingStrategy, SortableContext, useSortable } from \"@dnd-kit/sortable\";\nimport type { WidgetsByKind } from \"@pipe0/base\";\nimport { XIcon } from \"lucide-react\";\nimport { type ReactNode, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport useSWR from \"swr\";\nimport { useDebouncedFn } from \"../../../hooks/use-debounced-fn.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { FieldTypeBadge } from \"../../../widgets/field-type-badge.js\";\nimport { WidgetStrip } from \"../../../widgets/widget-strip.js\";\nimport { Button } from \"../../ui/button.js\";\nimport {\n Combobox,\n ComboboxChip,\n ComboboxChips,\n ComboboxChipsInput,\n ComboboxContent,\n ComboboxEmpty,\n ComboboxItem,\n ComboboxList,\n ComboboxTrigger,\n useComboboxAnchor,\n} from \"../../ui/combobox.js\";\nimport { IconGripVertical } from \"../icons.js\";\nimport { filterOptions, type PayloadOption } from \"../multi-select-popover-trigger.js\";\n\n/** Internal item type — the base-ui Combobox handles `{value, label}` natively. */\ntype ComboboxOption = PayloadOption & { __create?: boolean };\n\n/** Split widgets so `field_type` can render on the trailing edge of dropdown\n * rows while every other widget stays in the leading strip. */\nfunction splitWidgets(widgets: WidgetsByKind): {\n leading: WidgetsByKind | undefined;\n} {\n const { field_type: _ignored, ...rest } = widgets;\n const hasLeading = Object.values(rest).some((v) => v != null);\n return { leading: hasLeading ? rest : undefined };\n}\n\n/** Reorderable chip matching `ComboboxChip` visually, with a grip handle. */\nfunction SortableChip({\n id,\n onRemove,\n children,\n}: {\n id: string;\n onRemove: () => void;\n children: ReactNode;\n}) {\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({\n id,\n });\n const style: React.CSSProperties = {\n transform: transform\n ? `translate3d(${Math.round(transform.x)}px, ${Math.round(transform.y)}px, 0)`\n : undefined,\n transition,\n opacity: isDragging ? 0.6 : 1,\n };\n return (\n <span\n ref={setNodeRef}\n style={style}\n className={cn(\n \"pz:flex pz:h-[calc(--spacing(5.25))] pz:w-fit pz:items-center pz:gap-1 pz:rounded-sm pz:bg-muted pz:pl-0.5 pz:pr-0 pz:text-xs pz:font-medium pz:whitespace-nowrap pz:text-foreground\",\n )}\n {...attributes}\n >\n <span\n role=\"button\"\n tabIndex={-1}\n aria-label=\"Drag to reorder\"\n className=\"pz:flex pz:items-center pz:justify-center pz:cursor-grab pz:text-muted-foreground pz:hover:text-foreground pz:touch-none\"\n {...listeners}\n >\n <IconGripVertical width={12} height={12} />\n </span>\n {children}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n className=\"pz:opacity-50 pz:hover:opacity-100\"\n onClick={(e) => {\n e.stopPropagation();\n onRemove();\n }}\n aria-label=\"Remove\"\n >\n <XIcon className=\"pz:pointer-events-none\" />\n </Button>\n </span>\n );\n}\n\nexport interface SuggestComboboxProps {\n value: string[];\n onChange: (value: string[]) => void;\n options?: PayloadOption[];\n loadOptions?: (query: string) => Promise<PayloadOption[]>;\n allowCreate?: boolean;\n disableSearch?: boolean;\n maxItems?: number;\n excludeValues?: string[];\n ariaInvalid?: boolean;\n disabled?: boolean;\n debounceMs?: number;\n labelFor?: (value: string) => string;\n /** Optional leading icon (e.g. provider logo) rendered inside chips and list items. */\n iconFor?: (value: string) => ReactNode;\n emptyLabel?: string;\n placeholder?: string;\n /** When true, chips become drag-sortable and reorder the `value` array. */\n reorderable?: boolean;\n /**\n * Single-select mode: the selection renders as a plain value (not a\n * removable chip), picking an option REPLACES it, and the popover closes on\n * pick — a searchable analog of a native select. Clicking the selected\n * option again clears it. `value` stays an array (length ≤ 1) so callers\n * keep one contract across both modes; `maxItems`/`reorderable` are ignored.\n */\n single?: boolean;\n}\n\n/**\n * Unified combobox built on `@base-ui/react`'s Combobox primitive.\n *\n * - Chips + inline search input via `ComboboxChips` / `ComboboxChipsInput`.\n * - Static (`options`) or async (`loadOptions`) item sources. External\n * filtering in both cases so match-sorter ranking / server relevance is\n * honored and base-ui's built-in filter doesn't double-filter.\n * - Free-text creation (`allowCreate`) renders an inline \"Add \\\"X\\\"\" item.\n * - `maxItems`, `excludeValues`, `disableSearch`, `ariaInvalid`, `disabled`.\n * - Debounced async loader with an in-memory cache keyed by query string.\n */\nexport function SuggestCombobox({\n value,\n onChange,\n options,\n loadOptions,\n allowCreate = false,\n disableSearch = false,\n maxItems,\n excludeValues = [],\n ariaInvalid,\n disabled,\n debounceMs = 300,\n labelFor,\n iconFor,\n emptyLabel,\n placeholder,\n reorderable = false,\n single = false,\n}: SuggestComboboxProps) {\n const [input, setInput] = useState(\"\");\n const [debouncedQuery, setDebouncedQuery] = useState(\"\");\n const [open, setOpen] = useState(false);\n\n const isAsync = typeof loadOptions === \"function\";\n\n const debouncedSetQuery = useDebouncedFn((q: string) => setDebouncedQuery(q), debounceMs);\n\n // Per-instance cache key prefix so two comboboxes with the same query but\n // different `loadOptions` functions don't share results.\n const instanceId = useId();\n\n // SWR handles dedupe + caching by-query + race-safety. We defer the\n // empty-query fetch until the user opens the dropdown — some backends\n // (e.g. Crustdata persondb) don't accept empty queries, and on-mount\n // fetches are wasted work if the field is never opened. `keepPreviousData`\n // preserves the prior result set during a refetch so the UI dims rather\n // than empties.\n const swrKey: readonly [string, string] | null =\n loadOptions && (open || debouncedQuery !== \"\") ? [instanceId, debouncedQuery] : null;\n const {\n data: asyncResults = null,\n isLoading: swrIsLoading,\n isValidating: swrIsValidating,\n } = useSWR<PayloadOption[], Error, readonly [string, string] | null>(\n swrKey,\n async ([, q]) => {\n // `swrKey` was `null` when `loadOptions` is undefined, so the fetcher\n // never runs without one — the runtime check is just to satisfy TS.\n if (!loadOptions) return [];\n const opts = await loadOptions(q);\n return opts.filter((o) => !!o.value);\n },\n { keepPreviousData: true },\n );\n const loading = swrIsLoading || swrIsValidating;\n\n const excludeSet = useMemo(() => new Set(excludeValues), [excludeValues]);\n const selectedSet = useMemo(() => new Set(value), [value]);\n\n const anchor = useComboboxAnchor();\n\n const filteredSuggestions = useMemo<PayloadOption[]>(() => {\n const source = isAsync ? (asyncResults ?? []) : (options ?? []);\n const ranked = isAsync ? source : filterOptions(source, input);\n return ranked.filter((o) => !excludeSet.has(o.value));\n }, [isAsync, asyncResults, options, input, excludeSet]);\n\n const trimmedInput = input.trim();\n const showCreateRow =\n allowCreate &&\n trimmedInput.length > 0 &&\n !selectedSet.has(trimmedInput) &&\n !excludeSet.has(trimmedInput) &&\n !filteredSuggestions.some((o) => o.value.toLowerCase() === trimmedInput.toLowerCase());\n\n const resolveLabel = (v: string): string => {\n if (labelFor) return labelFor(v);\n const fromStatic = options?.find((o) => o.value === v)?.label;\n if (fromStatic) return fromStatic;\n const fromAsync = asyncResults?.find((o) => o.value === v)?.label;\n return fromAsync ?? v;\n };\n\n // Persist widgets per value across loadOptions cycles. Async results are\n // replaced with each query, so without this cache an icon shown when\n // picking a value would disappear on the next refetch. Updates run in an\n // effect (not during render) so the cache stays consistent under strict\n // mode and concurrent rendering.\n const widgetCacheRef = useRef<Map<string, WidgetsByKind>>(new Map());\n useEffect(() => {\n for (const opt of options ?? []) {\n if (opt.widgets) widgetCacheRef.current.set(opt.value, opt.widgets);\n }\n for (const opt of asyncResults ?? []) {\n if (opt.widgets) widgetCacheRef.current.set(opt.value, opt.widgets);\n }\n }, [options, asyncResults]);\n\n const resolveWidgets = (v: string): WidgetsByKind | undefined =>\n options?.find((o) => o.value === v)?.widgets ??\n asyncResults?.find((o) => o.value === v)?.widgets ??\n widgetCacheRef.current.get(v);\n\n const valueObjects = useMemo<ComboboxOption[]>(\n () =>\n value.map((v) => ({\n value: v,\n label: resolveLabel(v),\n widgets: resolveWidgets(v),\n })),\n // resolveLabel/resolveWidgets close over options/asyncResults/labelFor\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [value, options, asyncResults, labelFor],\n );\n\n const items = useMemo<ComboboxOption[]>(() => {\n const list: ComboboxOption[] = [];\n if (showCreateRow) {\n list.push({\n value: trimmedInput,\n label: `Add \"${trimmedInput}\"`,\n __create: true,\n });\n }\n for (const o of filteredSuggestions) list.push(o);\n return list;\n }, [showCreateRow, trimmedInput, filteredSuggestions]);\n\n const atMax = maxItems !== undefined && value.length >= maxItems;\n\n const sortSensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor),\n );\n\n const handleDragEnd = (event: DragEndEvent) => {\n const { active, over } = event;\n if (!over || active.id === over.id) return;\n const from = value.indexOf(String(active.id));\n const to = value.indexOf(String(over.id));\n if (from === -1 || to === -1) return;\n const next = [...value];\n const [moved] = next.splice(from, 1);\n next.splice(to, 0, moved);\n onChange(next);\n };\n\n return (\n <Combobox<ComboboxOption, true>\n multiple\n items={items}\n filter={null}\n open={open}\n onOpenChange={setOpen}\n value={valueObjects}\n onValueChange={(next) => {\n if (!Array.isArray(next)) return;\n if (single) {\n // Replace-on-pick: the base-ui multiple model APPENDS the picked\n // item, so the last valid entry is the user's choice; toggling the\n // selected item off yields an empty array (= clear).\n const last = [...next]\n .reverse()\n .map((item) => (typeof item === \"string\" ? item : (item as ComboboxOption).value))\n .find((v) => v && !excludeSet.has(v));\n onChange(last ? [last] : []);\n setInput(\"\");\n if (isAsync) debouncedSetQuery(\"\");\n setOpen(false);\n return;\n }\n // Unique by value, rewrite __create items to clean objects.\n const seen = new Set<string>();\n const deduped: string[] = [];\n for (const item of next) {\n const v = typeof item === \"string\" ? item : (item as ComboboxOption).value;\n if (!v || seen.has(v)) continue;\n if (excludeSet.has(v)) continue;\n if (maxItems !== undefined && deduped.length >= maxItems) break;\n seen.add(v);\n deduped.push(v);\n }\n onChange(deduped);\n setInput(\"\");\n if (isAsync) debouncedSetQuery(\"\");\n }}\n isItemEqualToValue={(a, b) => (a as ComboboxOption).value === (b as ComboboxOption).value}\n itemToStringLabel={(item) => (item as ComboboxOption).label}\n itemToStringValue={(item) => (item as ComboboxOption).value}\n inputValue={input}\n onInputValueChange={(v) => {\n setInput(v);\n if (isAsync) debouncedSetQuery(v);\n }}\n disabled={disabled}\n >\n <ComboboxChips aria-invalid={ariaInvalid || undefined} ref={anchor}>\n {single ? (\n // Select-like value display: a plain label, hidden while the user\n // types so the input reads like a search box over the selection.\n valueObjects[0] &&\n input === \"\" && (\n <span className=\"pz:flex pz:min-w-0 pz:items-center pz:gap-1.5 pz:text-sm\">\n {iconFor?.(valueObjects[0].value)}\n {valueObjects[0].widgets && (\n <WidgetStrip widgets={valueObjects[0].widgets} size={14} />\n )}\n <span className=\"pz:truncate\">{valueObjects[0].label}</span>\n </span>\n )\n ) : reorderable ? (\n <DndContext\n sensors={sortSensors}\n collisionDetection={closestCenter}\n onDragEnd={handleDragEnd}\n >\n <SortableContext items={value} strategy={horizontalListSortingStrategy}>\n {valueObjects.map((v) => (\n <SortableChip\n key={v.value}\n id={v.value}\n onRemove={() => onChange(value.filter((x) => x !== v.value))}\n >\n {iconFor?.(v.value)}\n {v.widgets && <WidgetStrip widgets={v.widgets} size={14} />}\n {v.label}\n </SortableChip>\n ))}\n </SortableContext>\n </DndContext>\n ) : (\n valueObjects.map((v) => (\n <ComboboxChip key={v.value}>\n {iconFor?.(v.value)}\n {v.widgets && <WidgetStrip widgets={v.widgets} size={14} />}\n {v.label}\n </ComboboxChip>\n ))\n )}\n <ComboboxChipsInput\n placeholder={value.length === 0 ? placeholder : undefined}\n readOnly={disableSearch}\n aria-invalid={ariaInvalid || undefined}\n onKeyDown={(e) => {\n if (e.key !== \"Enter\" || e.nativeEvent.isComposing) return;\n if (trimmedInput.length === 0) return;\n // Prevent the Enter from bubbling up and submitting an enclosing form.\n e.preventDefault();\n e.stopPropagation();\n if (!single && atMax) return;\n // Prefer an exact match against the visible suggestions, otherwise\n // fall back to creating a new value when allowed.\n const exact = filteredSuggestions.find(\n (o) => o.value.toLowerCase() === trimmedInput.toLowerCase(),\n );\n const toAdd = exact?.value ?? (allowCreate ? trimmedInput : null);\n if (!toAdd) return;\n if (excludeSet.has(toAdd)) return;\n if (single) {\n onChange([toAdd]);\n setInput(\"\");\n if (isAsync) debouncedSetQuery(\"\");\n setOpen(false);\n return;\n }\n if (selectedSet.has(toAdd)) return;\n onChange([...value, toAdd]);\n setInput(\"\");\n if (isAsync) debouncedSetQuery(\"\");\n }}\n />\n <ComboboxTrigger className=\"pz:ml-auto pz:bg-transparent pz:hover:bg-transparent\" />\n </ComboboxChips>\n <ComboboxContent anchor={anchor}>\n {loading && items.length > 0 && (\n // Refetch: prior items stay visible, dimmed, with a small corner\n // spinner so the layout doesn't jump while the new page loads.\n <span\n aria-label=\"Loading\"\n className=\"pz:absolute pz:right-2 pz:top-2 pz:z-10 pz:inline-block pz:h-3 pz:w-3 pz:animate-spin pz:rounded-full pz:border-2 pz:border-muted-foreground/30 pz:border-t-muted-foreground\"\n />\n )}\n {loading && items.length === 0 && (\n // Initial load: nothing to dim, so render a visible spinner in the\n // popover body to confirm options are being fetched.\n <div\n role=\"status\"\n aria-label=\"Loading\"\n className=\"pz:flex pz:items-center pz:justify-center pz:py-6\"\n >\n <span className=\"pz:inline-block pz:h-5 pz:w-5 pz:animate-spin pz:rounded-full pz:border-2 pz:border-muted-foreground/30 pz:border-t-muted-foreground\" />\n </div>\n )}\n <ComboboxList\n className={cn(\n loading &&\n items.length > 0 &&\n \"pz:opacity-50 pz:transition-opacity pz:pointer-events-none\",\n )}\n >\n {!single && atMax && (\n <div className=\"pz:px-2 pz:py-1.5 pz:text-xs pz:text-muted-foreground\">\n Maximum {maxItems} item{maxItems === 1 ? \"\" : \"s\"} reached.\n </div>\n )}\n\n {items.map((item) => {\n // Field-type widget renders on the trailing edge of the row so\n // the field name reads first; everything else stays in the\n // leading widget strip.\n const fieldType = !item.__create ? item.widgets?.field_type : undefined;\n const leadingWidgets =\n !item.__create && item.widgets ? splitWidgets(item.widgets).leading : undefined;\n return (\n <ComboboxItem\n key={`${item.__create ? \"__create__:\" : \"\"}${item.value}`}\n value={item}\n className={item.__create ? \"pz:italic\" : undefined}\n disabled={!single && !selectedSet.has(item.value) && atMax && !item.__create}\n >\n {!item.__create && iconFor?.(item.value)}\n {leadingWidgets && <WidgetStrip widgets={leadingWidgets} size={14} />}\n <span className=\"pz:flex-1 pz:truncate\">{item.label}</span>\n {fieldType && <FieldTypeBadge type={fieldType.type} format={fieldType.format} />}\n </ComboboxItem>\n );\n })}\n\n {!loading && items.length === 0 && (\n <ComboboxEmpty>\n {emptyLabel ??\n (allowCreate\n ? options || loadOptions\n ? \"No matches\"\n : \"Type to add\"\n : \"No options\")}\n </ComboboxEmpty>\n )}\n </ComboboxList>\n </ComboboxContent>\n </Combobox>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"suggest-combobox.mjs","names":["XIcon"],"sources":["../../../../src/components/internal/combobox/suggest-combobox.tsx"],"sourcesContent":["import {\n closestCenter,\n DndContext,\n type DragEndEvent,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n} from \"@dnd-kit/core\";\nimport { horizontalListSortingStrategy, SortableContext, useSortable } from \"@dnd-kit/sortable\";\nimport type { WidgetsByKind } from \"@pipe0/base\";\nimport { type ReactNode, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport useSWR from \"swr\";\nimport { useDebouncedFn } from \"../../../hooks/use-debounced-fn.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { FieldTypeBadge } from \"../../../widgets/field-type-badge.js\";\nimport { WidgetStrip } from \"../../../widgets/widget-strip.js\";\nimport { Button } from \"../../ui/button.js\";\nimport {\n Combobox,\n ComboboxChip,\n ComboboxChips,\n ComboboxChipsInput,\n ComboboxContent,\n ComboboxEmpty,\n ComboboxItem,\n ComboboxList,\n ComboboxTrigger,\n useComboboxAnchor,\n} from \"../../ui/combobox.js\";\nimport { IconGripVertical, X as XIcon } from \"../icons.js\";\nimport { filterOptions, type PayloadOption } from \"../multi-select-popover-trigger.js\";\n\n/** Internal item type — the base-ui Combobox handles `{value, label}` natively. */\ntype ComboboxOption = PayloadOption & { __create?: boolean };\n\n/** Split widgets so `field_type` can render on the trailing edge of dropdown\n * rows while every other widget stays in the leading strip. */\nfunction splitWidgets(widgets: WidgetsByKind): {\n leading: WidgetsByKind | undefined;\n} {\n const { field_type: _ignored, ...rest } = widgets;\n const hasLeading = Object.values(rest).some((v) => v != null);\n return { leading: hasLeading ? rest : undefined };\n}\n\n/** Reorderable chip matching `ComboboxChip` visually, with a grip handle. */\nfunction SortableChip({\n id,\n onRemove,\n children,\n}: {\n id: string;\n onRemove: () => void;\n children: ReactNode;\n}) {\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({\n id,\n });\n const style: React.CSSProperties = {\n transform: transform\n ? `translate3d(${Math.round(transform.x)}px, ${Math.round(transform.y)}px, 0)`\n : undefined,\n transition,\n opacity: isDragging ? 0.6 : 1,\n };\n return (\n <span\n ref={setNodeRef}\n style={style}\n className={cn(\n \"pz:flex pz:h-[calc(--spacing(5.25))] pz:w-fit pz:items-center pz:gap-1 pz:rounded-sm pz:bg-muted pz:pl-0.5 pz:pr-0 pz:text-xs pz:font-medium pz:whitespace-nowrap pz:text-foreground\",\n )}\n {...attributes}\n >\n <span\n role=\"button\"\n tabIndex={-1}\n aria-label=\"Drag to reorder\"\n className=\"pz:flex pz:items-center pz:justify-center pz:cursor-grab pz:text-muted-foreground pz:hover:text-foreground pz:touch-none\"\n {...listeners}\n >\n <IconGripVertical width={12} height={12} />\n </span>\n {children}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n className=\"pz:opacity-50 pz:hover:opacity-100\"\n onClick={(e) => {\n e.stopPropagation();\n onRemove();\n }}\n aria-label=\"Remove\"\n >\n <XIcon className=\"pz:pointer-events-none\" />\n </Button>\n </span>\n );\n}\n\nexport interface SuggestComboboxProps {\n value: string[];\n onChange: (value: string[]) => void;\n options?: PayloadOption[];\n loadOptions?: (query: string) => Promise<PayloadOption[]>;\n allowCreate?: boolean;\n disableSearch?: boolean;\n maxItems?: number;\n excludeValues?: string[];\n ariaInvalid?: boolean;\n disabled?: boolean;\n debounceMs?: number;\n labelFor?: (value: string) => string;\n /** Optional leading icon (e.g. provider logo) rendered inside chips and list items. */\n iconFor?: (value: string) => ReactNode;\n emptyLabel?: string;\n placeholder?: string;\n /** When true, chips become drag-sortable and reorder the `value` array. */\n reorderable?: boolean;\n /**\n * Single-select mode: the selection renders as a plain value (not a\n * removable chip), picking an option REPLACES it, and the popover closes on\n * pick — a searchable analog of a native select. Clicking the selected\n * option again clears it. `value` stays an array (length ≤ 1) so callers\n * keep one contract across both modes; `maxItems`/`reorderable` are ignored.\n */\n single?: boolean;\n}\n\n/**\n * Unified combobox built on `@base-ui/react`'s Combobox primitive.\n *\n * - Chips + inline search input via `ComboboxChips` / `ComboboxChipsInput`.\n * - Static (`options`) or async (`loadOptions`) item sources. External\n * filtering in both cases so match-sorter ranking / server relevance is\n * honored and base-ui's built-in filter doesn't double-filter.\n * - Free-text creation (`allowCreate`) renders an inline \"Add \\\"X\\\"\" item.\n * - `maxItems`, `excludeValues`, `disableSearch`, `ariaInvalid`, `disabled`.\n * - Debounced async loader with an in-memory cache keyed by query string.\n */\nexport function SuggestCombobox({\n value,\n onChange,\n options,\n loadOptions,\n allowCreate = false,\n disableSearch = false,\n maxItems,\n excludeValues = [],\n ariaInvalid,\n disabled,\n debounceMs = 300,\n labelFor,\n iconFor,\n emptyLabel,\n placeholder,\n reorderable = false,\n single = false,\n}: SuggestComboboxProps) {\n const [input, setInput] = useState(\"\");\n const [debouncedQuery, setDebouncedQuery] = useState(\"\");\n const [open, setOpen] = useState(false);\n\n const isAsync = typeof loadOptions === \"function\";\n\n const debouncedSetQuery = useDebouncedFn((q: string) => setDebouncedQuery(q), debounceMs);\n\n // Per-instance cache key prefix so two comboboxes with the same query but\n // different `loadOptions` functions don't share results.\n const instanceId = useId();\n\n // SWR handles dedupe + caching by-query + race-safety. We defer the\n // empty-query fetch until the user opens the dropdown — some backends\n // (e.g. Crustdata persondb) don't accept empty queries, and on-mount\n // fetches are wasted work if the field is never opened. `keepPreviousData`\n // preserves the prior result set during a refetch so the UI dims rather\n // than empties.\n const swrKey: readonly [string, string] | null =\n loadOptions && (open || debouncedQuery !== \"\") ? [instanceId, debouncedQuery] : null;\n const {\n data: asyncResults = null,\n isLoading: swrIsLoading,\n isValidating: swrIsValidating,\n } = useSWR<PayloadOption[], Error, readonly [string, string] | null>(\n swrKey,\n async ([, q]) => {\n // `swrKey` was `null` when `loadOptions` is undefined, so the fetcher\n // never runs without one — the runtime check is just to satisfy TS.\n if (!loadOptions) return [];\n const opts = await loadOptions(q);\n return opts.filter((o) => !!o.value);\n },\n { keepPreviousData: true },\n );\n const loading = swrIsLoading || swrIsValidating;\n\n const excludeSet = useMemo(() => new Set(excludeValues), [excludeValues]);\n const selectedSet = useMemo(() => new Set(value), [value]);\n\n const anchor = useComboboxAnchor();\n\n const filteredSuggestions = useMemo<PayloadOption[]>(() => {\n const source = isAsync ? (asyncResults ?? []) : (options ?? []);\n const ranked = isAsync ? source : filterOptions(source, input);\n return ranked.filter((o) => !excludeSet.has(o.value));\n }, [isAsync, asyncResults, options, input, excludeSet]);\n\n const trimmedInput = input.trim();\n const showCreateRow =\n allowCreate &&\n trimmedInput.length > 0 &&\n !selectedSet.has(trimmedInput) &&\n !excludeSet.has(trimmedInput) &&\n !filteredSuggestions.some((o) => o.value.toLowerCase() === trimmedInput.toLowerCase());\n\n const resolveLabel = (v: string): string => {\n if (labelFor) return labelFor(v);\n const fromStatic = options?.find((o) => o.value === v)?.label;\n if (fromStatic) return fromStatic;\n const fromAsync = asyncResults?.find((o) => o.value === v)?.label;\n return fromAsync ?? v;\n };\n\n // Persist widgets per value across loadOptions cycles. Async results are\n // replaced with each query, so without this cache an icon shown when\n // picking a value would disappear on the next refetch. Updates run in an\n // effect (not during render) so the cache stays consistent under strict\n // mode and concurrent rendering.\n const widgetCacheRef = useRef<Map<string, WidgetsByKind>>(new Map());\n useEffect(() => {\n for (const opt of options ?? []) {\n if (opt.widgets) widgetCacheRef.current.set(opt.value, opt.widgets);\n }\n for (const opt of asyncResults ?? []) {\n if (opt.widgets) widgetCacheRef.current.set(opt.value, opt.widgets);\n }\n }, [options, asyncResults]);\n\n const resolveWidgets = (v: string): WidgetsByKind | undefined =>\n options?.find((o) => o.value === v)?.widgets ??\n asyncResults?.find((o) => o.value === v)?.widgets ??\n widgetCacheRef.current.get(v);\n\n const valueObjects = useMemo<ComboboxOption[]>(\n () =>\n value.map((v) => ({\n value: v,\n label: resolveLabel(v),\n widgets: resolveWidgets(v),\n })),\n // resolveLabel/resolveWidgets close over options/asyncResults/labelFor\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [value, options, asyncResults, labelFor],\n );\n\n const items = useMemo<ComboboxOption[]>(() => {\n const list: ComboboxOption[] = [];\n if (showCreateRow) {\n list.push({\n value: trimmedInput,\n label: `Add \"${trimmedInput}\"`,\n __create: true,\n });\n }\n for (const o of filteredSuggestions) list.push(o);\n return list;\n }, [showCreateRow, trimmedInput, filteredSuggestions]);\n\n const atMax = maxItems !== undefined && value.length >= maxItems;\n\n const sortSensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor),\n );\n\n const handleDragEnd = (event: DragEndEvent) => {\n const { active, over } = event;\n if (!over || active.id === over.id) return;\n const from = value.indexOf(String(active.id));\n const to = value.indexOf(String(over.id));\n if (from === -1 || to === -1) return;\n const next = [...value];\n const [moved] = next.splice(from, 1);\n next.splice(to, 0, moved);\n onChange(next);\n };\n\n return (\n <Combobox<ComboboxOption, true>\n multiple\n items={items}\n filter={null}\n open={open}\n onOpenChange={setOpen}\n value={valueObjects}\n onValueChange={(next) => {\n if (!Array.isArray(next)) return;\n if (single) {\n // Replace-on-pick: the base-ui multiple model APPENDS the picked\n // item, so the last valid entry is the user's choice; toggling the\n // selected item off yields an empty array (= clear).\n const last = [...next]\n .reverse()\n .map((item) => (typeof item === \"string\" ? item : (item as ComboboxOption).value))\n .find((v) => v && !excludeSet.has(v));\n onChange(last ? [last] : []);\n setInput(\"\");\n if (isAsync) debouncedSetQuery(\"\");\n setOpen(false);\n return;\n }\n // Unique by value, rewrite __create items to clean objects.\n const seen = new Set<string>();\n const deduped: string[] = [];\n for (const item of next) {\n const v = typeof item === \"string\" ? item : (item as ComboboxOption).value;\n if (!v || seen.has(v)) continue;\n if (excludeSet.has(v)) continue;\n if (maxItems !== undefined && deduped.length >= maxItems) break;\n seen.add(v);\n deduped.push(v);\n }\n onChange(deduped);\n setInput(\"\");\n if (isAsync) debouncedSetQuery(\"\");\n }}\n isItemEqualToValue={(a, b) => (a as ComboboxOption).value === (b as ComboboxOption).value}\n itemToStringLabel={(item) => (item as ComboboxOption).label}\n itemToStringValue={(item) => (item as ComboboxOption).value}\n inputValue={input}\n onInputValueChange={(v) => {\n setInput(v);\n if (isAsync) debouncedSetQuery(v);\n }}\n disabled={disabled}\n >\n <ComboboxChips aria-invalid={ariaInvalid || undefined} ref={anchor}>\n {single ? (\n // Select-like value display: a plain label, hidden while the user\n // types so the input reads like a search box over the selection.\n valueObjects[0] &&\n input === \"\" && (\n <span className=\"pz:flex pz:min-w-0 pz:items-center pz:gap-1.5 pz:text-sm\">\n {iconFor?.(valueObjects[0].value)}\n {valueObjects[0].widgets && (\n <WidgetStrip widgets={valueObjects[0].widgets} size={14} />\n )}\n <span className=\"pz:truncate\">{valueObjects[0].label}</span>\n </span>\n )\n ) : reorderable ? (\n <DndContext\n sensors={sortSensors}\n collisionDetection={closestCenter}\n onDragEnd={handleDragEnd}\n >\n <SortableContext items={value} strategy={horizontalListSortingStrategy}>\n {valueObjects.map((v) => (\n <SortableChip\n key={v.value}\n id={v.value}\n onRemove={() => onChange(value.filter((x) => x !== v.value))}\n >\n {iconFor?.(v.value)}\n {v.widgets && <WidgetStrip widgets={v.widgets} size={14} />}\n {v.label}\n </SortableChip>\n ))}\n </SortableContext>\n </DndContext>\n ) : (\n valueObjects.map((v) => (\n <ComboboxChip key={v.value}>\n {iconFor?.(v.value)}\n {v.widgets && <WidgetStrip widgets={v.widgets} size={14} />}\n {v.label}\n </ComboboxChip>\n ))\n )}\n <ComboboxChipsInput\n placeholder={value.length === 0 ? placeholder : undefined}\n readOnly={disableSearch}\n aria-invalid={ariaInvalid || undefined}\n onKeyDown={(e) => {\n if (e.key !== \"Enter\" || e.nativeEvent.isComposing) return;\n if (trimmedInput.length === 0) return;\n // Prevent the Enter from bubbling up and submitting an enclosing form.\n e.preventDefault();\n e.stopPropagation();\n if (!single && atMax) return;\n // Prefer an exact match against the visible suggestions, otherwise\n // fall back to creating a new value when allowed.\n const exact = filteredSuggestions.find(\n (o) => o.value.toLowerCase() === trimmedInput.toLowerCase(),\n );\n const toAdd = exact?.value ?? (allowCreate ? trimmedInput : null);\n if (!toAdd) return;\n if (excludeSet.has(toAdd)) return;\n if (single) {\n onChange([toAdd]);\n setInput(\"\");\n if (isAsync) debouncedSetQuery(\"\");\n setOpen(false);\n return;\n }\n if (selectedSet.has(toAdd)) return;\n onChange([...value, toAdd]);\n setInput(\"\");\n if (isAsync) debouncedSetQuery(\"\");\n }}\n />\n <ComboboxTrigger className=\"pz:ml-auto pz:bg-transparent pz:hover:bg-transparent\" />\n </ComboboxChips>\n <ComboboxContent anchor={anchor}>\n {loading && items.length > 0 && (\n // Refetch: prior items stay visible, dimmed, with a small corner\n // spinner so the layout doesn't jump while the new page loads.\n <span\n aria-label=\"Loading\"\n className=\"pz:absolute pz:right-2 pz:top-2 pz:z-10 pz:inline-block pz:h-3 pz:w-3 pz:animate-spin pz:rounded-full pz:border-2 pz:border-muted-foreground/30 pz:border-t-muted-foreground\"\n />\n )}\n {loading && items.length === 0 && (\n // Initial load: nothing to dim, so render a visible spinner in the\n // popover body to confirm options are being fetched.\n <div\n role=\"status\"\n aria-label=\"Loading\"\n className=\"pz:flex pz:items-center pz:justify-center pz:py-6\"\n >\n <span className=\"pz:inline-block pz:h-5 pz:w-5 pz:animate-spin pz:rounded-full pz:border-2 pz:border-muted-foreground/30 pz:border-t-muted-foreground\" />\n </div>\n )}\n <ComboboxList\n className={cn(\n loading &&\n items.length > 0 &&\n \"pz:opacity-50 pz:transition-opacity pz:pointer-events-none\",\n )}\n >\n {!single && atMax && (\n <div className=\"pz:px-2 pz:py-1.5 pz:text-xs pz:text-muted-foreground\">\n Maximum {maxItems} item{maxItems === 1 ? \"\" : \"s\"} reached.\n </div>\n )}\n\n {items.map((item) => {\n // Field-type widget renders on the trailing edge of the row so\n // the field name reads first; everything else stays in the\n // leading widget strip.\n const fieldType = !item.__create ? item.widgets?.field_type : undefined;\n const leadingWidgets =\n !item.__create && item.widgets ? splitWidgets(item.widgets).leading : undefined;\n return (\n <ComboboxItem\n key={`${item.__create ? \"__create__:\" : \"\"}${item.value}`}\n value={item}\n className={item.__create ? \"pz:italic\" : undefined}\n disabled={!single && !selectedSet.has(item.value) && atMax && !item.__create}\n >\n {!item.__create && iconFor?.(item.value)}\n {leadingWidgets && <WidgetStrip widgets={leadingWidgets} size={14} />}\n <span className=\"pz:flex-1 pz:truncate\">{item.label}</span>\n {fieldType && <FieldTypeBadge type={fieldType.type} format={fieldType.format} />}\n </ComboboxItem>\n );\n })}\n\n {!loading && items.length === 0 && (\n <ComboboxEmpty>\n {emptyLabel ??\n (allowCreate\n ? options || loadOptions\n ? \"No matches\"\n : \"Type to add\"\n : \"No options\")}\n </ComboboxEmpty>\n )}\n </ComboboxList>\n </ComboboxContent>\n </Combobox>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { ForwardRefExoticComponent, RefAttributes } from "react";
2
+ import { HugeiconsProps } from "@hugeicons/react";
3
+
4
+ //#region src/components/internal/icons.d.ts
5
+ /**
6
+ * The package's icon set: Hugeicons (stroke-rounded, free) under the component
7
+ * names the code already used. `Icon*` are the compact 16px variants the
8
+ * internal controls render without a size class.
9
+ */
10
+ type IconProps = Omit<HugeiconsProps, "icon" | "ref">;
11
+ type Icon = ForwardRefExoticComponent<IconProps & RefAttributes<SVGSVGElement>>;
12
+ //#endregion
13
+ export { Icon };
14
+ //# sourceMappingURL=icons.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons.d.mts","names":[],"sources":["../../../src/components/internal/icons.tsx"],"mappings":";;;;;;AA4GA;;;KAAY,SAAA,GAAY,IAAA,CAAK,cAAA;AAAA,KACjB,IAAA,GAAO,yBAAA,CAA0B,SAAA,GAAY,aAAA,CAAc,aAAA"}
@@ -1,192 +1,144 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { forwardRef } from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { ActivityIcon, AlertCircleIcon, ArrowDownIcon, ArrowUpIcon, Asterisk02Icon, AtSignIcon, AwardIcon, BanknoteIcon, BookDashedIcon, BookOpenIcon, BookmarkIcon, BoxesIcon, BracesIcon, BrainIcon, BriefcaseBusinessIcon, BriefcaseIcon, Building03Icon, CalendarClockIcon, CalendarIcon, ChartColumnIcon, CheckCheckIcon, CheckIcon, ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, CodeIcon, Coins01Icon, Coins02Icon, CompassIcon, ContactIcon, CopyIcon, Database02Icon, Delete02Icon, DownloadIcon, EraserIcon, ExternalLinkIcon, EyeIcon, EyeOffIcon, FactoryIcon, FilterIcon, GlobeIcon, GripVerticalIcon, HammerIcon, HandshakeIcon, HashIcon, HelpCircleIcon, HistoryIcon, InfoIcon, InternetIcon, KeyIcon, LanguagesIcon, LibraryIcon, Linkedin01Icon, ListChecksIcon, ListIcon, ListPlusIcon, LoaderIcon, LockIcon, MailIcon, MapPinIcon, MedalIcon, MinusIcon, MoreHorizontalIcon, Mortarboard02Icon, PauseIcon, PencilEdit01Icon, PercentIcon, PhoneIcon, PlayIcon, PlugIcon, PlusIcon, RefreshCwIcon, ScanFaceIcon, SearchIcon, Settings01Icon, ShuffleIcon, SigmaIcon, SlidersHorizontalIcon, SparklesIcon, StarIcon, SwordsIcon, TableIcon, Tag01Icon, Tag02Icon, TextAlignStartIcon, TextIcon, ToggleLeftIcon, ToggleRightIcon, TriangleAlertIcon, TriangleDashedIcon, TypeIcon, UploadIcon, UserGroupIcon, UserIcon, WorkflowIcon, XIcon, ZapIcon } from "@hugeicons/core-free-icons";
4
+ import { HugeiconsIcon } from "@hugeicons/react";
2
5
 
3
6
  //#region src/components/internal/icons.tsx
4
- const defaults = {
5
- xmlns: "http://www.w3.org/2000/svg",
6
- width: 16,
7
- height: 16,
8
- viewBox: "0 0 24 24",
9
- fill: "none",
10
- stroke: "currentColor",
11
- strokeWidth: 2,
12
- strokeLinecap: "round",
13
- strokeLinejoin: "round"
14
- };
15
- function IconPlus(props) {
16
- return /* @__PURE__ */ jsxs("svg", {
7
+ /** Wraps one Hugeicons glyph as a component, so an icon can be passed around
8
+ * as a value (`icon: Mail`) and rendered as `<Mail className="size-4" />`. */
9
+ function icon(svg, displayName, defaults) {
10
+ const Glyph = forwardRef((props, ref) => /* @__PURE__ */ jsx(HugeiconsIcon, {
11
+ ref,
12
+ icon: svg,
17
13
  ...defaults,
18
- ...props,
19
- children: [/* @__PURE__ */ jsx("path", { d: "M5 12h14" }), /* @__PURE__ */ jsx("path", { d: "M12 5v14" })]
20
- });
21
- }
22
- function IconMinus(props) {
23
- return /* @__PURE__ */ jsx("svg", {
24
- ...defaults,
25
- ...props,
26
- children: /* @__PURE__ */ jsx("path", { d: "M5 12h14" })
27
- });
28
- }
29
- function IconInfo(props) {
30
- return /* @__PURE__ */ jsxs("svg", {
31
- ...defaults,
32
- ...props,
33
- children: [
34
- /* @__PURE__ */ jsx("circle", {
35
- cx: "12",
36
- cy: "12",
37
- r: "10"
38
- }),
39
- /* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
40
- /* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
41
- ]
42
- });
43
- }
44
- function IconWarning(props) {
45
- return /* @__PURE__ */ jsxs("svg", {
46
- ...defaults,
47
- ...props,
48
- children: [
49
- /* @__PURE__ */ jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
50
- /* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
51
- /* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
52
- ]
53
- });
54
- }
55
- function IconError(props) {
56
- return /* @__PURE__ */ jsxs("svg", {
57
- ...defaults,
58
- ...props,
59
- children: [
60
- /* @__PURE__ */ jsx("circle", {
61
- cx: "12",
62
- cy: "12",
63
- r: "10"
64
- }),
65
- /* @__PURE__ */ jsx("path", { d: "m15 9-6 6" }),
66
- /* @__PURE__ */ jsx("path", { d: "m9 9 6 6" })
67
- ]
68
- });
69
- }
70
- function IconPencil(props) {
71
- return /* @__PURE__ */ jsx("svg", {
72
- ...defaults,
73
- ...props,
74
- children: /* @__PURE__ */ jsx("path", { d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" })
75
- });
76
- }
77
- function IconTrash(props) {
78
- return /* @__PURE__ */ jsxs("svg", {
79
- ...defaults,
80
- ...props,
81
- children: [
82
- /* @__PURE__ */ jsx("path", { d: "M3 6h18" }),
83
- /* @__PURE__ */ jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
84
- /* @__PURE__ */ jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
85
- ]
86
- });
87
- }
88
- function IconChevronDown(props) {
89
- return /* @__PURE__ */ jsx("svg", {
90
- ...defaults,
91
- ...props,
92
- children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
93
- });
94
- }
95
- function IconLock(props) {
96
- return /* @__PURE__ */ jsxs("svg", {
97
- ...defaults,
98
- ...props,
99
- children: [/* @__PURE__ */ jsx("rect", {
100
- width: "18",
101
- height: "11",
102
- x: "3",
103
- y: "11",
104
- rx: "2",
105
- ry: "2"
106
- }), /* @__PURE__ */ jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })]
107
- });
108
- }
109
- function IconGripVertical(props) {
110
- return /* @__PURE__ */ jsxs("svg", {
111
- ...defaults,
112
- ...props,
113
- children: [
114
- /* @__PURE__ */ jsx("circle", {
115
- cx: "9",
116
- cy: "12",
117
- r: "1"
118
- }),
119
- /* @__PURE__ */ jsx("circle", {
120
- cx: "9",
121
- cy: "5",
122
- r: "1"
123
- }),
124
- /* @__PURE__ */ jsx("circle", {
125
- cx: "9",
126
- cy: "19",
127
- r: "1"
128
- }),
129
- /* @__PURE__ */ jsx("circle", {
130
- cx: "15",
131
- cy: "12",
132
- r: "1"
133
- }),
134
- /* @__PURE__ */ jsx("circle", {
135
- cx: "15",
136
- cy: "5",
137
- r: "1"
138
- }),
139
- /* @__PURE__ */ jsx("circle", {
140
- cx: "15",
141
- cy: "19",
142
- r: "1"
143
- })
144
- ]
145
- });
146
- }
147
- function IconCheck(props) {
148
- return /* @__PURE__ */ jsx("svg", {
149
- ...defaults,
150
- ...props,
151
- children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" })
152
- });
153
- }
154
- function IconEye(props) {
155
- return /* @__PURE__ */ jsxs("svg", {
156
- ...defaults,
157
- ...props,
158
- children: [/* @__PURE__ */ jsx("path", { d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" }), /* @__PURE__ */ jsx("circle", {
159
- cx: "12",
160
- cy: "12",
161
- r: "3"
162
- })]
163
- });
164
- }
165
- function IconEyeOff(props) {
166
- return /* @__PURE__ */ jsxs("svg", {
167
- ...defaults,
168
- ...props,
169
- children: [
170
- /* @__PURE__ */ jsx("path", { d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }),
171
- /* @__PURE__ */ jsx("path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }),
172
- /* @__PURE__ */ jsx("path", { d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }),
173
- /* @__PURE__ */ jsx("path", { d: "m2 2 20 20" })
174
- ]
175
- });
176
- }
177
- function IconRefresh(props) {
178
- return /* @__PURE__ */ jsxs("svg", {
179
- ...defaults,
180
- ...props,
181
- children: [
182
- /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }),
183
- /* @__PURE__ */ jsx("path", { d: "M21 3v5h-5" }),
184
- /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }),
185
- /* @__PURE__ */ jsx("path", { d: "M8 16H3v5" })
186
- ]
187
- });
14
+ ...props
15
+ }));
16
+ Glyph.displayName = displayName;
17
+ return Glyph;
188
18
  }
19
+ const Activity = icon(ActivityIcon, "Activity");
20
+ const AlertCircle = icon(AlertCircleIcon, "AlertCircle");
21
+ const ArrowDown = icon(ArrowDownIcon, "ArrowDown");
22
+ const ArrowUp = icon(ArrowUpIcon, "ArrowUp");
23
+ const Asterisk = icon(Asterisk02Icon, "Asterisk");
24
+ const AtSign = icon(AtSignIcon, "AtSign");
25
+ const Award = icon(AwardIcon, "Award");
26
+ const Banknote = icon(BanknoteIcon, "Banknote");
27
+ const BarChart3 = icon(ChartColumnIcon, "BarChart3");
28
+ const BookDashed = icon(BookDashedIcon, "BookDashed");
29
+ const BookOpen = icon(BookOpenIcon, "BookOpen");
30
+ const Bookmark = icon(BookmarkIcon, "Bookmark");
31
+ const Boxes = icon(BoxesIcon, "Boxes");
32
+ const Braces = icon(BracesIcon, "Braces");
33
+ const Brain = icon(BrainIcon, "Brain");
34
+ const Briefcase = icon(BriefcaseIcon, "Briefcase");
35
+ const BriefcaseBusiness = icon(BriefcaseBusinessIcon, "BriefcaseBusiness");
36
+ const Building2 = icon(Building03Icon, "Building2");
37
+ const Calendar = icon(CalendarIcon, "Calendar");
38
+ const CalendarClock = icon(CalendarClockIcon, "CalendarClock");
39
+ const Check = icon(CheckIcon, "Check");
40
+ const CheckCheck = icon(CheckCheckIcon, "CheckCheck");
41
+ const ChevronDown = icon(ChevronDownIcon, "ChevronDown");
42
+ const ChevronRight = icon(ChevronRightIcon, "ChevronRight");
43
+ const ChevronUp = icon(ChevronUpIcon, "ChevronUp");
44
+ const CircleHelp = icon(HelpCircleIcon, "CircleHelp");
45
+ const Code2 = icon(CodeIcon, "Code2");
46
+ const Cog = icon(Settings01Icon, "Cog");
47
+ const Coins = icon(Coins01Icon, "Coins");
48
+ const Compass = icon(CompassIcon, "Compass");
49
+ const Contact = icon(ContactIcon, "Contact");
50
+ /** Credit cost — the plain two-coin glyph (no currency sign: credits aren't dollars). */
51
+ const Coin = icon(Coins02Icon, "Coin");
52
+ const Copy = icon(CopyIcon, "Copy");
53
+ const Database = icon(Database02Icon, "Database");
54
+ const Download = icon(DownloadIcon, "Download");
55
+ const Eraser = icon(EraserIcon, "Eraser");
56
+ const ExternalLink = icon(ExternalLinkIcon, "ExternalLink");
57
+ const Eye = icon(EyeIcon, "Eye");
58
+ const EyeOff = icon(EyeOffIcon, "EyeOff");
59
+ const Factory = icon(FactoryIcon, "Factory");
60
+ const Filter = icon(FilterIcon, "Filter");
61
+ /** Domains / websites — a plain meridian globe. */
62
+ const Internet = icon(InternetIcon, "Internet");
63
+ const Globe = icon(GlobeIcon, "Globe");
64
+ const GraduationCap = icon(Mortarboard02Icon, "GraduationCap");
65
+ const GripVertical = icon(GripVerticalIcon, "GripVertical");
66
+ const Hammer = icon(HammerIcon, "Hammer");
67
+ const Handshake = icon(HandshakeIcon, "Handshake");
68
+ const Hash = icon(HashIcon, "Hash");
69
+ const HelpCircle = icon(HelpCircleIcon, "HelpCircle");
70
+ const History = icon(HistoryIcon, "History");
71
+ const Info = icon(InfoIcon, "Info");
72
+ const Key = icon(KeyIcon, "Key");
73
+ const Languages = icon(LanguagesIcon, "Languages");
74
+ const LetterText = icon(TextIcon, "LetterText");
75
+ const Library = icon(LibraryIcon, "Library");
76
+ const Linkedin = icon(Linkedin01Icon, "Linkedin");
77
+ const List = icon(ListIcon, "List");
78
+ const ListChecks = icon(ListChecksIcon, "ListChecks");
79
+ const ListPlus = icon(ListPlusIcon, "ListPlus");
80
+ const Loader = icon(LoaderIcon, "Loader");
81
+ const Lock = icon(LockIcon, "Lock");
82
+ const Mail = icon(MailIcon, "Mail");
83
+ const MapPin = icon(MapPinIcon, "MapPin");
84
+ const Medal = icon(MedalIcon, "Medal");
85
+ const Minus = icon(MinusIcon, "Minus");
86
+ const MoreHorizontal = icon(MoreHorizontalIcon, "MoreHorizontal");
87
+ const Pause = icon(PauseIcon, "Pause");
88
+ const Pencil = icon(PencilEdit01Icon, "Pencil");
89
+ const Percent = icon(PercentIcon, "Percent");
90
+ const Phone = icon(PhoneIcon, "Phone");
91
+ const Play = icon(PlayIcon, "Play");
92
+ const Plug = icon(PlugIcon, "Plug");
93
+ const Plus = icon(PlusIcon, "Plus");
94
+ const RefreshCw = icon(RefreshCwIcon, "RefreshCw");
95
+ const ScanFace = icon(ScanFaceIcon, "ScanFace");
96
+ const Search = icon(SearchIcon, "Search");
97
+ const Shuffle = icon(ShuffleIcon, "Shuffle");
98
+ const Sigma = icon(SigmaIcon, "Sigma");
99
+ const SlidersHorizontal = icon(SlidersHorizontalIcon, "SlidersHorizontal");
100
+ const Sparkles = icon(SparklesIcon, "Sparkles");
101
+ const Star = icon(StarIcon, "Star");
102
+ const Swords = icon(SwordsIcon, "Swords");
103
+ const Table = icon(TableIcon, "Table");
104
+ const Tag = icon(Tag01Icon, "Tag");
105
+ const Tags = icon(Tag02Icon, "Tags");
106
+ const TextAlignStart = icon(TextAlignStartIcon, "TextAlignStart");
107
+ const ToggleLeft = icon(ToggleLeftIcon, "ToggleLeft");
108
+ const ToggleRight = icon(ToggleRightIcon, "ToggleRight");
109
+ const Trash2 = icon(Delete02Icon, "Trash2");
110
+ const TriangleAlert = icon(TriangleAlertIcon, "TriangleAlert");
111
+ const TriangleDashed = icon(TriangleDashedIcon, "TriangleDashed");
112
+ const Type = icon(TypeIcon, "Type");
113
+ const Upload = icon(UploadIcon, "Upload");
114
+ const User = icon(UserIcon, "User");
115
+ const Users = icon(UserGroupIcon, "Users");
116
+ const Workflow = icon(WorkflowIcon, "Workflow");
117
+ const X = icon(XIcon, "X");
118
+ const Zap = icon(ZapIcon, "Zap");
119
+ const IconBraces = icon(BracesIcon, "IconBraces", { size: 16 });
120
+ const IconCheck = icon(CheckIcon, "IconCheck", { size: 16 });
121
+ const IconChevronDown = icon(ChevronDownIcon, "IconChevronDown", { size: 16 });
122
+ const IconChevronRight = icon(ChevronRightIcon, "IconChevronRight", { size: 16 });
123
+ const IconError = icon(AlertCircleIcon, "IconError", { size: 16 });
124
+ const IconEye = icon(EyeIcon, "IconEye", { size: 16 });
125
+ const IconEyeOff = icon(EyeOffIcon, "IconEyeOff", { size: 16 });
126
+ const IconGripVertical = icon(GripVerticalIcon, "IconGripVertical", { size: 16 });
127
+ const IconInfo = icon(InfoIcon, "IconInfo", { size: 16 });
128
+ const IconLock = icon(LockIcon, "IconLock", { size: 16 });
129
+ const IconMinus = icon(MinusIcon, "IconMinus", { size: 16 });
130
+ const IconMoreHorizontal = icon(MoreHorizontalIcon, "IconMoreHorizontal", { size: 16 });
131
+ const IconPencil = icon(PencilEdit01Icon, "IconPencil", { size: 16 });
132
+ const IconPlay = icon(PlayIcon, "IconPlay", {
133
+ size: 16,
134
+ fill: "currentColor"
135
+ });
136
+ const IconPlus = icon(PlusIcon, "IconPlus", { size: 16 });
137
+ const IconRefresh = icon(RefreshCwIcon, "IconRefresh", { size: 16 });
138
+ const IconTrash = icon(Delete02Icon, "IconTrash", { size: 16 });
139
+ const IconWarning = icon(TriangleAlertIcon, "IconWarning", { size: 16 });
140
+ const IconX = icon(XIcon, "IconX", { size: 16 });
189
141
 
190
142
  //#endregion
191
- export { IconCheck, IconChevronDown, IconError, IconEye, IconEyeOff, IconGripVertical, IconInfo, IconLock, IconMinus, IconPencil, IconPlus, IconRefresh, IconTrash, IconWarning };
143
+ export { Activity, ArrowDown, ArrowUp, Asterisk, AtSign, Award, Banknote, BarChart3, BookDashed, BookOpen, Bookmark, Boxes, Braces, Brain, Briefcase, BriefcaseBusiness, Building2, Calendar, CalendarClock, Check, CheckCheck, ChevronDown, ChevronUp, CircleHelp, Code2, Cog, Coin, Coins, Compass, Contact, Copy, Database, Download, Eraser, ExternalLink, Factory, Filter, GraduationCap, Hammer, Handshake, Hash, HelpCircle, History, IconCheck, IconChevronDown, IconError, IconEye, IconEyeOff, IconGripVertical, IconInfo, IconLock, IconMinus, IconPencil, IconPlus, IconRefresh, IconTrash, IconWarning, Internet, Key, Languages, LetterText, Library, Linkedin, List, ListChecks, ListPlus, Loader, Mail, MapPin, Medal, Pause, Percent, Phone, Plug, Plus, ScanFace, Search, Shuffle, Sigma, SlidersHorizontal, Sparkles, Star, Swords, Table, Tag, Tags, TextAlignStart, ToggleLeft, ToggleRight, Trash2, TriangleDashed, Type, Upload, User, Users, Workflow, X, Zap };
192
144
  //# sourceMappingURL=icons.mjs.map