@plexui/vue 0.1.12 → 0.2.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 +7 -3
- package/dist/es/components/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/es/components/Sortable/Sortable.js +90 -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 +3 -0
- 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 +406 -0
- package/dist/es/lib/sortable.js.map +1 -0
- package/dist/types/components/Sortable/Sortable.d.ts +94 -0
- package/dist/types/components/Sortable/index.d.ts +1 -0
- package/dist/types/components/TagInput/TagInput.d.ts +12 -1
- package/dist/types/components/Textarea/Textarea.d.ts +3 -3
- package/dist/types/lib/sortable.d.ts +9 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Textarea.js","sourceRoot":"","sources":["../../../../src/components/Textarea/Textarea.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n computed,\n defineComponent,\n h,\n nextTick,\n onMounted,\n ref,\n useId,\n watch,\n withDirectives,\n type PropType,\n type TextareaHTMLAttributes,\n} from \"vue\"\nimport type { ControlSize, Sizes, Variants } from \"@plexui/styles/types\"\nimport { vServerText } from \"../../lib/defaultValue.js\"\nimport { toCssVariables } from \"../../lib/helpers.js\"\nimport s from \"@plexui/styles/components/Textarea/Textarea.module.css\"\n\n// Vue port of packages/ui/src/components/Textarea/Textarea.tsx. Props,\n// defaults and rendered DOM match React 1:1 (checked by the parity tests).\n//\n// - `class` goes to the container; every other attribute and listener\n// (`style`, `placeholder`, `@input`, `@change`, ...) goes to the <textarea>,\n// like React's `className` and `...restProps`.\n// - Value: `value` + `update:value` mirror React's `value` prop\n// (`v-model:value`) and fire on every input. Uncontrolled use takes\n// `default-value`. Like a React controlled textarea, a `value` the parent\n// does not update after input is put back.\n// - As in React, the textarea's text content (its `defaultValue`) follows\n// the controlled value, or `default-value` when uncontrolled. It is set\n// through the `textContent` property, which Vue's server renderer prints\n// as the element's content, so `vServerText` can adjust it at SSR.\n\ntype ListenerValue = ((event: Event) => void) | Array<(event: Event) => void> | undefined\n\nconst invoke = (handler: unknown, event: Event) => {\n for (const fn of ([] as unknown[]).concat(handler ?? [])) {\n if (typeof fn === \"function\") fn(event)\n }\n}\n\nconst TextareaComponent = defineComponent({\n name: \"Textarea\",\n inheritAttrs: false,\n props: {\n /** Visual style of the textarea. @default \"outline\" */\n variant: { type: String as PropType<Variants<\"outline\" | \"soft\">>, default: \"outline\" },\n /** Controls the size of the textarea. @default \"md\" */\n size: { type: String as PropType<ControlSize>, default: \"md\" },\n /** Applies a negative margin using the current gutter to optically align the text with surrounding content. */\n opticallyAlign: String as PropType<\"start\" | \"end\">,\n /** Controls gutter on the edges of the textarea, defaults to value from `size`. */\n gutterSize: String as PropType<Sizes<\"2xs\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\">>,\n /** Disables the textarea visually and from interactions. @default false */\n disabled: { type: Boolean, default: false },\n /** Makes the textarea read-only. @default false */\n readOnly: { type: Boolean, default: false },\n /** Native spelling of `readOnly` (`<Textarea readonly />`). @default false */\n readonly: { type: Boolean, default: false },\n /** Mark the textarea as invalid. @default false */\n invalid: { type: Boolean, default: false },\n /** Allow autofill extensions to appear in the textarea. @default `true` when `name` is set, else `false` */\n allowAutofillExtensions: { type: Boolean, default: undefined },\n /** Select all contents of the textarea when mounted (and whenever it turns `true`). @default false */\n autoSelect: { type: Boolean, default: undefined },\n /** Default number of rows to display. @default 3 */\n rows: { type: Number, default: 3 },\n /** Automatically adjust the height of the textarea based on its contents. @default false */\n autoResize: { type: Boolean, default: undefined },\n /** Maximum number of rows that can be displayed when autoResize is enabled. @default Math.max(rows, 10) */\n maxRows: Number,\n id: String,\n name: String,\n /** Controlled value (`v-model:value`) */\n value: { type: [String, Number] as PropType<string | number>, default: undefined },\n /** Initial value when uncontrolled */\n defaultValue: { type: [String, Number] as PropType<string | number>, default: undefined },\n /** The textarea's `autocomplete` attribute */\n autoComplete: String,\n },\n emits: {\n \"update:value\": (_value: string) => true,\n /** Fired when the browser autofills the textarea */\n \"autofill\": () => true,\n },\n setup(props, { attrs, emit, expose }) {\n const textAreaRef = ref<HTMLTextAreaElement | null>(null)\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-${useId()}`\n const focused = ref(false)\n\n const computedMaxRows = computed(() =>\n props.autoResize ? Math.max(props.maxRows ?? 10, props.rows) : props.rows,\n )\n const controlledValue = () => props.value\n\n const runAutoSelect = () => {\n if (props.autoSelect) {\n textAreaRef.value?.select()\n }\n }\n\n const autosizeTextarea = () => {\n const textarea = textAreaRef.value\n if (!props.autoResize || !textarea || computedMaxRows.value === undefined) return\n textarea.style.height = \"0px\"\n const scrollHeight = textarea.scrollHeight\n textarea.style.height = scrollHeight + \"px\"\n }\n\n // The textarea's value is set imperatively, like React DOM does: a `value`\n // prop would make Vue mirror it into a `value` attribute, which React\n // never renders. Puts the controlled value back when it differs.\n const syncValue = () => {\n const textarea = textAreaRef.value\n const value = controlledValue()\n if (!textarea || value == null) return\n const nextValue = String(value)\n if (textarea.value !== nextValue) textarea.value = nextValue\n }\n\n // React runs these effects after mount and when their dependencies change\n onMounted(() => {\n const textarea = textAreaRef.value\n // React's initTextarea: the initial text becomes the (dirty) value\n const initialValue = textarea?.textContent ?? \"\"\n if (textarea && initialValue !== \"\") textarea.value = initialValue\n runAutoSelect()\n autosizeTextarea()\n })\n // React DOM writes a new value while committing, before any effect runs,\n // so the selection covers the value that arrives with `autoSelect`\n watch(\n () => props.autoSelect,\n () => {\n syncValue()\n runAutoSelect()\n },\n { flush: \"post\" },\n )\n // Resize if value changes outside of user input - e.g. form reset\n watch(\n () => [controlledValue(), props.rows, props.autoResize, computedMaxRows.value],\n () => {\n syncValue()\n autosizeTextarea()\n },\n { flush: \"post\" },\n )\n\n const handleInput = (evt: Event) => {\n const next = (evt.target as HTMLTextAreaElement).value\n emit(\"update:value\", next)\n autosizeTextarea()\n // A controlled value the parent did not update wins over the typed text,\n // as in React\n void nextTick(syncValue)\n }\n\n expose({\n /** The rendered <textarea> element */\n textarea: textAreaRef,\n focus: () => textAreaRef.value?.focus(),\n })\n\n return () => {\n const {\n class: className,\n onInput,\n onFocus,\n onBlur,\n onAnimationstart,\n ...restAttrs\n } = attrs as Record<string, unknown>\n\n const { variant, size, gutterSize, disabled, invalid, rows } = props\n const readOnly = props.readOnly || props.readonly\n // Default to `true` when presence of `name`\n const allowAutofillExtensions = props.allowAutofillExtensions ?? !!props.name\n const value = controlledValue()\n // React keeps the textarea's defaultValue (its text content) in sync with\n // the controlled value, else with `defaultValue`\n const textContent = String(value ?? props.defaultValue ?? \"\")\n\n return h(\n \"div\",\n {\n \"class\": [s.Container, className],\n \"data-variant\": variant,\n \"data-size\": size,\n \"data-gutter-size\": gutterSize,\n \"data-optically-align\": props.opticallyAlign,\n \"data-focused\": String(focused.value),\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.value}`,\n }),\n },\n [\n withDirectives(\n h(\"textarea\", {\n ...restAttrs,\n \"onInput\": (evt: Event) => {\n invoke(onInput as ListenerValue, evt)\n handleInput(evt)\n },\n \"ref\": textAreaRef,\n \"id\": props.id || (allowAutofillExtensions ? undefined : onePasswordPreventionId),\n \"class\": s.Textarea,\n \"name\": props.name,\n \"autocomplete\": props.autoComplete ?? restAttrs.autocomplete,\n // Expose the visual invalid state to assistive tech; an explicit aria-invalid wins\n \"aria-invalid\": restAttrs[\"aria-invalid\"] ?? (invalid || undefined),\n \"readonly\": readOnly,\n \"disabled\": disabled,\n \"rows\": rows,\n \"onFocus\": (evt: Event) => {\n focused.value = true\n invoke(onFocus as ListenerValue, evt)\n },\n \"onBlur\": (evt: Event) => {\n focused.value = false\n invoke(onBlur as ListenerValue, evt)\n },\n \"onAnimationstart\": (evt: AnimationEvent) => {\n invoke(onAnimationstart as ListenerValue, evt)\n if (evt.animationName === \"native-autofill-in\") {\n emit(\"autofill\")\n }\n },\n // Prevent LastPass and 1Password from appearing\n \"data-lpignore\": allowAutofillExtensions ? undefined : true,\n \"data-1p-ignore\": allowAutofillExtensions ? undefined : true,\n \"textContent\": textContent,\n }),\n [[vServerText, textContent]],\n ),\n ],\n )\n }\n },\n})\n\ntype TextareaOwnProps = InstanceType<typeof TextareaComponent>[\"$props\"]\n\n/** Textarea props plus the native <textarea> attributes that fall through to it, as in React. */\nexport type TextareaProps = TextareaOwnProps & Omit<TextareaHTMLAttributes, keyof TextareaOwnProps | \"readonly\">\n\nexport const Textarea = TextareaComponent as typeof TextareaComponent & { new (): { $props: TextareaProps } }\n"]}
|
|
1
|
+
{"version":3,"file":"Textarea.js","sourceRoot":"","sources":["../../../../src/components/Textarea/Textarea.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n computed,\n defineComponent,\n h,\n nextTick,\n onMounted,\n ref,\n useId,\n watch,\n withDirectives,\n type PropType,\n type TextareaHTMLAttributes,\n} from \"vue\"\nimport type { ControlSize, Sizes, Variants } from \"@plexui/styles/types\"\nimport { vServerText } from \"../../lib/defaultValue.js\"\nimport { toCssVariables } from \"../../lib/helpers.js\"\nimport s from \"@plexui/styles/components/Textarea/Textarea.module.css\"\n\n// Vue port of packages/ui/src/components/Textarea/Textarea.tsx. Props,\n// defaults and rendered DOM match React 1:1 (checked by the parity tests).\n//\n// - `class` goes to the container; every other attribute and listener\n// (`style`, `placeholder`, `@input`, `@change`, ...) goes to the <textarea>,\n// like React's `className` and `...restProps`.\n// - Value: `value` + `update:value` mirror React's `value` prop\n// (`v-model:value`) and fire on every input. Uncontrolled use takes\n// `default-value`. Like a React controlled textarea, a `value` the parent\n// does not update after input is put back.\n// - As in React, the textarea's text content (its `defaultValue`) follows\n// the controlled value, or `default-value` when uncontrolled. It is set\n// through the `textContent` property, which Vue's server renderer prints\n// as the element's content, so `vServerText` can adjust it at SSR.\n\ntype ListenerValue = ((event: Event) => void) | Array<(event: Event) => void> | undefined\n\nconst invoke = (handler: unknown, event: Event) => {\n for (const fn of ([] as unknown[]).concat(handler ?? [])) {\n if (typeof fn === \"function\") fn(event)\n }\n}\n\nconst TextareaComponent = defineComponent({\n name: \"Textarea\",\n inheritAttrs: false,\n props: {\n /** Visual style of the textarea. @default \"outline\" */\n variant: { type: String as PropType<Variants<\"outline\" | \"soft\" | \"ghost\">>, default: \"outline\" },\n /** Controls the size of the textarea. @default \"md\" */\n size: { type: String as PropType<ControlSize>, default: \"md\" },\n /** Applies a negative margin using the current gutter to optically align the text with surrounding content. */\n opticallyAlign: String as PropType<\"start\" | \"end\">,\n /** Controls gutter on the edges of the textarea, defaults to value from `size`. */\n gutterSize: String as PropType<Sizes<\"2xs\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\">>,\n /** Disables the textarea visually and from interactions. @default false */\n disabled: { type: Boolean, default: false },\n /** Makes the textarea read-only. @default false */\n readOnly: { type: Boolean, default: false },\n /** Native spelling of `readOnly` (`<Textarea readonly />`). @default false */\n readonly: { type: Boolean, default: false },\n /** Mark the textarea as invalid. @default false */\n invalid: { type: Boolean, default: false },\n /** Allow autofill extensions to appear in the textarea. @default `true` when `name` is set, else `false` */\n allowAutofillExtensions: { type: Boolean, default: undefined },\n /** Select all contents of the textarea when mounted (and whenever it turns `true`). @default false */\n autoSelect: { type: Boolean, default: undefined },\n /** Default number of rows to display. @default 3 */\n rows: { type: Number, default: 3 },\n /** Automatically adjust the height of the textarea based on its contents. @default false */\n autoResize: { type: Boolean, default: undefined },\n /** Maximum number of rows that can be displayed when autoResize is enabled. @default Math.max(rows, 10) */\n maxRows: Number,\n id: String,\n name: String,\n /** Controlled value (`v-model:value`) */\n value: { type: [String, Number] as PropType<string | number>, default: undefined },\n /** Initial value when uncontrolled */\n defaultValue: { type: [String, Number] as PropType<string | number>, default: undefined },\n /** The textarea's `autocomplete` attribute */\n autoComplete: String,\n },\n emits: {\n \"update:value\": (_value: string) => true,\n /** Fired when the browser autofills the textarea */\n \"autofill\": () => true,\n },\n setup(props, { attrs, emit, expose }) {\n const textAreaRef = ref<HTMLTextAreaElement | null>(null)\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-${useId()}`\n const focused = ref(false)\n\n const computedMaxRows = computed(() =>\n props.autoResize ? Math.max(props.maxRows ?? 10, props.rows) : props.rows,\n )\n const controlledValue = () => props.value\n\n const runAutoSelect = () => {\n if (props.autoSelect) {\n textAreaRef.value?.select()\n }\n }\n\n const autosizeTextarea = () => {\n const textarea = textAreaRef.value\n if (!props.autoResize || !textarea || computedMaxRows.value === undefined) return\n textarea.style.height = \"0px\"\n const scrollHeight = textarea.scrollHeight\n textarea.style.height = scrollHeight + \"px\"\n }\n\n // The textarea's value is set imperatively, like React DOM does: a `value`\n // prop would make Vue mirror it into a `value` attribute, which React\n // never renders. Puts the controlled value back when it differs.\n const syncValue = () => {\n const textarea = textAreaRef.value\n const value = controlledValue()\n if (!textarea || value == null) return\n const nextValue = String(value)\n if (textarea.value !== nextValue) textarea.value = nextValue\n }\n\n // React runs these effects after mount and when their dependencies change\n onMounted(() => {\n const textarea = textAreaRef.value\n // React's initTextarea: the initial text becomes the (dirty) value\n const initialValue = textarea?.textContent ?? \"\"\n if (textarea && initialValue !== \"\") textarea.value = initialValue\n runAutoSelect()\n autosizeTextarea()\n })\n // React DOM writes a new value while committing, before any effect runs,\n // so the selection covers the value that arrives with `autoSelect`\n watch(\n () => props.autoSelect,\n () => {\n syncValue()\n runAutoSelect()\n },\n { flush: \"post\" },\n )\n // Resize if value changes outside of user input - e.g. form reset\n watch(\n () => [controlledValue(), props.rows, props.autoResize, computedMaxRows.value],\n () => {\n syncValue()\n autosizeTextarea()\n },\n { flush: \"post\" },\n )\n\n const handleInput = (evt: Event) => {\n const next = (evt.target as HTMLTextAreaElement).value\n emit(\"update:value\", next)\n autosizeTextarea()\n // A controlled value the parent did not update wins over the typed text,\n // as in React\n void nextTick(syncValue)\n }\n\n expose({\n /** The rendered <textarea> element */\n textarea: textAreaRef,\n focus: () => textAreaRef.value?.focus(),\n })\n\n return () => {\n const {\n class: className,\n onInput,\n onFocus,\n onBlur,\n onAnimationstart,\n ...restAttrs\n } = attrs as Record<string, unknown>\n\n const { variant, size, gutterSize, disabled, invalid, rows } = props\n const readOnly = props.readOnly || props.readonly\n // Default to `true` when presence of `name`\n const allowAutofillExtensions = props.allowAutofillExtensions ?? !!props.name\n const value = controlledValue()\n // React keeps the textarea's defaultValue (its text content) in sync with\n // the controlled value, else with `defaultValue`\n const textContent = String(value ?? props.defaultValue ?? \"\")\n\n return h(\n \"div\",\n {\n \"class\": [s.Container, className],\n \"data-variant\": variant,\n \"data-size\": size,\n \"data-gutter-size\": gutterSize,\n \"data-optically-align\": props.opticallyAlign,\n \"data-focused\": String(focused.value),\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.value}`,\n }),\n },\n [\n withDirectives(\n h(\"textarea\", {\n ...restAttrs,\n \"onInput\": (evt: Event) => {\n invoke(onInput as ListenerValue, evt)\n handleInput(evt)\n },\n \"ref\": textAreaRef,\n \"id\": props.id || (allowAutofillExtensions ? undefined : onePasswordPreventionId),\n \"class\": s.Textarea,\n \"name\": props.name,\n \"autocomplete\": props.autoComplete ?? restAttrs.autocomplete,\n // Expose the visual invalid state to assistive tech; an explicit aria-invalid wins\n \"aria-invalid\": restAttrs[\"aria-invalid\"] ?? (invalid || undefined),\n \"readonly\": readOnly,\n \"disabled\": disabled,\n \"rows\": rows,\n \"onFocus\": (evt: Event) => {\n focused.value = true\n invoke(onFocus as ListenerValue, evt)\n },\n \"onBlur\": (evt: Event) => {\n focused.value = false\n invoke(onBlur as ListenerValue, evt)\n },\n \"onAnimationstart\": (evt: AnimationEvent) => {\n invoke(onAnimationstart as ListenerValue, evt)\n if (evt.animationName === \"native-autofill-in\") {\n emit(\"autofill\")\n }\n },\n // Prevent LastPass and 1Password from appearing\n \"data-lpignore\": allowAutofillExtensions ? undefined : true,\n \"data-1p-ignore\": allowAutofillExtensions ? undefined : true,\n \"textContent\": textContent,\n }),\n [[vServerText, textContent]],\n ),\n ],\n )\n }\n },\n})\n\ntype TextareaOwnProps = InstanceType<typeof TextareaComponent>[\"$props\"]\n\n/** Textarea props plus the native <textarea> attributes that fall through to it, as in React. */\nexport type TextareaProps = TextareaOwnProps & Omit<TextareaHTMLAttributes, keyof TextareaOwnProps | \"readonly\">\n\nexport const Textarea = TextareaComponent as typeof TextareaComponent & { new (): { $props: TextareaProps } }\n"]}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import s from "@plexui/styles/components/Sortable/Sortable.module.css";
|
|
2
|
+
const START_DISTANCE = 4;
|
|
3
|
+
const TOUCH_HOLD = 200;
|
|
4
|
+
const SCROLL_EDGE = 80;
|
|
5
|
+
const SCROLL_SLOW = 4;
|
|
6
|
+
const SCROLL_FAST = 30;
|
|
7
|
+
const NEAR_LIST = 40;
|
|
8
|
+
const LINE_INSET = 16;
|
|
9
|
+
const PART = 4;
|
|
10
|
+
// --cubic-enter: an animation here cannot read a CSS variable
|
|
11
|
+
const EASING = "cubic-bezier(.19,1,.22,1)";
|
|
12
|
+
const HANDLE = "[data-sortable-handle]";
|
|
13
|
+
const INTERACTIVE = [
|
|
14
|
+
"a[href]",
|
|
15
|
+
"button",
|
|
16
|
+
"input",
|
|
17
|
+
"select",
|
|
18
|
+
"summary",
|
|
19
|
+
"textarea",
|
|
20
|
+
"[contenteditable]",
|
|
21
|
+
"[role=button]",
|
|
22
|
+
"[role=checkbox]",
|
|
23
|
+
"[role=combobox]",
|
|
24
|
+
"[role=link]",
|
|
25
|
+
"[role=radio]",
|
|
26
|
+
"[role=slider]",
|
|
27
|
+
"[role=switch]",
|
|
28
|
+
"[role=tab]",
|
|
29
|
+
].join(",");
|
|
30
|
+
// Read at the moment of use: the setting can change while the page is open
|
|
31
|
+
const reduced = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
32
|
+
const itemsOf = (list) => [...list.children];
|
|
33
|
+
/** Grips stand in a table, elsewhere only when the list asks for them */
|
|
34
|
+
const showsHandles = (list) => (list.dataset.handles ?? String(list.tagName === "TBODY")) === "true";
|
|
35
|
+
/** The direct child of the list that holds `target` */
|
|
36
|
+
const itemOf = (list, target) => {
|
|
37
|
+
let node = target;
|
|
38
|
+
while (node && node.parentElement !== list)
|
|
39
|
+
node = node.parentElement;
|
|
40
|
+
return node;
|
|
41
|
+
};
|
|
42
|
+
/** The order after a move. `to` counts the list without the moved item. */
|
|
43
|
+
export const moveItem = (items, from, to) => {
|
|
44
|
+
const next = [...items];
|
|
45
|
+
next.splice(to, 0, ...next.splice(from, 1));
|
|
46
|
+
return next;
|
|
47
|
+
};
|
|
48
|
+
const labelOf = (item) => {
|
|
49
|
+
const named = item.dataset.sortableLabel ?? item.querySelector("[data-sortable-label], label")?.textContent;
|
|
50
|
+
if (named)
|
|
51
|
+
return named.trim();
|
|
52
|
+
// The cells of a row or the lines of a card run together: the first one is the name
|
|
53
|
+
const texts = document.createTreeWalker(item, NodeFilter.SHOW_TEXT);
|
|
54
|
+
while (texts.nextNode()) {
|
|
55
|
+
if (texts.currentNode.textContent?.trim())
|
|
56
|
+
return texts.currentNode.parentElement?.textContent?.trim() ?? "";
|
|
57
|
+
}
|
|
58
|
+
return "";
|
|
59
|
+
};
|
|
60
|
+
/** What a screen reader hears: a picture has no text of its own */
|
|
61
|
+
const nameOf = (item) => labelOf(item) || item.querySelector("img")?.alt || "";
|
|
62
|
+
function makeChip(item) {
|
|
63
|
+
const chip = document.createElement("div");
|
|
64
|
+
chip.className = s.Chip;
|
|
65
|
+
const image = item.querySelector("img");
|
|
66
|
+
if (image) {
|
|
67
|
+
const picture = document.createElement("img");
|
|
68
|
+
picture.className = s.ChipImage;
|
|
69
|
+
picture.src = image.currentSrc || image.src;
|
|
70
|
+
picture.alt = "";
|
|
71
|
+
chip.append(picture);
|
|
72
|
+
}
|
|
73
|
+
const text = labelOf(item);
|
|
74
|
+
if (text) {
|
|
75
|
+
const label = document.createElement("span");
|
|
76
|
+
label.className = s.ChipLabel;
|
|
77
|
+
label.textContent = text;
|
|
78
|
+
chip.append(label);
|
|
79
|
+
}
|
|
80
|
+
document.body.append(chip);
|
|
81
|
+
return chip;
|
|
82
|
+
}
|
|
83
|
+
function announce(text) {
|
|
84
|
+
let region = document.querySelector("[data-sortable-live]");
|
|
85
|
+
if (!region) {
|
|
86
|
+
region = document.createElement("div");
|
|
87
|
+
region.className = s.Live;
|
|
88
|
+
region.setAttribute("aria-live", "polite");
|
|
89
|
+
region.setAttribute("data-sortable-live", "");
|
|
90
|
+
document.body.append(region);
|
|
91
|
+
}
|
|
92
|
+
region.textContent = text;
|
|
93
|
+
}
|
|
94
|
+
const scrollParent = (list) => {
|
|
95
|
+
for (let node = list.parentElement; node && node !== document.body; node = node.parentElement) {
|
|
96
|
+
if (/auto|scroll/.test(getComputedStyle(node).overflowY) && node.scrollHeight > node.clientHeight)
|
|
97
|
+
return node;
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
};
|
|
101
|
+
// The nearer the edge the faster: slow to aim at a row, fast across a long list
|
|
102
|
+
const scrollStep = (depth) => SCROLL_SLOW + (SCROLL_FAST - SCROLL_SLOW) * Math.min(depth / SCROLL_EDGE, 1);
|
|
103
|
+
function listSlots(list, items) {
|
|
104
|
+
const box = list.getBoundingClientRect();
|
|
105
|
+
const rects = items.map((item) => item.getBoundingClientRect());
|
|
106
|
+
return Array.from({ length: items.length + 1 }, (_, gap) => {
|
|
107
|
+
const above = rects[gap - 1];
|
|
108
|
+
const below = rects[gap];
|
|
109
|
+
const y = above && below ? (above.bottom + below.top) / 2 : (below?.top ?? above.bottom);
|
|
110
|
+
return {
|
|
111
|
+
gap,
|
|
112
|
+
x: LINE_INSET,
|
|
113
|
+
y: y - box.top,
|
|
114
|
+
length: box.width - LINE_INSET * 2,
|
|
115
|
+
before: items[gap - 1],
|
|
116
|
+
after: items[gap],
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// One slot per gap, and two where a line wraps: the end of one line and the start of the next
|
|
121
|
+
function gridSlots(list, items) {
|
|
122
|
+
const box = list.getBoundingClientRect();
|
|
123
|
+
const half = (parseFloat(getComputedStyle(list).columnGap) || 8) / 2;
|
|
124
|
+
const rects = items.map((item) => item.getBoundingClientRect());
|
|
125
|
+
const slots = [];
|
|
126
|
+
const add = (gap, x, rect, before, after) => slots.push({ gap, x: x - box.left, y: rect.top - box.top, length: rect.height, before, after });
|
|
127
|
+
rects.forEach((rect, gap) => {
|
|
128
|
+
const previous = rects[gap - 1];
|
|
129
|
+
if (previous && Math.abs(previous.top - rect.top) < rect.height / 2) {
|
|
130
|
+
add(gap, (previous.right + rect.left) / 2, rect, items[gap - 1], items[gap]);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (previous)
|
|
134
|
+
add(gap, previous.right + half, previous, items[gap - 1]);
|
|
135
|
+
add(gap, rect.left - half, rect, undefined, items[gap]);
|
|
136
|
+
});
|
|
137
|
+
const last = rects.length - 1;
|
|
138
|
+
add(rects.length, rects[last].right + half, rects[last], items[last]);
|
|
139
|
+
return slots;
|
|
140
|
+
}
|
|
141
|
+
// The chip keeps its size and fades out: grown to the item, its text would stand over the item's own
|
|
142
|
+
function flyChip(chip, to, layout) {
|
|
143
|
+
if (reduced() || !to) {
|
|
144
|
+
chip.remove();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const from = chip.getBoundingClientRect();
|
|
148
|
+
const x = layout === "grid" ? to.left + (to.width - from.width) / 2 : to.left;
|
|
149
|
+
const y = to.top + (to.height - from.height) / 2;
|
|
150
|
+
chip.animate([
|
|
151
|
+
{ transform: `translate(${from.left}px, ${from.top}px)`, opacity: 1 },
|
|
152
|
+
{ transform: `translate(${x}px, ${y}px)`, opacity: 0 },
|
|
153
|
+
], { duration: 200, easing: EASING, fill: "forwards" }).onfinish = () => chip.remove();
|
|
154
|
+
}
|
|
155
|
+
/** The items that changed place slide there from where they stood */
|
|
156
|
+
async function commitWithFlip(items, from, to, commit, skip) {
|
|
157
|
+
const before = new Map(items.map((item) => [item, item.getBoundingClientRect()]));
|
|
158
|
+
await commit(from, to);
|
|
159
|
+
if (reduced())
|
|
160
|
+
return;
|
|
161
|
+
items
|
|
162
|
+
.filter((item) => item !== skip && item.isConnected)
|
|
163
|
+
.forEach((item) => {
|
|
164
|
+
const was = before.get(item);
|
|
165
|
+
const now = item.getBoundingClientRect();
|
|
166
|
+
const x = was.left - now.left;
|
|
167
|
+
const y = was.top - now.top;
|
|
168
|
+
if (x || y)
|
|
169
|
+
item.animate([{ transform: `translate(${x}px, ${y}px)` }, { transform: "none" }], { duration: 200, easing: EASING });
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
const swallow = (event) => {
|
|
173
|
+
event.stopPropagation();
|
|
174
|
+
event.preventDefault();
|
|
175
|
+
};
|
|
176
|
+
function startDrag(event, list, source, layout, immediate, commit) {
|
|
177
|
+
const touch = event.pointerType === "touch" && !immediate;
|
|
178
|
+
const origin = { x: event.clientX, y: event.clientY };
|
|
179
|
+
const pointer = { ...origin };
|
|
180
|
+
const holdTimer = touch ? setTimeout(begin, TOUCH_HOLD) : undefined;
|
|
181
|
+
const style = getComputedStyle(list);
|
|
182
|
+
const part = parseFloat(layout === "grid" ? style.columnGap : style.rowGap) ? PART : 0;
|
|
183
|
+
const scroller = scrollParent(list);
|
|
184
|
+
let drag;
|
|
185
|
+
window.addEventListener("pointermove", onMove);
|
|
186
|
+
window.addEventListener("pointerup", onUp);
|
|
187
|
+
window.addEventListener("pointercancel", onCancel);
|
|
188
|
+
function onMove({ clientX, clientY }) {
|
|
189
|
+
pointer.x = clientX;
|
|
190
|
+
pointer.y = clientY;
|
|
191
|
+
if (drag)
|
|
192
|
+
return;
|
|
193
|
+
const moved = Math.hypot(clientX - origin.x, clientY - origin.y) >= START_DISTANCE;
|
|
194
|
+
if (moved && touch)
|
|
195
|
+
stopListening();
|
|
196
|
+
else if (moved)
|
|
197
|
+
begin();
|
|
198
|
+
}
|
|
199
|
+
function onUp() {
|
|
200
|
+
finish(true);
|
|
201
|
+
}
|
|
202
|
+
function onCancel() {
|
|
203
|
+
finish(false);
|
|
204
|
+
}
|
|
205
|
+
function onKey(keyEvent) {
|
|
206
|
+
if (keyEvent.key !== "Escape")
|
|
207
|
+
return;
|
|
208
|
+
keyEvent.stopPropagation();
|
|
209
|
+
finish(false);
|
|
210
|
+
}
|
|
211
|
+
function blockScroll(touchEvent) {
|
|
212
|
+
touchEvent.preventDefault();
|
|
213
|
+
}
|
|
214
|
+
function stopListening() {
|
|
215
|
+
clearTimeout(holdTimer);
|
|
216
|
+
window.removeEventListener("pointermove", onMove);
|
|
217
|
+
window.removeEventListener("pointerup", onUp);
|
|
218
|
+
window.removeEventListener("pointercancel", onCancel);
|
|
219
|
+
window.removeEventListener("keydown", onKey, true);
|
|
220
|
+
window.removeEventListener("touchmove", blockScroll);
|
|
221
|
+
}
|
|
222
|
+
function begin() {
|
|
223
|
+
const items = itemsOf(list);
|
|
224
|
+
const slots = layout === "grid" ? gridSlots(list, items) : listSlots(list, items);
|
|
225
|
+
window.getSelection()?.removeAllRanges();
|
|
226
|
+
document.documentElement.classList.add(s.Dragging);
|
|
227
|
+
list.setAttribute("data-dragging", "");
|
|
228
|
+
source.setAttribute("data-sortable-source", "");
|
|
229
|
+
window.addEventListener("keydown", onKey, true);
|
|
230
|
+
window.addEventListener("touchmove", blockScroll, { passive: false });
|
|
231
|
+
const chip = makeChip(source);
|
|
232
|
+
const line = document.createElement("div");
|
|
233
|
+
line.className = s.Line;
|
|
234
|
+
line.hidden = true;
|
|
235
|
+
document.body.append(line);
|
|
236
|
+
const { width, height } = chip.getBoundingClientRect();
|
|
237
|
+
drag = { chip, line, width, height, items, slots };
|
|
238
|
+
tick();
|
|
239
|
+
}
|
|
240
|
+
function tick() {
|
|
241
|
+
if (!drag)
|
|
242
|
+
return;
|
|
243
|
+
autoScroll();
|
|
244
|
+
update();
|
|
245
|
+
drag.frame = requestAnimationFrame(tick);
|
|
246
|
+
}
|
|
247
|
+
function autoScroll() {
|
|
248
|
+
const box = scroller?.getBoundingClientRect() ?? { top: 0, bottom: window.innerHeight };
|
|
249
|
+
const above = box.top + SCROLL_EDGE - pointer.y;
|
|
250
|
+
const below = pointer.y - box.bottom + SCROLL_EDGE;
|
|
251
|
+
const step = above > 0 ? -scrollStep(above) : below > 0 ? scrollStep(below) : 0;
|
|
252
|
+
if (!step)
|
|
253
|
+
return;
|
|
254
|
+
if (scroller)
|
|
255
|
+
scroller.scrollTop += step;
|
|
256
|
+
else
|
|
257
|
+
window.scrollBy(0, step);
|
|
258
|
+
}
|
|
259
|
+
function update() {
|
|
260
|
+
if (!drag)
|
|
261
|
+
return;
|
|
262
|
+
drag.chip.style.transform = `translate(${pointer.x - drag.width / 2}px, ${pointer.y - drag.height / 2}px)`;
|
|
263
|
+
const box = source.getBoundingClientRect();
|
|
264
|
+
const overSource = pointer.x >= box.left && pointer.x <= box.right && pointer.y >= box.top && pointer.y <= box.bottom;
|
|
265
|
+
source.toggleAttribute("data-sortable-away", !overSource);
|
|
266
|
+
showGap(overSource ? undefined : nearestSlot());
|
|
267
|
+
}
|
|
268
|
+
function nearestSlot() {
|
|
269
|
+
if (!drag)
|
|
270
|
+
return undefined;
|
|
271
|
+
const box = list.getBoundingClientRect();
|
|
272
|
+
const near = pointer.x >= box.left - NEAR_LIST &&
|
|
273
|
+
pointer.x <= box.right + NEAR_LIST &&
|
|
274
|
+
pointer.y >= box.top - NEAR_LIST &&
|
|
275
|
+
pointer.y <= box.bottom + NEAR_LIST;
|
|
276
|
+
if (!near)
|
|
277
|
+
return undefined;
|
|
278
|
+
const distance = (slot) => {
|
|
279
|
+
if (layout === "list")
|
|
280
|
+
return Math.abs(pointer.y - (box.top + slot.y));
|
|
281
|
+
const top = box.top + slot.y;
|
|
282
|
+
return Math.hypot(pointer.x - (box.left + slot.x), Math.max(top - pointer.y, 0, pointer.y - top - slot.length));
|
|
283
|
+
};
|
|
284
|
+
const best = drag.slots.reduce((nearest, slot) => (distance(slot) < distance(nearest) ? slot : nearest));
|
|
285
|
+
const index = drag.items.indexOf(source);
|
|
286
|
+
return best.gap === index || best.gap === index + 1 ? undefined : best;
|
|
287
|
+
}
|
|
288
|
+
function shift(slot, distance) {
|
|
289
|
+
const move = (item, by) => {
|
|
290
|
+
if (!item || !part)
|
|
291
|
+
return;
|
|
292
|
+
item.style.transform = distance
|
|
293
|
+
? layout === "grid"
|
|
294
|
+
? `translateX(${by}px)`
|
|
295
|
+
: `translateY(${by}px)`
|
|
296
|
+
: "";
|
|
297
|
+
};
|
|
298
|
+
move(slot?.before, -distance);
|
|
299
|
+
move(slot?.after, distance);
|
|
300
|
+
}
|
|
301
|
+
function showGap(found) {
|
|
302
|
+
if (!drag)
|
|
303
|
+
return;
|
|
304
|
+
if (found !== drag.found) {
|
|
305
|
+
shift(drag.found, 0);
|
|
306
|
+
shift(found, part);
|
|
307
|
+
}
|
|
308
|
+
drag.found = found;
|
|
309
|
+
drag.line.hidden = !found;
|
|
310
|
+
if (!found)
|
|
311
|
+
return;
|
|
312
|
+
const box = list.getBoundingClientRect();
|
|
313
|
+
if (layout === "grid") {
|
|
314
|
+
drag.line.style.transform = `translate(${box.left + found.x - 1}px, ${box.top + found.y}px)`;
|
|
315
|
+
drag.line.style.height = `${found.length}px`;
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
drag.line.style.transform = `translate(${box.left + found.x}px, ${box.top + found.y - 1}px)`;
|
|
319
|
+
drag.line.style.width = `${found.length}px`;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function restoreSource() {
|
|
323
|
+
source.removeAttribute("data-sortable-source");
|
|
324
|
+
source.removeAttribute("data-sortable-away");
|
|
325
|
+
}
|
|
326
|
+
function settle(chip) {
|
|
327
|
+
flyChip(chip, source.isConnected ? source.getBoundingClientRect() : undefined, layout);
|
|
328
|
+
if (!reduced() && source.isConnected)
|
|
329
|
+
source.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 250, easing: EASING });
|
|
330
|
+
}
|
|
331
|
+
async function land(chip, items, { gap }, clear) {
|
|
332
|
+
const from = items.indexOf(source);
|
|
333
|
+
const to = gap > from ? gap - 1 : gap;
|
|
334
|
+
restoreSource();
|
|
335
|
+
const clearAndCommit = (moved, place) => {
|
|
336
|
+
clear();
|
|
337
|
+
return commit(moved, place);
|
|
338
|
+
};
|
|
339
|
+
await commitWithFlip(items, from, to, clearAndCommit, source);
|
|
340
|
+
announce(`${nameOf(source)}, ${to + 1} / ${items.length}`);
|
|
341
|
+
settle(chip);
|
|
342
|
+
}
|
|
343
|
+
function finish(drop) {
|
|
344
|
+
stopListening();
|
|
345
|
+
if (!drag)
|
|
346
|
+
return;
|
|
347
|
+
const { chip, line, found, items, frame } = drag;
|
|
348
|
+
if (frame)
|
|
349
|
+
cancelAnimationFrame(frame);
|
|
350
|
+
// The pointer lifts over a row: that click must not act on it
|
|
351
|
+
window.addEventListener("click", swallow, { capture: true, once: true });
|
|
352
|
+
setTimeout(() => window.removeEventListener("click", swallow, { capture: true }));
|
|
353
|
+
line.remove();
|
|
354
|
+
drag = undefined;
|
|
355
|
+
// On a drop this waits for the flip to read where the parted items stand
|
|
356
|
+
const clear = () => {
|
|
357
|
+
list.removeAttribute("data-dragging");
|
|
358
|
+
document.documentElement.classList.remove(s.Dragging);
|
|
359
|
+
shift(found, 0);
|
|
360
|
+
};
|
|
361
|
+
if (drop && found) {
|
|
362
|
+
void land(chip, items, found, clear);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
clear();
|
|
366
|
+
restoreSource();
|
|
367
|
+
settle(chip);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/** Starts a drag from a press inside the list. Goes on the list's `pointerdown`. */
|
|
371
|
+
export function sortablePointerDown(event, list, layout, commit) {
|
|
372
|
+
const target = event.target;
|
|
373
|
+
if (event.button || target.closest("[data-sortable]") !== list)
|
|
374
|
+
return;
|
|
375
|
+
const source = itemOf(list, target);
|
|
376
|
+
if (!source)
|
|
377
|
+
return;
|
|
378
|
+
const handle = target.closest(HANDLE);
|
|
379
|
+
// A grip in sight is the only way to drag, otherwise anything but a control starts it
|
|
380
|
+
if (source.querySelector(HANDLE) && showsHandles(list) ? !handle : target.closest(INTERACTIVE))
|
|
381
|
+
return;
|
|
382
|
+
startDrag(event, list, source, layout, !!handle, commit);
|
|
383
|
+
}
|
|
384
|
+
const KEY_STEPS = { ArrowUp: -1, ArrowLeft: -1, ArrowDown: 1, ArrowRight: 1 };
|
|
385
|
+
/** Moves the focused handle's item with the arrow keys. Goes on the list's `keydown`. */
|
|
386
|
+
export async function sortableKeyDown(event, list, commit) {
|
|
387
|
+
const step = KEY_STEPS[event.key];
|
|
388
|
+
const target = event.target;
|
|
389
|
+
if (!step || target.closest("[data-sortable]") !== list)
|
|
390
|
+
return;
|
|
391
|
+
const source = itemOf(list, target);
|
|
392
|
+
if (!source || (target !== source && !target.closest(HANDLE)))
|
|
393
|
+
return;
|
|
394
|
+
const items = itemsOf(list);
|
|
395
|
+
const from = items.indexOf(source);
|
|
396
|
+
const to = from + step;
|
|
397
|
+
if (to < 0 || to >= items.length)
|
|
398
|
+
return;
|
|
399
|
+
event.preventDefault();
|
|
400
|
+
await commitWithFlip(items, from, to, commit);
|
|
401
|
+
// Moving an element in the document drops its focus
|
|
402
|
+
if (target.isConnected && document.activeElement !== target)
|
|
403
|
+
target.focus();
|
|
404
|
+
announce(`${nameOf(source)}, ${to + 1} / ${items.length}`);
|
|
405
|
+
}
|
|
406
|
+
//# sourceMappingURL=sortable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sortable.js","sourceRoot":"","sources":["../../../src/lib/sortable.ts"],"names":[],"mappings":"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","sourcesContent":["import s from \"@plexui/styles/components/Sortable/Sortable.module.css\"\n\n// Drag to reorder, for any list of elements. Nothing moves until the drop: the\n// item leaves a flat slot and shrinks to a chip at the cursor, a line marks the\n// nearest gap, and only the drop changes the order. Items that stand apart part\n// at the gap. Rows that touch hold still: their text would run over the hairlines.\n\nexport type SortableLayout = \"list\" | \"grid\"\n\n/** Applies a move to the list. Resolves once the DOM shows the new order. */\nexport type SortableCommit = (from: number, to: number) => void | Promise<void>\n\ntype Slot = {\n gap: number\n /** Offsets from the list's corner, so a scroll under the drag keeps them true */\n x: number\n y: number\n length: number\n before?: Element\n after?: Element\n}\n\nconst START_DISTANCE = 4\nconst TOUCH_HOLD = 200\nconst SCROLL_EDGE = 80\nconst SCROLL_SLOW = 4\nconst SCROLL_FAST = 30\nconst NEAR_LIST = 40\nconst LINE_INSET = 16\nconst PART = 4\n// --cubic-enter: an animation here cannot read a CSS variable\nconst EASING = \"cubic-bezier(.19,1,.22,1)\"\nconst HANDLE = \"[data-sortable-handle]\"\nconst INTERACTIVE = [\n \"a[href]\",\n \"button\",\n \"input\",\n \"select\",\n \"summary\",\n \"textarea\",\n \"[contenteditable]\",\n \"[role=button]\",\n \"[role=checkbox]\",\n \"[role=combobox]\",\n \"[role=link]\",\n \"[role=radio]\",\n \"[role=slider]\",\n \"[role=switch]\",\n \"[role=tab]\",\n].join(\",\")\n\n// Read at the moment of use: the setting can change while the page is open\nconst reduced = () => window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n\nconst itemsOf = (list: HTMLElement) => [...list.children] as HTMLElement[]\n\n/** Grips stand in a table, elsewhere only when the list asks for them */\nconst showsHandles = (list: HTMLElement) => (list.dataset.handles ?? String(list.tagName === \"TBODY\")) === \"true\"\n\n/** The direct child of the list that holds `target` */\nconst itemOf = (list: HTMLElement, target: Element) => {\n let node: Element | null = target\n while (node && node.parentElement !== list) node = node.parentElement\n return node as HTMLElement | null\n}\n\n/** The order after a move. `to` counts the list without the moved item. */\nexport const moveItem = <T>(items: readonly T[], from: number, to: number) => {\n const next = [...items]\n next.splice(to, 0, ...next.splice(from, 1))\n return next\n}\n\nconst labelOf = (item: HTMLElement) => {\n const named = item.dataset.sortableLabel ?? item.querySelector(\"[data-sortable-label], label\")?.textContent\n if (named) return named.trim()\n // The cells of a row or the lines of a card run together: the first one is the name\n const texts = document.createTreeWalker(item, NodeFilter.SHOW_TEXT)\n while (texts.nextNode()) {\n if (texts.currentNode.textContent?.trim()) return texts.currentNode.parentElement?.textContent?.trim() ?? \"\"\n }\n return \"\"\n}\n\n/** What a screen reader hears: a picture has no text of its own */\nconst nameOf = (item: HTMLElement) => labelOf(item) || item.querySelector(\"img\")?.alt || \"\"\n\nfunction makeChip(item: HTMLElement) {\n const chip = document.createElement(\"div\")\n chip.className = s.Chip\n const image = item.querySelector(\"img\")\n if (image) {\n const picture = document.createElement(\"img\")\n picture.className = s.ChipImage\n picture.src = image.currentSrc || image.src\n picture.alt = \"\"\n chip.append(picture)\n }\n const text = labelOf(item)\n if (text) {\n const label = document.createElement(\"span\")\n label.className = s.ChipLabel\n label.textContent = text\n chip.append(label)\n }\n document.body.append(chip)\n return chip\n}\n\nfunction announce(text: string) {\n let region = document.querySelector(\"[data-sortable-live]\")\n if (!region) {\n region = document.createElement(\"div\")\n region.className = s.Live\n region.setAttribute(\"aria-live\", \"polite\")\n region.setAttribute(\"data-sortable-live\", \"\")\n document.body.append(region)\n }\n region.textContent = text\n}\n\nconst scrollParent = (list: HTMLElement) => {\n for (let node = list.parentElement; node && node !== document.body; node = node.parentElement) {\n if (/auto|scroll/.test(getComputedStyle(node).overflowY) && node.scrollHeight > node.clientHeight) return node\n }\n return undefined\n}\n\n// The nearer the edge the faster: slow to aim at a row, fast across a long list\nconst scrollStep = (depth: number) =>\n SCROLL_SLOW + (SCROLL_FAST - SCROLL_SLOW) * Math.min(depth / SCROLL_EDGE, 1)\n\nfunction listSlots(list: HTMLElement, items: HTMLElement[]) {\n const box = list.getBoundingClientRect()\n const rects = items.map((item) => item.getBoundingClientRect())\n return Array.from({ length: items.length + 1 }, (_, gap): Slot => {\n const above = rects[gap - 1]\n const below = rects[gap]\n const y = above && below ? (above.bottom + below.top) / 2 : (below?.top ?? above.bottom)\n return {\n gap,\n x: LINE_INSET,\n y: y - box.top,\n length: box.width - LINE_INSET * 2,\n before: items[gap - 1],\n after: items[gap],\n }\n })\n}\n\n// One slot per gap, and two where a line wraps: the end of one line and the start of the next\nfunction gridSlots(list: HTMLElement, items: HTMLElement[]) {\n const box = list.getBoundingClientRect()\n const half = (parseFloat(getComputedStyle(list).columnGap) || 8) / 2\n const rects = items.map((item) => item.getBoundingClientRect())\n const slots: Slot[] = []\n const add = (gap: number, x: number, rect: DOMRect, before?: Element, after?: Element) =>\n slots.push({ gap, x: x - box.left, y: rect.top - box.top, length: rect.height, before, after })\n\n rects.forEach((rect, gap) => {\n const previous = rects[gap - 1]\n if (previous && Math.abs(previous.top - rect.top) < rect.height / 2) {\n add(gap, (previous.right + rect.left) / 2, rect, items[gap - 1], items[gap])\n return\n }\n if (previous) add(gap, previous.right + half, previous, items[gap - 1])\n add(gap, rect.left - half, rect, undefined, items[gap])\n })\n const last = rects.length - 1\n add(rects.length, rects[last].right + half, rects[last], items[last])\n return slots\n}\n\n// The chip keeps its size and fades out: grown to the item, its text would stand over the item's own\nfunction flyChip(chip: HTMLElement, to: DOMRect | undefined, layout: SortableLayout) {\n if (reduced() || !to) {\n chip.remove()\n return\n }\n const from = chip.getBoundingClientRect()\n const x = layout === \"grid\" ? to.left + (to.width - from.width) / 2 : to.left\n const y = to.top + (to.height - from.height) / 2\n chip.animate(\n [\n { transform: `translate(${from.left}px, ${from.top}px)`, opacity: 1 },\n { transform: `translate(${x}px, ${y}px)`, opacity: 0 },\n ],\n { duration: 200, easing: EASING, fill: \"forwards\" },\n ).onfinish = () => chip.remove()\n}\n\n/** The items that changed place slide there from where they stood */\nasync function commitWithFlip(items: HTMLElement[], from: number, to: number, commit: SortableCommit, skip?: HTMLElement) {\n const before = new Map(items.map((item) => [item, item.getBoundingClientRect()]))\n await commit(from, to)\n if (reduced()) return\n items\n .filter((item) => item !== skip && item.isConnected)\n .forEach((item) => {\n const was = before.get(item) as DOMRect\n const now = item.getBoundingClientRect()\n const x = was.left - now.left\n const y = was.top - now.top\n if (x || y) item.animate([{ transform: `translate(${x}px, ${y}px)` }, { transform: \"none\" }], { duration: 200, easing: EASING })\n })\n}\n\nconst swallow = (event: Event) => {\n event.stopPropagation()\n event.preventDefault()\n}\n\nfunction startDrag(event: PointerEvent, list: HTMLElement, source: HTMLElement, layout: SortableLayout, immediate: boolean, commit: SortableCommit) {\n const touch = event.pointerType === \"touch\" && !immediate\n const origin = { x: event.clientX, y: event.clientY }\n const pointer = { ...origin }\n const holdTimer = touch ? setTimeout(begin, TOUCH_HOLD) : undefined\n const style = getComputedStyle(list)\n const part = parseFloat(layout === \"grid\" ? style.columnGap : style.rowGap) ? PART : 0\n const scroller = scrollParent(list)\n let drag:\n | {\n chip: HTMLElement\n line: HTMLElement\n width: number\n height: number\n items: HTMLElement[]\n slots: Slot[]\n frame?: number\n found?: Slot\n }\n | undefined\n\n window.addEventListener(\"pointermove\", onMove)\n window.addEventListener(\"pointerup\", onUp)\n window.addEventListener(\"pointercancel\", onCancel)\n\n function onMove({ clientX, clientY }: PointerEvent) {\n pointer.x = clientX\n pointer.y = clientY\n if (drag) return\n const moved = Math.hypot(clientX - origin.x, clientY - origin.y) >= START_DISTANCE\n if (moved && touch) stopListening()\n else if (moved) begin()\n }\n\n function onUp() {\n finish(true)\n }\n\n function onCancel() {\n finish(false)\n }\n\n function onKey(keyEvent: KeyboardEvent) {\n if (keyEvent.key !== \"Escape\") return\n keyEvent.stopPropagation()\n finish(false)\n }\n\n function blockScroll(touchEvent: TouchEvent) {\n touchEvent.preventDefault()\n }\n\n function stopListening() {\n clearTimeout(holdTimer)\n window.removeEventListener(\"pointermove\", onMove)\n window.removeEventListener(\"pointerup\", onUp)\n window.removeEventListener(\"pointercancel\", onCancel)\n window.removeEventListener(\"keydown\", onKey, true)\n window.removeEventListener(\"touchmove\", blockScroll)\n }\n\n function begin() {\n const items = itemsOf(list)\n const slots = layout === \"grid\" ? gridSlots(list, items) : listSlots(list, items)\n window.getSelection()?.removeAllRanges()\n document.documentElement.classList.add(s.Dragging)\n list.setAttribute(\"data-dragging\", \"\")\n source.setAttribute(\"data-sortable-source\", \"\")\n window.addEventListener(\"keydown\", onKey, true)\n window.addEventListener(\"touchmove\", blockScroll, { passive: false })\n const chip = makeChip(source)\n const line = document.createElement(\"div\")\n line.className = s.Line\n line.hidden = true\n document.body.append(line)\n const { width, height } = chip.getBoundingClientRect()\n drag = { chip, line, width, height, items, slots }\n tick()\n }\n\n function tick() {\n if (!drag) return\n autoScroll()\n update()\n drag.frame = requestAnimationFrame(tick)\n }\n\n function autoScroll() {\n const box = scroller?.getBoundingClientRect() ?? { top: 0, bottom: window.innerHeight }\n const above = box.top + SCROLL_EDGE - pointer.y\n const below = pointer.y - box.bottom + SCROLL_EDGE\n const step = above > 0 ? -scrollStep(above) : below > 0 ? scrollStep(below) : 0\n if (!step) return\n if (scroller) scroller.scrollTop += step\n else window.scrollBy(0, step)\n }\n\n function update() {\n if (!drag) return\n drag.chip.style.transform = `translate(${pointer.x - drag.width / 2}px, ${pointer.y - drag.height / 2}px)`\n const box = source.getBoundingClientRect()\n const overSource = pointer.x >= box.left && pointer.x <= box.right && pointer.y >= box.top && pointer.y <= box.bottom\n source.toggleAttribute(\"data-sortable-away\", !overSource)\n showGap(overSource ? undefined : nearestSlot())\n }\n\n function nearestSlot() {\n if (!drag) return undefined\n const box = list.getBoundingClientRect()\n const near =\n pointer.x >= box.left - NEAR_LIST &&\n pointer.x <= box.right + NEAR_LIST &&\n pointer.y >= box.top - NEAR_LIST &&\n pointer.y <= box.bottom + NEAR_LIST\n if (!near) return undefined\n const distance = (slot: Slot) => {\n if (layout === \"list\") return Math.abs(pointer.y - (box.top + slot.y))\n const top = box.top + slot.y\n return Math.hypot(pointer.x - (box.left + slot.x), Math.max(top - pointer.y, 0, pointer.y - top - slot.length))\n }\n const best = drag.slots.reduce((nearest, slot) => (distance(slot) < distance(nearest) ? slot : nearest))\n const index = drag.items.indexOf(source)\n return best.gap === index || best.gap === index + 1 ? undefined : best\n }\n\n function shift(slot: Slot | undefined, distance: number) {\n const move = (item: Element | undefined, by: number) => {\n if (!item || !part) return\n ;(item as HTMLElement).style.transform = distance\n ? layout === \"grid\"\n ? `translateX(${by}px)`\n : `translateY(${by}px)`\n : \"\"\n }\n move(slot?.before, -distance)\n move(slot?.after, distance)\n }\n\n function showGap(found: Slot | undefined) {\n if (!drag) return\n if (found !== drag.found) {\n shift(drag.found, 0)\n shift(found, part)\n }\n drag.found = found\n drag.line.hidden = !found\n if (!found) return\n const box = list.getBoundingClientRect()\n if (layout === \"grid\") {\n drag.line.style.transform = `translate(${box.left + found.x - 1}px, ${box.top + found.y}px)`\n drag.line.style.height = `${found.length}px`\n } else {\n drag.line.style.transform = `translate(${box.left + found.x}px, ${box.top + found.y - 1}px)`\n drag.line.style.width = `${found.length}px`\n }\n }\n\n function restoreSource() {\n source.removeAttribute(\"data-sortable-source\")\n source.removeAttribute(\"data-sortable-away\")\n }\n\n function settle(chip: HTMLElement) {\n flyChip(chip, source.isConnected ? source.getBoundingClientRect() : undefined, layout)\n if (!reduced() && source.isConnected) source.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 250, easing: EASING })\n }\n\n async function land(chip: HTMLElement, items: HTMLElement[], { gap }: Slot, clear: () => void) {\n const from = items.indexOf(source)\n const to = gap > from ? gap - 1 : gap\n restoreSource()\n const clearAndCommit: SortableCommit = (moved, place) => {\n clear()\n return commit(moved, place)\n }\n await commitWithFlip(items, from, to, clearAndCommit, source)\n announce(`${nameOf(source)}, ${to + 1} / ${items.length}`)\n settle(chip)\n }\n\n function finish(drop: boolean) {\n stopListening()\n if (!drag) return\n const { chip, line, found, items, frame } = drag\n if (frame) cancelAnimationFrame(frame)\n // The pointer lifts over a row: that click must not act on it\n window.addEventListener(\"click\", swallow, { capture: true, once: true })\n setTimeout(() => window.removeEventListener(\"click\", swallow, { capture: true }))\n line.remove()\n drag = undefined\n // On a drop this waits for the flip to read where the parted items stand\n const clear = () => {\n list.removeAttribute(\"data-dragging\")\n document.documentElement.classList.remove(s.Dragging)\n shift(found, 0)\n }\n if (drop && found) {\n void land(chip, items, found, clear)\n return\n }\n clear()\n restoreSource()\n settle(chip)\n }\n}\n\n/** Starts a drag from a press inside the list. Goes on the list's `pointerdown`. */\nexport function sortablePointerDown(event: PointerEvent, list: HTMLElement, layout: SortableLayout, commit: SortableCommit) {\n const target = event.target as Element\n if (event.button || target.closest(\"[data-sortable]\") !== list) return\n const source = itemOf(list, target)\n if (!source) return\n const handle = target.closest(HANDLE)\n // A grip in sight is the only way to drag, otherwise anything but a control starts it\n if (source.querySelector(HANDLE) && showsHandles(list) ? !handle : target.closest(INTERACTIVE)) return\n startDrag(event, list, source, layout, !!handle, commit)\n}\n\nconst KEY_STEPS: Record<string, number> = { ArrowUp: -1, ArrowLeft: -1, ArrowDown: 1, ArrowRight: 1 }\n\n/** Moves the focused handle's item with the arrow keys. Goes on the list's `keydown`. */\nexport async function sortableKeyDown(event: KeyboardEvent, list: HTMLElement, commit: SortableCommit) {\n const step = KEY_STEPS[event.key]\n const target = event.target as HTMLElement\n if (!step || target.closest(\"[data-sortable]\") !== list) return\n const source = itemOf(list, target)\n if (!source || (target !== source && !target.closest(HANDLE))) return\n const items = itemsOf(list)\n const from = items.indexOf(source)\n const to = from + step\n if (to < 0 || to >= items.length) return\n event.preventDefault()\n await commitWithFlip(items, from, to, commit)\n // Moving an element in the document drops its focus\n if (target.isConnected && document.activeElement !== target) target.focus()\n announce(`${nameOf(source)}, ${to + 1} / ${items.length}`)\n}\n"]}
|