jaci-ui 0.3.0 → 0.4.0

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 (112) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +48 -0
  3. package/dist/components/calendar/calendar.cjs +377 -0
  4. package/dist/components/calendar/calendar.cjs.map +1 -0
  5. package/dist/components/calendar/calendar.d.cts +56 -0
  6. package/dist/components/calendar/calendar.d.ts +56 -0
  7. package/dist/components/calendar/calendar.js +368 -0
  8. package/dist/components/calendar/calendar.js.map +1 -0
  9. package/dist/components/calendar/index.d.cts +2 -0
  10. package/dist/components/calendar/index.d.ts +2 -0
  11. package/dist/components/combobox/combobox.cjs +13 -1
  12. package/dist/components/combobox/combobox.cjs.map +1 -1
  13. package/dist/components/combobox/combobox.d.cts +7 -2
  14. package/dist/components/combobox/combobox.d.ts +7 -2
  15. package/dist/components/combobox/combobox.js +13 -2
  16. package/dist/components/combobox/combobox.js.map +1 -1
  17. package/dist/components/combobox/index.d.cts +2 -2
  18. package/dist/components/combobox/index.d.ts +2 -2
  19. package/dist/components/controls/input.cjs +26 -5
  20. package/dist/components/controls/input.cjs.map +1 -1
  21. package/dist/components/controls/input.d.cts +5 -2
  22. package/dist/components/controls/input.d.ts +5 -2
  23. package/dist/components/controls/input.js +28 -7
  24. package/dist/components/controls/input.js.map +1 -1
  25. package/dist/components/controls/textarea.cjs +26 -5
  26. package/dist/components/controls/textarea.cjs.map +1 -1
  27. package/dist/components/controls/textarea.d.cts +5 -2
  28. package/dist/components/controls/textarea.d.ts +5 -2
  29. package/dist/components/controls/textarea.js +28 -7
  30. package/dist/components/controls/textarea.js.map +1 -1
  31. package/dist/components/date-picker/date-picker.cjs +9 -9
  32. package/dist/components/date-picker/date-picker.cjs.map +1 -1
  33. package/dist/components/date-picker/date-picker.d.cts +3 -1
  34. package/dist/components/date-picker/date-picker.d.ts +3 -1
  35. package/dist/components/date-picker/date-picker.js +9 -9
  36. package/dist/components/date-picker/date-picker.js.map +1 -1
  37. package/dist/components/date-range-picker/date-range-picker.cjs +589 -0
  38. package/dist/components/date-range-picker/date-range-picker.cjs.map +1 -0
  39. package/dist/components/date-range-picker/date-range-picker.d.cts +134 -0
  40. package/dist/components/date-range-picker/date-range-picker.d.ts +134 -0
  41. package/dist/components/date-range-picker/date-range-picker.js +568 -0
  42. package/dist/components/date-range-picker/date-range-picker.js.map +1 -0
  43. package/dist/components/date-range-picker/index.d.cts +2 -0
  44. package/dist/components/date-range-picker/index.d.ts +2 -0
  45. package/dist/components/field/field.cjs +106 -11
  46. package/dist/components/field/field.cjs.map +1 -1
  47. package/dist/components/field/field.d.cts +22 -4
  48. package/dist/components/field/field.d.ts +22 -4
  49. package/dist/components/field/field.js +107 -13
  50. package/dist/components/field/field.js.map +1 -1
  51. package/dist/components/field/index.d.cts +2 -2
  52. package/dist/components/field/index.d.ts +2 -2
  53. package/dist/components/pin-input/index.d.cts +2 -0
  54. package/dist/components/pin-input/index.d.ts +2 -0
  55. package/dist/components/pin-input/pin-input.cjs +266 -0
  56. package/dist/components/pin-input/pin-input.cjs.map +1 -0
  57. package/dist/components/pin-input/pin-input.d.cts +46 -0
  58. package/dist/components/pin-input/pin-input.d.ts +46 -0
  59. package/dist/components/pin-input/pin-input.js +258 -0
  60. package/dist/components/pin-input/pin-input.js.map +1 -0
  61. package/dist/components/select/index.d.cts +2 -2
  62. package/dist/components/select/index.d.ts +2 -2
  63. package/dist/components/select/select.cjs +80 -3
  64. package/dist/components/select/select.cjs.map +1 -1
  65. package/dist/components/select/select.d.cts +18 -3
  66. package/dist/components/select/select.d.ts +18 -3
  67. package/dist/components/select/select.js +78 -5
  68. package/dist/components/select/select.js.map +1 -1
  69. package/dist/components/tags-input/tags-input.cjs +83 -5
  70. package/dist/components/tags-input/tags-input.cjs.map +1 -1
  71. package/dist/components/tags-input/tags-input.d.cts +3 -0
  72. package/dist/components/tags-input/tags-input.d.ts +3 -0
  73. package/dist/components/tags-input/tags-input.js +83 -5
  74. package/dist/components/tags-input/tags-input.js.map +1 -1
  75. package/dist/components/upload/index.d.cts +2 -2
  76. package/dist/components/upload/index.d.ts +2 -2
  77. package/dist/components/upload/upload.cjs +90 -10
  78. package/dist/components/upload/upload.cjs.map +1 -1
  79. package/dist/components/upload/upload.d.cts +25 -2
  80. package/dist/components/upload/upload.d.ts +25 -2
  81. package/dist/components/upload/upload.js +89 -11
  82. package/dist/components/upload/upload.js.map +1 -1
  83. package/dist/index.cjs +52 -1
  84. package/dist/index.d.cts +11 -5
  85. package/dist/index.d.ts +11 -5
  86. package/dist/index.js +9 -6
  87. package/dist/styled-system/recipes/calendar.cjs +43 -0
  88. package/dist/styled-system/recipes/calendar.cjs.map +1 -0
  89. package/dist/styled-system/recipes/calendar.js +43 -0
  90. package/dist/styled-system/recipes/calendar.js.map +1 -0
  91. package/dist/styled-system/recipes/combobox.cjs +1 -0
  92. package/dist/styled-system/recipes/combobox.cjs.map +1 -1
  93. package/dist/styled-system/recipes/combobox.js +1 -0
  94. package/dist/styled-system/recipes/combobox.js.map +1 -1
  95. package/dist/styled-system/recipes/date-range-picker.cjs +52 -0
  96. package/dist/styled-system/recipes/date-range-picker.cjs.map +1 -0
  97. package/dist/styled-system/recipes/date-range-picker.js +52 -0
  98. package/dist/styled-system/recipes/date-range-picker.js.map +1 -0
  99. package/dist/styled-system/recipes/pin-input.cjs +39 -0
  100. package/dist/styled-system/recipes/pin-input.cjs.map +1 -0
  101. package/dist/styled-system/recipes/pin-input.js +39 -0
  102. package/dist/styled-system/recipes/pin-input.js.map +1 -0
  103. package/dist/styled-system/recipes/select.cjs +5 -1
  104. package/dist/styled-system/recipes/select.cjs.map +1 -1
  105. package/dist/styled-system/recipes/select.js +5 -1
  106. package/dist/styled-system/recipes/select.js.map +1 -1
  107. package/dist/styled-system/recipes/upload.cjs +2 -0
  108. package/dist/styled-system/recipes/upload.cjs.map +1 -1
  109. package/dist/styled-system/recipes/upload.js +2 -0
  110. package/dist/styled-system/recipes/upload.js.map +1 -1
  111. package/dist/styles.css +745 -81
  112. package/package.json +3 -2
@@ -2,18 +2,42 @@
2
2
  import { cx } from "../../styled-system/css/cx.js";
3
3
  import { withRecipeClassName } from "../base-ui.js";
4
4
  import { select } from "../../styled-system/recipes/select.js";
5
- import { createContext, forwardRef, useContext } from "react";
5
+ import { createContext, forwardRef, useContext, useState } from "react";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
  import { Select } from "@base-ui/react/select";
8
8
  //#region src/components/select/select.tsx
9
9
  const SelectSizeContext = createContext("md");
10
+ const SelectAuxContext = createContext(null);
10
11
  function useSelectStyles() {
11
12
  return select({ size: useContext(SelectSizeContext) });
12
13
  }
13
- function SelectRoot$1({ size = "md", ...props }) {
14
+ function SelectRoot$1({ defaultValue, disabled = false, loading: _loading, multiple, onValueChange, size = "md", value: controlledValue, ...props }) {
15
+ const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue ?? (multiple ? [] : null));
16
+ const value = controlledValue === void 0 ? uncontrolledValue : controlledValue;
17
+ const clear = () => {
18
+ const nextValue = multiple ? [] : null;
19
+ if (controlledValue === void 0) setUncontrolledValue(nextValue);
20
+ onValueChange?.(nextValue, void 0);
21
+ };
14
22
  return /* @__PURE__ */ jsx(SelectSizeContext.Provider, {
15
23
  value: size,
16
- children: /* @__PURE__ */ jsx(Select.Root, { ...props })
24
+ children: /* @__PURE__ */ jsx(SelectAuxContext.Provider, {
25
+ value: {
26
+ clear,
27
+ disabled
28
+ },
29
+ children: /* @__PURE__ */ jsx(Select.Root, {
30
+ ...props,
31
+ disabled,
32
+ defaultValue: void 0,
33
+ multiple,
34
+ onValueChange: (nextValue, details) => {
35
+ if (controlledValue === void 0) setUncontrolledValue(nextValue);
36
+ onValueChange?.(nextValue, details);
37
+ },
38
+ value
39
+ })
40
+ })
17
41
  });
18
42
  }
19
43
  /** An accessible label that Base UI associates with the trigger automatically. */
@@ -133,6 +157,51 @@ const SelectSeparator = forwardRef(function SelectSeparator({ className, orienta
133
157
  "data-slot": "select-separator"
134
158
  });
135
159
  });
160
+ const SelectClear = forwardRef(function SelectClear({ children = "×", className, ...props }, ref) {
161
+ const context = useContext(SelectAuxContext);
162
+ const styles = useSelectStyles();
163
+ return /* @__PURE__ */ jsx("button", {
164
+ ...props,
165
+ "aria-label": props["aria-label"] ?? "Clear selection",
166
+ className: cx(styles.clear, className),
167
+ "data-slot": "select-clear",
168
+ disabled: props.disabled || context?.disabled,
169
+ onClick: (event) => {
170
+ context?.clear();
171
+ props.onClick?.(event);
172
+ },
173
+ ref,
174
+ type: "button",
175
+ children
176
+ });
177
+ });
178
+ const SelectEmpty = forwardRef(function SelectEmpty({ className, ...props }, ref) {
179
+ return /* @__PURE__ */ jsx("div", {
180
+ ...props,
181
+ className: cx(useSelectStyles().empty, className),
182
+ "data-slot": "select-empty",
183
+ ref
184
+ });
185
+ });
186
+ const SelectLoading = forwardRef(function SelectLoading({ children = "Loading…", className, ...props }, ref) {
187
+ return /* @__PURE__ */ jsx("div", {
188
+ ...props,
189
+ "aria-live": "polite",
190
+ className: cx(useSelectStyles().loading, className),
191
+ "data-slot": "select-loading",
192
+ ref,
193
+ children
194
+ });
195
+ });
196
+ const SelectStatus = forwardRef(function SelectStatus({ className, ...props }, ref) {
197
+ return /* @__PURE__ */ jsx("div", {
198
+ ...props,
199
+ "aria-live": "polite",
200
+ className: cx(useSelectStyles().status, className),
201
+ "data-slot": "select-status",
202
+ ref
203
+ });
204
+ });
136
205
  const Select$1 = {
137
206
  Root: SelectRoot$1,
138
207
  Label: SelectLabel,
@@ -148,9 +217,13 @@ const Select$1 = {
148
217
  ItemIndicator: SelectItemIndicator,
149
218
  Group: SelectGroup,
150
219
  GroupLabel: SelectGroupLabel,
151
- Separator: SelectSeparator
220
+ Separator: SelectSeparator,
221
+ Clear: SelectClear,
222
+ Empty: SelectEmpty,
223
+ Loading: SelectLoading,
224
+ Status: SelectStatus
152
225
  };
153
226
  //#endregion
154
- export { Select$1 as Select, SelectGroup, SelectGroupLabel, SelectIcon, SelectItem, SelectItemIndicator, SelectItemText, SelectLabel, SelectList, SelectPopup, SelectPortal, SelectPositioner, SelectRoot$1 as SelectRoot, SelectSeparator, SelectTrigger, SelectValue };
227
+ export { Select$1 as Select, SelectClear, SelectEmpty, SelectGroup, SelectGroupLabel, SelectIcon, SelectItem, SelectItemIndicator, SelectItemText, SelectLabel, SelectList, SelectLoading, SelectPopup, SelectPortal, SelectPositioner, SelectRoot$1 as SelectRoot, SelectSeparator, SelectStatus, SelectTrigger, SelectValue };
155
228
 
156
229
  //# sourceMappingURL=select.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","names":["SelectRoot","BaseSelect","Select"],"sources":["../../../src/components/select/select.tsx"],"sourcesContent":["\"use client\";\n\nimport { Select as BaseSelect } from \"@base-ui/react/select\";\nimport { createContext, forwardRef, useContext } from \"react\";\nimport type { ComponentPropsWithoutRef } from \"react\";\nimport type { SelectRoot as BaseSelectRoot } from \"@base-ui/react/select\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { select } from \"../../styled-system/recipes\";\nimport { withRecipeClassName } from \"../base-ui\";\n\nexport type SelectSize = \"sm\" | \"md\" | \"lg\";\n\nconst SelectSizeContext = createContext<SelectSize>(\"md\");\n\nfunction useSelectStyles() {\n return select({ size: useContext(SelectSizeContext) });\n}\n\n/**\n * Groups an accessible listbox select. Use `value`/`onValueChange` for a\n * controlled selection, or `defaultValue` for an uncontrolled selection.\n *\n * `size` is shared by all visual parts so the trigger stays aligned with the\n * library's `Input` component.\n */\nexport interface SelectRootProps<Value, Multiple extends boolean | undefined = false>\n extends BaseSelectRoot.Props<Value, Multiple> {\n size?: SelectSize;\n}\n\nexport function SelectRoot<Value = unknown, Multiple extends boolean | undefined = false>({\n size = \"md\",\n ...props\n}: SelectRootProps<Value, Multiple>) {\n return (\n <SelectSizeContext.Provider value={size}>\n <BaseSelect.Root {...props} />\n </SelectSizeContext.Provider>\n );\n}\n\nexport type SelectLabelProps = ComponentPropsWithoutRef<typeof BaseSelect.Label>;\n\n/** An accessible label that Base UI associates with the trigger automatically. */\nexport const SelectLabel = forwardRef<HTMLDivElement, SelectLabelProps>(function SelectLabel(\n { className, ...props },\n ref,\n) {\n return (\n <BaseSelect.Label\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().label, className)}\n data-slot=\"select-label\"\n />\n );\n});\n\nexport type SelectTriggerProps = ComponentPropsWithoutRef<typeof BaseSelect.Trigger>;\n\n/** The button-shaped control that opens the select popup. */\nexport const SelectTrigger = forwardRef<HTMLButtonElement, SelectTriggerProps>(\n function SelectTrigger({ className, ...props }, ref) {\n return (\n <BaseSelect.Trigger\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().trigger, className)}\n data-jaci-component=\"select\"\n data-slot=\"select-trigger\"\n />\n );\n },\n);\n\nexport type SelectValueProps = ComponentPropsWithoutRef<typeof BaseSelect.Value>;\n\n/** Renders the selected item's label, or its placeholder when empty. */\nexport const SelectValue = forwardRef<HTMLSpanElement, SelectValueProps>(function SelectValue(\n { className, ...props },\n ref,\n) {\n return (\n <BaseSelect.Value\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().value, className)}\n data-slot=\"select-value\"\n />\n );\n});\n\nexport type SelectIconProps = ComponentPropsWithoutRef<typeof BaseSelect.Icon>;\n\n/** A small chevron that rotates while the popup is open. */\nexport const SelectIcon = forwardRef<HTMLSpanElement, SelectIconProps>(function SelectIcon(\n { children = \"⌄\", className, ...props },\n ref,\n) {\n return (\n <BaseSelect.Icon\n {...props}\n aria-hidden=\"true\"\n ref={ref}\n className={withRecipeClassName(useSelectStyles().icon, className)}\n data-slot=\"select-icon\"\n >\n {children}\n </BaseSelect.Icon>\n );\n});\n\n/** Preserves Base UI's optional portal container and mount controls. */\nexport const SelectPortal = BaseSelect.Portal;\n\nexport type SelectPositionerProps = ComponentPropsWithoutRef<typeof BaseSelect.Positioner>;\n\nexport const SelectPositioner = forwardRef<HTMLDivElement, SelectPositionerProps>(\n function SelectPositioner({ className, ...props }, ref) {\n return (\n <BaseSelect.Positioner\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().positioner, className)}\n data-slot=\"select-positioner\"\n />\n );\n },\n);\n\nexport type SelectPopupProps = ComponentPropsWithoutRef<typeof BaseSelect.Popup>;\n\nexport const SelectPopup = forwardRef<HTMLDivElement, SelectPopupProps>(function SelectPopup(\n { className, ...props },\n ref,\n) {\n return (\n <BaseSelect.Popup\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().popup, className)}\n data-slot=\"select-popup\"\n />\n );\n});\n\nexport type SelectListProps = ComponentPropsWithoutRef<typeof BaseSelect.List>;\n\nexport const SelectList = forwardRef<HTMLDivElement, SelectListProps>(function SelectList(\n { className, ...props },\n ref,\n) {\n return (\n <BaseSelect.List\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().list, className)}\n data-slot=\"select-list\"\n />\n );\n});\n\nexport type SelectItemProps = ComponentPropsWithoutRef<typeof BaseSelect.Item>;\n\nexport const SelectItem = forwardRef<HTMLElement, SelectItemProps>(function SelectItem(\n { className, ...props },\n ref,\n) {\n return (\n <BaseSelect.Item\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().item, className)}\n data-slot=\"select-item\"\n />\n );\n});\n\nexport type SelectItemTextProps = ComponentPropsWithoutRef<typeof BaseSelect.ItemText>;\n\nexport const SelectItemText = forwardRef<HTMLDivElement, SelectItemTextProps>(\n function SelectItemText({ className, ...props }, ref) {\n return (\n <BaseSelect.ItemText\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().itemText, className)}\n data-slot=\"select-item-text\"\n />\n );\n },\n);\n\nexport type SelectItemIndicatorProps = ComponentPropsWithoutRef<typeof BaseSelect.ItemIndicator>;\n\nexport const SelectItemIndicator = forwardRef<HTMLSpanElement, SelectItemIndicatorProps>(\n function SelectItemIndicator({ children = \"✓\", className, ...props }, ref) {\n return (\n <BaseSelect.ItemIndicator\n {...props}\n aria-hidden=\"true\"\n ref={ref}\n className={withRecipeClassName(useSelectStyles().itemIndicator, className)}\n data-slot=\"select-item-indicator\"\n >\n {children}\n </BaseSelect.ItemIndicator>\n );\n },\n);\n\nexport type SelectGroupProps = ComponentPropsWithoutRef<typeof BaseSelect.Group>;\n\nexport const SelectGroup = forwardRef<HTMLDivElement, SelectGroupProps>(function SelectGroup(\n { className, ...props },\n ref,\n) {\n return (\n <BaseSelect.Group\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().group, className)}\n data-slot=\"select-group\"\n />\n );\n});\n\nexport type SelectGroupLabelProps = ComponentPropsWithoutRef<typeof BaseSelect.GroupLabel>;\n\nexport const SelectGroupLabel = forwardRef<HTMLDivElement, SelectGroupLabelProps>(\n function SelectGroupLabel({ className, ...props }, ref) {\n return (\n <BaseSelect.GroupLabel\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().groupLabel, className)}\n data-slot=\"select-group-label\"\n />\n );\n },\n);\n\n/**\n * A visual separator for groups in a listbox. It is intentionally hidden from\n * assistive technology because ARIA listboxes may only contain options or\n * option groups, not semantic separators.\n */\nexport interface SelectSeparatorProps extends ComponentPropsWithoutRef<\"div\"> {\n orientation?: \"horizontal\" | \"vertical\";\n}\n\nexport const SelectSeparator = forwardRef<HTMLDivElement, SelectSeparatorProps>(\n function SelectSeparator({ className, orientation = \"horizontal\", ...props }, ref) {\n return (\n <div\n {...props}\n aria-hidden=\"true\"\n ref={ref}\n className={cx(useSelectStyles().separator, className)}\n data-orientation={orientation}\n data-slot=\"select-separator\"\n />\n );\n },\n);\n\nexport const Select = {\n Root: SelectRoot,\n Label: SelectLabel,\n Trigger: SelectTrigger,\n Value: SelectValue,\n Icon: SelectIcon,\n Portal: SelectPortal,\n Positioner: SelectPositioner,\n Popup: SelectPopup,\n List: SelectList,\n Item: SelectItem,\n ItemText: SelectItemText,\n ItemIndicator: SelectItemIndicator,\n Group: SelectGroup,\n GroupLabel: SelectGroupLabel,\n Separator: SelectSeparator,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"select.js","names":["SelectRoot","BaseSelect","Select"],"sources":["../../../src/components/select/select.tsx"],"sourcesContent":["\"use client\";\n\nimport { Select as BaseSelect } from \"@base-ui/react/select\";\nimport { createContext, forwardRef, useContext, useState } from \"react\";\nimport type { ComponentPropsWithoutRef, ReactNode } from \"react\";\nimport type { SelectRoot as BaseSelectRoot } from \"@base-ui/react/select\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { select } from \"../../styled-system/recipes\";\nimport { withRecipeClassName } from \"../base-ui\";\n\nexport type SelectSize = \"sm\" | \"md\" | \"lg\";\n\nconst SelectSizeContext = createContext<SelectSize>(\"md\");\nconst SelectAuxContext = createContext<{ clear: () => void; disabled: boolean } | null>(null);\n\nfunction useSelectStyles() {\n return select({ size: useContext(SelectSizeContext) });\n}\n\n/**\n * Groups an accessible listbox select. Use `value`/`onValueChange` for a\n * controlled selection, or `defaultValue` for an uncontrolled selection.\n *\n * `size` is shared by all visual parts so the trigger stays aligned with the\n * library's `Input` component.\n */\nexport interface SelectRootProps<Value, Multiple extends boolean | undefined = false>\n extends BaseSelectRoot.Props<Value, Multiple> {\n size?: SelectSize;\n loading?: boolean;\n}\n\nexport function SelectRoot<Value = unknown, Multiple extends boolean | undefined = false>({\n defaultValue,\n disabled = false,\n loading: _loading,\n multiple,\n onValueChange,\n size = \"md\",\n value: controlledValue,\n ...props\n}: SelectRootProps<Value, Multiple>) {\n const [uncontrolledValue, setUncontrolledValue] = useState<unknown>(\n defaultValue ?? (multiple ? [] : null),\n );\n const value = controlledValue === undefined ? uncontrolledValue : controlledValue;\n const clear = () => {\n const nextValue = multiple ? [] : null;\n if (controlledValue === undefined) setUncontrolledValue(nextValue);\n onValueChange?.(nextValue as never, undefined as never);\n };\n return (\n <SelectSizeContext.Provider value={size}>\n <SelectAuxContext.Provider value={{ clear, disabled }}>\n <BaseSelect.Root\n {...props}\n disabled={disabled}\n defaultValue={undefined}\n multiple={multiple}\n onValueChange={(nextValue, details) => {\n if (controlledValue === undefined) setUncontrolledValue(nextValue);\n onValueChange?.(nextValue, details);\n }}\n value={value as SelectRootProps<Value, Multiple>[\"value\"]}\n />\n </SelectAuxContext.Provider>\n </SelectSizeContext.Provider>\n );\n}\n\nexport type SelectLabelProps = ComponentPropsWithoutRef<typeof BaseSelect.Label>;\n\n/** An accessible label that Base UI associates with the trigger automatically. */\nexport const SelectLabel = forwardRef<HTMLDivElement, SelectLabelProps>(function SelectLabel(\n { className, ...props },\n ref,\n) {\n return (\n <BaseSelect.Label\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().label, className)}\n data-slot=\"select-label\"\n />\n );\n});\n\nexport type SelectTriggerProps = ComponentPropsWithoutRef<typeof BaseSelect.Trigger>;\n\n/** The button-shaped control that opens the select popup. */\nexport const SelectTrigger = forwardRef<HTMLButtonElement, SelectTriggerProps>(\n function SelectTrigger({ className, ...props }, ref) {\n return (\n <BaseSelect.Trigger\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().trigger, className)}\n data-jaci-component=\"select\"\n data-slot=\"select-trigger\"\n />\n );\n },\n);\n\nexport type SelectValueProps = ComponentPropsWithoutRef<typeof BaseSelect.Value>;\n\n/** Renders the selected item's label, or its placeholder when empty. */\nexport const SelectValue = forwardRef<HTMLSpanElement, SelectValueProps>(function SelectValue(\n { className, ...props },\n ref,\n) {\n return (\n <BaseSelect.Value\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().value, className)}\n data-slot=\"select-value\"\n />\n );\n});\n\nexport type SelectIconProps = ComponentPropsWithoutRef<typeof BaseSelect.Icon>;\n\n/** A small chevron that rotates while the popup is open. */\nexport const SelectIcon = forwardRef<HTMLSpanElement, SelectIconProps>(function SelectIcon(\n { children = \"⌄\", className, ...props },\n ref,\n) {\n return (\n <BaseSelect.Icon\n {...props}\n aria-hidden=\"true\"\n ref={ref}\n className={withRecipeClassName(useSelectStyles().icon, className)}\n data-slot=\"select-icon\"\n >\n {children}\n </BaseSelect.Icon>\n );\n});\n\n/** Preserves Base UI's optional portal container and mount controls. */\nexport const SelectPortal = BaseSelect.Portal;\n\nexport type SelectPositionerProps = ComponentPropsWithoutRef<typeof BaseSelect.Positioner>;\n\nexport const SelectPositioner = forwardRef<HTMLDivElement, SelectPositionerProps>(\n function SelectPositioner({ className, ...props }, ref) {\n return (\n <BaseSelect.Positioner\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().positioner, className)}\n data-slot=\"select-positioner\"\n />\n );\n },\n);\n\nexport type SelectPopupProps = ComponentPropsWithoutRef<typeof BaseSelect.Popup>;\n\nexport const SelectPopup = forwardRef<HTMLDivElement, SelectPopupProps>(function SelectPopup(\n { className, ...props },\n ref,\n) {\n return (\n <BaseSelect.Popup\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().popup, className)}\n data-slot=\"select-popup\"\n />\n );\n});\n\nexport type SelectListProps = ComponentPropsWithoutRef<typeof BaseSelect.List>;\n\nexport const SelectList = forwardRef<HTMLDivElement, SelectListProps>(function SelectList(\n { className, ...props },\n ref,\n) {\n return (\n <BaseSelect.List\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().list, className)}\n data-slot=\"select-list\"\n />\n );\n});\n\nexport type SelectItemProps = ComponentPropsWithoutRef<typeof BaseSelect.Item>;\n\nexport const SelectItem = forwardRef<HTMLElement, SelectItemProps>(function SelectItem(\n { className, ...props },\n ref,\n) {\n return (\n <BaseSelect.Item\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().item, className)}\n data-slot=\"select-item\"\n />\n );\n});\n\nexport type SelectItemTextProps = ComponentPropsWithoutRef<typeof BaseSelect.ItemText>;\n\nexport const SelectItemText = forwardRef<HTMLDivElement, SelectItemTextProps>(\n function SelectItemText({ className, ...props }, ref) {\n return (\n <BaseSelect.ItemText\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().itemText, className)}\n data-slot=\"select-item-text\"\n />\n );\n },\n);\n\nexport type SelectItemIndicatorProps = ComponentPropsWithoutRef<typeof BaseSelect.ItemIndicator>;\n\nexport const SelectItemIndicator = forwardRef<HTMLSpanElement, SelectItemIndicatorProps>(\n function SelectItemIndicator({ children = \"✓\", className, ...props }, ref) {\n return (\n <BaseSelect.ItemIndicator\n {...props}\n aria-hidden=\"true\"\n ref={ref}\n className={withRecipeClassName(useSelectStyles().itemIndicator, className)}\n data-slot=\"select-item-indicator\"\n >\n {children}\n </BaseSelect.ItemIndicator>\n );\n },\n);\n\nexport type SelectGroupProps = ComponentPropsWithoutRef<typeof BaseSelect.Group>;\n\nexport const SelectGroup = forwardRef<HTMLDivElement, SelectGroupProps>(function SelectGroup(\n { className, ...props },\n ref,\n) {\n return (\n <BaseSelect.Group\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().group, className)}\n data-slot=\"select-group\"\n />\n );\n});\n\nexport type SelectGroupLabelProps = ComponentPropsWithoutRef<typeof BaseSelect.GroupLabel>;\n\nexport const SelectGroupLabel = forwardRef<HTMLDivElement, SelectGroupLabelProps>(\n function SelectGroupLabel({ className, ...props }, ref) {\n return (\n <BaseSelect.GroupLabel\n {...props}\n ref={ref}\n className={withRecipeClassName(useSelectStyles().groupLabel, className)}\n data-slot=\"select-group-label\"\n />\n );\n },\n);\n\n/**\n * A visual separator for groups in a listbox. It is intentionally hidden from\n * assistive technology because ARIA listboxes may only contain options or\n * option groups, not semantic separators.\n */\nexport interface SelectSeparatorProps extends ComponentPropsWithoutRef<\"div\"> {\n orientation?: \"horizontal\" | \"vertical\";\n}\n\nexport const SelectSeparator = forwardRef<HTMLDivElement, SelectSeparatorProps>(\n function SelectSeparator({ className, orientation = \"horizontal\", ...props }, ref) {\n return (\n <div\n {...props}\n aria-hidden=\"true\"\n ref={ref}\n className={cx(useSelectStyles().separator, className)}\n data-orientation={orientation}\n data-slot=\"select-separator\"\n />\n );\n },\n);\n\nexport interface SelectClearProps extends ComponentPropsWithoutRef<\"button\"> {\n children?: ReactNode;\n}\nexport const SelectClear = forwardRef<HTMLButtonElement, SelectClearProps>(function SelectClear(\n { children = \"×\", className, ...props },\n ref,\n) {\n const context = useContext(SelectAuxContext);\n const styles = useSelectStyles();\n return (\n <button\n {...props}\n aria-label={props[\"aria-label\"] ?? \"Clear selection\"}\n className={cx(styles.clear, className)}\n data-slot=\"select-clear\"\n disabled={props.disabled || context?.disabled}\n onClick={(event) => {\n context?.clear();\n props.onClick?.(event);\n }}\n ref={ref}\n type=\"button\"\n >\n {children}\n </button>\n );\n});\n\nexport type SelectEmptyProps = ComponentPropsWithoutRef<\"div\">;\nexport const SelectEmpty = forwardRef<HTMLDivElement, SelectEmptyProps>(function SelectEmpty(\n { className, ...props },\n ref,\n) {\n return (\n <div\n {...props}\n className={cx(useSelectStyles().empty, className)}\n data-slot=\"select-empty\"\n ref={ref}\n />\n );\n});\nexport type SelectLoadingProps = ComponentPropsWithoutRef<\"div\">;\nexport const SelectLoading = forwardRef<HTMLDivElement, SelectLoadingProps>(function SelectLoading(\n { children = \"Loading…\", className, ...props },\n ref,\n) {\n return (\n <div\n {...props}\n aria-live=\"polite\"\n className={cx(useSelectStyles().loading, className)}\n data-slot=\"select-loading\"\n ref={ref}\n >\n {children}\n </div>\n );\n});\nexport type SelectStatusProps = ComponentPropsWithoutRef<\"div\">;\nexport const SelectStatus = forwardRef<HTMLDivElement, SelectStatusProps>(function SelectStatus(\n { className, ...props },\n ref,\n) {\n return (\n <div\n {...props}\n aria-live=\"polite\"\n className={cx(useSelectStyles().status, className)}\n data-slot=\"select-status\"\n ref={ref}\n />\n );\n});\n\nexport const Select = {\n Root: SelectRoot,\n Label: SelectLabel,\n Trigger: SelectTrigger,\n Value: SelectValue,\n Icon: SelectIcon,\n Portal: SelectPortal,\n Positioner: SelectPositioner,\n Popup: SelectPopup,\n List: SelectList,\n Item: SelectItem,\n ItemText: SelectItemText,\n ItemIndicator: SelectItemIndicator,\n Group: SelectGroup,\n GroupLabel: SelectGroupLabel,\n Separator: SelectSeparator,\n Clear: SelectClear,\n Empty: SelectEmpty,\n Loading: SelectLoading,\n Status: SelectStatus,\n};\n"],"mappings":"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"}
@@ -18,12 +18,24 @@ function changedEvent(event, value) {
18
18
  target: nextTarget
19
19
  };
20
20
  }
21
- const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, className, data = [], defaultTags = [], defaultValue = "", disabled = false, id: providedId, label, onBlur, onChange, onFocus, onInputValueChange, onTagsChange, placeholder, tags: controlledTags, value: controlledInputValue, ...props }, ref) {
21
+ function tagKey(tag, index) {
22
+ return `${tag}-${index}`;
23
+ }
24
+ function hasDelimiter(value, delimiter) {
25
+ if (typeof delimiter === "string") return delimiter.length > 0 && value.includes(delimiter);
26
+ delimiter.lastIndex = 0;
27
+ const result = delimiter.test(value);
28
+ delimiter.lastIndex = 0;
29
+ return result;
30
+ }
31
+ const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, allowDuplicates = false, className, data = [], defaultTags = [], defaultValue = "", disabled = false, delimiter = /[,\n]/u, editable = false, id: providedId, label, onBlur, onChange, onFocus, onInputValueChange, onTagsChange, placeholder, tags: controlledTags, value: controlledInputValue, ...props }, ref) {
22
32
  const generatedId = (0, react.useId)();
23
33
  const id = providedId ?? generatedId;
24
34
  const [uncontrolledTags, setUncontrolledTags] = (0, react.useState)(() => [...defaultTags]);
25
35
  const [uncontrolledInputValue, setUncontrolledInputValue] = (0, react.useState)(defaultValue);
26
36
  const [focused, setFocused] = (0, react.useState)(false);
37
+ const [composing, setComposing] = (0, react.useState)(false);
38
+ const [editingTag, setEditingTag] = (0, react.useState)(null);
27
39
  const rootRef = (0, react.useRef)(null);
28
40
  const styles = require_tags_input.tagsInput();
29
41
  const selectedTags = controlledTags === void 0 ? uncontrolledTags : [...controlledTags];
@@ -43,11 +55,38 @@ const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby":
43
55
  ]);
44
56
  const addTag = (0, react.useCallback)((rawTag, event) => {
45
57
  const tag = rawTag.trim();
46
- if (tag && !selectedTags.includes(tag) && data.includes(tag)) updateTags([...selectedTags, tag]);
58
+ const isAllowed = data.length === 0 || data.includes(tag);
59
+ const duplicate = selectedTags.includes(tag);
60
+ if (tag && isAllowed && (allowDuplicates || !duplicate)) if (editingTag) {
61
+ updateTags([
62
+ ...selectedTags.slice(0, editingTag.index),
63
+ tag,
64
+ ...selectedTags.slice(editingTag.index)
65
+ ]);
66
+ setEditingTag(null);
67
+ } else updateTags([...selectedTags, tag]);
68
+ else if (editingTag) return;
69
+ clearInput(event);
70
+ }, [
71
+ allowDuplicates,
72
+ clearInput,
73
+ data,
74
+ editingTag,
75
+ selectedTags,
76
+ updateTags
77
+ ]);
78
+ const addDelimited = (0, react.useCallback)((rawValue, event) => {
79
+ const separator = typeof delimiter === "string" && delimiter.length === 0 ? /\r?\n/u : delimiter;
80
+ const values = rawValue.split(separator).map((value) => value.trim()).filter(Boolean);
81
+ const nextTags = [...selectedTags];
82
+ for (const value of values) if ((data.length === 0 || data.includes(value)) && (allowDuplicates || !nextTags.includes(value))) nextTags.push(value);
83
+ if (nextTags.length !== selectedTags.length) updateTags(nextTags);
47
84
  clearInput(event);
48
85
  }, [
86
+ allowDuplicates,
49
87
  clearInput,
50
88
  data,
89
+ delimiter,
51
90
  selectedTags,
52
91
  updateTags
53
92
  ]);
@@ -66,8 +105,8 @@ const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby":
66
105
  onChange?.(event);
67
106
  return;
68
107
  }
69
- if (nextValue.endsWith(",")) {
70
- addTag(nextValue.slice(0, -1), event);
108
+ if (!composing && hasDelimiter(nextValue, delimiter)) {
109
+ addDelimited(nextValue, event);
71
110
  return;
72
111
  }
73
112
  if (controlledInputValue === void 0) setUncontrolledInputValue(nextValue);
@@ -78,6 +117,41 @@ const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby":
78
117
  setFocused(false);
79
118
  onBlur?.(event);
80
119
  }
120
+ function handlePaste(event) {
121
+ const pasted = event.clipboardData.getData("text");
122
+ if (!pasted || !hasDelimiter(pasted, delimiter) && !pasted.includes("\n")) return;
123
+ event.preventDefault();
124
+ addDelimited(pasted);
125
+ }
126
+ function handleKeyDown(event) {
127
+ if (event.nativeEvent.isComposing || composing) return;
128
+ if (event.key === "Enter") {
129
+ event.preventDefault();
130
+ if (inputValue.trim()) addTag(inputValue);
131
+ else if (editingTag) setEditingTag(null);
132
+ } else if (event.key === "Escape" && editingTag) {
133
+ event.preventDefault();
134
+ updateTags([
135
+ ...selectedTags.slice(0, editingTag.index),
136
+ editingTag.value,
137
+ ...selectedTags.slice(editingTag.index)
138
+ ]);
139
+ clearInput();
140
+ setEditingTag(null);
141
+ } else if (event.key === "Backspace" && !inputValue && editable && selectedTags.length > 0) {
142
+ const index = selectedTags.length - 1;
143
+ const tag = selectedTags[index];
144
+ if (tag === void 0) return;
145
+ setEditingTag({
146
+ index,
147
+ value: tag
148
+ });
149
+ updateTags(selectedTags.slice(0, -1));
150
+ if (controlledInputValue === void 0) setUncontrolledInputValue(tag);
151
+ onInputValueChange?.(tag);
152
+ }
153
+ props.onKeyDown?.(event);
154
+ }
81
155
  function removeTag(index) {
82
156
  updateTags(selectedTags.filter((_, currentIndex) => currentIndex !== index));
83
157
  }
@@ -117,7 +191,7 @@ const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby":
117
191
  type: "button",
118
192
  children: "×"
119
193
  })]
120
- }, tag))
194
+ }, tagKey(tag, index)))
121
195
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
122
196
  ...props,
123
197
  ...ariaDescribedBy ? { "aria-describedby": ariaDescribedBy } : {},
@@ -129,10 +203,14 @@ const TagsInput = (0, react.forwardRef)(function TagsInput({ "aria-describedby":
129
203
  id,
130
204
  onBlur: handleBlur,
131
205
  onChange: handleChange,
206
+ onCompositionEnd: () => setComposing(false),
207
+ onCompositionStart: () => setComposing(true),
132
208
  onFocus: (event) => {
133
209
  setFocused(true);
134
210
  onFocus?.(event);
135
211
  },
212
+ onKeyDown: handleKeyDown,
213
+ onPaste: handlePaste,
136
214
  placeholder,
137
215
  ref,
138
216
  type: "text",
@@ -1 +1 @@
1
- {"version":3,"file":"tags-input.cjs","names":["tagsInput","cx"],"sources":["../../../src/components/tags-input/tags-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport type {\n ChangeEvent,\n ChangeEventHandler,\n ComponentPropsWithoutRef,\n FocusEvent,\n MouseEvent,\n ReactNode,\n} from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { tagsInput } from \"../../styled-system/recipes\";\n\nexport interface TagsInputProps\n extends Omit<\n ComponentPropsWithoutRef<\"input\">,\n \"children\" | \"defaultValue\" | \"onChange\" | \"type\" | \"value\"\n > {\n /** Current tags. Omit this prop to use the uncontrolled API. */\n tags?: readonly string[];\n /** Initial tags for the uncontrolled API. */\n defaultTags?: readonly string[];\n /** Called whenever a tag is added or removed. */\n onTagsChange?: (tags: string[]) => void;\n /** Optional suggestions. A tag is accepted after a comma only when it is in this list. */\n data?: readonly string[];\n /** Visible label for the input. */\n label?: ReactNode;\n /** Current text query. This is the native input value, not the selected tags. */\n value?: string;\n /** Initial text query for an uncontrolled input. */\n defaultValue?: string;\n /** Native input change callback. */\n onChange?: ChangeEventHandler<HTMLInputElement>;\n /** Optional callback useful when a controlled query is cleared by selecting a tag. */\n onInputValueChange?: (value: string) => void;\n}\n\nfunction changedEvent(event: ChangeEvent<HTMLInputElement>, value: string) {\n const target = event.target;\n const nextTarget = {\n checked: target.checked,\n id: target.id,\n name: target.name,\n value,\n } as HTMLInputElement;\n\n return {\n ...event,\n currentTarget: nextTarget,\n target: nextTarget,\n } as ChangeEvent<HTMLInputElement>;\n}\n\nexport const TagsInput = forwardRef<HTMLInputElement, TagsInputProps>(function TagsInput(\n {\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-label\": ariaLabel,\n className,\n data = [],\n defaultTags = [],\n defaultValue = \"\",\n disabled = false,\n id: providedId,\n label,\n onBlur,\n onChange,\n onFocus,\n onInputValueChange,\n onTagsChange,\n placeholder,\n tags: controlledTags,\n value: controlledInputValue,\n ...props\n },\n ref,\n) {\n const generatedId = useId();\n const id = providedId ?? generatedId;\n const [uncontrolledTags, setUncontrolledTags] = useState<string[]>(() => [...defaultTags]);\n const [uncontrolledInputValue, setUncontrolledInputValue] = useState(defaultValue);\n const [focused, setFocused] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n const styles = tagsInput();\n const selectedTags = controlledTags === undefined ? uncontrolledTags : [...controlledTags];\n const inputValue =\n controlledInputValue === undefined ? uncontrolledInputValue : controlledInputValue;\n\n const updateTags = useCallback(\n (nextTags: string[]) => {\n if (controlledTags === undefined) setUncontrolledTags(nextTags);\n onTagsChange?.(nextTags);\n },\n [controlledTags, onTagsChange],\n );\n\n const clearInput = useCallback(\n (event?: ChangeEvent<HTMLInputElement>) => {\n if (controlledInputValue === undefined) setUncontrolledInputValue(\"\");\n onInputValueChange?.(\"\");\n if (event) onChange?.(changedEvent(event, \"\"));\n },\n [controlledInputValue, onChange, onInputValueChange],\n );\n\n const addTag = useCallback(\n (rawTag: string, event?: ChangeEvent<HTMLInputElement>) => {\n const tag = rawTag.trim();\n if (tag && !selectedTags.includes(tag) && data.includes(tag)) {\n updateTags([...selectedTags, tag]);\n }\n clearInput(event);\n },\n [clearInput, data, selectedTags, updateTags],\n );\n\n const suggestions = useMemo(() => {\n const query = inputValue.trim().toLocaleLowerCase();\n return data.filter(\n (item) =>\n !selectedTags.includes(item) &&\n (query.length === 0 || item.toLocaleLowerCase().includes(query)),\n );\n }, [data, inputValue, selectedTags]);\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n const nextValue = event.currentTarget.value;\n if (nextValue.trim() === \"\") {\n clearInput(event);\n onChange?.(event);\n return;\n }\n\n if (nextValue.endsWith(\",\")) {\n addTag(nextValue.slice(0, -1), event);\n return;\n }\n\n if (controlledInputValue === undefined) setUncontrolledInputValue(nextValue);\n onInputValueChange?.(nextValue);\n onChange?.(event);\n }\n\n function handleBlur(event: FocusEvent<HTMLInputElement>) {\n setFocused(false);\n onBlur?.(event);\n }\n\n function removeTag(index: number) {\n updateTags(selectedTags.filter((_, currentIndex) => currentIndex !== index));\n }\n\n return (\n <div\n className={styles.root}\n data-jaci-component=\"tags-input\"\n data-slot=\"tags-input\"\n ref={rootRef}\n >\n {label ? (\n <label className={styles.label} data-slot=\"tags-input-label\" htmlFor={id}>\n {label}\n </label>\n ) : null}\n <div\n className={cx(styles.control, className)}\n data-disabled={disabled || undefined}\n data-focus={focused || undefined}\n data-slot=\"tags-input-control\"\n >\n <div className={styles.tagList} data-slot=\"tags-input-tag-list\">\n {selectedTags.map((tag, index) => (\n <span className={styles.tag} data-slot=\"tags-input-tag\" key={tag}>\n <span className={styles.tagLabel} data-slot=\"tags-input-tag-label\">\n {tag}\n </span>\n <button\n aria-label={`Remove ${tag}`}\n className={styles.tagRemove}\n data-slot=\"tags-input-tag-remove\"\n disabled={disabled}\n onClick={() => removeTag(index)}\n type=\"button\"\n >\n ×\n </button>\n </span>\n ))}\n </div>\n <input\n {...props}\n {...(ariaDescribedBy ? { \"aria-describedby\": ariaDescribedBy } : {})}\n {...(ariaInvalid !== undefined ? { \"aria-invalid\": ariaInvalid } : {})}\n {...(ariaLabel ? { \"aria-label\": ariaLabel } : {})}\n autoComplete=\"off\"\n className={styles.input}\n disabled={disabled}\n id={id}\n onBlur={handleBlur}\n onChange={handleChange}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n placeholder={placeholder}\n ref={ref}\n type=\"text\"\n value={inputValue}\n />\n </div>\n {focused && data.length > 0 ? (\n <div className={styles.positioner} data-slot=\"tags-input-positioner\">\n <div\n aria-label=\"Suggestions\"\n className={styles.list}\n data-slot=\"tags-input-list\"\n role=\"listbox\"\n >\n {suggestions.length > 0 ? (\n suggestions.map((item) => (\n <button\n aria-selected={false}\n className={styles.item}\n data-slot=\"tags-input-item\"\n key={item}\n onClick={() => addTag(item)}\n onMouseDown={(event: MouseEvent<HTMLButtonElement>) => event.preventDefault()}\n role=\"option\"\n type=\"button\"\n >\n {item}\n </button>\n ))\n ) : (\n <div className={styles.empty} data-slot=\"tags-input-empty\">\n No options\n </div>\n )}\n </div>\n </div>\n ) : null}\n </div>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"tags-input.cjs","names":["tagsInput","cx"],"sources":["../../../src/components/tags-input/tags-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport type {\n ChangeEvent,\n ChangeEventHandler,\n ComponentPropsWithoutRef,\n FocusEvent,\n KeyboardEvent,\n MouseEvent,\n ClipboardEvent,\n ReactNode,\n} from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { tagsInput } from \"../../styled-system/recipes\";\n\nexport interface TagsInputProps\n extends Omit<\n ComponentPropsWithoutRef<\"input\">,\n \"children\" | \"defaultValue\" | \"onChange\" | \"type\" | \"value\"\n > {\n /** Current tags. Omit this prop to use the uncontrolled API. */\n tags?: readonly string[];\n /** Initial tags for the uncontrolled API. */\n defaultTags?: readonly string[];\n /** Called whenever a tag is added or removed. */\n onTagsChange?: (tags: string[]) => void;\n /** Optional suggestions. A tag is accepted after a comma only when it is in this list. */\n data?: readonly string[];\n /** Visible label for the input. */\n label?: ReactNode;\n /** Current text query. This is the native input value, not the selected tags. */\n value?: string;\n /** Initial text query for an uncontrolled input. */\n defaultValue?: string;\n /** Native input change callback. */\n onChange?: ChangeEventHandler<HTMLInputElement>;\n /** Optional callback useful when a controlled query is cleared by selecting a tag. */\n onInputValueChange?: (value: string) => void;\n allowDuplicates?: boolean;\n delimiter?: string | RegExp;\n editable?: boolean;\n}\n\nfunction changedEvent(event: ChangeEvent<HTMLInputElement>, value: string) {\n const target = event.target;\n const nextTarget = {\n checked: target.checked,\n id: target.id,\n name: target.name,\n value,\n } as HTMLInputElement;\n\n return {\n ...event,\n currentTarget: nextTarget,\n target: nextTarget,\n } as ChangeEvent<HTMLInputElement>;\n}\n\nfunction tagKey(tag: string, index: number) {\n return `${tag}-${index}`;\n}\n\nfunction hasDelimiter(value: string, delimiter: string | RegExp) {\n if (typeof delimiter === \"string\") return delimiter.length > 0 && value.includes(delimiter);\n delimiter.lastIndex = 0;\n const result = delimiter.test(value);\n delimiter.lastIndex = 0;\n return result;\n}\n\nexport const TagsInput = forwardRef<HTMLInputElement, TagsInputProps>(function TagsInput(\n {\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-label\": ariaLabel,\n allowDuplicates = false,\n className,\n data = [],\n defaultTags = [],\n defaultValue = \"\",\n disabled = false,\n delimiter = /[,\\n]/u,\n editable = false,\n id: providedId,\n label,\n onBlur,\n onChange,\n onFocus,\n onInputValueChange,\n onTagsChange,\n placeholder,\n tags: controlledTags,\n value: controlledInputValue,\n ...props\n },\n ref,\n) {\n const generatedId = useId();\n const id = providedId ?? generatedId;\n const [uncontrolledTags, setUncontrolledTags] = useState<string[]>(() => [...defaultTags]);\n const [uncontrolledInputValue, setUncontrolledInputValue] = useState(defaultValue);\n const [focused, setFocused] = useState(false);\n const [composing, setComposing] = useState(false);\n const [editingTag, setEditingTag] = useState<{ index: number; value: string } | null>(null);\n const rootRef = useRef<HTMLDivElement>(null);\n const styles = tagsInput();\n const selectedTags = controlledTags === undefined ? uncontrolledTags : [...controlledTags];\n const inputValue =\n controlledInputValue === undefined ? uncontrolledInputValue : controlledInputValue;\n\n const updateTags = useCallback(\n (nextTags: string[]) => {\n if (controlledTags === undefined) setUncontrolledTags(nextTags);\n onTagsChange?.(nextTags);\n },\n [controlledTags, onTagsChange],\n );\n\n const clearInput = useCallback(\n (event?: ChangeEvent<HTMLInputElement>) => {\n if (controlledInputValue === undefined) setUncontrolledInputValue(\"\");\n onInputValueChange?.(\"\");\n if (event) onChange?.(changedEvent(event, \"\"));\n },\n [controlledInputValue, onChange, onInputValueChange],\n );\n\n const addTag = useCallback(\n (rawTag: string, event?: ChangeEvent<HTMLInputElement>) => {\n const tag = rawTag.trim();\n const isAllowed = data.length === 0 || data.includes(tag);\n const duplicate = selectedTags.includes(tag);\n if (tag && isAllowed && (allowDuplicates || !duplicate)) {\n if (editingTag) {\n updateTags([\n ...selectedTags.slice(0, editingTag.index),\n tag,\n ...selectedTags.slice(editingTag.index),\n ]);\n setEditingTag(null);\n } else {\n updateTags([...selectedTags, tag]);\n }\n } else if (editingTag) {\n return;\n }\n clearInput(event);\n },\n [allowDuplicates, clearInput, data, editingTag, selectedTags, updateTags],\n );\n\n const addDelimited = useCallback(\n (rawValue: string, event?: ChangeEvent<HTMLInputElement>) => {\n const separator =\n typeof delimiter === \"string\" && delimiter.length === 0 ? /\\r?\\n/u : delimiter;\n const values = rawValue\n .split(separator)\n .map((value) => value.trim())\n .filter(Boolean);\n const nextTags = [...selectedTags];\n for (const value of values) {\n const isAllowed = data.length === 0 || data.includes(value);\n if (isAllowed && (allowDuplicates || !nextTags.includes(value))) nextTags.push(value);\n }\n if (nextTags.length !== selectedTags.length) updateTags(nextTags);\n clearInput(event);\n },\n [allowDuplicates, clearInput, data, delimiter, selectedTags, updateTags],\n );\n\n const suggestions = useMemo(() => {\n const query = inputValue.trim().toLocaleLowerCase();\n return data.filter(\n (item) =>\n !selectedTags.includes(item) &&\n (query.length === 0 || item.toLocaleLowerCase().includes(query)),\n );\n }, [data, inputValue, selectedTags]);\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n const nextValue = event.currentTarget.value;\n if (nextValue.trim() === \"\") {\n clearInput(event);\n onChange?.(event);\n return;\n }\n\n if (!composing && hasDelimiter(nextValue, delimiter)) {\n addDelimited(nextValue, event);\n return;\n }\n\n if (controlledInputValue === undefined) setUncontrolledInputValue(nextValue);\n onInputValueChange?.(nextValue);\n onChange?.(event);\n }\n\n function handleBlur(event: FocusEvent<HTMLInputElement>) {\n setFocused(false);\n onBlur?.(event);\n }\n\n function handlePaste(event: ClipboardEvent<HTMLInputElement>) {\n const pasted = event.clipboardData.getData(\"text\");\n if (!pasted || (!hasDelimiter(pasted, delimiter) && !pasted.includes(\"\\n\"))) return;\n event.preventDefault();\n addDelimited(pasted);\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n if (event.nativeEvent.isComposing || composing) return;\n if (event.key === \"Enter\") {\n event.preventDefault();\n if (inputValue.trim()) addTag(inputValue);\n else if (editingTag) setEditingTag(null);\n } else if (event.key === \"Escape\" && editingTag) {\n event.preventDefault();\n updateTags([\n ...selectedTags.slice(0, editingTag.index),\n editingTag.value,\n ...selectedTags.slice(editingTag.index),\n ]);\n clearInput();\n setEditingTag(null);\n } else if (event.key === \"Backspace\" && !inputValue && editable && selectedTags.length > 0) {\n const index = selectedTags.length - 1;\n const tag = selectedTags[index];\n if (tag === undefined) return;\n setEditingTag({ index, value: tag });\n updateTags(selectedTags.slice(0, -1));\n if (controlledInputValue === undefined) setUncontrolledInputValue(tag);\n onInputValueChange?.(tag);\n }\n props.onKeyDown?.(event);\n }\n\n function removeTag(index: number) {\n updateTags(selectedTags.filter((_, currentIndex) => currentIndex !== index));\n }\n\n return (\n <div\n className={styles.root}\n data-jaci-component=\"tags-input\"\n data-slot=\"tags-input\"\n ref={rootRef}\n >\n {label ? (\n <label className={styles.label} data-slot=\"tags-input-label\" htmlFor={id}>\n {label}\n </label>\n ) : null}\n <div\n className={cx(styles.control, className)}\n data-disabled={disabled || undefined}\n data-focus={focused || undefined}\n data-slot=\"tags-input-control\"\n >\n <div className={styles.tagList} data-slot=\"tags-input-tag-list\">\n {selectedTags.map((tag, index) => (\n <span className={styles.tag} data-slot=\"tags-input-tag\" key={tagKey(tag, index)}>\n <span className={styles.tagLabel} data-slot=\"tags-input-tag-label\">\n {tag}\n </span>\n <button\n aria-label={`Remove ${tag}`}\n className={styles.tagRemove}\n data-slot=\"tags-input-tag-remove\"\n disabled={disabled}\n onClick={() => removeTag(index)}\n type=\"button\"\n >\n ×\n </button>\n </span>\n ))}\n </div>\n <input\n {...props}\n {...(ariaDescribedBy ? { \"aria-describedby\": ariaDescribedBy } : {})}\n {...(ariaInvalid !== undefined ? { \"aria-invalid\": ariaInvalid } : {})}\n {...(ariaLabel ? { \"aria-label\": ariaLabel } : {})}\n autoComplete=\"off\"\n className={styles.input}\n disabled={disabled}\n id={id}\n onBlur={handleBlur}\n onChange={handleChange}\n onCompositionEnd={() => setComposing(false)}\n onCompositionStart={() => setComposing(true)}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n placeholder={placeholder}\n ref={ref}\n type=\"text\"\n value={inputValue}\n />\n </div>\n {focused && data.length > 0 ? (\n <div className={styles.positioner} data-slot=\"tags-input-positioner\">\n <div\n aria-label=\"Suggestions\"\n className={styles.list}\n data-slot=\"tags-input-list\"\n role=\"listbox\"\n >\n {suggestions.length > 0 ? (\n suggestions.map((item) => (\n <button\n aria-selected={false}\n className={styles.item}\n data-slot=\"tags-input-item\"\n key={item}\n onClick={() => addTag(item)}\n onMouseDown={(event: MouseEvent<HTMLButtonElement>) => event.preventDefault()}\n role=\"option\"\n type=\"button\"\n >\n {item}\n </button>\n ))\n ) : (\n <div className={styles.empty} data-slot=\"tags-input-empty\">\n No options\n </div>\n )}\n </div>\n </div>\n ) : null}\n </div>\n );\n});\n"],"mappings":"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"}
@@ -19,6 +19,9 @@ interface TagsInputProps extends Omit<ComponentPropsWithoutRef<"input">, "childr
19
19
  onChange?: ChangeEventHandler<HTMLInputElement>;
20
20
  /** Optional callback useful when a controlled query is cleared by selecting a tag. */
21
21
  onInputValueChange?: (value: string) => void;
22
+ allowDuplicates?: boolean;
23
+ delimiter?: string | RegExp;
24
+ editable?: boolean;
22
25
  }
23
26
  declare const TagsInput: import("react").ForwardRefExoticComponent<TagsInputProps & import("react").RefAttributes<HTMLInputElement>>;
24
27
  //#endregion
@@ -19,6 +19,9 @@ interface TagsInputProps extends Omit<ComponentPropsWithoutRef<"input">, "childr
19
19
  onChange?: ChangeEventHandler<HTMLInputElement>;
20
20
  /** Optional callback useful when a controlled query is cleared by selecting a tag. */
21
21
  onInputValueChange?: (value: string) => void;
22
+ allowDuplicates?: boolean;
23
+ delimiter?: string | RegExp;
24
+ editable?: boolean;
22
25
  }
23
26
  declare const TagsInput: import("react").ForwardRefExoticComponent<TagsInputProps & import("react").RefAttributes<HTMLInputElement>>;
24
27
  //#endregion
@@ -18,12 +18,24 @@ function changedEvent(event, value) {
18
18
  target: nextTarget
19
19
  };
20
20
  }
21
- const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, className, data = [], defaultTags = [], defaultValue = "", disabled = false, id: providedId, label, onBlur, onChange, onFocus, onInputValueChange, onTagsChange, placeholder, tags: controlledTags, value: controlledInputValue, ...props }, ref) {
21
+ function tagKey(tag, index) {
22
+ return `${tag}-${index}`;
23
+ }
24
+ function hasDelimiter(value, delimiter) {
25
+ if (typeof delimiter === "string") return delimiter.length > 0 && value.includes(delimiter);
26
+ delimiter.lastIndex = 0;
27
+ const result = delimiter.test(value);
28
+ delimiter.lastIndex = 0;
29
+ return result;
30
+ }
31
+ const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, allowDuplicates = false, className, data = [], defaultTags = [], defaultValue = "", disabled = false, delimiter = /[,\n]/u, editable = false, id: providedId, label, onBlur, onChange, onFocus, onInputValueChange, onTagsChange, placeholder, tags: controlledTags, value: controlledInputValue, ...props }, ref) {
22
32
  const generatedId = useId();
23
33
  const id = providedId ?? generatedId;
24
34
  const [uncontrolledTags, setUncontrolledTags] = useState(() => [...defaultTags]);
25
35
  const [uncontrolledInputValue, setUncontrolledInputValue] = useState(defaultValue);
26
36
  const [focused, setFocused] = useState(false);
37
+ const [composing, setComposing] = useState(false);
38
+ const [editingTag, setEditingTag] = useState(null);
27
39
  const rootRef = useRef(null);
28
40
  const styles = tagsInput();
29
41
  const selectedTags = controlledTags === void 0 ? uncontrolledTags : [...controlledTags];
@@ -43,11 +55,38 @@ const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescri
43
55
  ]);
44
56
  const addTag = useCallback((rawTag, event) => {
45
57
  const tag = rawTag.trim();
46
- if (tag && !selectedTags.includes(tag) && data.includes(tag)) updateTags([...selectedTags, tag]);
58
+ const isAllowed = data.length === 0 || data.includes(tag);
59
+ const duplicate = selectedTags.includes(tag);
60
+ if (tag && isAllowed && (allowDuplicates || !duplicate)) if (editingTag) {
61
+ updateTags([
62
+ ...selectedTags.slice(0, editingTag.index),
63
+ tag,
64
+ ...selectedTags.slice(editingTag.index)
65
+ ]);
66
+ setEditingTag(null);
67
+ } else updateTags([...selectedTags, tag]);
68
+ else if (editingTag) return;
69
+ clearInput(event);
70
+ }, [
71
+ allowDuplicates,
72
+ clearInput,
73
+ data,
74
+ editingTag,
75
+ selectedTags,
76
+ updateTags
77
+ ]);
78
+ const addDelimited = useCallback((rawValue, event) => {
79
+ const separator = typeof delimiter === "string" && delimiter.length === 0 ? /\r?\n/u : delimiter;
80
+ const values = rawValue.split(separator).map((value) => value.trim()).filter(Boolean);
81
+ const nextTags = [...selectedTags];
82
+ for (const value of values) if ((data.length === 0 || data.includes(value)) && (allowDuplicates || !nextTags.includes(value))) nextTags.push(value);
83
+ if (nextTags.length !== selectedTags.length) updateTags(nextTags);
47
84
  clearInput(event);
48
85
  }, [
86
+ allowDuplicates,
49
87
  clearInput,
50
88
  data,
89
+ delimiter,
51
90
  selectedTags,
52
91
  updateTags
53
92
  ]);
@@ -66,8 +105,8 @@ const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescri
66
105
  onChange?.(event);
67
106
  return;
68
107
  }
69
- if (nextValue.endsWith(",")) {
70
- addTag(nextValue.slice(0, -1), event);
108
+ if (!composing && hasDelimiter(nextValue, delimiter)) {
109
+ addDelimited(nextValue, event);
71
110
  return;
72
111
  }
73
112
  if (controlledInputValue === void 0) setUncontrolledInputValue(nextValue);
@@ -78,6 +117,41 @@ const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescri
78
117
  setFocused(false);
79
118
  onBlur?.(event);
80
119
  }
120
+ function handlePaste(event) {
121
+ const pasted = event.clipboardData.getData("text");
122
+ if (!pasted || !hasDelimiter(pasted, delimiter) && !pasted.includes("\n")) return;
123
+ event.preventDefault();
124
+ addDelimited(pasted);
125
+ }
126
+ function handleKeyDown(event) {
127
+ if (event.nativeEvent.isComposing || composing) return;
128
+ if (event.key === "Enter") {
129
+ event.preventDefault();
130
+ if (inputValue.trim()) addTag(inputValue);
131
+ else if (editingTag) setEditingTag(null);
132
+ } else if (event.key === "Escape" && editingTag) {
133
+ event.preventDefault();
134
+ updateTags([
135
+ ...selectedTags.slice(0, editingTag.index),
136
+ editingTag.value,
137
+ ...selectedTags.slice(editingTag.index)
138
+ ]);
139
+ clearInput();
140
+ setEditingTag(null);
141
+ } else if (event.key === "Backspace" && !inputValue && editable && selectedTags.length > 0) {
142
+ const index = selectedTags.length - 1;
143
+ const tag = selectedTags[index];
144
+ if (tag === void 0) return;
145
+ setEditingTag({
146
+ index,
147
+ value: tag
148
+ });
149
+ updateTags(selectedTags.slice(0, -1));
150
+ if (controlledInputValue === void 0) setUncontrolledInputValue(tag);
151
+ onInputValueChange?.(tag);
152
+ }
153
+ props.onKeyDown?.(event);
154
+ }
81
155
  function removeTag(index) {
82
156
  updateTags(selectedTags.filter((_, currentIndex) => currentIndex !== index));
83
157
  }
@@ -117,7 +191,7 @@ const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescri
117
191
  type: "button",
118
192
  children: "×"
119
193
  })]
120
- }, tag))
194
+ }, tagKey(tag, index)))
121
195
  }), /* @__PURE__ */ jsx("input", {
122
196
  ...props,
123
197
  ...ariaDescribedBy ? { "aria-describedby": ariaDescribedBy } : {},
@@ -129,10 +203,14 @@ const TagsInput = forwardRef(function TagsInput({ "aria-describedby": ariaDescri
129
203
  id,
130
204
  onBlur: handleBlur,
131
205
  onChange: handleChange,
206
+ onCompositionEnd: () => setComposing(false),
207
+ onCompositionStart: () => setComposing(true),
132
208
  onFocus: (event) => {
133
209
  setFocused(true);
134
210
  onFocus?.(event);
135
211
  },
212
+ onKeyDown: handleKeyDown,
213
+ onPaste: handlePaste,
136
214
  placeholder,
137
215
  ref,
138
216
  type: "text",
@@ -1 +1 @@
1
- {"version":3,"file":"tags-input.js","names":[],"sources":["../../../src/components/tags-input/tags-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport type {\n ChangeEvent,\n ChangeEventHandler,\n ComponentPropsWithoutRef,\n FocusEvent,\n MouseEvent,\n ReactNode,\n} from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { tagsInput } from \"../../styled-system/recipes\";\n\nexport interface TagsInputProps\n extends Omit<\n ComponentPropsWithoutRef<\"input\">,\n \"children\" | \"defaultValue\" | \"onChange\" | \"type\" | \"value\"\n > {\n /** Current tags. Omit this prop to use the uncontrolled API. */\n tags?: readonly string[];\n /** Initial tags for the uncontrolled API. */\n defaultTags?: readonly string[];\n /** Called whenever a tag is added or removed. */\n onTagsChange?: (tags: string[]) => void;\n /** Optional suggestions. A tag is accepted after a comma only when it is in this list. */\n data?: readonly string[];\n /** Visible label for the input. */\n label?: ReactNode;\n /** Current text query. This is the native input value, not the selected tags. */\n value?: string;\n /** Initial text query for an uncontrolled input. */\n defaultValue?: string;\n /** Native input change callback. */\n onChange?: ChangeEventHandler<HTMLInputElement>;\n /** Optional callback useful when a controlled query is cleared by selecting a tag. */\n onInputValueChange?: (value: string) => void;\n}\n\nfunction changedEvent(event: ChangeEvent<HTMLInputElement>, value: string) {\n const target = event.target;\n const nextTarget = {\n checked: target.checked,\n id: target.id,\n name: target.name,\n value,\n } as HTMLInputElement;\n\n return {\n ...event,\n currentTarget: nextTarget,\n target: nextTarget,\n } as ChangeEvent<HTMLInputElement>;\n}\n\nexport const TagsInput = forwardRef<HTMLInputElement, TagsInputProps>(function TagsInput(\n {\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-label\": ariaLabel,\n className,\n data = [],\n defaultTags = [],\n defaultValue = \"\",\n disabled = false,\n id: providedId,\n label,\n onBlur,\n onChange,\n onFocus,\n onInputValueChange,\n onTagsChange,\n placeholder,\n tags: controlledTags,\n value: controlledInputValue,\n ...props\n },\n ref,\n) {\n const generatedId = useId();\n const id = providedId ?? generatedId;\n const [uncontrolledTags, setUncontrolledTags] = useState<string[]>(() => [...defaultTags]);\n const [uncontrolledInputValue, setUncontrolledInputValue] = useState(defaultValue);\n const [focused, setFocused] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n const styles = tagsInput();\n const selectedTags = controlledTags === undefined ? uncontrolledTags : [...controlledTags];\n const inputValue =\n controlledInputValue === undefined ? uncontrolledInputValue : controlledInputValue;\n\n const updateTags = useCallback(\n (nextTags: string[]) => {\n if (controlledTags === undefined) setUncontrolledTags(nextTags);\n onTagsChange?.(nextTags);\n },\n [controlledTags, onTagsChange],\n );\n\n const clearInput = useCallback(\n (event?: ChangeEvent<HTMLInputElement>) => {\n if (controlledInputValue === undefined) setUncontrolledInputValue(\"\");\n onInputValueChange?.(\"\");\n if (event) onChange?.(changedEvent(event, \"\"));\n },\n [controlledInputValue, onChange, onInputValueChange],\n );\n\n const addTag = useCallback(\n (rawTag: string, event?: ChangeEvent<HTMLInputElement>) => {\n const tag = rawTag.trim();\n if (tag && !selectedTags.includes(tag) && data.includes(tag)) {\n updateTags([...selectedTags, tag]);\n }\n clearInput(event);\n },\n [clearInput, data, selectedTags, updateTags],\n );\n\n const suggestions = useMemo(() => {\n const query = inputValue.trim().toLocaleLowerCase();\n return data.filter(\n (item) =>\n !selectedTags.includes(item) &&\n (query.length === 0 || item.toLocaleLowerCase().includes(query)),\n );\n }, [data, inputValue, selectedTags]);\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n const nextValue = event.currentTarget.value;\n if (nextValue.trim() === \"\") {\n clearInput(event);\n onChange?.(event);\n return;\n }\n\n if (nextValue.endsWith(\",\")) {\n addTag(nextValue.slice(0, -1), event);\n return;\n }\n\n if (controlledInputValue === undefined) setUncontrolledInputValue(nextValue);\n onInputValueChange?.(nextValue);\n onChange?.(event);\n }\n\n function handleBlur(event: FocusEvent<HTMLInputElement>) {\n setFocused(false);\n onBlur?.(event);\n }\n\n function removeTag(index: number) {\n updateTags(selectedTags.filter((_, currentIndex) => currentIndex !== index));\n }\n\n return (\n <div\n className={styles.root}\n data-jaci-component=\"tags-input\"\n data-slot=\"tags-input\"\n ref={rootRef}\n >\n {label ? (\n <label className={styles.label} data-slot=\"tags-input-label\" htmlFor={id}>\n {label}\n </label>\n ) : null}\n <div\n className={cx(styles.control, className)}\n data-disabled={disabled || undefined}\n data-focus={focused || undefined}\n data-slot=\"tags-input-control\"\n >\n <div className={styles.tagList} data-slot=\"tags-input-tag-list\">\n {selectedTags.map((tag, index) => (\n <span className={styles.tag} data-slot=\"tags-input-tag\" key={tag}>\n <span className={styles.tagLabel} data-slot=\"tags-input-tag-label\">\n {tag}\n </span>\n <button\n aria-label={`Remove ${tag}`}\n className={styles.tagRemove}\n data-slot=\"tags-input-tag-remove\"\n disabled={disabled}\n onClick={() => removeTag(index)}\n type=\"button\"\n >\n ×\n </button>\n </span>\n ))}\n </div>\n <input\n {...props}\n {...(ariaDescribedBy ? { \"aria-describedby\": ariaDescribedBy } : {})}\n {...(ariaInvalid !== undefined ? { \"aria-invalid\": ariaInvalid } : {})}\n {...(ariaLabel ? { \"aria-label\": ariaLabel } : {})}\n autoComplete=\"off\"\n className={styles.input}\n disabled={disabled}\n id={id}\n onBlur={handleBlur}\n onChange={handleChange}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n placeholder={placeholder}\n ref={ref}\n type=\"text\"\n value={inputValue}\n />\n </div>\n {focused && data.length > 0 ? (\n <div className={styles.positioner} data-slot=\"tags-input-positioner\">\n <div\n aria-label=\"Suggestions\"\n className={styles.list}\n data-slot=\"tags-input-list\"\n role=\"listbox\"\n >\n {suggestions.length > 0 ? (\n suggestions.map((item) => (\n <button\n aria-selected={false}\n className={styles.item}\n data-slot=\"tags-input-item\"\n key={item}\n onClick={() => addTag(item)}\n onMouseDown={(event: MouseEvent<HTMLButtonElement>) => event.preventDefault()}\n role=\"option\"\n type=\"button\"\n >\n {item}\n </button>\n ))\n ) : (\n <div className={styles.empty} data-slot=\"tags-input-empty\">\n No options\n </div>\n )}\n </div>\n </div>\n ) : null}\n </div>\n );\n});\n"],"mappings":";;;;;;AAwCA,SAAS,aAAa,OAAsC,OAAe;CACzE,MAAM,SAAS,MAAM;CACrB,MAAM,aAAa;EACjB,SAAS,OAAO;EAChB,IAAI,OAAO;EACX,MAAM,OAAO;EACb;CACF;CAEA,OAAO;EACL,GAAG;EACH,eAAe;EACf,QAAQ;CACV;AACF;AAEA,MAAa,YAAY,WAA6C,SAAS,UAC7E,EACE,oBAAoB,iBACpB,gBAAgB,aAChB,cAAc,WACd,WACA,OAAO,CAAC,GACR,cAAc,CAAC,GACf,eAAe,IACf,WAAW,OACX,IAAI,YACJ,OACA,QACA,UACA,SACA,oBACA,cACA,aACA,MAAM,gBACN,OAAO,sBACP,GAAG,SAEL,KACA;CACA,MAAM,cAAc,MAAM;CAC1B,MAAM,KAAK,cAAc;CACzB,MAAM,CAAC,kBAAkB,uBAAuB,eAAyB,CAAC,GAAG,WAAW,CAAC;CACzF,MAAM,CAAC,wBAAwB,6BAA6B,SAAS,YAAY;CACjF,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,UAAU,OAAuB,IAAI;CAC3C,MAAM,SAAS,UAAU;CACzB,MAAM,eAAe,mBAAmB,KAAA,IAAY,mBAAmB,CAAC,GAAG,cAAc;CACzF,MAAM,aACJ,yBAAyB,KAAA,IAAY,yBAAyB;CAEhE,MAAM,aAAa,aAChB,aAAuB;EACtB,IAAI,mBAAmB,KAAA,GAAW,oBAAoB,QAAQ;EAC9D,eAAe,QAAQ;CACzB,GACA,CAAC,gBAAgB,YAAY,CAC/B;CAEA,MAAM,aAAa,aAChB,UAA0C;EACzC,IAAI,yBAAyB,KAAA,GAAW,0BAA0B,EAAE;EACpE,qBAAqB,EAAE;EACvB,IAAI,OAAO,WAAW,aAAa,OAAO,EAAE,CAAC;CAC/C,GACA;EAAC;EAAsB;EAAU;CAAkB,CACrD;CAEA,MAAM,SAAS,aACZ,QAAgB,UAA0C;EACzD,MAAM,MAAM,OAAO,KAAK;EACxB,IAAI,OAAO,CAAC,aAAa,SAAS,GAAG,KAAK,KAAK,SAAS,GAAG,GACzD,WAAW,CAAC,GAAG,cAAc,GAAG,CAAC;EAEnC,WAAW,KAAK;CAClB,GACA;EAAC;EAAY;EAAM;EAAc;CAAU,CAC7C;CAEA,MAAM,cAAc,cAAc;EAChC,MAAM,QAAQ,WAAW,KAAK,CAAC,CAAC,kBAAkB;EAClD,OAAO,KAAK,QACT,SACC,CAAC,aAAa,SAAS,IAAI,MAC1B,MAAM,WAAW,KAAK,KAAK,kBAAkB,CAAC,CAAC,SAAS,KAAK,EAClE;CACF,GAAG;EAAC;EAAM;EAAY;CAAY,CAAC;CAEnC,SAAS,aAAa,OAAsC;EAC1D,MAAM,YAAY,MAAM,cAAc;EACtC,IAAI,UAAU,KAAK,MAAM,IAAI;GAC3B,WAAW,KAAK;GAChB,WAAW,KAAK;GAChB;EACF;EAEA,IAAI,UAAU,SAAS,GAAG,GAAG;GAC3B,OAAO,UAAU,MAAM,GAAG,EAAE,GAAG,KAAK;GACpC;EACF;EAEA,IAAI,yBAAyB,KAAA,GAAW,0BAA0B,SAAS;EAC3E,qBAAqB,SAAS;EAC9B,WAAW,KAAK;CAClB;CAEA,SAAS,WAAW,OAAqC;EACvD,WAAW,KAAK;EAChB,SAAS,KAAK;CAChB;CAEA,SAAS,UAAU,OAAe;EAChC,WAAW,aAAa,QAAQ,GAAG,iBAAiB,iBAAiB,KAAK,CAAC;CAC7E;CAEA,OACE,qBAAC,OAAD;EACE,WAAW,OAAO;EAClB,uBAAoB;EACpB,aAAU;EACV,KAAK;YAJP;GAMG,QACC,oBAAC,SAAD;IAAO,WAAW,OAAO;IAAO,aAAU;IAAmB,SAAS;cACnE;GACI,CAAA,IACL;GACJ,qBAAC,OAAD;IACE,WAAW,GAAG,OAAO,SAAS,SAAS;IACvC,iBAAe,YAAY,KAAA;IAC3B,cAAY,WAAW,KAAA;IACvB,aAAU;cAJZ,CAME,oBAAC,OAAD;KAAK,WAAW,OAAO;KAAS,aAAU;eACvC,aAAa,KAAK,KAAK,UACtB,qBAAC,QAAD;MAAM,WAAW,OAAO;MAAK,aAAU;gBAAvC,CACE,oBAAC,QAAD;OAAM,WAAW,OAAO;OAAU,aAAU;iBACzC;MACG,CAAA,GACN,oBAAC,UAAD;OACE,cAAY,UAAU;OACtB,WAAW,OAAO;OAClB,aAAU;OACA;OACV,eAAe,UAAU,KAAK;OAC9B,MAAK;iBACN;MAEO,CAAA,CACJ;QAduD,GAcvD,CACP;IACE,CAAA,GACL,oBAAC,SAAD;KACE,GAAI;KACJ,GAAK,kBAAkB,EAAE,oBAAoB,gBAAgB,IAAI,CAAC;KAClE,GAAK,gBAAgB,KAAA,IAAY,EAAE,gBAAgB,YAAY,IAAI,CAAC;KACpE,GAAK,YAAY,EAAE,cAAc,UAAU,IAAI,CAAC;KAChD,cAAa;KACb,WAAW,OAAO;KACR;KACN;KACJ,QAAQ;KACR,UAAU;KACV,UAAU,UAAU;MAClB,WAAW,IAAI;MACf,UAAU,KAAK;KACjB;KACa;KACR;KACL,MAAK;KACL,OAAO;IACR,CAAA,CACE;;GACJ,WAAW,KAAK,SAAS,IACxB,oBAAC,OAAD;IAAK,WAAW,OAAO;IAAY,aAAU;cAC3C,oBAAC,OAAD;KACE,cAAW;KACX,WAAW,OAAO;KAClB,aAAU;KACV,MAAK;eAEJ,YAAY,SAAS,IACpB,YAAY,KAAK,SACf,oBAAC,UAAD;MACE,iBAAe;MACf,WAAW,OAAO;MAClB,aAAU;MAEV,eAAe,OAAO,IAAI;MAC1B,cAAc,UAAyC,MAAM,eAAe;MAC5E,MAAK;MACL,MAAK;gBAEJ;KACK,GAPD,IAOC,CACT,IAED,oBAAC,OAAD;MAAK,WAAW,OAAO;MAAO,aAAU;gBAAmB;KAEtD,CAAA;IAEJ,CAAA;GACF,CAAA,IACH;EACD;;AAET,CAAC"}
1
+ {"version":3,"file":"tags-input.js","names":[],"sources":["../../../src/components/tags-input/tags-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport type {\n ChangeEvent,\n ChangeEventHandler,\n ComponentPropsWithoutRef,\n FocusEvent,\n KeyboardEvent,\n MouseEvent,\n ClipboardEvent,\n ReactNode,\n} from \"react\";\n\nimport { cx } from \"../../styled-system/css\";\nimport { tagsInput } from \"../../styled-system/recipes\";\n\nexport interface TagsInputProps\n extends Omit<\n ComponentPropsWithoutRef<\"input\">,\n \"children\" | \"defaultValue\" | \"onChange\" | \"type\" | \"value\"\n > {\n /** Current tags. Omit this prop to use the uncontrolled API. */\n tags?: readonly string[];\n /** Initial tags for the uncontrolled API. */\n defaultTags?: readonly string[];\n /** Called whenever a tag is added or removed. */\n onTagsChange?: (tags: string[]) => void;\n /** Optional suggestions. A tag is accepted after a comma only when it is in this list. */\n data?: readonly string[];\n /** Visible label for the input. */\n label?: ReactNode;\n /** Current text query. This is the native input value, not the selected tags. */\n value?: string;\n /** Initial text query for an uncontrolled input. */\n defaultValue?: string;\n /** Native input change callback. */\n onChange?: ChangeEventHandler<HTMLInputElement>;\n /** Optional callback useful when a controlled query is cleared by selecting a tag. */\n onInputValueChange?: (value: string) => void;\n allowDuplicates?: boolean;\n delimiter?: string | RegExp;\n editable?: boolean;\n}\n\nfunction changedEvent(event: ChangeEvent<HTMLInputElement>, value: string) {\n const target = event.target;\n const nextTarget = {\n checked: target.checked,\n id: target.id,\n name: target.name,\n value,\n } as HTMLInputElement;\n\n return {\n ...event,\n currentTarget: nextTarget,\n target: nextTarget,\n } as ChangeEvent<HTMLInputElement>;\n}\n\nfunction tagKey(tag: string, index: number) {\n return `${tag}-${index}`;\n}\n\nfunction hasDelimiter(value: string, delimiter: string | RegExp) {\n if (typeof delimiter === \"string\") return delimiter.length > 0 && value.includes(delimiter);\n delimiter.lastIndex = 0;\n const result = delimiter.test(value);\n delimiter.lastIndex = 0;\n return result;\n}\n\nexport const TagsInput = forwardRef<HTMLInputElement, TagsInputProps>(function TagsInput(\n {\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-label\": ariaLabel,\n allowDuplicates = false,\n className,\n data = [],\n defaultTags = [],\n defaultValue = \"\",\n disabled = false,\n delimiter = /[,\\n]/u,\n editable = false,\n id: providedId,\n label,\n onBlur,\n onChange,\n onFocus,\n onInputValueChange,\n onTagsChange,\n placeholder,\n tags: controlledTags,\n value: controlledInputValue,\n ...props\n },\n ref,\n) {\n const generatedId = useId();\n const id = providedId ?? generatedId;\n const [uncontrolledTags, setUncontrolledTags] = useState<string[]>(() => [...defaultTags]);\n const [uncontrolledInputValue, setUncontrolledInputValue] = useState(defaultValue);\n const [focused, setFocused] = useState(false);\n const [composing, setComposing] = useState(false);\n const [editingTag, setEditingTag] = useState<{ index: number; value: string } | null>(null);\n const rootRef = useRef<HTMLDivElement>(null);\n const styles = tagsInput();\n const selectedTags = controlledTags === undefined ? uncontrolledTags : [...controlledTags];\n const inputValue =\n controlledInputValue === undefined ? uncontrolledInputValue : controlledInputValue;\n\n const updateTags = useCallback(\n (nextTags: string[]) => {\n if (controlledTags === undefined) setUncontrolledTags(nextTags);\n onTagsChange?.(nextTags);\n },\n [controlledTags, onTagsChange],\n );\n\n const clearInput = useCallback(\n (event?: ChangeEvent<HTMLInputElement>) => {\n if (controlledInputValue === undefined) setUncontrolledInputValue(\"\");\n onInputValueChange?.(\"\");\n if (event) onChange?.(changedEvent(event, \"\"));\n },\n [controlledInputValue, onChange, onInputValueChange],\n );\n\n const addTag = useCallback(\n (rawTag: string, event?: ChangeEvent<HTMLInputElement>) => {\n const tag = rawTag.trim();\n const isAllowed = data.length === 0 || data.includes(tag);\n const duplicate = selectedTags.includes(tag);\n if (tag && isAllowed && (allowDuplicates || !duplicate)) {\n if (editingTag) {\n updateTags([\n ...selectedTags.slice(0, editingTag.index),\n tag,\n ...selectedTags.slice(editingTag.index),\n ]);\n setEditingTag(null);\n } else {\n updateTags([...selectedTags, tag]);\n }\n } else if (editingTag) {\n return;\n }\n clearInput(event);\n },\n [allowDuplicates, clearInput, data, editingTag, selectedTags, updateTags],\n );\n\n const addDelimited = useCallback(\n (rawValue: string, event?: ChangeEvent<HTMLInputElement>) => {\n const separator =\n typeof delimiter === \"string\" && delimiter.length === 0 ? /\\r?\\n/u : delimiter;\n const values = rawValue\n .split(separator)\n .map((value) => value.trim())\n .filter(Boolean);\n const nextTags = [...selectedTags];\n for (const value of values) {\n const isAllowed = data.length === 0 || data.includes(value);\n if (isAllowed && (allowDuplicates || !nextTags.includes(value))) nextTags.push(value);\n }\n if (nextTags.length !== selectedTags.length) updateTags(nextTags);\n clearInput(event);\n },\n [allowDuplicates, clearInput, data, delimiter, selectedTags, updateTags],\n );\n\n const suggestions = useMemo(() => {\n const query = inputValue.trim().toLocaleLowerCase();\n return data.filter(\n (item) =>\n !selectedTags.includes(item) &&\n (query.length === 0 || item.toLocaleLowerCase().includes(query)),\n );\n }, [data, inputValue, selectedTags]);\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n const nextValue = event.currentTarget.value;\n if (nextValue.trim() === \"\") {\n clearInput(event);\n onChange?.(event);\n return;\n }\n\n if (!composing && hasDelimiter(nextValue, delimiter)) {\n addDelimited(nextValue, event);\n return;\n }\n\n if (controlledInputValue === undefined) setUncontrolledInputValue(nextValue);\n onInputValueChange?.(nextValue);\n onChange?.(event);\n }\n\n function handleBlur(event: FocusEvent<HTMLInputElement>) {\n setFocused(false);\n onBlur?.(event);\n }\n\n function handlePaste(event: ClipboardEvent<HTMLInputElement>) {\n const pasted = event.clipboardData.getData(\"text\");\n if (!pasted || (!hasDelimiter(pasted, delimiter) && !pasted.includes(\"\\n\"))) return;\n event.preventDefault();\n addDelimited(pasted);\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n if (event.nativeEvent.isComposing || composing) return;\n if (event.key === \"Enter\") {\n event.preventDefault();\n if (inputValue.trim()) addTag(inputValue);\n else if (editingTag) setEditingTag(null);\n } else if (event.key === \"Escape\" && editingTag) {\n event.preventDefault();\n updateTags([\n ...selectedTags.slice(0, editingTag.index),\n editingTag.value,\n ...selectedTags.slice(editingTag.index),\n ]);\n clearInput();\n setEditingTag(null);\n } else if (event.key === \"Backspace\" && !inputValue && editable && selectedTags.length > 0) {\n const index = selectedTags.length - 1;\n const tag = selectedTags[index];\n if (tag === undefined) return;\n setEditingTag({ index, value: tag });\n updateTags(selectedTags.slice(0, -1));\n if (controlledInputValue === undefined) setUncontrolledInputValue(tag);\n onInputValueChange?.(tag);\n }\n props.onKeyDown?.(event);\n }\n\n function removeTag(index: number) {\n updateTags(selectedTags.filter((_, currentIndex) => currentIndex !== index));\n }\n\n return (\n <div\n className={styles.root}\n data-jaci-component=\"tags-input\"\n data-slot=\"tags-input\"\n ref={rootRef}\n >\n {label ? (\n <label className={styles.label} data-slot=\"tags-input-label\" htmlFor={id}>\n {label}\n </label>\n ) : null}\n <div\n className={cx(styles.control, className)}\n data-disabled={disabled || undefined}\n data-focus={focused || undefined}\n data-slot=\"tags-input-control\"\n >\n <div className={styles.tagList} data-slot=\"tags-input-tag-list\">\n {selectedTags.map((tag, index) => (\n <span className={styles.tag} data-slot=\"tags-input-tag\" key={tagKey(tag, index)}>\n <span className={styles.tagLabel} data-slot=\"tags-input-tag-label\">\n {tag}\n </span>\n <button\n aria-label={`Remove ${tag}`}\n className={styles.tagRemove}\n data-slot=\"tags-input-tag-remove\"\n disabled={disabled}\n onClick={() => removeTag(index)}\n type=\"button\"\n >\n ×\n </button>\n </span>\n ))}\n </div>\n <input\n {...props}\n {...(ariaDescribedBy ? { \"aria-describedby\": ariaDescribedBy } : {})}\n {...(ariaInvalid !== undefined ? { \"aria-invalid\": ariaInvalid } : {})}\n {...(ariaLabel ? { \"aria-label\": ariaLabel } : {})}\n autoComplete=\"off\"\n className={styles.input}\n disabled={disabled}\n id={id}\n onBlur={handleBlur}\n onChange={handleChange}\n onCompositionEnd={() => setComposing(false)}\n onCompositionStart={() => setComposing(true)}\n onFocus={(event) => {\n setFocused(true);\n onFocus?.(event);\n }}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n placeholder={placeholder}\n ref={ref}\n type=\"text\"\n value={inputValue}\n />\n </div>\n {focused && data.length > 0 ? (\n <div className={styles.positioner} data-slot=\"tags-input-positioner\">\n <div\n aria-label=\"Suggestions\"\n className={styles.list}\n data-slot=\"tags-input-list\"\n role=\"listbox\"\n >\n {suggestions.length > 0 ? (\n suggestions.map((item) => (\n <button\n aria-selected={false}\n className={styles.item}\n data-slot=\"tags-input-item\"\n key={item}\n onClick={() => addTag(item)}\n onMouseDown={(event: MouseEvent<HTMLButtonElement>) => event.preventDefault()}\n role=\"option\"\n type=\"button\"\n >\n {item}\n </button>\n ))\n ) : (\n <div className={styles.empty} data-slot=\"tags-input-empty\">\n No options\n </div>\n )}\n </div>\n </div>\n ) : null}\n </div>\n );\n});\n"],"mappings":"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"}