@plexui/ui 0.20.16 → 0.21.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.
@@ -19,16 +19,17 @@ export const RadioGroup = ({ onChange, children, className, direction = "row", d
19
19
  }), [disabled, direction]);
20
20
  return (_jsx(RadioGroupContext, { value: store, children: _jsx(RadixRadioGroup.Root, { className: clsx(s.RadioGroup, className), "data-direction": direction, onValueChange: onChange, disabled: disabled, ...restProps, children: children }) }));
21
21
  };
22
- const Item = ({ value, disabled: itemDisabled = false, required, children, description, className, block = false, orientation = "left", ...restProps }) => {
22
+ const Item = ({ value, disabled: itemDisabled = false, required, children, description, className, block = false, orientation = "left", id: idProp, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...restProps }) => {
23
23
  const { disabled: groupDisabled } = useRadioGroupContext();
24
24
  const disabled = groupDisabled || itemDisabled;
25
25
  const id = useId();
26
- const itemId = `${value}-${id}`;
26
+ // These name the radio itself: a `Field` around the item labels it through them
27
+ const itemId = idProp ?? `${value}-${id}`;
27
28
  const descriptionId = description ? `${itemId}-description` : undefined;
28
29
  const label = (_jsxs("label", { htmlFor: itemId, className: clsx(s.RadioLabel, className), "data-disabled": disabled ? "" : undefined, "data-block": block ? "" : undefined, "data-orientation": orientation, onMouseDown: (event) => {
29
30
  if (!event.defaultPrevented && event.detail > 1)
30
31
  event.preventDefault();
31
- }, children: [_jsx("div", { className: s.RadioIndicatorWrapper, children: _jsx(RadixRadioGroup.Item, { id: itemId, value: value, disabled: disabled, required: required, className: s.RadioItem, "aria-describedby": descriptionId, children: _jsx(RadixRadioGroup.Indicator, { className: s.RadioIndicator }) }) }), children] }));
32
+ }, children: [_jsx("div", { className: s.RadioIndicatorWrapper, children: _jsx(RadixRadioGroup.Item, { id: itemId, value: value, disabled: disabled, required: required, className: s.RadioItem, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": [descriptionId, ariaDescribedBy].filter(Boolean).join(" ") || undefined, children: _jsx(RadixRadioGroup.Indicator, { className: s.RadioIndicator }) }) }), children] }));
32
33
  return (
33
34
  // Providing an extra wrapper enables `label` to be inline-flex, avoiding clickable whitespace
34
35
  // when radio options are of varied lengths.
@@ -1 +1 @@
1
- {"version":3,"file":"RadioGroup.js","sourceRoot":"","sources":["../../../../src/components/RadioGroup/RadioGroup.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\"\n\nimport clsx from \"clsx\"\nimport { RadioGroup as RadixRadioGroup } from \"radix-ui\"\nimport React, { createContext, use, useId, useMemo } from \"react\"\nimport s from \"@plexui/styles/components/RadioGroup/RadioGroup.module.css\"\n\ntype Direction = \"col\" | \"row\"\n\ntype RadioGroupContextValue = {\n disabled: boolean\n direction: Direction\n}\n\nconst RadioGroupContext = createContext<RadioGroupContextValue | null>(null)\n\nconst useRadioGroupContext = () => {\n const context = use(RadioGroupContext)\n\n if (!context) {\n throw new Error(\"RadioGroup components must be wrapped in <RadioGroup />\")\n }\n\n return context\n}\n\nexport type RadioGroupProps<T extends string> = {\n \"defaultValue\"?: T\n \"value\"?: T\n \"name\"?: string\n \"onChange\"?: (value: T) => void\n /** Accessible label for the radio options */\n \"aria-label\"?: string\n /** Determines the layout direction of the radio items\n * @default row\n */\n \"direction\"?: Direction\n /** Controls whether the entire radio group is disabled */\n \"disabled\"?: boolean\n /** Class applied to the radio group container */\n \"className\"?: string\n \"children\": React.ReactNode\n \"required\"?: boolean\n}\n\nexport const RadioGroup = <T extends string>({\n onChange,\n children,\n className,\n direction = \"row\",\n disabled = false,\n ...restProps\n}: RadioGroupProps<T>) => {\n const store = useMemo<RadioGroupContextValue>(\n () => ({\n disabled,\n direction,\n }),\n [disabled, direction],\n )\n\n return (\n <RadioGroupContext value={store}>\n <RadixRadioGroup.Root\n className={clsx(s.RadioGroup, className)}\n data-direction={direction}\n onValueChange={onChange}\n disabled={disabled}\n {...restProps}\n >\n {children}\n </RadixRadioGroup.Root>\n </RadioGroupContext>\n )\n}\n\nexport type RadioGroupItemProps<T extends string> = {\n value: T\n /** Determines if a given radio item is disabled */\n disabled?: boolean\n required?: boolean\n block?: boolean\n className?: string\n /**\n * The orientation of the radio indicator relative to the label.\n *\n * @default \"left\"\n */\n orientation?: \"left\" | \"right\"\n children: React.ReactNode\n /** Optional second line below the option's label. Linked via `aria-describedby`. */\n description?: React.ReactNode\n}\n\nconst Item = <T extends string>({\n value,\n disabled: itemDisabled = false,\n required,\n children,\n description,\n className,\n block = false,\n orientation = \"left\",\n ...restProps\n}: RadioGroupItemProps<T>) => {\n const { disabled: groupDisabled } = useRadioGroupContext()\n const disabled = groupDisabled || itemDisabled\n\n const id = useId()\n const itemId = `${value}-${id}`\n const descriptionId = description ? `${itemId}-description` : undefined\n\n const label = (\n <label\n htmlFor={itemId}\n className={clsx(s.RadioLabel, className)}\n data-disabled={disabled ? \"\" : undefined}\n data-block={block ? \"\" : undefined}\n data-orientation={orientation}\n onMouseDown={(event) => {\n if (!event.defaultPrevented && event.detail > 1) event.preventDefault()\n }}\n >\n <div className={s.RadioIndicatorWrapper}>\n <RadixRadioGroup.Item\n id={itemId}\n value={value}\n disabled={disabled}\n required={required}\n className={s.RadioItem}\n aria-describedby={descriptionId}\n >\n <RadixRadioGroup.Indicator className={s.RadioIndicator} />\n </RadixRadioGroup.Item>\n </div>\n {children}\n </label>\n )\n\n return (\n // Providing an extra wrapper enables `label` to be inline-flex, avoiding clickable whitespace\n // when radio options are of varied lengths.\n // NOTE: Important that this is `flex` to prevent the `inline-flex` label from extra, unintentional whitespace.\n <div className={clsx(\"flex\", block && \"w-full\")} {...restProps}>\n {description ? (\n // The description stays outside the label, so it describes the radio instead of naming it\n <div className={s.RadioItemGroup} data-block={block ? \"\" : undefined} data-orientation={orientation}>\n {label}\n <span id={descriptionId} className={s.RadioDescription} data-disabled={disabled ? \"\" : undefined}>\n {description}\n </span>\n </div>\n ) : (\n label\n )}\n </div>\n )\n}\n\nRadioGroup.Item = Item\n"]}
1
+ {"version":3,"file":"RadioGroup.js","sourceRoot":"","sources":["../../../../src/components/RadioGroup/RadioGroup.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,UAAU,IAAI,eAAe,EAAE,MAAM,UAAU,CAAA;AACxD,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AACjE,OAAO,CAAC,MAAM,4DAA4D,CAAA;AAS1E,MAAM,iBAAiB,GAAG,aAAa,CAAgC,IAAI,CAAC,CAAA;AAE5E,MAAM,oBAAoB,GAAG,GAAG,EAAE;IAChC,MAAM,OAAO,GAAG,GAAG,CAAC,iBAAiB,CAAC,CAAA;IAEtC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAA;IAC5E,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAqBD,MAAM,CAAC,MAAM,UAAU,GAAG,CAAmB,EAC3C,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAS,GAAG,KAAK,EACjB,QAAQ,GAAG,KAAK,EAChB,GAAG,SAAS,EACO,EAAE,EAAE;IACvB,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC;QACL,QAAQ;QACR,SAAS;KACV,CAAC,EACF,CAAC,QAAQ,EAAE,SAAS,CAAC,CACtB,CAAA;IAED,OAAO,CACL,KAAC,iBAAiB,IAAC,KAAK,EAAE,KAAK,YAC7B,KAAC,eAAe,CAAC,IAAI,IACnB,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU,EAAE,SAAS,CAAC,oBACxB,SAAS,EACzB,aAAa,EAAE,QAAQ,EACvB,QAAQ,EAAE,QAAQ,KACd,SAAS,YAEZ,QAAQ,GACY,GACL,CACrB,CAAA;AACH,CAAC,CAAA;AA0BD,MAAM,IAAI,GAAG,CAAmB,EAC9B,KAAK,EACL,QAAQ,EAAE,YAAY,GAAG,KAAK,EAC9B,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,SAAS,EACT,KAAK,GAAG,KAAK,EACb,WAAW,GAAG,MAAM,EACpB,EAAE,EAAE,MAAM,EACV,YAAY,EAAE,SAAS,EACvB,iBAAiB,EAAE,cAAc,EACjC,kBAAkB,EAAE,eAAe,EACnC,GAAG,SAAS,EACW,EAAE,EAAE;IAC3B,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,oBAAoB,EAAE,CAAA;IAC1D,MAAM,QAAQ,GAAG,aAAa,IAAI,YAAY,CAAA;IAE9C,MAAM,EAAE,GAAG,KAAK,EAAE,CAAA;IAClB,gFAAgF;IAChF,MAAM,MAAM,GAAG,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE,EAAE,CAAA;IACzC,MAAM,aAAa,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,MAAM,cAAc,CAAC,CAAC,CAAC,SAAS,CAAA;IAEvE,MAAM,KAAK,GAAG,CACZ,iBACE,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU,EAAE,SAAS,CAAC,mBACzB,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,gBAC5B,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,sBAChB,WAAW,EAC7B,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE;YACrB,IAAI,CAAC,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;gBAAE,KAAK,CAAC,cAAc,EAAE,CAAA;QACzE,CAAC,aAED,cAAK,SAAS,EAAE,CAAC,CAAC,qBAAqB,YACrC,KAAC,eAAe,CAAC,IAAI,IACnB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,CAAC,CAAC,SAAS,gBACV,SAAS,qBACJ,cAAc,sBACb,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,YAEzF,KAAC,eAAe,CAAC,SAAS,IAAC,SAAS,EAAE,CAAC,CAAC,cAAc,GAAI,GACrC,GACnB,EACL,QAAQ,IACH,CACT,CAAA;IAED,OAAO;IACL,8FAA8F;IAC9F,4CAA4C;IAC5C,+GAA+G;IAC/G,cAAK,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,QAAQ,CAAC,KAAM,SAAS,YAC3D,WAAW,CAAC,CAAC,CAAC;QACb,0FAA0F;QAC1F,eAAK,SAAS,EAAE,CAAC,CAAC,cAAc,gBAAc,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,sBAAoB,WAAW,aAChG,KAAK,EACN,eAAM,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,CAAC,gBAAgB,mBAAiB,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,YAC7F,WAAW,GACP,IACH,CACP,CAAC,CAAC,CAAC,CACF,KAAK,CACN,GACG,CACP,CAAA;AACH,CAAC,CAAA;AAED,UAAU,CAAC,IAAI,GAAG,IAAI,CAAA","sourcesContent":["\"use client\"\n\nimport clsx from \"clsx\"\nimport { RadioGroup as RadixRadioGroup } from \"radix-ui\"\nimport React, { createContext, use, useId, useMemo } from \"react\"\nimport s from \"@plexui/styles/components/RadioGroup/RadioGroup.module.css\"\n\ntype Direction = \"col\" | \"row\"\n\ntype RadioGroupContextValue = {\n disabled: boolean\n direction: Direction\n}\n\nconst RadioGroupContext = createContext<RadioGroupContextValue | null>(null)\n\nconst useRadioGroupContext = () => {\n const context = use(RadioGroupContext)\n\n if (!context) {\n throw new Error(\"RadioGroup components must be wrapped in <RadioGroup />\")\n }\n\n return context\n}\n\nexport type RadioGroupProps<T extends string> = {\n \"defaultValue\"?: T\n \"value\"?: T\n \"name\"?: string\n \"onChange\"?: (value: T) => void\n /** Accessible label for the radio options */\n \"aria-label\"?: string\n /** Determines the layout direction of the radio items\n * @default row\n */\n \"direction\"?: Direction\n /** Controls whether the entire radio group is disabled */\n \"disabled\"?: boolean\n /** Class applied to the radio group container */\n \"className\"?: string\n \"children\": React.ReactNode\n \"required\"?: boolean\n}\n\nexport const RadioGroup = <T extends string>({\n onChange,\n children,\n className,\n direction = \"row\",\n disabled = false,\n ...restProps\n}: RadioGroupProps<T>) => {\n const store = useMemo<RadioGroupContextValue>(\n () => ({\n disabled,\n direction,\n }),\n [disabled, direction],\n )\n\n return (\n <RadioGroupContext value={store}>\n <RadixRadioGroup.Root\n className={clsx(s.RadioGroup, className)}\n data-direction={direction}\n onValueChange={onChange}\n disabled={disabled}\n {...restProps}\n >\n {children}\n </RadixRadioGroup.Root>\n </RadioGroupContext>\n )\n}\n\nexport type RadioGroupItemProps<T extends string> = {\n value: T\n /** Determines if a given radio item is disabled */\n disabled?: boolean\n required?: boolean\n block?: boolean\n className?: string\n /**\n * The orientation of the radio indicator relative to the label.\n *\n * @default \"left\"\n */\n orientation?: \"left\" | \"right\"\n /** Id of the radio, so a label outside the item can target it with `htmlFor` */\n id?: string\n \"aria-label\"?: string\n \"aria-labelledby\"?: string\n \"aria-describedby\"?: string\n /** The option's label. Leave it out when a `Field` around the item names it. */\n children?: React.ReactNode\n /** Optional second line below the option's label. Linked via `aria-describedby`. */\n description?: React.ReactNode\n}\n\nconst Item = <T extends string>({\n value,\n disabled: itemDisabled = false,\n required,\n children,\n description,\n className,\n block = false,\n orientation = \"left\",\n id: idProp,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n ...restProps\n}: RadioGroupItemProps<T>) => {\n const { disabled: groupDisabled } = useRadioGroupContext()\n const disabled = groupDisabled || itemDisabled\n\n const id = useId()\n // These name the radio itself: a `Field` around the item labels it through them\n const itemId = idProp ?? `${value}-${id}`\n const descriptionId = description ? `${itemId}-description` : undefined\n\n const label = (\n <label\n htmlFor={itemId}\n className={clsx(s.RadioLabel, className)}\n data-disabled={disabled ? \"\" : undefined}\n data-block={block ? \"\" : undefined}\n data-orientation={orientation}\n onMouseDown={(event) => {\n if (!event.defaultPrevented && event.detail > 1) event.preventDefault()\n }}\n >\n <div className={s.RadioIndicatorWrapper}>\n <RadixRadioGroup.Item\n id={itemId}\n value={value}\n disabled={disabled}\n required={required}\n className={s.RadioItem}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={[descriptionId, ariaDescribedBy].filter(Boolean).join(\" \") || undefined}\n >\n <RadixRadioGroup.Indicator className={s.RadioIndicator} />\n </RadixRadioGroup.Item>\n </div>\n {children}\n </label>\n )\n\n return (\n // Providing an extra wrapper enables `label` to be inline-flex, avoiding clickable whitespace\n // when radio options are of varied lengths.\n // NOTE: Important that this is `flex` to prevent the `inline-flex` label from extra, unintentional whitespace.\n <div className={clsx(\"flex\", block && \"w-full\")} {...restProps}>\n {description ? (\n // The description stays outside the label, so it describes the radio instead of naming it\n <div className={s.RadioItemGroup} data-block={block ? \"\" : undefined} data-orientation={orientation}>\n {label}\n <span id={descriptionId} className={s.RadioDescription} data-disabled={disabled ? \"\" : undefined}>\n {description}\n </span>\n </div>\n ) : (\n label\n )}\n </div>\n )\n}\n\nRadioGroup.Item = Item\n"]}
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ import { flushSync } from "react-dom";
5
+ import { useLatestValue } from "../../hooks/useLatestValue.js";
6
+ import { moveItem, sortableKeyDown, sortablePointerDown } from "../../lib/sortable.js";
7
+ import { GripVertical } from "../Icon/index.js";
8
+ import s from "@plexui/styles/components/Sortable/Sortable.module.css";
9
+ const preventNativeDrag = (event) => event.preventDefault();
10
+ /** Reorders its direct children by dragging, from anywhere but a control, or by a `Sortable.Handle` where grips are shown. */
11
+ export function Sortable({ value, onChange, layout = "list", as: Tag = "div", disabled = false, handles, className, children, onPointerDown, onKeyDown, ...rest }) {
12
+ const latest = useLatestValue({ value, onChange });
13
+ const commit = (from, to) => {
14
+ flushSync(() => latest.current.onChange?.(moveItem(latest.current.value, from, to)));
15
+ };
16
+ const handlePointerDown = (event) => {
17
+ onPointerDown?.(event);
18
+ if (!disabled)
19
+ sortablePointerDown(event.nativeEvent, event.currentTarget, layout, commit);
20
+ };
21
+ const handleKeyDown = (event) => {
22
+ onKeyDown?.(event);
23
+ if (!disabled)
24
+ void sortableKeyDown(event.nativeEvent, event.currentTarget, commit);
25
+ };
26
+ return (_jsx(Tag, { ...rest, className: clsx(s.Sortable, className), "data-sortable": "", "data-layout": layout, "data-disabled": disabled ? "" : undefined, "data-handles": handles?.toString(), onPointerDown: handlePointerDown, onKeyDown: handleKeyDown, onDragStart: preventNativeDrag, children: children }));
27
+ }
28
+ /** The grip of an item. Arrow keys move the item while the handle has focus. */
29
+ function SortableHandle({ "aria-label": ariaLabel = "Reorder", className, children, ...rest }) {
30
+ return (_jsx("button", { ...rest, type: "button", className: clsx(s.Handle, className), "data-sortable-handle": "", "aria-label": ariaLabel, children: children ?? _jsx(GripVertical, { "aria-hidden": true }) }));
31
+ }
32
+ Sortable.Handle = SortableHandle;
33
+ //# sourceMappingURL=Sortable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sortable.js","sourceRoot":"","sources":["../../../../src/components/Sortable/Sortable.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,IAAI,MAAM,MAAM,CAAA;AAEvB,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,mBAAmB,EAAuB,MAAM,oBAAoB,CAAA;AACxG,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAEtC,OAAO,CAAC,MAAM,wDAAwD,CAAA;AAqCtE,MAAM,iBAAiB,GAAG,CAAC,KAAgB,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,CAAA;AAEtE,8HAA8H;AAC9H,MAAM,UAAU,QAAQ,CAAI,EAC1B,KAAK,EACL,QAAQ,EACR,MAAM,GAAG,MAAM,EACf,EAAE,EAAE,GAAG,GAAG,KAAK,EACf,QAAQ,GAAG,KAAK,EAChB,OAAO,EACP,SAAS,EACT,QAAQ,EACR,aAAa,EACb,SAAS,EACT,GAAG,IAAI,EACU;IACjB,MAAM,MAAM,GAAG,cAAc,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;IAElD,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAU,EAAE,EAAE;QAC1C,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;IACtF,CAAC,CAAA;IAED,MAAM,iBAAiB,GAAG,CAAC,KAAmC,EAAE,EAAE;QAChE,aAAa,EAAE,CAAC,KAAK,CAAC,CAAA;QACtB,IAAI,CAAC,QAAQ;YAAE,mBAAmB,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5F,CAAC,CAAA;IAED,MAAM,aAAa,GAAG,CAAC,KAAoC,EAAE,EAAE;QAC7D,SAAS,EAAE,CAAC,KAAK,CAAC,CAAA;QAClB,IAAI,CAAC,QAAQ;YAAE,KAAK,eAAe,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;IACrF,CAAC,CAAA;IAED,OAAO,CACL,KAAC,GAAG,OACE,IAAI,EACR,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,mBACxB,EAAE,iBACH,MAAM,mBACJ,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,kBAC1B,OAAO,EAAE,QAAQ,EAAE,EACjC,aAAa,EAAE,iBAAiB,EAChC,SAAS,EAAE,aAAa,EACxB,WAAW,EAAE,iBAAiB,YAE7B,QAAQ,GACL,CACP,CAAA;AACH,CAAC;AAUD,gFAAgF;AAChF,SAAS,cAAc,CAAC,EAAE,YAAY,EAAE,SAAS,GAAG,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAuB;IAChH,OAAO,CACL,oBAAY,IAAI,EAAE,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,0BAAuB,EAAE,gBAAa,SAAS,YAChH,QAAQ,IAAI,KAAC,YAAY,0BAAe,GAClC,CACV,CAAA;AACH,CAAC;AAED,QAAQ,CAAC,MAAM,GAAG,cAAc,CAAA","sourcesContent":["\"use client\"\n\nimport clsx from \"clsx\"\nimport type { ComponentProps, DragEvent, ElementType, KeyboardEvent, PointerEvent, ReactNode } from \"react\"\nimport { flushSync } from \"react-dom\"\n\nimport { useLatestValue } from \"../../hooks/useLatestValue\"\nimport { moveItem, sortableKeyDown, sortablePointerDown, type SortableLayout } from \"../../lib/sortable\"\nimport { GripVertical } from \"../Icon\"\n\nimport s from \"@plexui/styles/components/Sortable/Sortable.module.css\"\n\nexport type SortableProps<T> = {\n /**\n * The list, in the order of the children\n */\n value: readonly T[]\n /**\n * Called with the new order after a drop\n */\n onChange?: (value: T[]) => void\n /**\n * `list` for rows one under another, `grid` for items that stand side by side\n * @default list\n */\n layout?: SortableLayout\n /**\n * Element to render, such as `TableBody` for table rows\n * @default div\n */\n as?: ElementType\n /**\n * Turns dragging off\n * @default false\n */\n disabled?: boolean\n /**\n * Shows the grips of `Sortable.Handle`. A hidden grip still takes the keyboard, and the item drags from anywhere but a control.\n * @default true in a table, otherwise false\n */\n handles?: boolean\n /**\n * The items, one direct child per entry of `value`, keyed by the entry and not by its index\n */\n children?: ReactNode\n} & Omit<ComponentProps<\"div\">, \"children\" | \"onChange\">\n\nconst preventNativeDrag = (event: DragEvent) => event.preventDefault()\n\n/** Reorders its direct children by dragging, from anywhere but a control, or by a `Sortable.Handle` where grips are shown. */\nexport function Sortable<T>({\n value,\n onChange,\n layout = \"list\",\n as: Tag = \"div\",\n disabled = false,\n handles,\n className,\n children,\n onPointerDown,\n onKeyDown,\n ...rest\n}: SortableProps<T>) {\n const latest = useLatestValue({ value, onChange })\n\n const commit = (from: number, to: number) => {\n flushSync(() => latest.current.onChange?.(moveItem(latest.current.value, from, to)))\n }\n\n const handlePointerDown = (event: PointerEvent<HTMLDivElement>) => {\n onPointerDown?.(event)\n if (!disabled) sortablePointerDown(event.nativeEvent, event.currentTarget, layout, commit)\n }\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event)\n if (!disabled) void sortableKeyDown(event.nativeEvent, event.currentTarget, commit)\n }\n\n return (\n <Tag\n {...rest}\n className={clsx(s.Sortable, className)}\n data-sortable=\"\"\n data-layout={layout}\n data-disabled={disabled ? \"\" : undefined}\n data-handles={handles?.toString()}\n onPointerDown={handlePointerDown}\n onKeyDown={handleKeyDown}\n onDragStart={preventNativeDrag}\n >\n {children}\n </Tag>\n )\n}\n\nexport type SortableHandleProps = {\n /**\n * Name of the handle for assistive technology\n * @default Reorder\n */\n \"aria-label\"?: string\n} & ComponentProps<\"button\">\n\n/** The grip of an item. Arrow keys move the item while the handle has focus. */\nfunction SortableHandle({ \"aria-label\": ariaLabel = \"Reorder\", className, children, ...rest }: SortableHandleProps) {\n return (\n <button {...rest} type=\"button\" className={clsx(s.Handle, className)} data-sortable-handle=\"\" aria-label={ariaLabel}>\n {children ?? <GripVertical aria-hidden />}\n </button>\n )\n}\n\nSortable.Handle = SortableHandle\n"]}
@@ -0,0 +1,2 @@
1
+ export { Sortable } from "./Sortable.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/Sortable/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAgD,MAAM,YAAY,CAAA","sourcesContent":["export { Sortable, type SortableHandleProps, type SortableProps } from \"./Sortable\"\n"]}
@@ -12,7 +12,7 @@ const INPUT_CURSOR_TOLERANCE_PX = 10;
12
12
  const CONTAINER_PADDING_PX = 24;
13
13
  const SHAKE_ANIMATION_DURATION_MS = 500;
14
14
  export const TagInput = (props) => {
15
- const { defaultValue: defaultValueProp = [], value: controlledValue, size = "xl", onChange, validator, maxTags, placeholder = "", autoFocus = false, rows = 1, delimiters = [",", " "], disabled, pill = false, id, } = props;
15
+ const { defaultValue: defaultValueProp = [], value: controlledValue, size = "xl", variant = "outline", onChange, validator, maxTags, placeholder = "", autoFocus = false, rows = 1, delimiters = [",", " "], disabled, pill = false, id, } = props;
16
16
  const [internalTags, setInternalTags] = useState(defaultValueProp);
17
17
  const value = controlledValue ?? internalTags;
18
18
  const isControlled = controlledValue !== undefined;
@@ -331,7 +331,7 @@ export const TagInput = (props) => {
331
331
  };
332
332
  // @ts-expect-error - Fix once usehooks-ts is updated https://github.com/juliencrn/usehooks-ts/pull/675
333
333
  useOnClickOutside(containerRef, handleContainerBlur);
334
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: s.Container, ref: containerRef, onKeyDown: handleContainerKeyDown, onMouseDown: handleContainerMouseDown, onFocus: handleFocus, onBlur: handleBlur, "data-size": size, "data-focused": focused ? "" : undefined, "data-empty": tags.size === 0 ? "" : undefined, "data-pill": pill ? "" : undefined,
334
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: s.Container, ref: containerRef, onKeyDown: handleContainerKeyDown, onMouseDown: handleContainerMouseDown, onFocus: handleFocus, onBlur: handleBlur, "data-size": size, "data-variant": variant, "data-focused": focused ? "" : undefined, "data-empty": tags.size === 0 ? "" : undefined, "data-pill": pill ? "" : undefined,
335
335
  // At maxTags the input is gone, so the container becomes the tab stop;
336
336
  // arrow keys and Backspace then select and remove tags from here
337
337
  tabIndex: showInput || disabled ? -1 : 0, style: toCssVariables({ "min-rows": String(rows) }), "data-disabled": disabled ? "" : undefined, children: _jsxs("div", { className: s.TagInput, children: [Array.from(tags.values()).map((tag) => (_jsxs("div", { className: s.Tag, tabIndex: -1, onClick: () => setSelectedTag(tag.value), "data-tag": tag.value, "data-invalid": !tag.valid ? "" : undefined, "data-duplicate": duplicateTags.has(tag.value) ? "" : undefined, children: [_jsx("span", { className: "overflow-hidden text-ellipsis", title: tag.value, children: _jsx("span", { className: s.TagValue, children: tag.value }) }), _jsx("div", { className: s.TagRemove, onClick: () => removeTag(tag.value), role: "button", "aria-label": "Remove item", children: _jsx(X, {}) })] }, tag.value))), showInput && (_jsxs("div", { className: s.InputContainer, children: [_jsx("span", { ref: measureRef, className: s.InputMeasure }), _jsx("div", { className: s.InputWrapper, children: _jsx("input", { id: id, className: s.Input, ref: inputRef, value: currentInput, onChange: (e) => {
@@ -1 +1 @@
1
- {"version":3,"file":"TagInput.js","sourceRoot":"","sources":["../../../../src/components/TagInput/TagInput.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAElD,OAAO,EAAE,CAAC,EAAE,MAAM,SAAS,CAAA;AAC3B,OAAO,CAAC,MAAM,wDAAwD,CAAA;AAuEtE,oGAAoG;AACpG,MAAM,yBAAyB,GAAG,EAAE,CAAA;AACpC,mEAAmE;AACnE,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAC/B,MAAM,2BAA2B,GAAG,GAAG,CAAA;AAEvC,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAoB,EAAE,EAAE;IAC/C,MAAM,EACJ,YAAY,EAAE,gBAAgB,GAAG,EAAE,EACnC,KAAK,EAAE,eAAe,EACtB,IAAI,GAAG,IAAI,EACX,QAAQ,EACR,SAAS,EACT,OAAO,EACP,WAAW,GAAG,EAAE,EAChB,SAAS,GAAG,KAAK,EACjB,IAAI,GAAG,CAAC,EACR,UAAU,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,EACvB,QAAQ,EACR,IAAI,GAAG,KAAK,EACZ,EAAE,GACH,GAAG,KAAK,CAAA;IAET,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAA;IAElE,MAAM,KAAK,GAAG,eAAe,IAAI,YAAY,CAAA;IAC7C,MAAM,YAAY,GAAG,eAAe,KAAK,SAAS,CAAA;IAElD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;IAElF,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;IACpD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAA;IACjD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IACnE,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAc,IAAI,GAAG,EAAE,CAAC,CAAA;IAC1E,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAS,CAAC,CAAC,CAAA;IACvD,MAAM,eAAe,GAAG,cAAc,CAAC,SAAS,CAAC,CAAA;IACjD,MAAM,cAAc,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAA;IAE/C,MAAM,QAAQ,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAA;IAC/C,MAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAA;IACjD,MAAM,UAAU,GAAG,MAAM,CAAkB,IAAI,CAAC,CAAA;IAEhD,MAAM,SAAS,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,GAAG,OAAO,CAAA;IAEjD,oDAAoD;IACpD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;YACtC,OAAM;QACR,CAAC;QACD,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,IAAI,YAAY,CAAC,OAAO,CAAA;QAC/D,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,cAAc,CAAC,KAAK,EAAE,CAAA;QACxB,CAAC;aAAM,CAAC;YACN,MAAM,kBAAkB,GAAG,YAAY,CAAC,OAAO,CAAC,aAAa,CAC3D,cAAc,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CACpB,CAAA;YACvB,IAAI,kBAAkB,EAAE,CAAC;gBACvB,kBAAkB,CAAC,KAAK,EAAE,CAAA;YAC5B,CAAC;iBAAM,CAAC;gBACN,cAAc,CAAC,KAAK,EAAE,CAAA;YACxB,CAAC;QACH,CAAC;IACH,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAA,CAAC,qEAAqE;IAE3G,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAA;IAEjE,6DAA6D;IAC7D,EAAE;IACF,4CAA4C;IAC5C,0DAA0D;IAC1D,4DAA4D;IAC5D,kEAAkE;IAClE,mEAAmE;IACnE,sEAAsE;IACtE,mEAAmE;IACnE,+DAA+D;IAC/D,iEAAiE;IACjE,EAAE;IACF,qEAAqE;IACrE,kEAAkE;IAClE,6BAA6B;IAC7B,eAAe,CAAC,GAAG,EAAE;QACnB,IAAI,CAAC,YAAY,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;YACjD,OAAM;QACR,CAAC;QACD,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAA;QACtC,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAA;QAElC,MAAM,SAAS,GAAG,GAAG,EAAE;YACrB,MAAM,aAAa,GAAG,SAAS,CAAC,qBAAqB,EAAE,CAAA;YACvD,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAC5B,SAAS,CAAC,gBAAgB,CAAC,YAAY,CAAC,CACrB,CAAA;YAErB,6FAA6F;YAC7F,MAAM,SAAS,GACb,WAAW,CAAC,MAAM,GAAG,CAAC;gBACpB,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,qBAAqB,EAAE,CAAC,KAAK;gBACnE,CAAC,CAAC,aAAa,CAAC,IAAI,CAAA;YAExB,OAAO,CAAC,WAAW,GAAG,YAAY,IAAI,eAAe,IAAI,EAAE,CAAA;YAE3D,wEAAwE;YACxE,wDAAwD;YACxD,MAAM,SAAS,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC,KAAK,GAAG,yBAAyB,CAAA;YAEnF,oGAAoG;YACpG,MAAM,cAAc,GAAG,aAAa,CAAC,KAAK,GAAG,SAAS,GAAG,oBAAoB,CAAA;YAE7E,IAAI,SAAS,IAAI,cAAc,EAAE,CAAC;gBAChC,aAAa,CAAC,cAAc,CAAC,CAAA;YAC/B,CAAC;iBAAM,CAAC;gBACN,iGAAiG;gBACjG,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,GAAG,aAAa,CAAC,IAAI,GAAG,oBAAoB,CAAA;gBAC7E,aAAa,CAAC,KAAK,CAAC,CAAA;YACtB,CAAC;QACH,CAAC,CAAA;QAED,8DAA8D;QAC9D,SAAS,EAAE,CAAA;QAEX,sEAAsE;QACtE,kEAAkE;QAClE,mEAAmE;QACnE,sDAAsD;QACtD,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,SAAS,CAAC,CAAA;QAC9C,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QAC3B,MAAM,KAAK,GAAG,SAAS,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAA;QACtD,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;QAE/C,OAAO,GAAG,EAAE;YACV,QAAQ,CAAC,UAAU,EAAE,CAAA;QACvB,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,YAAY,EAAE,eAAe,EAAE,IAAI,CAAC,CAAC,CAAA,CAAC,+DAA+D;IAEzG,+CAA+C;IAC/C,0BAA0B;IAC1B,+CAA+C;IAE/C,oEAAoE;IACpE,qDAAqD;IACrD,MAAM,OAAO,GAAG,CAAC,SAAmB,EAAE,EAAE;QACtC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAA;QAC5B,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,OAAO,IAAI,MAAM,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC;gBACtC,MAAK;YACP,CAAC;YACD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAA;YAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,SAAQ;YACV,CAAC;YACD,IAAI,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBACxB,gBAAgB,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAA;gBAC9D,UAAU,CACR,GAAG,EAAE,CACH,gBAAgB,CAAC,CAAC,QAAQ,EAAE,EAAE;oBAC5B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAA;oBACjC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;oBACvB,OAAO,OAAO,CAAA;gBAChB,CAAC,CAAC,EACJ,2BAA2B,CAC5B,CAAA;gBACD,SAAQ;YACV,CAAC;YACD,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,CAAA;YAC3D,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;YACxD,OAAO,GAAG,IAAI,CAAA;QAChB,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAM;QACR,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;QAC3C,cAAc,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAA;QAEjC,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,eAAe,CAAC,OAAO,CAAC,CAAA;QAC1B,CAAC;IACH,CAAC,CAAA;IAED,MAAM,MAAM,GAAG,CAAC,QAAgB,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;IAExD,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,EAAE;QAChC,IAAI,QAAQ,EAAE,CAAC;YACb,OAAM;QACR,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAClB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;QAC3C,cAAc,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAA;QACjC,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,eAAe,CAAC,OAAO,CAAC,CAAA;QAC1B,CAAC;QACD,IAAI,WAAW,KAAK,GAAG,EAAE,CAAC;YACxB,cAAc,CAAC,IAAI,CAAC,CAAA;QACtB,CAAC;IACH,CAAC,CAAA;IAED,MAAM,iBAAiB,GAAG,GAAG,EAAE;QAC7B,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACpB,OAAM;QACR,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QACvC,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAA;QAC7C,CAAC;aAAM,CAAC;YACN,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;YAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAA;YACxC,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;QAChC,CAAC;IACH,CAAC,CAAA;IAED,MAAM,aAAa,GAAG,GAAG,EAAE;QACzB,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,OAAM;QACR,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QACvC,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QAC9C,MAAM,KAAK,GAAG,SAAS,GAAG,CAAC,CAAA;QAC3B,IAAI,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAC3B,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;QAChC,CAAC;aAAM,CAAC;YACN,cAAc,CAAC,IAAI,CAAC,CAAA;QACtB,CAAC;IACH,CAAC,CAAA;IAED,+CAA+C;IAC/C,mBAAmB;IACnB,+CAA+C;IAE/C,MAAM,wBAAwB,GAAG,GAAG,EAAE;QACpC,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAA;QAC9B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,KAAK,CAAC,cAAc,KAAK,CAAC,IAAI,KAAK,CAAC,YAAY,KAAK,CAAC,CAAA;IAC/D,CAAC,CAAA;IAED,MAAM,mBAAmB,GAAG,GAAG,EAAE;QAC/B,UAAU,CAAC,KAAK,CAAC,CAAA;QACjB,cAAc,CAAC,IAAI,CAAC,CAAA;QACpB,uEAAuE;QACvE,IAAI,YAAY,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,MAAM,CAAC,YAAY,CAAC,CAAA;YACpB,eAAe,CAAC,EAAE,CAAC,CAAA;QACrB,CAAC;IACH,CAAC,CAAA;IAED,+CAA+C;IAC/C,iBAAiB;IACjB,+CAA+C;IAE/C,MAAM,sBAAsB,GAAG,CAAC,CAAwC,EAAE,EAAE;QAC1E,qFAAqF;QACrF,IAAI,QAAQ,EAAE,CAAC;YACb,OAAM;QACR,CAAC;QACD,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YACjD,IAAI,wBAAwB,EAAE,EAAE,CAAC;gBAC/B,CAAC,CAAC,cAAc,EAAE,CAAA;gBAClB,iBAAiB,EAAE,CAAA;YACrB,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,YAAY,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;YAC3D,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;gBACzB,CAAC,CAAC,cAAc,EAAE,CAAA;gBAClB,aAAa,EAAE,CAAA;YACjB,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;YACjC,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;gBACzB,SAAS,CAAC,WAAW,CAAC,CAAA;YACxB,CAAC;iBAAM,IAAI,wBAAwB,EAAE,EAAE,CAAC;gBACtC,iBAAiB,EAAE,CAAA;YACrB,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IAED,MAAM,kBAAkB,GAAG,CAAC,CAAwC,EAAE,EAAE;QACtE,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YACpD,CAAC,CAAC,cAAc,EAAE,CAAA;YAClB,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,CAAC,YAAY,CAAC,CAAA;gBACpB,eAAe,CAAC,EAAE,CAAC,CAAA;YACrB,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;YAC3B,IAAI,YAAY,EAAE,CAAC;gBACjB,CAAC,CAAC,cAAc,EAAE,CAAA;gBAClB,MAAM,CAAC,YAAY,CAAC,CAAA;gBACpB,eAAe,CAAC,EAAE,CAAC,CAAA;YACrB,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IAED,MAAM,WAAW,GAAG,CAAC,CAAyC,EAAE,EAAE;QAChE,MAAM,IAAI,GAAG,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QAC5C,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,CAAA;QAElB,0BAA0B;QAC1B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,EAAE,CAAA;YACnB,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;gBAC5B,QAAQ,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAA;YAC5C,CAAC;YACD,KAAK,GAAG,QAAQ,CAAA;QAClB,CAAC;QAED,qBAAqB;QACrB,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAE7B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,CAAC,CAAC,cAAc,EAAE,CAAA;YAClB,OAAO,CAAC,KAAK,CAAC,CAAA;QAChB,CAAC;IACH,CAAC,CAAA;IAED,MAAM,wBAAwB,GAAG,CAAC,CAAmB,EAAE,EAAE;QACvD,kDAAkD;QAClD,UAAU,CAAC,IAAI,CAAC,CAAA;QAEhB,gEAAgE;QAChE,+CAA+C;QAC/C,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,YAAY,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC9E,OAAM;QACR,CAAC;QAED,8DAA8D;QAC9D,cAAc,CAAC,IAAI,CAAC,CAAA;QAEpB,yFAAyF;QACzF,IACE,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,MAAM,YAAY,OAAO;YAC3B,QAAQ,CAAC,OAAO;YAChB,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,EACpC,CAAC;YACD,CAAC,CAAC,cAAc,EAAE,CAAA;YAElB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAA;YAC7E,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAA;YAE9B,wFAAwF;YACxF,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACnB,IAAI,OAAO,GAAG,GAAG,IAAI,OAAO,GAAG,IAAI,EAAE,CAAC;oBACpC,+BAA+B;oBAC/B,QAAQ,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;gBAC1C,CAAC;qBAAM,IAAI,OAAO,GAAG,MAAM,IAAI,OAAO,GAAG,KAAK,EAAE,CAAC;oBAC/C,kCAAkC;oBAClC,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAA;oBAC5C,QAAQ,CAAC,OAAO,CAAC,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;gBACpD,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAA;YAC3B,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IAED,MAAM,WAAW,GAAG,CAAC,CAAmB,EAAE,EAAE;QAC1C,kEAAkE;QAClE,gEAAgE;QAChE,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,YAAY,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC9E,OAAM;QACR,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,CAAA;IAClB,CAAC,CAAA;IAED,MAAM,UAAU,GAAG,CAAC,CAAmB,EAAE,EAAE;QACzC,qDAAqD;QACrD,sEAAsE;QACtE,qDAAqD;QACrD,IAAI,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC,CAAC,aAAa,YAAY,IAAI,CAAC,EAAE,CAAC;YAC3D,OAAM;QACR,CAAC;QACD,0EAA0E;QAC1E,IAAI,YAAY,CAAC,OAAO,IAAI,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC;YAC3E,2DAA2D;YAC3D,IAAI,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC;gBACnE,cAAc,CAAC,IAAI,CAAC,CAAA;YACtB,CAAC;YACD,OAAM;QACR,CAAC;QACD,mBAAmB,EAAE,CAAA;IACvB,CAAC,CAAA;IAED,uGAAuG;IACvG,iBAAiB,CAAC,YAAY,EAAE,mBAAmB,CAAC,CAAA;IAEpD,OAAO,CACL,8BACE,cACE,SAAS,EAAE,CAAC,CAAC,SAAS,EACtB,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,sBAAsB,EACjC,WAAW,EAAE,wBAAwB,EACrC,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,eACP,IAAI,kBACD,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,gBAC1B,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,eACjC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;gBAChC,uEAAuE;gBACvE,iEAAiE;gBACjE,QAAQ,EAAE,SAAS,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACxC,KAAK,EAAE,cAAc,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,mBACpC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,YAExC,eAAK,SAAS,EAAE,CAAC,CAAC,QAAQ,aACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACtC,eAEE,SAAS,EAAE,CAAC,CAAC,GAAG,EAChB,QAAQ,EAAE,CAAC,CAAC,EACZ,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,cAC9B,GAAG,CAAC,KAAK,kBACL,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,oBACzB,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,aAE7D,eAAM,SAAS,EAAC,+BAA+B,EAAC,KAAK,EAAE,GAAG,CAAC,KAAK,YAC9D,eAAM,SAAS,EAAE,CAAC,CAAC,QAAQ,YAAG,GAAG,CAAC,KAAK,GAAQ,GAC1C,EACP,cACE,SAAS,EAAE,CAAC,CAAC,SAAS,EACtB,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EACnC,IAAI,EAAC,QAAQ,gBACF,aAAa,YAExB,KAAC,CAAC,KAAG,GACD,KAlBD,GAAG,CAAC,KAAK,CAmBV,CACP,CAAC,EACD,SAAS,IAAI,CACZ,eAAK,SAAS,EAAE,CAAC,CAAC,cAAc,aAE9B,eAAM,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC,YAAY,GAAI,EACpD,cAAK,SAAS,EAAE,CAAC,CAAC,YAAY,YAC5B,gBACE,EAAE,EAAE,EAAE,EACN,SAAS,EAAE,CAAC,CAAC,KAAK,EAClB,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;4CACd,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;wCACjC,CAAC,EACD,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,kBAAkB,EAC7B,WAAW,EAAE,eAAe,EAC5B,KAAK,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,EAC5B,QAAQ,EAAE,QAAQ,GAClB,GACE,IACF,CACP,IACG,GACF,EAEL,OAAO,CAAC,CAAC,CAAC,CACT,eAAK,SAAS,EAAE,CAAC,CAAC,KAAK,aACpB,IAAI,CAAC,IAAI,EACV,eAAM,SAAS,EAAC,OAAO,kBAAS,EAC/B,OAAO,IACJ,CACP,CAAC,CAAC,CAAC,IAAI,IACP,CACJ,CAAA;AACH,CAAC,CAAA","sourcesContent":["\"use client\"\n\nimport React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\"\nimport { useOnClickOutside } from \"usehooks-ts\"\nimport { useLatestValue } from \"../../hooks/useLatestValue\"\nimport { toCssVariables } from \"../../lib/helpers\"\nimport type { Sizes } from \"../../types\"\nimport { X } from \"../Icon\"\nimport s from \"@plexui/styles/components/TagInput/TagInput.module.css\"\n\nexport type Tag = {\n value: string\n valid: boolean\n}\n\nexport type TagInputProps = {\n /**\n * The state of the tag input when it is initially rendered, used when uncontrolled\n */\n defaultValue?: Tag[]\n /**\n * The value of the tag input, used to control the tag input\n */\n value?: Tag[]\n /**\n * Allows the tag input to be targeted with htmlFor\n */\n id?: string\n /**\n * Corresponds with Input height when rows=1\n * @default xl\n */\n size?: Sizes<\"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\">\n /**\n * Callback function invoked when the tag list changes\n */\n onChange?: (tags: Tag[]) => void\n /**\n * Placeholder text for the input\n */\n placeholder?: string\n /**\n * A function that returns whether a given value is a valid tag.\n * Invalid tags are highlighted in red.\n * Tags are evaluated for validity only on creation; changing the validator function later has no effect\n */\n validator?: (value: string) => boolean\n /**\n * The maximum number of tags allowed before the input is disabled; displays a counter below the input\n */\n maxTags?: number\n /**\n * Whether to focus this input on mount\n * @default false\n */\n autoFocus?: boolean\n /**\n * The minimum number of rows for the tag input\n * @default 1\n */\n rows?: number\n /**\n * Controls what characters will count towards creating a new tag\n * @default [\",\", \" \"]\n */\n delimiters?: string[]\n /**\n * Disables the tag input visually and from interactions\n */\n disabled?: boolean\n /**\n * Renders the container (and inner chips) with a fully-rounded radius,\n * matching `<Input pill />` and `<SelectControl pill />` for horizontal\n * filter rows that mix all three controls.\n * @default false\n */\n pill?: boolean\n}\n\n// How many pixels to reserve on the right of the input as a buffer before wrapping to the next line\nconst INPUT_CURSOR_TOLERANCE_PX = 10\n// The total space reserved on either side of the input for padding\nconst CONTAINER_PADDING_PX = 24\nconst SHAKE_ANIMATION_DURATION_MS = 500\n\nexport const TagInput = (props: TagInputProps) => {\n const {\n defaultValue: defaultValueProp = [],\n value: controlledValue,\n size = \"xl\",\n onChange,\n validator,\n maxTags,\n placeholder = \"\",\n autoFocus = false,\n rows = 1,\n delimiters = [\",\", \" \"],\n disabled,\n pill = false,\n id,\n } = props\n\n const [internalTags, setInternalTags] = useState(defaultValueProp)\n\n const value = controlledValue ?? internalTags\n const isControlled = controlledValue !== undefined\n\n const tags = useMemo(() => new Map(value.map((tag) => [tag.value, tag])), [value])\n\n const [currentInput, setCurrentInput] = useState(\"\")\n const [focused, setFocused] = useState(autoFocus)\n const [selectedTag, setSelectedTag] = useState<string | null>(null)\n const [duplicateTags, setDuplicateTags] = useState<Set<string>>(new Set())\n const [inputWidth, setInputWidth] = useState<number>(0)\n const validatorLatest = useLatestValue(validator)\n const onChangeLatest = useLatestValue(onChange)\n\n const inputRef = useRef<HTMLInputElement>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n const measureRef = useRef<HTMLSpanElement>(null)\n\n const showInput = !maxTags || tags.size < maxTags\n\n // Handle focusing the correct part of the tag input\n useEffect(() => {\n if (!focused || !containerRef.current) {\n return\n }\n const defaultFocusEl = inputRef.current ?? containerRef.current\n if (selectedTag === null) {\n defaultFocusEl.focus()\n } else {\n const selectedTagElement = containerRef.current.querySelector(\n `[data-tag=\"${CSS.escape(selectedTag)}\"]`,\n ) as HTMLElement | null\n if (selectedTagElement) {\n selectedTagElement.focus()\n } else {\n defaultFocusEl.focus()\n }\n }\n }, [focused, selectedTag, showInput]) // Include showInput so we focus the container when we hide the input\n\n const placeholderText = tags.size === 0 ? placeholder : undefined\n\n // Handle positioning the real input inside of the container.\n //\n // Reasons we need to recompute input width:\n // 1. Tags added / removed → last-tag right edge changes\n // 2. User typing into input → measured text width changes\n // 3. Container or chip dimensions change for any other reason —\n // window resize, font load, parent layout shift, OR a `size`/\n // `pill` prop flip that re-tunes chip height + font + gutter via\n // CSS variables. Without watching for (3), `inputWidth` stays\n // stale and the next typing flushes a one-off layout jump\n // (visible as an extra wrap row right after toggling size).\n //\n // ResizeObserver covers (3) — it fires whenever the container or any\n // chip changes box-size, including the synchronous CSS-var-driven\n // reflow on a `size` toggle.\n useLayoutEffect(() => {\n if (!containerRef.current || !measureRef.current) {\n return\n }\n const container = containerRef.current\n const measure = measureRef.current\n\n const recompute = () => {\n const containerRect = container.getBoundingClientRect()\n const tagElements = Array.from(\n container.querySelectorAll(\"[data-tag]\"),\n ) as HTMLDivElement[]\n\n // Grab the right edge of the last tag if it exists, otherwise the left edge of the container\n const leftBound =\n tagElements.length > 0\n ? tagElements[tagElements.length - 1].getBoundingClientRect().right\n : containerRect.left\n\n measure.textContent = currentInput || placeholderText || \"\"\n\n // By padding the text width, we ensure the input wraps to the next line\n // before we get to the point where we start to overflow\n const textWidth = measure.getBoundingClientRect().width + INPUT_CURSOR_TOLERANCE_PX\n\n // Calculate the remaining space based on the last tag's right edge (or container's left if no tags)\n const remainingSpace = containerRect.right - leftBound - CONTAINER_PADDING_PX\n\n if (textWidth <= remainingSpace) {\n setInputWidth(remainingSpace)\n } else {\n // We are going to flow to a new line, so recalculate remaining space based on the container size\n const width = containerRect.right - containerRect.left - CONTAINER_PADDING_PX\n setInputWidth(width)\n }\n }\n\n // Initial measurement (covers tags-array changes and typing).\n recompute()\n\n // Watch the container + every chip for any layout change. When `size`\n // changes the container itself usually keeps the same width (it's\n // `width: 100%`), but each chip's box does change — observing both\n // is the cheapest way to catch every relevant reflow.\n const observer = new ResizeObserver(recompute)\n observer.observe(container)\n const chips = container.querySelectorAll(\"[data-tag]\")\n chips.forEach((chip) => observer.observe(chip))\n\n return () => {\n observer.disconnect()\n }\n }, [currentInput, placeholderText, tags]) // Include tags so we reflow the container when we delete a tag\n\n // ============================================\n // Tag lifecycle functions\n // ============================================\n\n // Adds every value in one update: a paste of \"a,b\" must not compute\n // each addition from the same stale `tags` snapshot.\n const addTags = (tagValues: string[]) => {\n const newSet = new Map(tags)\n let changed = false\n for (const tagValue of tagValues) {\n if (maxTags && newSet.size >= maxTags) {\n break\n }\n const trimmed = tagValue.trim()\n if (!trimmed) {\n continue\n }\n if (newSet.has(trimmed)) {\n setDuplicateTags((prevTags) => new Set(prevTags).add(trimmed))\n setTimeout(\n () =>\n setDuplicateTags((prevTags) => {\n const newTags = new Set(prevTags)\n newTags.delete(trimmed)\n return newTags\n }),\n SHAKE_ANIMATION_DURATION_MS,\n )\n continue\n }\n const validTag = validatorLatest.current?.(trimmed) ?? true\n newSet.set(trimmed, { value: trimmed, valid: validTag })\n changed = true\n }\n if (!changed) {\n return\n }\n const newTags = Array.from(newSet.values())\n onChangeLatest.current?.(newTags)\n\n if (!isControlled) {\n setInternalTags(newTags)\n }\n }\n\n const addTag = (tagValue: string) => addTags([tagValue])\n\n const removeTag = (tag: string) => {\n if (disabled) {\n return\n }\n const newSet = new Map(tags)\n newSet.delete(tag)\n const newTags = Array.from(newSet.values())\n onChangeLatest.current?.(newTags)\n if (!isControlled) {\n setInternalTags(newTags)\n }\n if (selectedTag === tag) {\n setSelectedTag(null)\n }\n }\n\n const selectPreviousTag = () => {\n if (tags.size === 0) {\n return\n }\n const tagList = Array.from(tags.keys())\n if (selectedTag === null) {\n setSelectedTag(tagList[tagList.length - 1])\n } else {\n const prevIndex = tagList.indexOf(selectedTag)\n const index = Math.max(0, prevIndex - 1)\n setSelectedTag(tagList[index])\n }\n }\n\n const selectNextTag = () => {\n if (selectedTag === null) {\n return\n }\n const tagList = Array.from(tags.keys())\n const prevIndex = tagList.indexOf(selectedTag)\n const index = prevIndex + 1\n if (index < tagList.length) {\n setSelectedTag(tagList[index])\n } else {\n setSelectedTag(null)\n }\n }\n\n // ============================================\n // Helper functions\n // ============================================\n\n const isEmptyStartingSelection = () => {\n const input = inputRef.current\n if (!input) {\n return true\n }\n return input.selectionStart === 0 && input.selectionEnd === 0\n }\n\n const handleContainerBlur = () => {\n setFocused(false)\n setSelectedTag(null)\n // A disabled tag input keeps the pending text instead of committing it\n if (currentInput && !disabled) {\n addTag(currentInput)\n setCurrentInput(\"\")\n }\n }\n\n // ============================================\n // Event handlers\n // ============================================\n\n const handleContainerKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n // A tag can keep focus while the input turns disabled: no selecting or removing then\n if (disabled) {\n return\n }\n if (e.key === \"ArrowLeft\" || e.key === \"ArrowUp\") {\n if (isEmptyStartingSelection()) {\n e.preventDefault()\n selectPreviousTag()\n }\n } else if (e.key === \"ArrowRight\" || e.key === \"ArrowDown\") {\n if (selectedTag !== null) {\n e.preventDefault()\n selectNextTag()\n }\n } else if (e.key === \"Backspace\") {\n if (selectedTag !== null) {\n removeTag(selectedTag)\n } else if (isEmptyStartingSelection()) {\n selectPreviousTag()\n }\n }\n }\n\n const handleInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\" || delimiters.includes(e.key)) {\n e.preventDefault()\n if (currentInput) {\n addTag(currentInput)\n setCurrentInput(\"\")\n }\n } else if (e.key === \"Tab\") {\n if (currentInput) {\n e.preventDefault()\n addTag(currentInput)\n setCurrentInput(\"\")\n }\n }\n }\n\n const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {\n const text = e.clipboardData.getData(\"Text\")\n let parts = [text]\n\n // Split by all delimiters\n for (const delimiter of delimiters) {\n const newParts = []\n for (const segment of parts) {\n newParts.push(...segment.split(delimiter))\n }\n parts = newParts\n }\n\n // Remove empty parts\n parts = parts.filter(Boolean)\n\n if (parts.length > 1) {\n e.preventDefault()\n addTags(parts)\n }\n }\n\n const handleContainerMouseDown = (e: React.MouseEvent) => {\n // All clicks in the container focus the container\n setFocused(true)\n\n // If the click is on a tag pill, we are done. The click handler\n // for the specific tag will handle focusing it\n if (e.target && e.target instanceof Element && e.target.closest(\"[data-tag]\")) {\n return\n }\n\n // Since we know we didn't click a tag, clear any selected tag\n setSelectedTag(null)\n\n // If this click is outside of the input, prevent it from \"stealing\" focus from the input\n if (\n e.target &&\n e.target instanceof Element &&\n inputRef.current &&\n !inputRef.current.contains(e.target)\n ) {\n e.preventDefault()\n\n const { left, right, top, bottom } = inputRef.current.getBoundingClientRect()\n const { clientX, clientY } = e\n\n // If this is the first click, clear the selection. Otherwise, select the input contents\n if (e.detail === 1) {\n if (clientY < top || clientX < left) {\n // \"Before\" the input: top/left\n inputRef.current.setSelectionRange(0, 0)\n } else if (clientY > bottom || clientX > right) {\n // \"After\" the input: bottom/right\n const length = inputRef.current.value.length\n inputRef.current.setSelectionRange(length, length)\n }\n } else {\n inputRef.current.select()\n }\n }\n }\n\n const handleFocus = (e: React.FocusEvent) => {\n // If the focus is on a tag pill, do not trigger input focus logic\n // The click handler for the specific tag will handle it instead\n if (e.target && e.target instanceof Element && e.target.closest(\"[data-tag]\")) {\n return\n }\n setFocused(true)\n }\n\n const handleBlur = (e: React.FocusEvent) => {\n // If there is no related target, don't remove focus.\n // This occurs on a click inside the container but not on any element.\n // The handleClickOutside handler handles other cases\n if (!e.relatedTarget || !(e.relatedTarget instanceof Node)) {\n return\n }\n // Don't clear focus if focus is only moved elsewhere within the container\n if (containerRef.current && containerRef.current.contains(e.relatedTarget)) {\n // If we just tabbed into the input, clear the selected tag\n if (inputRef.current && inputRef.current.contains(e.relatedTarget)) {\n setSelectedTag(null)\n }\n return\n }\n handleContainerBlur()\n }\n\n // @ts-expect-error - Fix once usehooks-ts is updated https://github.com/juliencrn/usehooks-ts/pull/675\n useOnClickOutside(containerRef, handleContainerBlur)\n\n return (\n <>\n <div\n className={s.Container}\n ref={containerRef}\n onKeyDown={handleContainerKeyDown}\n onMouseDown={handleContainerMouseDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n data-size={size}\n data-focused={focused ? \"\" : undefined}\n data-empty={tags.size === 0 ? \"\" : undefined}\n data-pill={pill ? \"\" : undefined}\n // At maxTags the input is gone, so the container becomes the tab stop;\n // arrow keys and Backspace then select and remove tags from here\n tabIndex={showInput || disabled ? -1 : 0}\n style={toCssVariables({ \"min-rows\": String(rows) })}\n data-disabled={disabled ? \"\" : undefined}\n >\n <div className={s.TagInput}>\n {Array.from(tags.values()).map((tag) => (\n <div\n key={tag.value}\n className={s.Tag}\n tabIndex={-1}\n onClick={() => setSelectedTag(tag.value)}\n data-tag={tag.value}\n data-invalid={!tag.valid ? \"\" : undefined}\n data-duplicate={duplicateTags.has(tag.value) ? \"\" : undefined}\n >\n <span className=\"overflow-hidden text-ellipsis\" title={tag.value}>\n <span className={s.TagValue}>{tag.value}</span>\n </span>\n <div\n className={s.TagRemove}\n onClick={() => removeTag(tag.value)}\n role=\"button\"\n aria-label=\"Remove item\"\n >\n <X />\n </div>\n </div>\n ))}\n {showInput && (\n <div className={s.InputContainer}>\n {/* Hidden element to measure input width */}\n <span ref={measureRef} className={s.InputMeasure} />\n <div className={s.InputWrapper}>\n <input\n id={id}\n className={s.Input}\n ref={inputRef}\n value={currentInput}\n onChange={(e) => {\n setCurrentInput(e.target.value)\n }}\n onPaste={handlePaste}\n onKeyDown={handleInputKeyDown}\n placeholder={placeholderText}\n style={{ width: inputWidth }}\n disabled={disabled}\n />\n </div>\n </div>\n )}\n </div>\n </div>\n {/* 0 means no limit, as for the input above: no counter (and no stray \"0\") */}\n {maxTags ? (\n <div className={s.Limit}>\n {tags.size}\n <span className=\"mx-px\">/</span>\n {maxTags}\n </div>\n ) : null}\n </>\n )\n}\n"]}
1
+ {"version":3,"file":"TagInput.js","sourceRoot":"","sources":["../../../../src/components/TagInput/TagInput.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\"\n\nimport React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\"\nimport { useOnClickOutside } from \"usehooks-ts\"\nimport { useLatestValue } from \"../../hooks/useLatestValue\"\nimport { toCssVariables } from \"../../lib/helpers\"\nimport type { Sizes, Variants } from \"../../types\"\nimport { X } from \"../Icon\"\nimport s from \"@plexui/styles/components/TagInput/TagInput.module.css\"\n\nexport type Tag = {\n value: string\n valid: boolean\n}\n\nexport type TagInputProps = {\n /**\n * The state of the tag input when it is initially rendered, used when uncontrolled\n */\n defaultValue?: Tag[]\n /**\n * The value of the tag input, used to control the tag input\n */\n value?: Tag[]\n /**\n * Allows the tag input to be targeted with htmlFor\n */\n id?: string\n /**\n * Corresponds with Input height when rows=1\n * @default xl\n */\n size?: Sizes<\"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\">\n /**\n * Visual style. `ghost` has no frame, for a field that its container already frames\n * @default outline\n */\n variant?: Variants<\"outline\" | \"ghost\">\n /**\n * Callback function invoked when the tag list changes\n */\n onChange?: (tags: Tag[]) => void\n /**\n * Placeholder text for the input\n */\n placeholder?: string\n /**\n * A function that returns whether a given value is a valid tag.\n * Invalid tags are highlighted in red.\n * Tags are evaluated for validity only on creation; changing the validator function later has no effect\n */\n validator?: (value: string) => boolean\n /**\n * The maximum number of tags allowed before the input is disabled; displays a counter below the input\n */\n maxTags?: number\n /**\n * Whether to focus this input on mount\n * @default false\n */\n autoFocus?: boolean\n /**\n * The minimum number of rows for the tag input\n * @default 1\n */\n rows?: number\n /**\n * Controls what characters will count towards creating a new tag\n * @default [\",\", \" \"]\n */\n delimiters?: string[]\n /**\n * Disables the tag input visually and from interactions\n */\n disabled?: boolean\n /**\n * Renders the container (and inner chips) with a fully-rounded radius,\n * matching `<Input pill />` and `<SelectControl pill />` for horizontal\n * filter rows that mix all three controls.\n * @default false\n */\n pill?: boolean\n}\n\n// How many pixels to reserve on the right of the input as a buffer before wrapping to the next line\nconst INPUT_CURSOR_TOLERANCE_PX = 10\n// The total space reserved on either side of the input for padding\nconst CONTAINER_PADDING_PX = 24\nconst SHAKE_ANIMATION_DURATION_MS = 500\n\nexport const TagInput = (props: TagInputProps) => {\n const {\n defaultValue: defaultValueProp = [],\n value: controlledValue,\n size = \"xl\",\n variant = \"outline\",\n onChange,\n validator,\n maxTags,\n placeholder = \"\",\n autoFocus = false,\n rows = 1,\n delimiters = [\",\", \" \"],\n disabled,\n pill = false,\n id,\n } = props\n\n const [internalTags, setInternalTags] = useState(defaultValueProp)\n\n const value = controlledValue ?? internalTags\n const isControlled = controlledValue !== undefined\n\n const tags = useMemo(() => new Map(value.map((tag) => [tag.value, tag])), [value])\n\n const [currentInput, setCurrentInput] = useState(\"\")\n const [focused, setFocused] = useState(autoFocus)\n const [selectedTag, setSelectedTag] = useState<string | null>(null)\n const [duplicateTags, setDuplicateTags] = useState<Set<string>>(new Set())\n const [inputWidth, setInputWidth] = useState<number>(0)\n const validatorLatest = useLatestValue(validator)\n const onChangeLatest = useLatestValue(onChange)\n\n const inputRef = useRef<HTMLInputElement>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n const measureRef = useRef<HTMLSpanElement>(null)\n\n const showInput = !maxTags || tags.size < maxTags\n\n // Handle focusing the correct part of the tag input\n useEffect(() => {\n if (!focused || !containerRef.current) {\n return\n }\n const defaultFocusEl = inputRef.current ?? containerRef.current\n if (selectedTag === null) {\n defaultFocusEl.focus()\n } else {\n const selectedTagElement = containerRef.current.querySelector(\n `[data-tag=\"${CSS.escape(selectedTag)}\"]`,\n ) as HTMLElement | null\n if (selectedTagElement) {\n selectedTagElement.focus()\n } else {\n defaultFocusEl.focus()\n }\n }\n }, [focused, selectedTag, showInput]) // Include showInput so we focus the container when we hide the input\n\n const placeholderText = tags.size === 0 ? placeholder : undefined\n\n // Handle positioning the real input inside of the container.\n //\n // Reasons we need to recompute input width:\n // 1. Tags added / removed → last-tag right edge changes\n // 2. User typing into input → measured text width changes\n // 3. Container or chip dimensions change for any other reason —\n // window resize, font load, parent layout shift, OR a `size`/\n // `pill` prop flip that re-tunes chip height + font + gutter via\n // CSS variables. Without watching for (3), `inputWidth` stays\n // stale and the next typing flushes a one-off layout jump\n // (visible as an extra wrap row right after toggling size).\n //\n // ResizeObserver covers (3) — it fires whenever the container or any\n // chip changes box-size, including the synchronous CSS-var-driven\n // reflow on a `size` toggle.\n useLayoutEffect(() => {\n if (!containerRef.current || !measureRef.current) {\n return\n }\n const container = containerRef.current\n const measure = measureRef.current\n\n const recompute = () => {\n const containerRect = container.getBoundingClientRect()\n const tagElements = Array.from(\n container.querySelectorAll(\"[data-tag]\"),\n ) as HTMLDivElement[]\n\n // Grab the right edge of the last tag if it exists, otherwise the left edge of the container\n const leftBound =\n tagElements.length > 0\n ? tagElements[tagElements.length - 1].getBoundingClientRect().right\n : containerRect.left\n\n measure.textContent = currentInput || placeholderText || \"\"\n\n // By padding the text width, we ensure the input wraps to the next line\n // before we get to the point where we start to overflow\n const textWidth = measure.getBoundingClientRect().width + INPUT_CURSOR_TOLERANCE_PX\n\n // Calculate the remaining space based on the last tag's right edge (or container's left if no tags)\n const remainingSpace = containerRect.right - leftBound - CONTAINER_PADDING_PX\n\n if (textWidth <= remainingSpace) {\n setInputWidth(remainingSpace)\n } else {\n // We are going to flow to a new line, so recalculate remaining space based on the container size\n const width = containerRect.right - containerRect.left - CONTAINER_PADDING_PX\n setInputWidth(width)\n }\n }\n\n // Initial measurement (covers tags-array changes and typing).\n recompute()\n\n // Watch the container + every chip for any layout change. When `size`\n // changes the container itself usually keeps the same width (it's\n // `width: 100%`), but each chip's box does change — observing both\n // is the cheapest way to catch every relevant reflow.\n const observer = new ResizeObserver(recompute)\n observer.observe(container)\n const chips = container.querySelectorAll(\"[data-tag]\")\n chips.forEach((chip) => observer.observe(chip))\n\n return () => {\n observer.disconnect()\n }\n }, [currentInput, placeholderText, tags]) // Include tags so we reflow the container when we delete a tag\n\n // ============================================\n // Tag lifecycle functions\n // ============================================\n\n // Adds every value in one update: a paste of \"a,b\" must not compute\n // each addition from the same stale `tags` snapshot.\n const addTags = (tagValues: string[]) => {\n const newSet = new Map(tags)\n let changed = false\n for (const tagValue of tagValues) {\n if (maxTags && newSet.size >= maxTags) {\n break\n }\n const trimmed = tagValue.trim()\n if (!trimmed) {\n continue\n }\n if (newSet.has(trimmed)) {\n setDuplicateTags((prevTags) => new Set(prevTags).add(trimmed))\n setTimeout(\n () =>\n setDuplicateTags((prevTags) => {\n const newTags = new Set(prevTags)\n newTags.delete(trimmed)\n return newTags\n }),\n SHAKE_ANIMATION_DURATION_MS,\n )\n continue\n }\n const validTag = validatorLatest.current?.(trimmed) ?? true\n newSet.set(trimmed, { value: trimmed, valid: validTag })\n changed = true\n }\n if (!changed) {\n return\n }\n const newTags = Array.from(newSet.values())\n onChangeLatest.current?.(newTags)\n\n if (!isControlled) {\n setInternalTags(newTags)\n }\n }\n\n const addTag = (tagValue: string) => addTags([tagValue])\n\n const removeTag = (tag: string) => {\n if (disabled) {\n return\n }\n const newSet = new Map(tags)\n newSet.delete(tag)\n const newTags = Array.from(newSet.values())\n onChangeLatest.current?.(newTags)\n if (!isControlled) {\n setInternalTags(newTags)\n }\n if (selectedTag === tag) {\n setSelectedTag(null)\n }\n }\n\n const selectPreviousTag = () => {\n if (tags.size === 0) {\n return\n }\n const tagList = Array.from(tags.keys())\n if (selectedTag === null) {\n setSelectedTag(tagList[tagList.length - 1])\n } else {\n const prevIndex = tagList.indexOf(selectedTag)\n const index = Math.max(0, prevIndex - 1)\n setSelectedTag(tagList[index])\n }\n }\n\n const selectNextTag = () => {\n if (selectedTag === null) {\n return\n }\n const tagList = Array.from(tags.keys())\n const prevIndex = tagList.indexOf(selectedTag)\n const index = prevIndex + 1\n if (index < tagList.length) {\n setSelectedTag(tagList[index])\n } else {\n setSelectedTag(null)\n }\n }\n\n // ============================================\n // Helper functions\n // ============================================\n\n const isEmptyStartingSelection = () => {\n const input = inputRef.current\n if (!input) {\n return true\n }\n return input.selectionStart === 0 && input.selectionEnd === 0\n }\n\n const handleContainerBlur = () => {\n setFocused(false)\n setSelectedTag(null)\n // A disabled tag input keeps the pending text instead of committing it\n if (currentInput && !disabled) {\n addTag(currentInput)\n setCurrentInput(\"\")\n }\n }\n\n // ============================================\n // Event handlers\n // ============================================\n\n const handleContainerKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n // A tag can keep focus while the input turns disabled: no selecting or removing then\n if (disabled) {\n return\n }\n if (e.key === \"ArrowLeft\" || e.key === \"ArrowUp\") {\n if (isEmptyStartingSelection()) {\n e.preventDefault()\n selectPreviousTag()\n }\n } else if (e.key === \"ArrowRight\" || e.key === \"ArrowDown\") {\n if (selectedTag !== null) {\n e.preventDefault()\n selectNextTag()\n }\n } else if (e.key === \"Backspace\") {\n if (selectedTag !== null) {\n removeTag(selectedTag)\n } else if (isEmptyStartingSelection()) {\n selectPreviousTag()\n }\n }\n }\n\n const handleInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\" || delimiters.includes(e.key)) {\n e.preventDefault()\n if (currentInput) {\n addTag(currentInput)\n setCurrentInput(\"\")\n }\n } else if (e.key === \"Tab\") {\n if (currentInput) {\n e.preventDefault()\n addTag(currentInput)\n setCurrentInput(\"\")\n }\n }\n }\n\n const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {\n const text = e.clipboardData.getData(\"Text\")\n let parts = [text]\n\n // Split by all delimiters\n for (const delimiter of delimiters) {\n const newParts = []\n for (const segment of parts) {\n newParts.push(...segment.split(delimiter))\n }\n parts = newParts\n }\n\n // Remove empty parts\n parts = parts.filter(Boolean)\n\n if (parts.length > 1) {\n e.preventDefault()\n addTags(parts)\n }\n }\n\n const handleContainerMouseDown = (e: React.MouseEvent) => {\n // All clicks in the container focus the container\n setFocused(true)\n\n // If the click is on a tag pill, we are done. The click handler\n // for the specific tag will handle focusing it\n if (e.target && e.target instanceof Element && e.target.closest(\"[data-tag]\")) {\n return\n }\n\n // Since we know we didn't click a tag, clear any selected tag\n setSelectedTag(null)\n\n // If this click is outside of the input, prevent it from \"stealing\" focus from the input\n if (\n e.target &&\n e.target instanceof Element &&\n inputRef.current &&\n !inputRef.current.contains(e.target)\n ) {\n e.preventDefault()\n\n const { left, right, top, bottom } = inputRef.current.getBoundingClientRect()\n const { clientX, clientY } = e\n\n // If this is the first click, clear the selection. Otherwise, select the input contents\n if (e.detail === 1) {\n if (clientY < top || clientX < left) {\n // \"Before\" the input: top/left\n inputRef.current.setSelectionRange(0, 0)\n } else if (clientY > bottom || clientX > right) {\n // \"After\" the input: bottom/right\n const length = inputRef.current.value.length\n inputRef.current.setSelectionRange(length, length)\n }\n } else {\n inputRef.current.select()\n }\n }\n }\n\n const handleFocus = (e: React.FocusEvent) => {\n // If the focus is on a tag pill, do not trigger input focus logic\n // The click handler for the specific tag will handle it instead\n if (e.target && e.target instanceof Element && e.target.closest(\"[data-tag]\")) {\n return\n }\n setFocused(true)\n }\n\n const handleBlur = (e: React.FocusEvent) => {\n // If there is no related target, don't remove focus.\n // This occurs on a click inside the container but not on any element.\n // The handleClickOutside handler handles other cases\n if (!e.relatedTarget || !(e.relatedTarget instanceof Node)) {\n return\n }\n // Don't clear focus if focus is only moved elsewhere within the container\n if (containerRef.current && containerRef.current.contains(e.relatedTarget)) {\n // If we just tabbed into the input, clear the selected tag\n if (inputRef.current && inputRef.current.contains(e.relatedTarget)) {\n setSelectedTag(null)\n }\n return\n }\n handleContainerBlur()\n }\n\n // @ts-expect-error - Fix once usehooks-ts is updated https://github.com/juliencrn/usehooks-ts/pull/675\n useOnClickOutside(containerRef, handleContainerBlur)\n\n return (\n <>\n <div\n className={s.Container}\n ref={containerRef}\n onKeyDown={handleContainerKeyDown}\n onMouseDown={handleContainerMouseDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n data-size={size}\n data-variant={variant}\n data-focused={focused ? \"\" : undefined}\n data-empty={tags.size === 0 ? \"\" : undefined}\n data-pill={pill ? \"\" : undefined}\n // At maxTags the input is gone, so the container becomes the tab stop;\n // arrow keys and Backspace then select and remove tags from here\n tabIndex={showInput || disabled ? -1 : 0}\n style={toCssVariables({ \"min-rows\": String(rows) })}\n data-disabled={disabled ? \"\" : undefined}\n >\n <div className={s.TagInput}>\n {Array.from(tags.values()).map((tag) => (\n <div\n key={tag.value}\n className={s.Tag}\n tabIndex={-1}\n onClick={() => setSelectedTag(tag.value)}\n data-tag={tag.value}\n data-invalid={!tag.valid ? \"\" : undefined}\n data-duplicate={duplicateTags.has(tag.value) ? \"\" : undefined}\n >\n <span className=\"overflow-hidden text-ellipsis\" title={tag.value}>\n <span className={s.TagValue}>{tag.value}</span>\n </span>\n <div\n className={s.TagRemove}\n onClick={() => removeTag(tag.value)}\n role=\"button\"\n aria-label=\"Remove item\"\n >\n <X />\n </div>\n </div>\n ))}\n {showInput && (\n <div className={s.InputContainer}>\n {/* Hidden element to measure input width */}\n <span ref={measureRef} className={s.InputMeasure} />\n <div className={s.InputWrapper}>\n <input\n id={id}\n className={s.Input}\n ref={inputRef}\n value={currentInput}\n onChange={(e) => {\n setCurrentInput(e.target.value)\n }}\n onPaste={handlePaste}\n onKeyDown={handleInputKeyDown}\n placeholder={placeholderText}\n style={{ width: inputWidth }}\n disabled={disabled}\n />\n </div>\n </div>\n )}\n </div>\n </div>\n {/* 0 means no limit, as for the input above: no counter (and no stray \"0\") */}\n {maxTags ? (\n <div className={s.Limit}>\n {tags.size}\n <span className=\"mx-px\">/</span>\n {maxTags}\n </div>\n ) : null}\n </>\n )\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"Textarea.js","sourceRoot":"","sources":["../../../../src/components/Textarea/Textarea.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\"\n\nimport clsx from \"clsx\"\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\"\nimport { mergeRefs } from \"react-merge-refs\"\nimport { toCssVariables } from \"../../lib/helpers\"\nimport { type ControlSize, type Sizes, type Variants } from \"../../types\"\nimport s from \"@plexui/styles/components/Textarea/Textarea.module.css\"\n\nexport type TextareaProps = {\n /**\n * Visual style of the textarea\n * @default outline\n */\n variant?: Variants<\"outline\" | \"soft\">\n /**\n * Controls the size of the textarea\n *\n * | 3xs | 2xs | xs | sm | md | lg | xl | 2xl | 3xl |\n * | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- |\n * | `22px` | `24px` | `26px` | `28px` | `32px` | `36px` | `40px` | `44px` | `48px` |\n *\n * @default md\n */\n size?: ControlSize\n /**\n * Applies a negative margin using the current gutter to optically align the text with surrounding content.\n */\n opticallyAlign?: \"start\" | \"end\"\n /**\n * Controls gutter on the edges of the textarea, defaults to value from `size`.\n *\n * | 2xs | xs | sm | md | lg | xl |\n * | ------ | ------ | ------ | ------ | ------ | ------ |\n * | `6px` | `8px` | `10px` | `12px` | `14px` | `16px` |\n */\n gutterSize?: Sizes<\"2xs\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\">\n /**\n * Disables the textarea visually and from interactions\n * @default false\n */\n disabled?: boolean\n /**\n * Mark the textarea as invalid\n * @default false\n */\n invalid?: boolean\n /**\n * Allow autofill extensions to appear in the textarea\n * @default false\n */\n allowAutofillExtensions?: boolean\n /**\n * Select all contents of the textarea when mounted.\n * @default false\n */\n autoSelect?: boolean\n /** Callback invoked when the textarea is autofilled by the browser */\n onAutofill?: () => void\n /**\n * Default number of rows to display\n * @default 3\n */\n rows?: number\n /**\n * Automatically adjust the height of the textarea based on its contents.\n * @default false\n */\n autoResize?: boolean\n /**\n * Maximum number of rows that can be displayed when autoResize is enabled.\n * @default Math.max(rows, 10)\n */\n maxRows?: number\n /** Ref for the textarea */\n ref?: React.Ref<HTMLTextAreaElement | null>\n} & Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, \"disabled\" | \"size\">\n\nexport const Textarea = (props: TextareaProps) => {\n const textAreaRef = useRef<HTMLTextAreaElement | null>(null)\n const inputIdSuffix = useId()\n // Prevent 1Password from appearing: 1Password won't display if it thinks this field is for searching\n // Allow user to override this for connecting to labels.\n const onePasswordPreventionId = `search-ui-input-${inputIdSuffix}`\n const {\n id,\n name,\n variant = \"outline\",\n size = \"md\",\n opticallyAlign,\n gutterSize,\n className,\n autoComplete,\n disabled = false,\n readOnly = false,\n invalid = false,\n // Default to `true` when presence of `name`\n allowAutofillExtensions = !!name,\n onFocus,\n onBlur,\n onAnimationStart,\n onAutofill,\n autoSelect,\n rows = 3,\n maxRows,\n autoResize,\n ref,\n onChange,\n ...restProps\n } = props\n\n const [focused, setFocused] = useState<boolean>(false)\n const computedMaxRows = autoResize ? Math.max(maxRows ?? 10, rows) : rows\n\n useEffect(() => {\n if (autoSelect) {\n textAreaRef.current?.select()\n }\n }, [autoSelect])\n\n const handleAnimationStart = (evt: React.AnimationEvent<HTMLTextAreaElement>) => {\n onAnimationStart?.(evt)\n if (evt.animationName === \"native-autofill-in\") {\n onAutofill?.()\n }\n }\n\n const autosizeTextarea = useCallback(() => {\n if (!autoResize || !textAreaRef.current || computedMaxRows === undefined) return\n textAreaRef.current.style.height = \"0px\"\n const scrollHeight = textAreaRef.current.scrollHeight\n textAreaRef.current.style.height = scrollHeight + \"px\"\n }, [autoResize, computedMaxRows])\n\n // Resize if value changes outside of user input - e.g. form reset\n useEffect(() => {\n autosizeTextarea()\n }, [props.value, rows, autosizeTextarea])\n\n return (\n <div\n className={clsx(s.Container, className)}\n data-variant={variant}\n data-size={size}\n data-optically-align={opticallyAlign}\n data-gutter-size={gutterSize}\n data-focused={focused}\n data-disabled={disabled ? \"\" : undefined}\n data-readonly={readOnly ? \"\" : undefined}\n data-invalid={invalid ? \"\" : undefined}\n style={toCssVariables({\n \"textarea-min-rows\": `${rows}`,\n \"textarea-max-rows\": `${computedMaxRows}`,\n })}\n >\n <textarea\n {...restProps}\n onChange={(evt) => {\n onChange?.(evt)\n autosizeTextarea()\n }}\n ref={mergeRefs([textAreaRef, ref])}\n id={id || (allowAutofillExtensions ? undefined : onePasswordPreventionId)}\n className={s.Textarea}\n name={name}\n autoComplete={autoComplete}\n // Expose the visual invalid state to assistive tech; an explicit aria-invalid wins\n aria-invalid={restProps[\"aria-invalid\"] ?? (invalid || undefined)}\n readOnly={readOnly}\n disabled={disabled}\n rows={rows}\n onFocus={(evt) => {\n setFocused(true)\n onFocus?.(evt)\n }}\n onBlur={(evt) => {\n setFocused(false)\n onBlur?.(evt)\n }}\n onAnimationStart={handleAnimationStart}\n // Prevent LastPass and 1Password from appearing\n data-lpignore={allowAutofillExtensions ? undefined : true}\n data-1p-ignore={allowAutofillExtensions ? undefined : true}\n />\n </div>\n )\n}\n"]}
1
+ {"version":3,"file":"Textarea.js","sourceRoot":"","sources":["../../../../src/components/Textarea/Textarea.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\"\n\nimport clsx from \"clsx\"\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\"\nimport { mergeRefs } from \"react-merge-refs\"\nimport { toCssVariables } from \"../../lib/helpers\"\nimport { type ControlSize, type Sizes, type Variants } from \"../../types\"\nimport s from \"@plexui/styles/components/Textarea/Textarea.module.css\"\n\nexport type TextareaProps = {\n /**\n * Visual style of the textarea\n * @default outline\n */\n variant?: Variants<\"outline\" | \"soft\" | \"ghost\">\n /**\n * Controls the size of the textarea\n *\n * | 3xs | 2xs | xs | sm | md | lg | xl | 2xl | 3xl |\n * | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- |\n * | `22px` | `24px` | `26px` | `28px` | `32px` | `36px` | `40px` | `44px` | `48px` |\n *\n * @default md\n */\n size?: ControlSize\n /**\n * Applies a negative margin using the current gutter to optically align the text with surrounding content.\n */\n opticallyAlign?: \"start\" | \"end\"\n /**\n * Controls gutter on the edges of the textarea, defaults to value from `size`.\n *\n * | 2xs | xs | sm | md | lg | xl |\n * | ------ | ------ | ------ | ------ | ------ | ------ |\n * | `6px` | `8px` | `10px` | `12px` | `14px` | `16px` |\n */\n gutterSize?: Sizes<\"2xs\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\">\n /**\n * Disables the textarea visually and from interactions\n * @default false\n */\n disabled?: boolean\n /**\n * Mark the textarea as invalid\n * @default false\n */\n invalid?: boolean\n /**\n * Allow autofill extensions to appear in the textarea\n * @default false\n */\n allowAutofillExtensions?: boolean\n /**\n * Select all contents of the textarea when mounted.\n * @default false\n */\n autoSelect?: boolean\n /** Callback invoked when the textarea is autofilled by the browser */\n onAutofill?: () => void\n /**\n * Default number of rows to display\n * @default 3\n */\n rows?: number\n /**\n * Automatically adjust the height of the textarea based on its contents.\n * @default false\n */\n autoResize?: boolean\n /**\n * Maximum number of rows that can be displayed when autoResize is enabled.\n * @default Math.max(rows, 10)\n */\n maxRows?: number\n /** Ref for the textarea */\n ref?: React.Ref<HTMLTextAreaElement | null>\n} & Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, \"disabled\" | \"size\">\n\nexport const Textarea = (props: TextareaProps) => {\n const textAreaRef = useRef<HTMLTextAreaElement | null>(null)\n const inputIdSuffix = useId()\n // Prevent 1Password from appearing: 1Password won't display if it thinks this field is for searching\n // Allow user to override this for connecting to labels.\n const onePasswordPreventionId = `search-ui-input-${inputIdSuffix}`\n const {\n id,\n name,\n variant = \"outline\",\n size = \"md\",\n opticallyAlign,\n gutterSize,\n className,\n autoComplete,\n disabled = false,\n readOnly = false,\n invalid = false,\n // Default to `true` when presence of `name`\n allowAutofillExtensions = !!name,\n onFocus,\n onBlur,\n onAnimationStart,\n onAutofill,\n autoSelect,\n rows = 3,\n maxRows,\n autoResize,\n ref,\n onChange,\n ...restProps\n } = props\n\n const [focused, setFocused] = useState<boolean>(false)\n const computedMaxRows = autoResize ? Math.max(maxRows ?? 10, rows) : rows\n\n useEffect(() => {\n if (autoSelect) {\n textAreaRef.current?.select()\n }\n }, [autoSelect])\n\n const handleAnimationStart = (evt: React.AnimationEvent<HTMLTextAreaElement>) => {\n onAnimationStart?.(evt)\n if (evt.animationName === \"native-autofill-in\") {\n onAutofill?.()\n }\n }\n\n const autosizeTextarea = useCallback(() => {\n if (!autoResize || !textAreaRef.current || computedMaxRows === undefined) return\n textAreaRef.current.style.height = \"0px\"\n const scrollHeight = textAreaRef.current.scrollHeight\n textAreaRef.current.style.height = scrollHeight + \"px\"\n }, [autoResize, computedMaxRows])\n\n // Resize if value changes outside of user input - e.g. form reset\n useEffect(() => {\n autosizeTextarea()\n }, [props.value, rows, autosizeTextarea])\n\n return (\n <div\n className={clsx(s.Container, className)}\n data-variant={variant}\n data-size={size}\n data-optically-align={opticallyAlign}\n data-gutter-size={gutterSize}\n data-focused={focused}\n data-disabled={disabled ? \"\" : undefined}\n data-readonly={readOnly ? \"\" : undefined}\n data-invalid={invalid ? \"\" : undefined}\n style={toCssVariables({\n \"textarea-min-rows\": `${rows}`,\n \"textarea-max-rows\": `${computedMaxRows}`,\n })}\n >\n <textarea\n {...restProps}\n onChange={(evt) => {\n onChange?.(evt)\n autosizeTextarea()\n }}\n ref={mergeRefs([textAreaRef, ref])}\n id={id || (allowAutofillExtensions ? undefined : onePasswordPreventionId)}\n className={s.Textarea}\n name={name}\n autoComplete={autoComplete}\n // Expose the visual invalid state to assistive tech; an explicit aria-invalid wins\n aria-invalid={restProps[\"aria-invalid\"] ?? (invalid || undefined)}\n readOnly={readOnly}\n disabled={disabled}\n rows={rows}\n onFocus={(evt) => {\n setFocused(true)\n onFocus?.(evt)\n }}\n onBlur={(evt) => {\n setFocused(false)\n onBlur?.(evt)\n }}\n onAnimationStart={handleAnimationStart}\n // Prevent LastPass and 1Password from appearing\n data-lpignore={allowAutofillExtensions ? undefined : true}\n data-1p-ignore={allowAutofillExtensions ? undefined : true}\n />\n </div>\n )\n}\n"]}
@@ -0,0 +1,406 @@
1
+ import s from "@plexui/styles/components/Sortable/Sortable.module.css";
2
+ const START_DISTANCE = 4;
3
+ const TOUCH_HOLD = 200;
4
+ const SCROLL_EDGE = 80;
5
+ const SCROLL_SLOW = 4;
6
+ const SCROLL_FAST = 30;
7
+ const NEAR_LIST = 40;
8
+ const LINE_INSET = 16;
9
+ const PART = 4;
10
+ // --cubic-enter: an animation here cannot read a CSS variable
11
+ const EASING = "cubic-bezier(.19,1,.22,1)";
12
+ const HANDLE = "[data-sortable-handle]";
13
+ const INTERACTIVE = [
14
+ "a[href]",
15
+ "button",
16
+ "input",
17
+ "select",
18
+ "summary",
19
+ "textarea",
20
+ "[contenteditable]",
21
+ "[role=button]",
22
+ "[role=checkbox]",
23
+ "[role=combobox]",
24
+ "[role=link]",
25
+ "[role=radio]",
26
+ "[role=slider]",
27
+ "[role=switch]",
28
+ "[role=tab]",
29
+ ].join(",");
30
+ // Read at the moment of use: the setting can change while the page is open
31
+ const reduced = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches;
32
+ const itemsOf = (list) => [...list.children];
33
+ /** Grips stand in a table, elsewhere only when the list asks for them */
34
+ const showsHandles = (list) => (list.dataset.handles ?? String(list.tagName === "TBODY")) === "true";
35
+ /** The direct child of the list that holds `target` */
36
+ const itemOf = (list, target) => {
37
+ let node = target;
38
+ while (node && node.parentElement !== list)
39
+ node = node.parentElement;
40
+ return node;
41
+ };
42
+ /** The order after a move. `to` counts the list without the moved item. */
43
+ export const moveItem = (items, from, to) => {
44
+ const next = [...items];
45
+ next.splice(to, 0, ...next.splice(from, 1));
46
+ return next;
47
+ };
48
+ const labelOf = (item) => {
49
+ const named = item.dataset.sortableLabel ?? item.querySelector("[data-sortable-label], label")?.textContent;
50
+ if (named)
51
+ return named.trim();
52
+ // The cells of a row or the lines of a card run together: the first one is the name
53
+ const texts = document.createTreeWalker(item, NodeFilter.SHOW_TEXT);
54
+ while (texts.nextNode()) {
55
+ if (texts.currentNode.textContent?.trim())
56
+ return texts.currentNode.parentElement?.textContent?.trim() ?? "";
57
+ }
58
+ return "";
59
+ };
60
+ /** What a screen reader hears: a picture has no text of its own */
61
+ const nameOf = (item) => labelOf(item) || item.querySelector("img")?.alt || "";
62
+ function makeChip(item) {
63
+ const chip = document.createElement("div");
64
+ chip.className = s.Chip;
65
+ const image = item.querySelector("img");
66
+ if (image) {
67
+ const picture = document.createElement("img");
68
+ picture.className = s.ChipImage;
69
+ picture.src = image.currentSrc || image.src;
70
+ picture.alt = "";
71
+ chip.append(picture);
72
+ }
73
+ const text = labelOf(item);
74
+ if (text) {
75
+ const label = document.createElement("span");
76
+ label.className = s.ChipLabel;
77
+ label.textContent = text;
78
+ chip.append(label);
79
+ }
80
+ document.body.append(chip);
81
+ return chip;
82
+ }
83
+ function announce(text) {
84
+ let region = document.querySelector("[data-sortable-live]");
85
+ if (!region) {
86
+ region = document.createElement("div");
87
+ region.className = s.Live;
88
+ region.setAttribute("aria-live", "polite");
89
+ region.setAttribute("data-sortable-live", "");
90
+ document.body.append(region);
91
+ }
92
+ region.textContent = text;
93
+ }
94
+ const scrollParent = (list) => {
95
+ for (let node = list.parentElement; node && node !== document.body; node = node.parentElement) {
96
+ if (/auto|scroll/.test(getComputedStyle(node).overflowY) && node.scrollHeight > node.clientHeight)
97
+ return node;
98
+ }
99
+ return undefined;
100
+ };
101
+ // The nearer the edge the faster: slow to aim at a row, fast across a long list
102
+ const scrollStep = (depth) => SCROLL_SLOW + (SCROLL_FAST - SCROLL_SLOW) * Math.min(depth / SCROLL_EDGE, 1);
103
+ function listSlots(list, items) {
104
+ const box = list.getBoundingClientRect();
105
+ const rects = items.map((item) => item.getBoundingClientRect());
106
+ return Array.from({ length: items.length + 1 }, (_, gap) => {
107
+ const above = rects[gap - 1];
108
+ const below = rects[gap];
109
+ const y = above && below ? (above.bottom + below.top) / 2 : (below?.top ?? above.bottom);
110
+ return {
111
+ gap,
112
+ x: LINE_INSET,
113
+ y: y - box.top,
114
+ length: box.width - LINE_INSET * 2,
115
+ before: items[gap - 1],
116
+ after: items[gap],
117
+ };
118
+ });
119
+ }
120
+ // One slot per gap, and two where a line wraps: the end of one line and the start of the next
121
+ function gridSlots(list, items) {
122
+ const box = list.getBoundingClientRect();
123
+ const half = (parseFloat(getComputedStyle(list).columnGap) || 8) / 2;
124
+ const rects = items.map((item) => item.getBoundingClientRect());
125
+ const slots = [];
126
+ const add = (gap, x, rect, before, after) => slots.push({ gap, x: x - box.left, y: rect.top - box.top, length: rect.height, before, after });
127
+ rects.forEach((rect, gap) => {
128
+ const previous = rects[gap - 1];
129
+ if (previous && Math.abs(previous.top - rect.top) < rect.height / 2) {
130
+ add(gap, (previous.right + rect.left) / 2, rect, items[gap - 1], items[gap]);
131
+ return;
132
+ }
133
+ if (previous)
134
+ add(gap, previous.right + half, previous, items[gap - 1]);
135
+ add(gap, rect.left - half, rect, undefined, items[gap]);
136
+ });
137
+ const last = rects.length - 1;
138
+ add(rects.length, rects[last].right + half, rects[last], items[last]);
139
+ return slots;
140
+ }
141
+ // The chip keeps its size and fades out: grown to the item, its text would stand over the item's own
142
+ function flyChip(chip, to, layout) {
143
+ if (reduced() || !to) {
144
+ chip.remove();
145
+ return;
146
+ }
147
+ const from = chip.getBoundingClientRect();
148
+ const x = layout === "grid" ? to.left + (to.width - from.width) / 2 : to.left;
149
+ const y = to.top + (to.height - from.height) / 2;
150
+ chip.animate([
151
+ { transform: `translate(${from.left}px, ${from.top}px)`, opacity: 1 },
152
+ { transform: `translate(${x}px, ${y}px)`, opacity: 0 },
153
+ ], { duration: 200, easing: EASING, fill: "forwards" }).onfinish = () => chip.remove();
154
+ }
155
+ /** The items that changed place slide there from where they stood */
156
+ async function commitWithFlip(items, from, to, commit, skip) {
157
+ const before = new Map(items.map((item) => [item, item.getBoundingClientRect()]));
158
+ await commit(from, to);
159
+ if (reduced())
160
+ return;
161
+ items
162
+ .filter((item) => item !== skip && item.isConnected)
163
+ .forEach((item) => {
164
+ const was = before.get(item);
165
+ const now = item.getBoundingClientRect();
166
+ const x = was.left - now.left;
167
+ const y = was.top - now.top;
168
+ if (x || y)
169
+ item.animate([{ transform: `translate(${x}px, ${y}px)` }, { transform: "none" }], { duration: 200, easing: EASING });
170
+ });
171
+ }
172
+ const swallow = (event) => {
173
+ event.stopPropagation();
174
+ event.preventDefault();
175
+ };
176
+ function startDrag(event, list, source, layout, immediate, commit) {
177
+ const touch = event.pointerType === "touch" && !immediate;
178
+ const origin = { x: event.clientX, y: event.clientY };
179
+ const pointer = { ...origin };
180
+ const holdTimer = touch ? setTimeout(begin, TOUCH_HOLD) : undefined;
181
+ const style = getComputedStyle(list);
182
+ const part = parseFloat(layout === "grid" ? style.columnGap : style.rowGap) ? PART : 0;
183
+ const scroller = scrollParent(list);
184
+ let drag;
185
+ window.addEventListener("pointermove", onMove);
186
+ window.addEventListener("pointerup", onUp);
187
+ window.addEventListener("pointercancel", onCancel);
188
+ function onMove({ clientX, clientY }) {
189
+ pointer.x = clientX;
190
+ pointer.y = clientY;
191
+ if (drag)
192
+ return;
193
+ const moved = Math.hypot(clientX - origin.x, clientY - origin.y) >= START_DISTANCE;
194
+ if (moved && touch)
195
+ stopListening();
196
+ else if (moved)
197
+ begin();
198
+ }
199
+ function onUp() {
200
+ finish(true);
201
+ }
202
+ function onCancel() {
203
+ finish(false);
204
+ }
205
+ function onKey(keyEvent) {
206
+ if (keyEvent.key !== "Escape")
207
+ return;
208
+ keyEvent.stopPropagation();
209
+ finish(false);
210
+ }
211
+ function blockScroll(touchEvent) {
212
+ touchEvent.preventDefault();
213
+ }
214
+ function stopListening() {
215
+ clearTimeout(holdTimer);
216
+ window.removeEventListener("pointermove", onMove);
217
+ window.removeEventListener("pointerup", onUp);
218
+ window.removeEventListener("pointercancel", onCancel);
219
+ window.removeEventListener("keydown", onKey, true);
220
+ window.removeEventListener("touchmove", blockScroll);
221
+ }
222
+ function begin() {
223
+ const items = itemsOf(list);
224
+ const slots = layout === "grid" ? gridSlots(list, items) : listSlots(list, items);
225
+ window.getSelection()?.removeAllRanges();
226
+ document.documentElement.classList.add(s.Dragging);
227
+ list.setAttribute("data-dragging", "");
228
+ source.setAttribute("data-sortable-source", "");
229
+ window.addEventListener("keydown", onKey, true);
230
+ window.addEventListener("touchmove", blockScroll, { passive: false });
231
+ const chip = makeChip(source);
232
+ const line = document.createElement("div");
233
+ line.className = s.Line;
234
+ line.hidden = true;
235
+ document.body.append(line);
236
+ const { width, height } = chip.getBoundingClientRect();
237
+ drag = { chip, line, width, height, items, slots };
238
+ tick();
239
+ }
240
+ function tick() {
241
+ if (!drag)
242
+ return;
243
+ autoScroll();
244
+ update();
245
+ drag.frame = requestAnimationFrame(tick);
246
+ }
247
+ function autoScroll() {
248
+ const box = scroller?.getBoundingClientRect() ?? { top: 0, bottom: window.innerHeight };
249
+ const above = box.top + SCROLL_EDGE - pointer.y;
250
+ const below = pointer.y - box.bottom + SCROLL_EDGE;
251
+ const step = above > 0 ? -scrollStep(above) : below > 0 ? scrollStep(below) : 0;
252
+ if (!step)
253
+ return;
254
+ if (scroller)
255
+ scroller.scrollTop += step;
256
+ else
257
+ window.scrollBy(0, step);
258
+ }
259
+ function update() {
260
+ if (!drag)
261
+ return;
262
+ drag.chip.style.transform = `translate(${pointer.x - drag.width / 2}px, ${pointer.y - drag.height / 2}px)`;
263
+ const box = source.getBoundingClientRect();
264
+ const overSource = pointer.x >= box.left && pointer.x <= box.right && pointer.y >= box.top && pointer.y <= box.bottom;
265
+ source.toggleAttribute("data-sortable-away", !overSource);
266
+ showGap(overSource ? undefined : nearestSlot());
267
+ }
268
+ function nearestSlot() {
269
+ if (!drag)
270
+ return undefined;
271
+ const box = list.getBoundingClientRect();
272
+ const near = pointer.x >= box.left - NEAR_LIST &&
273
+ pointer.x <= box.right + NEAR_LIST &&
274
+ pointer.y >= box.top - NEAR_LIST &&
275
+ pointer.y <= box.bottom + NEAR_LIST;
276
+ if (!near)
277
+ return undefined;
278
+ const distance = (slot) => {
279
+ if (layout === "list")
280
+ return Math.abs(pointer.y - (box.top + slot.y));
281
+ const top = box.top + slot.y;
282
+ return Math.hypot(pointer.x - (box.left + slot.x), Math.max(top - pointer.y, 0, pointer.y - top - slot.length));
283
+ };
284
+ const best = drag.slots.reduce((nearest, slot) => (distance(slot) < distance(nearest) ? slot : nearest));
285
+ const index = drag.items.indexOf(source);
286
+ return best.gap === index || best.gap === index + 1 ? undefined : best;
287
+ }
288
+ function shift(slot, distance) {
289
+ const move = (item, by) => {
290
+ if (!item || !part)
291
+ return;
292
+ item.style.transform = distance
293
+ ? layout === "grid"
294
+ ? `translateX(${by}px)`
295
+ : `translateY(${by}px)`
296
+ : "";
297
+ };
298
+ move(slot?.before, -distance);
299
+ move(slot?.after, distance);
300
+ }
301
+ function showGap(found) {
302
+ if (!drag)
303
+ return;
304
+ if (found !== drag.found) {
305
+ shift(drag.found, 0);
306
+ shift(found, part);
307
+ }
308
+ drag.found = found;
309
+ drag.line.hidden = !found;
310
+ if (!found)
311
+ return;
312
+ const box = list.getBoundingClientRect();
313
+ if (layout === "grid") {
314
+ drag.line.style.transform = `translate(${box.left + found.x - 1}px, ${box.top + found.y}px)`;
315
+ drag.line.style.height = `${found.length}px`;
316
+ }
317
+ else {
318
+ drag.line.style.transform = `translate(${box.left + found.x}px, ${box.top + found.y - 1}px)`;
319
+ drag.line.style.width = `${found.length}px`;
320
+ }
321
+ }
322
+ function restoreSource() {
323
+ source.removeAttribute("data-sortable-source");
324
+ source.removeAttribute("data-sortable-away");
325
+ }
326
+ function settle(chip) {
327
+ flyChip(chip, source.isConnected ? source.getBoundingClientRect() : undefined, layout);
328
+ if (!reduced() && source.isConnected)
329
+ source.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 250, easing: EASING });
330
+ }
331
+ async function land(chip, items, { gap }, clear) {
332
+ const from = items.indexOf(source);
333
+ const to = gap > from ? gap - 1 : gap;
334
+ restoreSource();
335
+ const clearAndCommit = (moved, place) => {
336
+ clear();
337
+ return commit(moved, place);
338
+ };
339
+ await commitWithFlip(items, from, to, clearAndCommit, source);
340
+ announce(`${nameOf(source)}, ${to + 1} / ${items.length}`);
341
+ settle(chip);
342
+ }
343
+ function finish(drop) {
344
+ stopListening();
345
+ if (!drag)
346
+ return;
347
+ const { chip, line, found, items, frame } = drag;
348
+ if (frame)
349
+ cancelAnimationFrame(frame);
350
+ // The pointer lifts over a row: that click must not act on it
351
+ window.addEventListener("click", swallow, { capture: true, once: true });
352
+ setTimeout(() => window.removeEventListener("click", swallow, { capture: true }));
353
+ line.remove();
354
+ drag = undefined;
355
+ // On a drop this waits for the flip to read where the parted items stand
356
+ const clear = () => {
357
+ list.removeAttribute("data-dragging");
358
+ document.documentElement.classList.remove(s.Dragging);
359
+ shift(found, 0);
360
+ };
361
+ if (drop && found) {
362
+ void land(chip, items, found, clear);
363
+ return;
364
+ }
365
+ clear();
366
+ restoreSource();
367
+ settle(chip);
368
+ }
369
+ }
370
+ /** Starts a drag from a press inside the list. Goes on the list's `pointerdown`. */
371
+ export function sortablePointerDown(event, list, layout, commit) {
372
+ const target = event.target;
373
+ if (event.button || target.closest("[data-sortable]") !== list)
374
+ return;
375
+ const source = itemOf(list, target);
376
+ if (!source)
377
+ return;
378
+ const handle = target.closest(HANDLE);
379
+ // A grip in sight is the only way to drag, otherwise anything but a control starts it
380
+ if (source.querySelector(HANDLE) && showsHandles(list) ? !handle : target.closest(INTERACTIVE))
381
+ return;
382
+ startDrag(event, list, source, layout, !!handle, commit);
383
+ }
384
+ const KEY_STEPS = { ArrowUp: -1, ArrowLeft: -1, ArrowDown: 1, ArrowRight: 1 };
385
+ /** Moves the focused handle's item with the arrow keys. Goes on the list's `keydown`. */
386
+ export async function sortableKeyDown(event, list, commit) {
387
+ const step = KEY_STEPS[event.key];
388
+ const target = event.target;
389
+ if (!step || target.closest("[data-sortable]") !== list)
390
+ return;
391
+ const source = itemOf(list, target);
392
+ if (!source || (target !== source && !target.closest(HANDLE)))
393
+ return;
394
+ const items = itemsOf(list);
395
+ const from = items.indexOf(source);
396
+ const to = from + step;
397
+ if (to < 0 || to >= items.length)
398
+ return;
399
+ event.preventDefault();
400
+ await commitWithFlip(items, from, to, commit);
401
+ // Moving an element in the document drops its focus
402
+ if (target.isConnected && document.activeElement !== target)
403
+ target.focus();
404
+ announce(`${nameOf(source)}, ${to + 1} / ${items.length}`);
405
+ }
406
+ //# sourceMappingURL=sortable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sortable.js","sourceRoot":"","sources":["../../../src/lib/sortable.ts"],"names":[],"mappings":"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","sourcesContent":["import s from \"@plexui/styles/components/Sortable/Sortable.module.css\"\n\n// Drag to reorder, for any list of elements. Nothing moves until the drop: the\n// item leaves a flat slot and shrinks to a chip at the cursor, a line marks the\n// nearest gap, and only the drop changes the order. Items that stand apart part\n// at the gap. Rows that touch hold still: their text would run over the hairlines.\n\nexport type SortableLayout = \"list\" | \"grid\"\n\n/** Applies a move to the list. Resolves once the DOM shows the new order. */\nexport type SortableCommit = (from: number, to: number) => void | Promise<void>\n\ntype Slot = {\n gap: number\n /** Offsets from the list's corner, so a scroll under the drag keeps them true */\n x: number\n y: number\n length: number\n before?: Element\n after?: Element\n}\n\nconst START_DISTANCE = 4\nconst TOUCH_HOLD = 200\nconst SCROLL_EDGE = 80\nconst SCROLL_SLOW = 4\nconst SCROLL_FAST = 30\nconst NEAR_LIST = 40\nconst LINE_INSET = 16\nconst PART = 4\n// --cubic-enter: an animation here cannot read a CSS variable\nconst EASING = \"cubic-bezier(.19,1,.22,1)\"\nconst HANDLE = \"[data-sortable-handle]\"\nconst INTERACTIVE = [\n \"a[href]\",\n \"button\",\n \"input\",\n \"select\",\n \"summary\",\n \"textarea\",\n \"[contenteditable]\",\n \"[role=button]\",\n \"[role=checkbox]\",\n \"[role=combobox]\",\n \"[role=link]\",\n \"[role=radio]\",\n \"[role=slider]\",\n \"[role=switch]\",\n \"[role=tab]\",\n].join(\",\")\n\n// Read at the moment of use: the setting can change while the page is open\nconst reduced = () => window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n\nconst itemsOf = (list: HTMLElement) => [...list.children] as HTMLElement[]\n\n/** Grips stand in a table, elsewhere only when the list asks for them */\nconst showsHandles = (list: HTMLElement) => (list.dataset.handles ?? String(list.tagName === \"TBODY\")) === \"true\"\n\n/** The direct child of the list that holds `target` */\nconst itemOf = (list: HTMLElement, target: Element) => {\n let node: Element | null = target\n while (node && node.parentElement !== list) node = node.parentElement\n return node as HTMLElement | null\n}\n\n/** The order after a move. `to` counts the list without the moved item. */\nexport const moveItem = <T>(items: readonly T[], from: number, to: number) => {\n const next = [...items]\n next.splice(to, 0, ...next.splice(from, 1))\n return next\n}\n\nconst labelOf = (item: HTMLElement) => {\n const named = item.dataset.sortableLabel ?? item.querySelector(\"[data-sortable-label], label\")?.textContent\n if (named) return named.trim()\n // The cells of a row or the lines of a card run together: the first one is the name\n const texts = document.createTreeWalker(item, NodeFilter.SHOW_TEXT)\n while (texts.nextNode()) {\n if (texts.currentNode.textContent?.trim()) return texts.currentNode.parentElement?.textContent?.trim() ?? \"\"\n }\n return \"\"\n}\n\n/** What a screen reader hears: a picture has no text of its own */\nconst nameOf = (item: HTMLElement) => labelOf(item) || item.querySelector(\"img\")?.alt || \"\"\n\nfunction makeChip(item: HTMLElement) {\n const chip = document.createElement(\"div\")\n chip.className = s.Chip\n const image = item.querySelector(\"img\")\n if (image) {\n const picture = document.createElement(\"img\")\n picture.className = s.ChipImage\n picture.src = image.currentSrc || image.src\n picture.alt = \"\"\n chip.append(picture)\n }\n const text = labelOf(item)\n if (text) {\n const label = document.createElement(\"span\")\n label.className = s.ChipLabel\n label.textContent = text\n chip.append(label)\n }\n document.body.append(chip)\n return chip\n}\n\nfunction announce(text: string) {\n let region = document.querySelector(\"[data-sortable-live]\")\n if (!region) {\n region = document.createElement(\"div\")\n region.className = s.Live\n region.setAttribute(\"aria-live\", \"polite\")\n region.setAttribute(\"data-sortable-live\", \"\")\n document.body.append(region)\n }\n region.textContent = text\n}\n\nconst scrollParent = (list: HTMLElement) => {\n for (let node = list.parentElement; node && node !== document.body; node = node.parentElement) {\n if (/auto|scroll/.test(getComputedStyle(node).overflowY) && node.scrollHeight > node.clientHeight) return node\n }\n return undefined\n}\n\n// The nearer the edge the faster: slow to aim at a row, fast across a long list\nconst scrollStep = (depth: number) =>\n SCROLL_SLOW + (SCROLL_FAST - SCROLL_SLOW) * Math.min(depth / SCROLL_EDGE, 1)\n\nfunction listSlots(list: HTMLElement, items: HTMLElement[]) {\n const box = list.getBoundingClientRect()\n const rects = items.map((item) => item.getBoundingClientRect())\n return Array.from({ length: items.length + 1 }, (_, gap): Slot => {\n const above = rects[gap - 1]\n const below = rects[gap]\n const y = above && below ? (above.bottom + below.top) / 2 : (below?.top ?? above.bottom)\n return {\n gap,\n x: LINE_INSET,\n y: y - box.top,\n length: box.width - LINE_INSET * 2,\n before: items[gap - 1],\n after: items[gap],\n }\n })\n}\n\n// One slot per gap, and two where a line wraps: the end of one line and the start of the next\nfunction gridSlots(list: HTMLElement, items: HTMLElement[]) {\n const box = list.getBoundingClientRect()\n const half = (parseFloat(getComputedStyle(list).columnGap) || 8) / 2\n const rects = items.map((item) => item.getBoundingClientRect())\n const slots: Slot[] = []\n const add = (gap: number, x: number, rect: DOMRect, before?: Element, after?: Element) =>\n slots.push({ gap, x: x - box.left, y: rect.top - box.top, length: rect.height, before, after })\n\n rects.forEach((rect, gap) => {\n const previous = rects[gap - 1]\n if (previous && Math.abs(previous.top - rect.top) < rect.height / 2) {\n add(gap, (previous.right + rect.left) / 2, rect, items[gap - 1], items[gap])\n return\n }\n if (previous) add(gap, previous.right + half, previous, items[gap - 1])\n add(gap, rect.left - half, rect, undefined, items[gap])\n })\n const last = rects.length - 1\n add(rects.length, rects[last].right + half, rects[last], items[last])\n return slots\n}\n\n// The chip keeps its size and fades out: grown to the item, its text would stand over the item's own\nfunction flyChip(chip: HTMLElement, to: DOMRect | undefined, layout: SortableLayout) {\n if (reduced() || !to) {\n chip.remove()\n return\n }\n const from = chip.getBoundingClientRect()\n const x = layout === \"grid\" ? to.left + (to.width - from.width) / 2 : to.left\n const y = to.top + (to.height - from.height) / 2\n chip.animate(\n [\n { transform: `translate(${from.left}px, ${from.top}px)`, opacity: 1 },\n { transform: `translate(${x}px, ${y}px)`, opacity: 0 },\n ],\n { duration: 200, easing: EASING, fill: \"forwards\" },\n ).onfinish = () => chip.remove()\n}\n\n/** The items that changed place slide there from where they stood */\nasync function commitWithFlip(items: HTMLElement[], from: number, to: number, commit: SortableCommit, skip?: HTMLElement) {\n const before = new Map(items.map((item) => [item, item.getBoundingClientRect()]))\n await commit(from, to)\n if (reduced()) return\n items\n .filter((item) => item !== skip && item.isConnected)\n .forEach((item) => {\n const was = before.get(item) as DOMRect\n const now = item.getBoundingClientRect()\n const x = was.left - now.left\n const y = was.top - now.top\n if (x || y) item.animate([{ transform: `translate(${x}px, ${y}px)` }, { transform: \"none\" }], { duration: 200, easing: EASING })\n })\n}\n\nconst swallow = (event: Event) => {\n event.stopPropagation()\n event.preventDefault()\n}\n\nfunction startDrag(event: PointerEvent, list: HTMLElement, source: HTMLElement, layout: SortableLayout, immediate: boolean, commit: SortableCommit) {\n const touch = event.pointerType === \"touch\" && !immediate\n const origin = { x: event.clientX, y: event.clientY }\n const pointer = { ...origin }\n const holdTimer = touch ? setTimeout(begin, TOUCH_HOLD) : undefined\n const style = getComputedStyle(list)\n const part = parseFloat(layout === \"grid\" ? style.columnGap : style.rowGap) ? PART : 0\n const scroller = scrollParent(list)\n let drag:\n | {\n chip: HTMLElement\n line: HTMLElement\n width: number\n height: number\n items: HTMLElement[]\n slots: Slot[]\n frame?: number\n found?: Slot\n }\n | undefined\n\n window.addEventListener(\"pointermove\", onMove)\n window.addEventListener(\"pointerup\", onUp)\n window.addEventListener(\"pointercancel\", onCancel)\n\n function onMove({ clientX, clientY }: PointerEvent) {\n pointer.x = clientX\n pointer.y = clientY\n if (drag) return\n const moved = Math.hypot(clientX - origin.x, clientY - origin.y) >= START_DISTANCE\n if (moved && touch) stopListening()\n else if (moved) begin()\n }\n\n function onUp() {\n finish(true)\n }\n\n function onCancel() {\n finish(false)\n }\n\n function onKey(keyEvent: KeyboardEvent) {\n if (keyEvent.key !== \"Escape\") return\n keyEvent.stopPropagation()\n finish(false)\n }\n\n function blockScroll(touchEvent: TouchEvent) {\n touchEvent.preventDefault()\n }\n\n function stopListening() {\n clearTimeout(holdTimer)\n window.removeEventListener(\"pointermove\", onMove)\n window.removeEventListener(\"pointerup\", onUp)\n window.removeEventListener(\"pointercancel\", onCancel)\n window.removeEventListener(\"keydown\", onKey, true)\n window.removeEventListener(\"touchmove\", blockScroll)\n }\n\n function begin() {\n const items = itemsOf(list)\n const slots = layout === \"grid\" ? gridSlots(list, items) : listSlots(list, items)\n window.getSelection()?.removeAllRanges()\n document.documentElement.classList.add(s.Dragging)\n list.setAttribute(\"data-dragging\", \"\")\n source.setAttribute(\"data-sortable-source\", \"\")\n window.addEventListener(\"keydown\", onKey, true)\n window.addEventListener(\"touchmove\", blockScroll, { passive: false })\n const chip = makeChip(source)\n const line = document.createElement(\"div\")\n line.className = s.Line\n line.hidden = true\n document.body.append(line)\n const { width, height } = chip.getBoundingClientRect()\n drag = { chip, line, width, height, items, slots }\n tick()\n }\n\n function tick() {\n if (!drag) return\n autoScroll()\n update()\n drag.frame = requestAnimationFrame(tick)\n }\n\n function autoScroll() {\n const box = scroller?.getBoundingClientRect() ?? { top: 0, bottom: window.innerHeight }\n const above = box.top + SCROLL_EDGE - pointer.y\n const below = pointer.y - box.bottom + SCROLL_EDGE\n const step = above > 0 ? -scrollStep(above) : below > 0 ? scrollStep(below) : 0\n if (!step) return\n if (scroller) scroller.scrollTop += step\n else window.scrollBy(0, step)\n }\n\n function update() {\n if (!drag) return\n drag.chip.style.transform = `translate(${pointer.x - drag.width / 2}px, ${pointer.y - drag.height / 2}px)`\n const box = source.getBoundingClientRect()\n const overSource = pointer.x >= box.left && pointer.x <= box.right && pointer.y >= box.top && pointer.y <= box.bottom\n source.toggleAttribute(\"data-sortable-away\", !overSource)\n showGap(overSource ? undefined : nearestSlot())\n }\n\n function nearestSlot() {\n if (!drag) return undefined\n const box = list.getBoundingClientRect()\n const near =\n pointer.x >= box.left - NEAR_LIST &&\n pointer.x <= box.right + NEAR_LIST &&\n pointer.y >= box.top - NEAR_LIST &&\n pointer.y <= box.bottom + NEAR_LIST\n if (!near) return undefined\n const distance = (slot: Slot) => {\n if (layout === \"list\") return Math.abs(pointer.y - (box.top + slot.y))\n const top = box.top + slot.y\n return Math.hypot(pointer.x - (box.left + slot.x), Math.max(top - pointer.y, 0, pointer.y - top - slot.length))\n }\n const best = drag.slots.reduce((nearest, slot) => (distance(slot) < distance(nearest) ? slot : nearest))\n const index = drag.items.indexOf(source)\n return best.gap === index || best.gap === index + 1 ? undefined : best\n }\n\n function shift(slot: Slot | undefined, distance: number) {\n const move = (item: Element | undefined, by: number) => {\n if (!item || !part) return\n ;(item as HTMLElement).style.transform = distance\n ? layout === \"grid\"\n ? `translateX(${by}px)`\n : `translateY(${by}px)`\n : \"\"\n }\n move(slot?.before, -distance)\n move(slot?.after, distance)\n }\n\n function showGap(found: Slot | undefined) {\n if (!drag) return\n if (found !== drag.found) {\n shift(drag.found, 0)\n shift(found, part)\n }\n drag.found = found\n drag.line.hidden = !found\n if (!found) return\n const box = list.getBoundingClientRect()\n if (layout === \"grid\") {\n drag.line.style.transform = `translate(${box.left + found.x - 1}px, ${box.top + found.y}px)`\n drag.line.style.height = `${found.length}px`\n } else {\n drag.line.style.transform = `translate(${box.left + found.x}px, ${box.top + found.y - 1}px)`\n drag.line.style.width = `${found.length}px`\n }\n }\n\n function restoreSource() {\n source.removeAttribute(\"data-sortable-source\")\n source.removeAttribute(\"data-sortable-away\")\n }\n\n function settle(chip: HTMLElement) {\n flyChip(chip, source.isConnected ? source.getBoundingClientRect() : undefined, layout)\n if (!reduced() && source.isConnected) source.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 250, easing: EASING })\n }\n\n async function land(chip: HTMLElement, items: HTMLElement[], { gap }: Slot, clear: () => void) {\n const from = items.indexOf(source)\n const to = gap > from ? gap - 1 : gap\n restoreSource()\n const clearAndCommit: SortableCommit = (moved, place) => {\n clear()\n return commit(moved, place)\n }\n await commitWithFlip(items, from, to, clearAndCommit, source)\n announce(`${nameOf(source)}, ${to + 1} / ${items.length}`)\n settle(chip)\n }\n\n function finish(drop: boolean) {\n stopListening()\n if (!drag) return\n const { chip, line, found, items, frame } = drag\n if (frame) cancelAnimationFrame(frame)\n // The pointer lifts over a row: that click must not act on it\n window.addEventListener(\"click\", swallow, { capture: true, once: true })\n setTimeout(() => window.removeEventListener(\"click\", swallow, { capture: true }))\n line.remove()\n drag = undefined\n // On a drop this waits for the flip to read where the parted items stand\n const clear = () => {\n list.removeAttribute(\"data-dragging\")\n document.documentElement.classList.remove(s.Dragging)\n shift(found, 0)\n }\n if (drop && found) {\n void land(chip, items, found, clear)\n return\n }\n clear()\n restoreSource()\n settle(chip)\n }\n}\n\n/** Starts a drag from a press inside the list. Goes on the list's `pointerdown`. */\nexport function sortablePointerDown(event: PointerEvent, list: HTMLElement, layout: SortableLayout, commit: SortableCommit) {\n const target = event.target as Element\n if (event.button || target.closest(\"[data-sortable]\") !== list) return\n const source = itemOf(list, target)\n if (!source) return\n const handle = target.closest(HANDLE)\n // A grip in sight is the only way to drag, otherwise anything but a control starts it\n if (source.querySelector(HANDLE) && showsHandles(list) ? !handle : target.closest(INTERACTIVE)) return\n startDrag(event, list, source, layout, !!handle, commit)\n}\n\nconst KEY_STEPS: Record<string, number> = { ArrowUp: -1, ArrowLeft: -1, ArrowDown: 1, ArrowRight: 1 }\n\n/** Moves the focused handle's item with the arrow keys. Goes on the list's `keydown`. */\nexport async function sortableKeyDown(event: KeyboardEvent, list: HTMLElement, commit: SortableCommit) {\n const step = KEY_STEPS[event.key]\n const target = event.target as HTMLElement\n if (!step || target.closest(\"[data-sortable]\") !== list) return\n const source = itemOf(list, target)\n if (!source || (target !== source && !target.closest(HANDLE))) return\n const items = itemsOf(list)\n const from = items.indexOf(source)\n const to = from + step\n if (to < 0 || to >= items.length) return\n event.preventDefault()\n await commitWithFlip(items, from, to, commit)\n // Moving an element in the document drops its focus\n if (target.isConnected && document.activeElement !== target) target.focus()\n announce(`${nameOf(source)}, ${to + 1} / ${items.length}`)\n}\n"]}
@@ -20,7 +20,7 @@ export type RadioGroupProps<T extends string> = {
20
20
  };
21
21
  export declare const RadioGroup: {
22
22
  <T extends string>({ onChange, children, className, direction, disabled, ...restProps }: RadioGroupProps<T>): import("react/jsx-runtime").JSX.Element;
23
- Item: <T extends string>({ value, disabled: itemDisabled, required, children, description, className, block, orientation, ...restProps }: RadioGroupItemProps<T>) => import("react/jsx-runtime").JSX.Element;
23
+ Item: <T extends string>({ value, disabled: itemDisabled, required, children, description, className, block, orientation, id: idProp, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...restProps }: RadioGroupItemProps<T>) => import("react/jsx-runtime").JSX.Element;
24
24
  };
25
25
  export type RadioGroupItemProps<T extends string> = {
26
26
  value: T;
@@ -35,7 +35,13 @@ export type RadioGroupItemProps<T extends string> = {
35
35
  * @default "left"
36
36
  */
37
37
  orientation?: "left" | "right";
38
- children: React.ReactNode;
38
+ /** Id of the radio, so a label outside the item can target it with `htmlFor` */
39
+ id?: string;
40
+ "aria-label"?: string;
41
+ "aria-labelledby"?: string;
42
+ "aria-describedby"?: string;
43
+ /** The option's label. Leave it out when a `Field` around the item names it. */
44
+ children?: React.ReactNode;
39
45
  /** Optional second line below the option's label. Linked via `aria-describedby`. */
40
46
  description?: React.ReactNode;
41
47
  };
@@ -0,0 +1,51 @@
1
+ import type { ComponentProps, ElementType, ReactNode } from "react";
2
+ import { type SortableLayout } from "../../lib/sortable.js";
3
+ export type SortableProps<T> = {
4
+ /**
5
+ * The list, in the order of the children
6
+ */
7
+ value: readonly T[];
8
+ /**
9
+ * Called with the new order after a drop
10
+ */
11
+ onChange?: (value: T[]) => void;
12
+ /**
13
+ * `list` for rows one under another, `grid` for items that stand side by side
14
+ * @default list
15
+ */
16
+ layout?: SortableLayout;
17
+ /**
18
+ * Element to render, such as `TableBody` for table rows
19
+ * @default div
20
+ */
21
+ as?: ElementType;
22
+ /**
23
+ * Turns dragging off
24
+ * @default false
25
+ */
26
+ disabled?: boolean;
27
+ /**
28
+ * Shows the grips of `Sortable.Handle`. A hidden grip still takes the keyboard, and the item drags from anywhere but a control.
29
+ * @default true in a table, otherwise false
30
+ */
31
+ handles?: boolean;
32
+ /**
33
+ * The items, one direct child per entry of `value`, keyed by the entry and not by its index
34
+ */
35
+ children?: ReactNode;
36
+ } & Omit<ComponentProps<"div">, "children" | "onChange">;
37
+ /** Reorders its direct children by dragging, from anywhere but a control, or by a `Sortable.Handle` where grips are shown. */
38
+ export declare function Sortable<T>({ value, onChange, layout, as: Tag, disabled, handles, className, children, onPointerDown, onKeyDown, ...rest }: SortableProps<T>): import("react/jsx-runtime").JSX.Element;
39
+ export declare namespace Sortable {
40
+ var Handle: typeof SortableHandle;
41
+ }
42
+ export type SortableHandleProps = {
43
+ /**
44
+ * Name of the handle for assistive technology
45
+ * @default Reorder
46
+ */
47
+ "aria-label"?: string;
48
+ } & ComponentProps<"button">;
49
+ /** The grip of an item. Arrow keys move the item while the handle has focus. */
50
+ declare function SortableHandle({ "aria-label": ariaLabel, className, children, ...rest }: SortableHandleProps): import("react/jsx-runtime").JSX.Element;
51
+ export {};
@@ -0,0 +1 @@
1
+ export { Sortable, type SortableHandleProps, type SortableProps } from "./Sortable.js";
@@ -1,4 +1,4 @@
1
- import type { Sizes } from "../../types.js";
1
+ import type { Sizes, Variants } from "../../types.js";
2
2
  export type Tag = {
3
3
  value: string;
4
4
  valid: boolean;
@@ -21,6 +21,11 @@ export type TagInputProps = {
21
21
  * @default xl
22
22
  */
23
23
  size?: Sizes<"sm" | "md" | "lg" | "xl" | "2xl" | "3xl">;
24
+ /**
25
+ * Visual style. `ghost` has no frame, for a field that its container already frames
26
+ * @default outline
27
+ */
28
+ variant?: Variants<"outline" | "ghost">;
24
29
  /**
25
30
  * Callback function invoked when the tag list changes
26
31
  */
@@ -4,7 +4,7 @@ export type TextareaProps = {
4
4
  * Visual style of the textarea
5
5
  * @default outline
6
6
  */
7
- variant?: Variants<"outline" | "soft">;
7
+ variant?: Variants<"outline" | "soft" | "ghost">;
8
8
  /**
9
9
  * Controls the size of the textarea
10
10
  *
@@ -0,0 +1,9 @@
1
+ export type SortableLayout = "list" | "grid";
2
+ /** Applies a move to the list. Resolves once the DOM shows the new order. */
3
+ export type SortableCommit = (from: number, to: number) => void | Promise<void>;
4
+ /** The order after a move. `to` counts the list without the moved item. */
5
+ export declare const moveItem: <T>(items: readonly T[], from: number, to: number) => T[];
6
+ /** Starts a drag from a press inside the list. Goes on the list's `pointerdown`. */
7
+ export declare function sortablePointerDown(event: PointerEvent, list: HTMLElement, layout: SortableLayout, commit: SortableCommit): void;
8
+ /** Moves the focused handle's item with the arrow keys. Goes on the list's `keydown`. */
9
+ export declare function sortableKeyDown(event: KeyboardEvent, list: HTMLElement, commit: SortableCommit): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plexui/ui",
3
- "version": "0.20.16",
3
+ "version": "0.21.0",
4
4
  "description": "Modern design system for building high-quality applications",
5
5
  "type": "module",
6
6
  "license": "MIT",