@plexui/ui 0.20.16 → 0.22.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.
- package/dist/es/components/RadioGroup/RadioGroup.js +4 -3
- package/dist/es/components/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/es/components/Sortable/Sortable.js +49 -0
- package/dist/es/components/Sortable/Sortable.js.map +1 -0
- package/dist/es/components/Sortable/index.js +2 -0
- package/dist/es/components/Sortable/index.js.map +1 -0
- package/dist/es/components/TagInput/TagInput.js +2 -2
- package/dist/es/components/TagInput/TagInput.js.map +1 -1
- package/dist/es/components/Textarea/Textarea.js.map +1 -1
- package/dist/es/lib/sortable.js +524 -0
- package/dist/es/lib/sortable.js.map +1 -0
- package/dist/types/components/RadioGroup/RadioGroup.d.ts +8 -2
- package/dist/types/components/Sortable/Sortable.d.ts +85 -0
- package/dist/types/components/Sortable/index.d.ts +2 -0
- package/dist/types/components/TagInput/TagInput.d.ts +6 -1
- package/dist/types/components/Textarea/Textarea.d.ts +1 -1
- package/dist/types/lib/sortable.d.ts +27 -0
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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":"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;
|
|
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 /** 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,49 @@
|
|
|
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, group, name, onMove, onHold, className, children, onPointerDown, onKeyDown, ...rest }) {
|
|
12
|
+
const latest = useLatestValue({ value, onChange, onMove, onHold, name });
|
|
13
|
+
const commit = (from, to) => {
|
|
14
|
+
const { value: items, onChange: change, onMove: move, name: list } = latest.current;
|
|
15
|
+
flushSync(() => {
|
|
16
|
+
change?.(moveItem(items, from, to));
|
|
17
|
+
move?.({ item: items[from], from: list, fromIndex: from, to: list, toIndex: to });
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
const link = {
|
|
21
|
+
move: (fromIndex, to, toIndex) => {
|
|
22
|
+
const { value: items, onMove: move, name: from } = latest.current;
|
|
23
|
+
flushSync(() => move?.({ item: items[fromIndex], from: from, fromIndex, to, toIndex }));
|
|
24
|
+
},
|
|
25
|
+
hold: (target) => latest.current.onHold?.(target),
|
|
26
|
+
};
|
|
27
|
+
const handlePointerDown = (event) => {
|
|
28
|
+
onPointerDown?.(event);
|
|
29
|
+
if (!disabled)
|
|
30
|
+
sortablePointerDown(event.nativeEvent, event.currentTarget, layout, commit, link);
|
|
31
|
+
};
|
|
32
|
+
const handleKeyDown = (event) => {
|
|
33
|
+
onKeyDown?.(event);
|
|
34
|
+
if (!disabled)
|
|
35
|
+
void sortableKeyDown(event.nativeEvent, event.currentTarget, commit, link);
|
|
36
|
+
};
|
|
37
|
+
return (_jsx(Tag, { ...rest, className: clsx(s.Sortable, className), "data-sortable": "", "data-layout": layout, "data-disabled": disabled ? "" : undefined, "data-handles": handles?.toString(), "data-group": group, "data-name": name, onPointerDown: handlePointerDown, onKeyDown: handleKeyDown, onDragStart: preventNativeDrag, children: children }));
|
|
38
|
+
}
|
|
39
|
+
/** The grip of an item. Arrow keys move the item while the handle has focus. */
|
|
40
|
+
function SortableHandle({ "aria-label": ariaLabel = "Reorder", className, children, ...rest }) {
|
|
41
|
+
return (_jsx("button", { ...rest, type: "button", className: clsx(s.Handle, className), "data-sortable-handle": "", "aria-label": ariaLabel, children: children ?? _jsx(GripVertical, { "aria-hidden": true }) }));
|
|
42
|
+
}
|
|
43
|
+
/** A place outside the lists that takes an item whole: a folder, a category in a side list. */
|
|
44
|
+
function SortableTarget({ group, name, as: Tag = "div", className, ...rest }) {
|
|
45
|
+
return _jsx(Tag, { ...rest, className: clsx(s.Target, className), "data-sortable-target": group, "data-name": name });
|
|
46
|
+
}
|
|
47
|
+
Sortable.Handle = SortableHandle;
|
|
48
|
+
Sortable.Target = SortableTarget;
|
|
49
|
+
//# sourceMappingURL=Sortable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sortable.js","sourceRoot":"","sources":["../../../../src/components/Sortable/Sortable.tsx"],"names":[],"mappings":"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","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, type SortableMove } 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 * Lists of one group trade items, and a `Sortable.Target` of that group takes them. Give each list a `name`.\n */\n group?: string\n /**\n * Names the list in `onMove`\n */\n name?: string\n /**\n * An item moved, inside this list or out of it. Out of it the app updates both lists: `onChange` is for a move inside one.\n */\n onMove?: (move: SortableMove<T>) => void\n /**\n * A drag rests on a `Sortable.Target` of the group: open what it stands for or go there\n */\n onHold?: (name: string) => void\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 group,\n name,\n onMove,\n onHold,\n className,\n children,\n onPointerDown,\n onKeyDown,\n ...rest\n}: SortableProps<T>) {\n const latest = useLatestValue({ value, onChange, onMove, onHold, name })\n\n const commit = (from: number, to: number) => {\n const { value: items, onChange: change, onMove: move, name: list } = latest.current\n flushSync(() => {\n change?.(moveItem(items, from, to))\n move?.({ item: items[from], from: list as string, fromIndex: from, to: list as string, toIndex: to })\n })\n }\n\n const link = {\n move: (fromIndex: number, to: string, toIndex?: number) => {\n const { value: items, onMove: move, name: from } = latest.current\n flushSync(() => move?.({ item: items[fromIndex], from: from as string, fromIndex, to, toIndex }))\n },\n hold: (target: string) => latest.current.onHold?.(target),\n }\n\n const handlePointerDown = (event: PointerEvent<HTMLDivElement>) => {\n onPointerDown?.(event)\n if (!disabled) sortablePointerDown(event.nativeEvent, event.currentTarget, layout, commit, link)\n }\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event)\n if (!disabled) void sortableKeyDown(event.nativeEvent, event.currentTarget, commit, link)\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 data-group={group}\n data-name={name}\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\nexport type SortableTargetProps = {\n /**\n * The `group` of the lists whose items it takes\n */\n group: string\n /**\n * Names the target in `onMove` and `onHold` on the list\n */\n name: string\n /**\n * Element to render\n * @default div\n */\n as?: ElementType\n} & ComponentProps<\"div\">\n\n/** A place outside the lists that takes an item whole: a folder, a category in a side list. */\nfunction SortableTarget({ group, name, as: Tag = \"div\", className, ...rest }: SortableTargetProps) {\n return <Tag {...rest} className={clsx(s.Target, className)} data-sortable-target={group} data-name={name} />\n}\n\nSortable.Handle = SortableHandle\nSortable.Target = SortableTarget\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/Sortable/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,GAIT,MAAM,YAAY,CAAA","sourcesContent":["export {\n Sortable,\n type SortableHandleProps,\n type SortableProps,\n type SortableTargetProps,\n} from \"./Sortable\"\nexport type { SortableMove } from \"../../lib/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":"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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 } 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,524 @@
|
|
|
1
|
+
import s from "@plexui/styles/components/Sortable/Sortable.module.css";
|
|
2
|
+
const START_DISTANCE = 4;
|
|
3
|
+
const TOUCH_HOLD = 200;
|
|
4
|
+
// Long enough to pass over a target on the way to another
|
|
5
|
+
const TARGET_HOLD = 500;
|
|
6
|
+
const SCROLL_EDGE = 80;
|
|
7
|
+
const SCROLL_SLOW = 4;
|
|
8
|
+
const SCROLL_FAST = 30;
|
|
9
|
+
const NEAR_LIST = 40;
|
|
10
|
+
const LINE_INSET = 16;
|
|
11
|
+
const PART = 4;
|
|
12
|
+
// --cubic-enter: an animation here cannot read a CSS variable
|
|
13
|
+
const EASING = "cubic-bezier(.19,1,.22,1)";
|
|
14
|
+
const HANDLE = "[data-sortable-handle]";
|
|
15
|
+
const GRAB = "[data-sortable-grab]";
|
|
16
|
+
const STATIC = "data-sortable-static";
|
|
17
|
+
const INTERACTIVE = [
|
|
18
|
+
"a[href]",
|
|
19
|
+
"button",
|
|
20
|
+
"input",
|
|
21
|
+
"select",
|
|
22
|
+
"summary",
|
|
23
|
+
"textarea",
|
|
24
|
+
"[contenteditable]",
|
|
25
|
+
"[role=button]",
|
|
26
|
+
"[role=checkbox]",
|
|
27
|
+
"[role=combobox]",
|
|
28
|
+
"[role=link]",
|
|
29
|
+
"[role=radio]",
|
|
30
|
+
"[role=slider]",
|
|
31
|
+
"[role=switch]",
|
|
32
|
+
"[role=tab]",
|
|
33
|
+
].join(",");
|
|
34
|
+
// Read at the moment of use: the setting can change while the page is open
|
|
35
|
+
const reduced = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
36
|
+
/** How far the line stands in from the sides of the list */
|
|
37
|
+
const lineInset = (list) => {
|
|
38
|
+
const inset = parseFloat(getComputedStyle(list).getPropertyValue("--sortable-line-inset"));
|
|
39
|
+
return Number.isNaN(inset) ? LINE_INSET : inset;
|
|
40
|
+
};
|
|
41
|
+
/** A head row or an empty message stands among the items and is not one of them */
|
|
42
|
+
const itemsOf = (list) => [...list.children].filter((item) => !item.hasAttribute(STATIC));
|
|
43
|
+
/** The lists a drag from this one can reach, in the order of the page */
|
|
44
|
+
const listsOf = (list) => {
|
|
45
|
+
const { group } = list.dataset;
|
|
46
|
+
if (!group)
|
|
47
|
+
return [list];
|
|
48
|
+
return [...document.querySelectorAll(`[data-sortable][data-group="${CSS.escape(group)}"]:not([data-disabled])`)];
|
|
49
|
+
};
|
|
50
|
+
/** Grips stand in a table, elsewhere only when the list asks for them */
|
|
51
|
+
const showsHandles = (list) => (list.dataset.handles ?? String(list.tagName === "TBODY")) === "true";
|
|
52
|
+
/** The direct child of the list that holds `target` */
|
|
53
|
+
const itemOf = (list, target) => {
|
|
54
|
+
let node = target;
|
|
55
|
+
while (node && node.parentElement !== list)
|
|
56
|
+
node = node.parentElement;
|
|
57
|
+
return node;
|
|
58
|
+
};
|
|
59
|
+
/** The order after a move. `to` counts the list without the moved item. */
|
|
60
|
+
export const moveItem = (items, from, to) => {
|
|
61
|
+
const next = [...items];
|
|
62
|
+
next.splice(to, 0, ...next.splice(from, 1));
|
|
63
|
+
return next;
|
|
64
|
+
};
|
|
65
|
+
const labelOf = (item) => {
|
|
66
|
+
const named = item.dataset.sortableLabel ?? item.querySelector("[data-sortable-label], label")?.textContent;
|
|
67
|
+
if (named)
|
|
68
|
+
return named.trim();
|
|
69
|
+
// The cells of a row or the lines of a card run together: the first one is the name
|
|
70
|
+
const texts = document.createTreeWalker(item, NodeFilter.SHOW_TEXT);
|
|
71
|
+
while (texts.nextNode()) {
|
|
72
|
+
if (texts.currentNode.textContent?.trim())
|
|
73
|
+
return texts.currentNode.parentElement?.textContent?.trim() ?? "";
|
|
74
|
+
}
|
|
75
|
+
return "";
|
|
76
|
+
};
|
|
77
|
+
/** What a screen reader hears: a picture has no text of its own */
|
|
78
|
+
const nameOf = (item) => labelOf(item) || item.querySelector("img")?.alt || "";
|
|
79
|
+
function makeChip(item) {
|
|
80
|
+
const chip = document.createElement("div");
|
|
81
|
+
chip.className = s.Chip;
|
|
82
|
+
const image = item.querySelector("[data-sortable-image], img");
|
|
83
|
+
if (image instanceof HTMLImageElement) {
|
|
84
|
+
const picture = document.createElement("img");
|
|
85
|
+
picture.className = s.ChipImage;
|
|
86
|
+
picture.src = image.currentSrc || image.src;
|
|
87
|
+
picture.alt = "";
|
|
88
|
+
chip.append(picture);
|
|
89
|
+
}
|
|
90
|
+
else if (image) {
|
|
91
|
+
// A cropped background: the chip takes the element as it stands, at the chip's size
|
|
92
|
+
const picture = image.cloneNode(false);
|
|
93
|
+
picture.className = s.ChipImage;
|
|
94
|
+
picture.hidden = false;
|
|
95
|
+
chip.append(picture);
|
|
96
|
+
}
|
|
97
|
+
const text = labelOf(item);
|
|
98
|
+
if (text) {
|
|
99
|
+
const label = document.createElement("span");
|
|
100
|
+
label.className = s.ChipLabel;
|
|
101
|
+
label.textContent = text;
|
|
102
|
+
chip.append(label);
|
|
103
|
+
}
|
|
104
|
+
document.body.append(chip);
|
|
105
|
+
return chip;
|
|
106
|
+
}
|
|
107
|
+
function announce(text) {
|
|
108
|
+
let region = document.querySelector("[data-sortable-live]");
|
|
109
|
+
if (!region) {
|
|
110
|
+
region = document.createElement("div");
|
|
111
|
+
region.className = s.Live;
|
|
112
|
+
region.setAttribute("aria-live", "polite");
|
|
113
|
+
region.setAttribute("data-sortable-live", "");
|
|
114
|
+
document.body.append(region);
|
|
115
|
+
}
|
|
116
|
+
region.textContent = text;
|
|
117
|
+
}
|
|
118
|
+
const scrollParent = (start) => {
|
|
119
|
+
for (let node = start; node && node !== document.body; node = node.parentElement) {
|
|
120
|
+
if (/auto|scroll/.test(getComputedStyle(node).overflowY) && node.scrollHeight > node.clientHeight)
|
|
121
|
+
return node;
|
|
122
|
+
}
|
|
123
|
+
return undefined;
|
|
124
|
+
};
|
|
125
|
+
// The nearer the edge the faster: slow to aim at a row, fast across a long list
|
|
126
|
+
const scrollStep = (depth) => SCROLL_SLOW + (SCROLL_FAST - SCROLL_SLOW) * Math.min(depth / SCROLL_EDGE, 1);
|
|
127
|
+
function listSlots(list, items) {
|
|
128
|
+
const box = list.getBoundingClientRect();
|
|
129
|
+
const inset = lineInset(list);
|
|
130
|
+
if (!items.length) {
|
|
131
|
+
// Above a message or an empty row: the line must not cross its text
|
|
132
|
+
const rest = (list.lastElementChild ?? list).getBoundingClientRect();
|
|
133
|
+
return [{ gap: 0, x: inset, y: rest.top - box.top, length: box.width - inset * 2 }];
|
|
134
|
+
}
|
|
135
|
+
const rects = items.map((item) => item.getBoundingClientRect());
|
|
136
|
+
return Array.from({ length: items.length + 1 }, (_, gap) => {
|
|
137
|
+
const above = rects[gap - 1];
|
|
138
|
+
const below = rects[gap];
|
|
139
|
+
const y = above && below ? (above.bottom + below.top) / 2 : (below?.top ?? above.bottom);
|
|
140
|
+
return {
|
|
141
|
+
gap,
|
|
142
|
+
x: inset,
|
|
143
|
+
y: y - box.top,
|
|
144
|
+
length: box.width - inset * 2,
|
|
145
|
+
before: items[gap - 1],
|
|
146
|
+
after: items[gap],
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
// One slot per gap, and two where a line wraps: the end of one line and the start of the next
|
|
151
|
+
function gridSlots(list, items) {
|
|
152
|
+
const box = list.getBoundingClientRect();
|
|
153
|
+
if (!items.length)
|
|
154
|
+
return [{ gap: 0, x: 0, y: 0, length: box.height }];
|
|
155
|
+
const half = (parseFloat(getComputedStyle(list).columnGap) || 8) / 2;
|
|
156
|
+
const rects = items.map((item) => item.getBoundingClientRect());
|
|
157
|
+
const slots = [];
|
|
158
|
+
const add = (gap, x, rect, before, after) => slots.push({ gap, x: x - box.left, y: rect.top - box.top, length: rect.height, before, after });
|
|
159
|
+
rects.forEach((rect, gap) => {
|
|
160
|
+
const previous = rects[gap - 1];
|
|
161
|
+
if (previous && Math.abs(previous.top - rect.top) < rect.height / 2) {
|
|
162
|
+
add(gap, (previous.right + rect.left) / 2, rect, items[gap - 1], items[gap]);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (previous)
|
|
166
|
+
add(gap, previous.right + half, previous, items[gap - 1]);
|
|
167
|
+
add(gap, rect.left - half, rect, undefined, items[gap]);
|
|
168
|
+
});
|
|
169
|
+
const last = rects.length - 1;
|
|
170
|
+
add(rects.length, rects[last].right + half, rects[last], items[last]);
|
|
171
|
+
return slots;
|
|
172
|
+
}
|
|
173
|
+
// The chip keeps its size and fades out: grown to the item, its text would stand over the item's own
|
|
174
|
+
function flyChip(chip, to, layout) {
|
|
175
|
+
if (reduced() || !to) {
|
|
176
|
+
chip.remove();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const from = chip.getBoundingClientRect();
|
|
180
|
+
const x = layout === "grid" ? to.left + (to.width - from.width) / 2 : to.left;
|
|
181
|
+
const y = to.top + (to.height - from.height) / 2;
|
|
182
|
+
chip.animate([
|
|
183
|
+
{ transform: `translate(${from.left}px, ${from.top}px)`, opacity: 1 },
|
|
184
|
+
{ transform: `translate(${x}px, ${y}px)`, opacity: 0 },
|
|
185
|
+
], { duration: 200, easing: EASING, fill: "forwards" }).onfinish = () => chip.remove();
|
|
186
|
+
}
|
|
187
|
+
/** The items that changed place slide there from where they stood */
|
|
188
|
+
async function flip(items, work, skip) {
|
|
189
|
+
const before = new Map(items.map((item) => [item, item.getBoundingClientRect()]));
|
|
190
|
+
await work();
|
|
191
|
+
if (reduced())
|
|
192
|
+
return;
|
|
193
|
+
items
|
|
194
|
+
.filter((item) => item !== skip && item.isConnected)
|
|
195
|
+
.forEach((item) => {
|
|
196
|
+
const was = before.get(item);
|
|
197
|
+
const now = item.getBoundingClientRect();
|
|
198
|
+
const x = was.left - now.left;
|
|
199
|
+
const y = was.top - now.top;
|
|
200
|
+
if (x || y)
|
|
201
|
+
item.animate([{ transform: `translate(${x}px, ${y}px)` }, { transform: "none" }], { duration: 200, easing: EASING });
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
const swallow = (event) => {
|
|
205
|
+
event.stopPropagation();
|
|
206
|
+
event.preventDefault();
|
|
207
|
+
};
|
|
208
|
+
function startDrag(event, list, source, layout, immediate, commit, link) {
|
|
209
|
+
const touch = event.pointerType === "touch" && !immediate;
|
|
210
|
+
const origin = { x: event.clientX, y: event.clientY };
|
|
211
|
+
const pointer = { ...origin };
|
|
212
|
+
const holdTimer = touch ? setTimeout(begin, TOUCH_HOLD) : undefined;
|
|
213
|
+
const style = getComputedStyle(list);
|
|
214
|
+
const part = parseFloat(layout === "grid" ? style.columnGap : style.rowGap) ? PART : 0;
|
|
215
|
+
const scroller = scrollParent(list);
|
|
216
|
+
const { group } = list.dataset;
|
|
217
|
+
const targets = group ? `[data-sortable-target="${CSS.escape(group)}"]` : undefined;
|
|
218
|
+
let drag;
|
|
219
|
+
window.addEventListener("pointermove", onMove);
|
|
220
|
+
window.addEventListener("pointerup", onUp);
|
|
221
|
+
window.addEventListener("pointercancel", onCancel);
|
|
222
|
+
function onMove({ clientX, clientY }) {
|
|
223
|
+
pointer.x = clientX;
|
|
224
|
+
pointer.y = clientY;
|
|
225
|
+
if (drag)
|
|
226
|
+
return;
|
|
227
|
+
const moved = Math.hypot(clientX - origin.x, clientY - origin.y) >= START_DISTANCE;
|
|
228
|
+
if (moved && touch)
|
|
229
|
+
stopListening();
|
|
230
|
+
else if (moved)
|
|
231
|
+
begin();
|
|
232
|
+
}
|
|
233
|
+
function onUp() {
|
|
234
|
+
finish(true);
|
|
235
|
+
}
|
|
236
|
+
function onCancel() {
|
|
237
|
+
finish(false);
|
|
238
|
+
}
|
|
239
|
+
function onKey(keyEvent) {
|
|
240
|
+
if (keyEvent.key !== "Escape")
|
|
241
|
+
return;
|
|
242
|
+
keyEvent.stopPropagation();
|
|
243
|
+
finish(false);
|
|
244
|
+
}
|
|
245
|
+
function blockScroll(touchEvent) {
|
|
246
|
+
touchEvent.preventDefault();
|
|
247
|
+
}
|
|
248
|
+
function stopListening() {
|
|
249
|
+
clearTimeout(holdTimer);
|
|
250
|
+
window.removeEventListener("pointermove", onMove);
|
|
251
|
+
window.removeEventListener("pointerup", onUp);
|
|
252
|
+
window.removeEventListener("pointercancel", onCancel);
|
|
253
|
+
window.removeEventListener("keydown", onKey, true);
|
|
254
|
+
window.removeEventListener("touchmove", blockScroll);
|
|
255
|
+
}
|
|
256
|
+
function measure() {
|
|
257
|
+
return listsOf(list).map((entry) => {
|
|
258
|
+
const items = itemsOf(entry);
|
|
259
|
+
entry.setAttribute("data-dragging", "");
|
|
260
|
+
return { list: entry, items, slots: layout === "grid" ? gridSlots(entry, items) : listSlots(entry, items) };
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
function begin() {
|
|
264
|
+
const areas = measure();
|
|
265
|
+
window.getSelection()?.removeAllRanges();
|
|
266
|
+
document.documentElement.classList.add(s.Dragging);
|
|
267
|
+
source.setAttribute("data-sortable-source", "");
|
|
268
|
+
window.addEventListener("keydown", onKey, true);
|
|
269
|
+
window.addEventListener("touchmove", blockScroll, { passive: false });
|
|
270
|
+
const chip = makeChip(source);
|
|
271
|
+
const line = document.createElement("div");
|
|
272
|
+
line.className = s.Line;
|
|
273
|
+
line.hidden = true;
|
|
274
|
+
document.body.append(line);
|
|
275
|
+
const { width, height } = chip.getBoundingClientRect();
|
|
276
|
+
drag = { chip, line, width, height, areas };
|
|
277
|
+
tick();
|
|
278
|
+
}
|
|
279
|
+
function tick() {
|
|
280
|
+
if (!drag)
|
|
281
|
+
return;
|
|
282
|
+
drag.under = targets ? document.elementFromPoint(pointer.x, pointer.y) : undefined;
|
|
283
|
+
autoScroll();
|
|
284
|
+
update();
|
|
285
|
+
drag.frame = requestAnimationFrame(tick);
|
|
286
|
+
}
|
|
287
|
+
/** A column of targets with a scroll of its own scrolls alone, the page stays */
|
|
288
|
+
function panelUnder() {
|
|
289
|
+
const panel = targets && drag?.under ? scrollParent(drag.under) : undefined;
|
|
290
|
+
return panel && panel !== scroller && panel.querySelector(targets) ? panel : undefined;
|
|
291
|
+
}
|
|
292
|
+
function autoScroll() {
|
|
293
|
+
const area = panelUnder() ?? scroller;
|
|
294
|
+
const box = area?.getBoundingClientRect() ?? { top: 0, bottom: window.innerHeight };
|
|
295
|
+
const above = box.top + SCROLL_EDGE - pointer.y;
|
|
296
|
+
const below = pointer.y - box.bottom + SCROLL_EDGE;
|
|
297
|
+
const step = above > 0 ? -scrollStep(above) : below > 0 ? scrollStep(below) : 0;
|
|
298
|
+
if (!step)
|
|
299
|
+
return;
|
|
300
|
+
if (area)
|
|
301
|
+
area.scrollTop += step;
|
|
302
|
+
else
|
|
303
|
+
window.scrollBy(0, step);
|
|
304
|
+
}
|
|
305
|
+
function update() {
|
|
306
|
+
if (!drag)
|
|
307
|
+
return;
|
|
308
|
+
drag.chip.style.transform = `translate(${pointer.x - drag.width / 2}px, ${pointer.y - drag.height / 2}px)`;
|
|
309
|
+
const target = (targets && drag.under?.closest(targets)) || undefined;
|
|
310
|
+
if (target !== drag.target)
|
|
311
|
+
pointAt(target);
|
|
312
|
+
const box = source.getBoundingClientRect();
|
|
313
|
+
const overSource = pointer.x >= box.left && pointer.x <= box.right && pointer.y >= box.top && pointer.y <= box.bottom;
|
|
314
|
+
source.toggleAttribute("data-sortable-away", !overSource);
|
|
315
|
+
showGap(target || overSource ? undefined : nearestSlot());
|
|
316
|
+
}
|
|
317
|
+
function pointAt(target) {
|
|
318
|
+
if (!drag)
|
|
319
|
+
return;
|
|
320
|
+
clearTimeout(drag.rest);
|
|
321
|
+
drag.target?.removeAttribute("data-over");
|
|
322
|
+
drag.target = target;
|
|
323
|
+
if (!target)
|
|
324
|
+
return;
|
|
325
|
+
target.setAttribute("data-over", "");
|
|
326
|
+
drag.rest = setTimeout(() => {
|
|
327
|
+
link?.hold?.(target.dataset.name);
|
|
328
|
+
// What the hold changed is drawn two frames on: a list opened, the page jumped
|
|
329
|
+
requestAnimationFrame(() => requestAnimationFrame(() => drag && (drag.areas = measure())));
|
|
330
|
+
}, TARGET_HOLD);
|
|
331
|
+
}
|
|
332
|
+
function distance(slot, box) {
|
|
333
|
+
if (layout === "list")
|
|
334
|
+
return Math.abs(pointer.y - (box.top + slot.y));
|
|
335
|
+
const top = box.top + slot.y;
|
|
336
|
+
return Math.hypot(pointer.x - (box.left + slot.x), Math.max(top - pointer.y, 0, pointer.y - top - slot.length));
|
|
337
|
+
}
|
|
338
|
+
function nearestSlot() {
|
|
339
|
+
if (!drag)
|
|
340
|
+
return undefined;
|
|
341
|
+
let best;
|
|
342
|
+
let closest = Infinity;
|
|
343
|
+
for (const area of drag.areas) {
|
|
344
|
+
const box = area.list.getBoundingClientRect();
|
|
345
|
+
const near = pointer.x >= box.left - NEAR_LIST &&
|
|
346
|
+
pointer.x <= box.right + NEAR_LIST &&
|
|
347
|
+
pointer.y >= box.top - NEAR_LIST &&
|
|
348
|
+
pointer.y <= box.bottom + NEAR_LIST;
|
|
349
|
+
if (!near)
|
|
350
|
+
continue;
|
|
351
|
+
for (const slot of area.slots) {
|
|
352
|
+
const away = distance(slot, box);
|
|
353
|
+
if (away < closest) {
|
|
354
|
+
closest = away;
|
|
355
|
+
best = { area, slot };
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (!best)
|
|
360
|
+
return undefined;
|
|
361
|
+
const index = best.area.items.indexOf(source);
|
|
362
|
+
return index >= 0 && (best.slot.gap === index || best.slot.gap === index + 1) ? undefined : best;
|
|
363
|
+
}
|
|
364
|
+
function shift(slot, by) {
|
|
365
|
+
const move = (item, offset) => {
|
|
366
|
+
if (!item || !part)
|
|
367
|
+
return;
|
|
368
|
+
item.style.transform = by
|
|
369
|
+
? layout === "grid"
|
|
370
|
+
? `translateX(${offset}px)`
|
|
371
|
+
: `translateY(${offset}px)`
|
|
372
|
+
: "";
|
|
373
|
+
};
|
|
374
|
+
move(slot?.before, -by);
|
|
375
|
+
move(slot?.after, by);
|
|
376
|
+
}
|
|
377
|
+
function showGap(found) {
|
|
378
|
+
if (!drag)
|
|
379
|
+
return;
|
|
380
|
+
if (found?.slot !== drag.found?.slot) {
|
|
381
|
+
shift(drag.found?.slot, 0);
|
|
382
|
+
shift(found?.slot, part);
|
|
383
|
+
}
|
|
384
|
+
drag.found = found;
|
|
385
|
+
drag.line.hidden = !found;
|
|
386
|
+
if (!found)
|
|
387
|
+
return;
|
|
388
|
+
const box = found.area.list.getBoundingClientRect();
|
|
389
|
+
if (layout === "grid") {
|
|
390
|
+
drag.line.style.transform = `translate(${box.left + found.slot.x - 1}px, ${box.top + found.slot.y}px)`;
|
|
391
|
+
drag.line.style.height = `${found.slot.length}px`;
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
drag.line.style.transform = `translate(${box.left + found.slot.x}px, ${box.top + found.slot.y - 1}px)`;
|
|
395
|
+
drag.line.style.width = `${found.slot.length}px`;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function restoreSource() {
|
|
399
|
+
source.removeAttribute("data-sortable-source");
|
|
400
|
+
source.removeAttribute("data-sortable-away");
|
|
401
|
+
}
|
|
402
|
+
function settle(chip, item = source) {
|
|
403
|
+
const shown = item?.isConnected ? item : undefined;
|
|
404
|
+
flyChip(chip, shown?.getBoundingClientRect(), layout);
|
|
405
|
+
if (!reduced() && shown)
|
|
406
|
+
shown.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 250, easing: EASING });
|
|
407
|
+
}
|
|
408
|
+
async function land(chip, areas, { area, slot: { gap } }, clear) {
|
|
409
|
+
const from = itemsOf(list).indexOf(source);
|
|
410
|
+
restoreSource();
|
|
411
|
+
if (area.list === list) {
|
|
412
|
+
const to = gap > from ? gap - 1 : gap;
|
|
413
|
+
await flip(area.items, () => {
|
|
414
|
+
clear();
|
|
415
|
+
return commit(from, to);
|
|
416
|
+
}, source);
|
|
417
|
+
announce(`${nameOf(source)}, ${to + 1} / ${area.items.length}`);
|
|
418
|
+
settle(chip);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const name = nameOf(source);
|
|
422
|
+
await flip(areas.flatMap(({ items }) => items), () => {
|
|
423
|
+
clear();
|
|
424
|
+
return link?.move(from, area.list.dataset.name, gap);
|
|
425
|
+
}, source);
|
|
426
|
+
announce(`${name}, ${gap + 1} / ${area.items.length + 1}`);
|
|
427
|
+
// A framework builds the row anew under its new parent
|
|
428
|
+
settle(chip, source.isConnected ? source : itemsOf(area.list)[gap]);
|
|
429
|
+
}
|
|
430
|
+
async function dropOn(chip, areas, name, clear) {
|
|
431
|
+
const from = itemsOf(list).indexOf(source);
|
|
432
|
+
restoreSource();
|
|
433
|
+
await flip(areas.flatMap(({ items }) => items), () => {
|
|
434
|
+
clear();
|
|
435
|
+
return link?.move(from, name);
|
|
436
|
+
}, source);
|
|
437
|
+
flyChip(chip, undefined, layout);
|
|
438
|
+
}
|
|
439
|
+
function finish(drop) {
|
|
440
|
+
stopListening();
|
|
441
|
+
if (!drag)
|
|
442
|
+
return;
|
|
443
|
+
const { chip, line, found, areas, frame, target, rest } = drag;
|
|
444
|
+
if (frame)
|
|
445
|
+
cancelAnimationFrame(frame);
|
|
446
|
+
clearTimeout(rest);
|
|
447
|
+
target?.removeAttribute("data-over");
|
|
448
|
+
// The pointer lifts over a row: that click must not act on it
|
|
449
|
+
window.addEventListener("click", swallow, { capture: true, once: true });
|
|
450
|
+
setTimeout(() => window.removeEventListener("click", swallow, { capture: true }));
|
|
451
|
+
line.remove();
|
|
452
|
+
drag = undefined;
|
|
453
|
+
// On a drop this waits for the flip to read where the parted items stand
|
|
454
|
+
const clear = () => {
|
|
455
|
+
areas.forEach((area) => area.list.removeAttribute("data-dragging"));
|
|
456
|
+
document.documentElement.classList.remove(s.Dragging);
|
|
457
|
+
shift(found?.slot, 0);
|
|
458
|
+
};
|
|
459
|
+
const targetName = target?.dataset.name;
|
|
460
|
+
if (drop && targetName && targetName !== list.dataset.name) {
|
|
461
|
+
void dropOn(chip, areas, targetName, clear);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
if (drop && found && !target) {
|
|
465
|
+
void land(chip, areas, found, clear);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
clear();
|
|
469
|
+
restoreSource();
|
|
470
|
+
settle(chip);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
/** Starts a drag from a press inside the list. Goes on the list's `pointerdown`. */
|
|
474
|
+
export function sortablePointerDown(event, list, layout, commit, link) {
|
|
475
|
+
const target = event.target;
|
|
476
|
+
if (event.button || target.closest("[data-sortable]") !== list)
|
|
477
|
+
return;
|
|
478
|
+
const source = itemOf(list, target);
|
|
479
|
+
if (!source || source.hasAttribute(STATIC))
|
|
480
|
+
return;
|
|
481
|
+
const handle = target.closest(HANDLE);
|
|
482
|
+
const control = target.closest(INTERACTIVE);
|
|
483
|
+
// A grip in sight is the only way to drag, otherwise anything but a control
|
|
484
|
+
// starts it, and a control that opens its row on a click may let a pull through
|
|
485
|
+
if (source.querySelector(HANDLE) && showsHandles(list) ? !handle : control && !control.closest(GRAB))
|
|
486
|
+
return;
|
|
487
|
+
startDrag(event, list, source, layout, !!handle, commit, link);
|
|
488
|
+
}
|
|
489
|
+
const KEY_STEPS = { ArrowUp: -1, ArrowLeft: -1, ArrowDown: 1, ArrowRight: 1 };
|
|
490
|
+
/** Moves the focused handle's item with the arrow keys. Goes on the list's `keydown`. */
|
|
491
|
+
export async function sortableKeyDown(event, list, commit, link) {
|
|
492
|
+
const step = KEY_STEPS[event.key];
|
|
493
|
+
const target = event.target;
|
|
494
|
+
if (!step || target.closest("[data-sortable]") !== list)
|
|
495
|
+
return;
|
|
496
|
+
const source = itemOf(list, target);
|
|
497
|
+
if (!source || (target !== source && !target.closest(HANDLE)))
|
|
498
|
+
return;
|
|
499
|
+
const items = itemsOf(list);
|
|
500
|
+
const from = items.indexOf(source);
|
|
501
|
+
const to = from + step;
|
|
502
|
+
if (to < 0 || to >= items.length) {
|
|
503
|
+
// Past the end of the list: on to the next list of the group
|
|
504
|
+
const lists = listsOf(list);
|
|
505
|
+
const next = lists[lists.indexOf(list) + step];
|
|
506
|
+
if (!next || !link)
|
|
507
|
+
return;
|
|
508
|
+
event.preventDefault();
|
|
509
|
+
const place = step > 0 ? 0 : itemsOf(next).length;
|
|
510
|
+
const name = nameOf(source);
|
|
511
|
+
await flip(lists.flatMap(itemsOf), () => link.move(from, next.dataset.name, place));
|
|
512
|
+
const landed = itemsOf(next)[place];
|
|
513
|
+
(landed?.querySelector(HANDLE) ?? landed)?.focus();
|
|
514
|
+
announce(`${name}, ${place + 1} / ${itemsOf(next).length}`);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
event.preventDefault();
|
|
518
|
+
await flip(items, () => commit(from, to));
|
|
519
|
+
// Moving an element in the document drops its focus
|
|
520
|
+
if (target.isConnected && document.activeElement !== target)
|
|
521
|
+
target.focus();
|
|
522
|
+
announce(`${nameOf(source)}, ${to + 1} / ${items.length}`);
|
|
523
|
+
}
|
|
524
|
+
//# 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// Lists of one group trade items, and a target outside them takes an item whole.\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\n/** A move inside a list, into another list of its group or onto a target */\nexport type SortableMove<T = unknown> = {\n /** The entry of `value` that moved */\n item: T\n /** The `name` of the list it left */\n from: string\n /** Its place there */\n fromIndex: number\n /** The `name` of the list or the target it reached */\n to: string\n /** Its place after the move. A target has no places: the app decides. */\n toIndex?: number\n}\n\n/** What a list does when an item leaves it. Resolves once the DOM shows the move. */\nexport type SortableLink = {\n move: (fromIndex: number, to: string, toIndex?: number) => void | Promise<void>\n hold?: (name: string) => void\n}\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\ntype Area = {\n list: HTMLElement\n items: HTMLElement[]\n slots: Slot[]\n}\n\ntype Found = {\n area: Area\n slot: Slot\n}\n\nconst START_DISTANCE = 4\nconst TOUCH_HOLD = 200\n// Long enough to pass over a target on the way to another\nconst TARGET_HOLD = 500\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 GRAB = \"[data-sortable-grab]\"\nconst STATIC = \"data-sortable-static\"\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\n/** How far the line stands in from the sides of the list */\nconst lineInset = (list: HTMLElement) => {\n const inset = parseFloat(getComputedStyle(list).getPropertyValue(\"--sortable-line-inset\"))\n return Number.isNaN(inset) ? LINE_INSET : inset\n}\n\n/** A head row or an empty message stands among the items and is not one of them */\nconst itemsOf = (list: HTMLElement) => ([...list.children] as HTMLElement[]).filter((item) => !item.hasAttribute(STATIC))\n\n/** The lists a drag from this one can reach, in the order of the page */\nconst listsOf = (list: HTMLElement) => {\n const { group } = list.dataset\n if (!group) return [list]\n return [...document.querySelectorAll<HTMLElement>(`[data-sortable][data-group=\"${CSS.escape(group)}\"]:not([data-disabled])`)]\n}\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<HTMLElement>(\"[data-sortable-image], img\")\n if (image instanceof HTMLImageElement) {\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 } else if (image) {\n // A cropped background: the chip takes the element as it stands, at the chip's size\n const picture = image.cloneNode(false) as HTMLElement\n picture.className = s.ChipImage\n picture.hidden = false\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 = (start: Element | null) => {\n for (let node = start; 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 inset = lineInset(list)\n if (!items.length) {\n // Above a message or an empty row: the line must not cross its text\n const rest = (list.lastElementChild ?? list).getBoundingClientRect()\n return [{ gap: 0, x: inset, y: rest.top - box.top, length: box.width - inset * 2 }]\n }\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: inset,\n y: y - box.top,\n length: box.width - 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 if (!items.length) return [{ gap: 0, x: 0, y: 0, length: box.height }]\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 flip(items: HTMLElement[], work: () => void | Promise<void>, skip?: HTMLElement) {\n const before = new Map(items.map((item) => [item, item.getBoundingClientRect()]))\n await work()\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(\n event: PointerEvent,\n list: HTMLElement,\n source: HTMLElement,\n layout: SortableLayout,\n immediate: boolean,\n commit: SortableCommit,\n link?: SortableLink,\n) {\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 const { group } = list.dataset\n const targets = group ? `[data-sortable-target=\"${CSS.escape(group)}\"]` : undefined\n let drag:\n | {\n chip: HTMLElement\n line: HTMLElement\n width: number\n height: number\n areas: Area[]\n frame?: number\n found?: Found\n under?: Element | null\n target?: HTMLElement\n rest?: ReturnType<typeof setTimeout>\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 measure() {\n return listsOf(list).map((entry) => {\n const items = itemsOf(entry)\n entry.setAttribute(\"data-dragging\", \"\")\n return { list: entry, items, slots: layout === \"grid\" ? gridSlots(entry, items) : listSlots(entry, items) }\n })\n }\n\n function begin() {\n const areas = measure()\n window.getSelection()?.removeAllRanges()\n document.documentElement.classList.add(s.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, areas }\n tick()\n }\n\n function tick() {\n if (!drag) return\n drag.under = targets ? document.elementFromPoint(pointer.x, pointer.y) : undefined\n autoScroll()\n update()\n drag.frame = requestAnimationFrame(tick)\n }\n\n /** A column of targets with a scroll of its own scrolls alone, the page stays */\n function panelUnder() {\n const panel = targets && drag?.under ? scrollParent(drag.under) : undefined\n return panel && panel !== scroller && panel.querySelector(targets as string) ? panel : undefined\n }\n\n function autoScroll() {\n const area = panelUnder() ?? scroller\n const box = area?.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 (area) area.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 target = (targets && drag.under?.closest<HTMLElement>(targets)) || undefined\n if (target !== drag.target) pointAt(target)\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(target || overSource ? undefined : nearestSlot())\n }\n\n function pointAt(target: HTMLElement | undefined) {\n if (!drag) return\n clearTimeout(drag.rest)\n drag.target?.removeAttribute(\"data-over\")\n drag.target = target\n if (!target) return\n target.setAttribute(\"data-over\", \"\")\n drag.rest = setTimeout(() => {\n link?.hold?.(target.dataset.name as string)\n // What the hold changed is drawn two frames on: a list opened, the page jumped\n requestAnimationFrame(() => requestAnimationFrame(() => drag && (drag.areas = measure())))\n }, TARGET_HOLD)\n }\n\n function distance(slot: Slot, box: DOMRect) {\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\n function nearestSlot() {\n if (!drag) return undefined\n let best: Found | undefined\n let closest = Infinity\n for (const area of drag.areas) {\n const box = area.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) continue\n for (const slot of area.slots) {\n const away = distance(slot, box)\n if (away < closest) {\n closest = away\n best = { area, slot }\n }\n }\n }\n if (!best) return undefined\n const index = best.area.items.indexOf(source)\n return index >= 0 && (best.slot.gap === index || best.slot.gap === index + 1) ? undefined : best\n }\n\n function shift(slot: Slot | undefined, by: number) {\n const move = (item: Element | undefined, offset: number) => {\n if (!item || !part) return\n ;(item as HTMLElement).style.transform = by\n ? layout === \"grid\"\n ? `translateX(${offset}px)`\n : `translateY(${offset}px)`\n : \"\"\n }\n move(slot?.before, -by)\n move(slot?.after, by)\n }\n\n function showGap(found: Found | undefined) {\n if (!drag) return\n if (found?.slot !== drag.found?.slot) {\n shift(drag.found?.slot, 0)\n shift(found?.slot, part)\n }\n drag.found = found\n drag.line.hidden = !found\n if (!found) return\n const box = found.area.list.getBoundingClientRect()\n if (layout === \"grid\") {\n drag.line.style.transform = `translate(${box.left + found.slot.x - 1}px, ${box.top + found.slot.y}px)`\n drag.line.style.height = `${found.slot.length}px`\n } else {\n drag.line.style.transform = `translate(${box.left + found.slot.x}px, ${box.top + found.slot.y - 1}px)`\n drag.line.style.width = `${found.slot.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, item: HTMLElement | undefined = source) {\n const shown = item?.isConnected ? item : undefined\n flyChip(chip, shown?.getBoundingClientRect(), layout)\n if (!reduced() && shown) shown.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 250, easing: EASING })\n }\n\n async function land(chip: HTMLElement, areas: Area[], { area, slot: { gap } }: Found, clear: () => void) {\n const from = itemsOf(list).indexOf(source)\n restoreSource()\n if (area.list === list) {\n const to = gap > from ? gap - 1 : gap\n await flip(\n area.items,\n () => {\n clear()\n return commit(from, to)\n },\n source,\n )\n announce(`${nameOf(source)}, ${to + 1} / ${area.items.length}`)\n settle(chip)\n return\n }\n const name = nameOf(source)\n await flip(\n areas.flatMap(({ items }) => items),\n () => {\n clear()\n return link?.move(from, area.list.dataset.name as string, gap)\n },\n source,\n )\n announce(`${name}, ${gap + 1} / ${area.items.length + 1}`)\n // A framework builds the row anew under its new parent\n settle(chip, source.isConnected ? source : itemsOf(area.list)[gap])\n }\n\n async function dropOn(chip: HTMLElement, areas: Area[], name: string, clear: () => void) {\n const from = itemsOf(list).indexOf(source)\n restoreSource()\n await flip(\n areas.flatMap(({ items }) => items),\n () => {\n clear()\n return link?.move(from, name)\n },\n source,\n )\n flyChip(chip, undefined, layout)\n }\n\n function finish(drop: boolean) {\n stopListening()\n if (!drag) return\n const { chip, line, found, areas, frame, target, rest } = drag\n if (frame) cancelAnimationFrame(frame)\n clearTimeout(rest)\n target?.removeAttribute(\"data-over\")\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 areas.forEach((area) => area.list.removeAttribute(\"data-dragging\"))\n document.documentElement.classList.remove(s.Dragging)\n shift(found?.slot, 0)\n }\n const targetName = target?.dataset.name\n if (drop && targetName && targetName !== list.dataset.name) {\n void dropOn(chip, areas, targetName, clear)\n return\n }\n if (drop && found && !target) {\n void land(chip, areas, 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(\n event: PointerEvent,\n list: HTMLElement,\n layout: SortableLayout,\n commit: SortableCommit,\n link?: SortableLink,\n) {\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 || source.hasAttribute(STATIC)) return\n const handle = target.closest(HANDLE)\n const control = target.closest(INTERACTIVE)\n // A grip in sight is the only way to drag, otherwise anything but a control\n // starts it, and a control that opens its row on a click may let a pull through\n if (source.querySelector(HANDLE) && showsHandles(list) ? !handle : control && !control.closest(GRAB)) return\n startDrag(event, list, source, layout, !!handle, commit, link)\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, link?: SortableLink) {\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) {\n // Past the end of the list: on to the next list of the group\n const lists = listsOf(list)\n const next = lists[lists.indexOf(list) + step]\n if (!next || !link) return\n event.preventDefault()\n const place = step > 0 ? 0 : itemsOf(next).length\n const name = nameOf(source)\n await flip(lists.flatMap(itemsOf), () => link.move(from, next.dataset.name as string, place))\n const landed = itemsOf(next)[place]\n ;(landed?.querySelector<HTMLElement>(HANDLE) ?? landed)?.focus()\n announce(`${name}, ${place + 1} / ${itemsOf(next).length}`)\n return\n }\n event.preventDefault()\n await flip(items, () => commit(from, to))\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
|
-
|
|
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,85 @@
|
|
|
1
|
+
import type { ComponentProps, ElementType, ReactNode } from "react";
|
|
2
|
+
import { type SortableLayout, type SortableMove } 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
|
+
* Lists of one group trade items, and a `Sortable.Target` of that group takes them. Give each list a `name`.
|
|
34
|
+
*/
|
|
35
|
+
group?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Names the list in `onMove`
|
|
38
|
+
*/
|
|
39
|
+
name?: string;
|
|
40
|
+
/**
|
|
41
|
+
* An item moved, inside this list or out of it. Out of it the app updates both lists: `onChange` is for a move inside one.
|
|
42
|
+
*/
|
|
43
|
+
onMove?: (move: SortableMove<T>) => void;
|
|
44
|
+
/**
|
|
45
|
+
* A drag rests on a `Sortable.Target` of the group: open what it stands for or go there
|
|
46
|
+
*/
|
|
47
|
+
onHold?: (name: string) => void;
|
|
48
|
+
/**
|
|
49
|
+
* The items, one direct child per entry of `value`, keyed by the entry and not by its index
|
|
50
|
+
*/
|
|
51
|
+
children?: ReactNode;
|
|
52
|
+
} & Omit<ComponentProps<"div">, "children" | "onChange">;
|
|
53
|
+
/** Reorders its direct children by dragging, from anywhere but a control, or by a `Sortable.Handle` where grips are shown. */
|
|
54
|
+
export declare function Sortable<T>({ value, onChange, layout, as: Tag, disabled, handles, group, name, onMove, onHold, className, children, onPointerDown, onKeyDown, ...rest }: SortableProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
55
|
+
export declare namespace Sortable {
|
|
56
|
+
var Handle: typeof SortableHandle;
|
|
57
|
+
var Target: typeof SortableTarget;
|
|
58
|
+
}
|
|
59
|
+
export type SortableHandleProps = {
|
|
60
|
+
/**
|
|
61
|
+
* Name of the handle for assistive technology
|
|
62
|
+
* @default Reorder
|
|
63
|
+
*/
|
|
64
|
+
"aria-label"?: string;
|
|
65
|
+
} & ComponentProps<"button">;
|
|
66
|
+
/** The grip of an item. Arrow keys move the item while the handle has focus. */
|
|
67
|
+
declare function SortableHandle({ "aria-label": ariaLabel, className, children, ...rest }: SortableHandleProps): import("react/jsx-runtime").JSX.Element;
|
|
68
|
+
export type SortableTargetProps = {
|
|
69
|
+
/**
|
|
70
|
+
* The `group` of the lists whose items it takes
|
|
71
|
+
*/
|
|
72
|
+
group: string;
|
|
73
|
+
/**
|
|
74
|
+
* Names the target in `onMove` and `onHold` on the list
|
|
75
|
+
*/
|
|
76
|
+
name: string;
|
|
77
|
+
/**
|
|
78
|
+
* Element to render
|
|
79
|
+
* @default div
|
|
80
|
+
*/
|
|
81
|
+
as?: ElementType;
|
|
82
|
+
} & ComponentProps<"div">;
|
|
83
|
+
/** A place outside the lists that takes an item whole: a folder, a category in a side list. */
|
|
84
|
+
declare function SortableTarget({ group, name, as: Tag, className, ...rest }: SortableTargetProps): import("react/jsx-runtime").JSX.Element;
|
|
85
|
+
export {};
|
|
@@ -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
|
*/
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
/** A move inside a list, into another list of its group or onto a target */
|
|
5
|
+
export type SortableMove<T = unknown> = {
|
|
6
|
+
/** The entry of `value` that moved */
|
|
7
|
+
item: T;
|
|
8
|
+
/** The `name` of the list it left */
|
|
9
|
+
from: string;
|
|
10
|
+
/** Its place there */
|
|
11
|
+
fromIndex: number;
|
|
12
|
+
/** The `name` of the list or the target it reached */
|
|
13
|
+
to: string;
|
|
14
|
+
/** Its place after the move. A target has no places: the app decides. */
|
|
15
|
+
toIndex?: number;
|
|
16
|
+
};
|
|
17
|
+
/** What a list does when an item leaves it. Resolves once the DOM shows the move. */
|
|
18
|
+
export type SortableLink = {
|
|
19
|
+
move: (fromIndex: number, to: string, toIndex?: number) => void | Promise<void>;
|
|
20
|
+
hold?: (name: string) => void;
|
|
21
|
+
};
|
|
22
|
+
/** The order after a move. `to` counts the list without the moved item. */
|
|
23
|
+
export declare const moveItem: <T>(items: readonly T[], from: number, to: number) => T[];
|
|
24
|
+
/** Starts a drag from a press inside the list. Goes on the list's `pointerdown`. */
|
|
25
|
+
export declare function sortablePointerDown(event: PointerEvent, list: HTMLElement, layout: SortableLayout, commit: SortableCommit, link?: SortableLink): void;
|
|
26
|
+
/** Moves the focused handle's item with the arrow keys. Goes on the list's `keydown`. */
|
|
27
|
+
export declare function sortableKeyDown(event: KeyboardEvent, list: HTMLElement, commit: SortableCommit, link?: SortableLink): Promise<void>;
|