gridsmith-ui 0.30.2 → 0.31.1
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/CHANGELOG.md +71 -1
- package/dist/charts.d.ts +114 -1
- package/dist/charts.js +1113 -206
- package/dist/charts.js.map +1 -1
- package/dist/chat.js +20 -12
- package/dist/chat.js.map +1 -1
- package/dist/{chunk-HVGHGBKV.js → chunk-2TCCSKL3.js} +6 -7
- package/dist/chunk-2TCCSKL3.js.map +1 -0
- package/dist/{chunk-L6HWACK7.js → chunk-53F7GVBQ.js} +6 -3
- package/dist/chunk-53F7GVBQ.js.map +1 -0
- package/dist/chunk-BHTAVJIG.js +285 -0
- package/dist/chunk-BHTAVJIG.js.map +1 -0
- package/dist/{chunk-WWN6CQKI.js → chunk-DR6WEI3A.js} +60 -7
- package/dist/chunk-DR6WEI3A.js.map +1 -0
- package/dist/{chunk-4HJDOZ2M.js → chunk-EZ3ZGFGA.js} +203 -34
- package/dist/chunk-EZ3ZGFGA.js.map +1 -0
- package/dist/{chunk-MIUSSYVE.js → chunk-HQFTP7Y5.js} +27 -52
- package/dist/chunk-HQFTP7Y5.js.map +1 -0
- package/dist/{chunk-SIWERRP2.js → chunk-I6JJ7KKP.js} +14 -15
- package/dist/chunk-I6JJ7KKP.js.map +1 -0
- package/dist/chunk-IOHGE4EW.js +755 -0
- package/dist/chunk-IOHGE4EW.js.map +1 -0
- package/dist/{chunk-USHW523V.js → chunk-IZW5XHKA.js} +73 -699
- package/dist/chunk-IZW5XHKA.js.map +1 -0
- package/dist/{chunk-ZASGFL5D.js → chunk-K3NAFQ4S.js} +3 -4
- package/dist/chunk-K3NAFQ4S.js.map +1 -0
- package/dist/{chunk-ACLKDE5W.js → chunk-KSJTMSYA.js} +23 -11
- package/dist/chunk-KSJTMSYA.js.map +1 -0
- package/dist/{chunk-SQHKC4HA.js → chunk-NDL2JGQ7.js} +8 -7
- package/dist/chunk-NDL2JGQ7.js.map +1 -0
- package/dist/{chunk-TYNDXMBV.js → chunk-QOMP4D6V.js} +3 -3
- package/dist/{chunk-TYNDXMBV.js.map → chunk-QOMP4D6V.js.map} +1 -1
- package/dist/{chunk-BZRZENBS.js → chunk-SLGGZ3MU.js} +6 -6
- package/dist/{chunk-BZRZENBS.js.map → chunk-SLGGZ3MU.js.map} +1 -1
- package/dist/{chunk-K6IDOCGE.js → chunk-TH272DIC.js} +10 -10
- package/dist/{chunk-K6IDOCGE.js.map → chunk-TH272DIC.js.map} +1 -1
- package/dist/chunk-V73LHNRL.js +51 -0
- package/dist/chunk-V73LHNRL.js.map +1 -0
- package/dist/{chunk-ZCDA74RQ.js → chunk-W3GY5ROH.js} +3 -3
- package/dist/{chunk-ZCDA74RQ.js.map → chunk-W3GY5ROH.js.map} +1 -1
- package/dist/data-grid.js +11 -12
- package/dist/data-grid.js.map +1 -1
- package/dist/editor-mentions.js +2 -3
- package/dist/editor-mentions.js.map +1 -1
- package/dist/editor.js +4 -6
- package/dist/editor.js.map +1 -1
- package/dist/icons.js +1 -1
- package/dist/index.d.ts +100 -16
- package/dist/index.js +1177 -660
- package/dist/index.js.map +1 -1
- package/dist/kanban.js +10 -11
- package/dist/kanban.js.map +1 -1
- package/dist/resizable.js +10 -11
- package/dist/resizable.js.map +1 -1
- package/dist/virtualized.js +11 -12
- package/dist/virtualized.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-4HJDOZ2M.js.map +0 -1
- package/dist/chunk-6C2IUMY7.js +0 -46
- package/dist/chunk-6C2IUMY7.js.map +0 -1
- package/dist/chunk-7Q46JXSK.js +0 -51
- package/dist/chunk-7Q46JXSK.js.map +0 -1
- package/dist/chunk-ACLKDE5W.js.map +0 -1
- package/dist/chunk-HVGHGBKV.js.map +0 -1
- package/dist/chunk-L6HWACK7.js.map +0 -1
- package/dist/chunk-MIUSSYVE.js.map +0 -1
- package/dist/chunk-NKUCS67T.js +0 -13
- package/dist/chunk-NKUCS67T.js.map +0 -1
- package/dist/chunk-SIWERRP2.js.map +0 -1
- package/dist/chunk-SQHKC4HA.js.map +0 -1
- package/dist/chunk-USHW523V.js.map +0 -1
- package/dist/chunk-WWN6CQKI.js.map +0 -1
- package/dist/chunk-YL5KD5RC.js +0 -49
- package/dist/chunk-YL5KD5RC.js.map +0 -1
- package/dist/chunk-ZASGFL5D.js.map +0 -1
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { useReportedChange, useOptionalFormContext } from './chunk-72SDAX54.js';
|
|
3
|
+
import { ControlSizeContext } from './chunk-HQFTP7Y5.js';
|
|
3
4
|
import { useInputModality } from './chunk-7RKC5C7G.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { useLatest, useFocusLeave, usePopupFocus, focusAfterRemoval, usePosition, usePressMarks, focusInitial, focusAfterSelfRemoval, tabStops } from './chunk-4HJDOZ2M.js';
|
|
11
|
-
import { cn, ERROR_HAS_FOCUS_RING, FIELD_HAS_FOCUS_RING, ERROR_FOCUS_RING, FIELD_FOCUS_RING, FOCUS_RING, ERROR_ACTIVE_RING, ACTIVE_RING, ERROR_FOCUS_WITHIN_RING, FIELD_FOCUS_WITHIN_RING, GLASS_DROPDOWN } from './chunk-YL5KD5RC.js';
|
|
12
|
-
import { createContext, useContext, useRef, useCallback, useEffect, useId, useState, Children, isValidElement, useMemo, cloneElement, useLayoutEffect } from 'react';
|
|
13
|
-
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
|
+
import { useTextOverflow } from './chunk-W3GY5ROH.js';
|
|
6
|
+
import { useRevealFocus, AutoTooltip } from './chunk-DR6WEI3A.js';
|
|
7
|
+
import { warnNameLeftOut, Icon } from './chunk-53F7GVBQ.js';
|
|
8
|
+
import { cn, ERROR_HAS_FOCUS_RING, FIELD_HAS_FOCUS_RING, ERROR_FOCUS_RING, FIELD_FOCUS_RING, FOCUS_RING, useLatest, useFocusLeave, usePopupFocus, focusAfterRemoval, ERROR_ACTIVE_RING, ACTIVE_RING } from './chunk-EZ3ZGFGA.js';
|
|
9
|
+
import { createContext, useContext, useRef, useCallback, useEffect, useId, useState, Children, isValidElement, useLayoutEffect, useMemo, cloneElement } from 'react';
|
|
10
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
14
11
|
import { createPortal } from 'react-dom';
|
|
15
12
|
|
|
16
13
|
// ../../showcase/src/lib/control-looks.ts
|
|
@@ -180,7 +177,14 @@ var TOGGLE_SIZES = {
|
|
|
180
177
|
sm: { w: 32, h: 18, thumb: 12 },
|
|
181
178
|
md: { w: 44, h: 24, thumb: 16 }
|
|
182
179
|
};
|
|
183
|
-
function Toggle({ checked, onChange, label, disabled, error = false, layout = "full", size = "md", "aria-labelledby":
|
|
180
|
+
function Toggle({ checked, onChange, label, disabled, error = false, layout = "full", size = "md", "aria-label": pageLabel, "aria-labelledby": pageLabelledBy, ...rest }) {
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
if (!label) return;
|
|
183
|
+
if (pageLabel) warnNameLeftOut("Toggle", "aria-label", pageLabel, "its own label");
|
|
184
|
+
if (pageLabelledBy) warnNameLeftOut("Toggle", "aria-labelledby", pageLabelledBy, "its own label");
|
|
185
|
+
}, [label, pageLabel, pageLabelledBy]);
|
|
186
|
+
const ariaLabel = label ? void 0 : pageLabel;
|
|
187
|
+
const ariaLabelledBy = label ? void 0 : pageLabelledBy;
|
|
184
188
|
const change = useReportedChange(onChange, checked);
|
|
185
189
|
const dims = TOGGLE_SIZES[size];
|
|
186
190
|
const travel = dims.w - dims.thumb - 3;
|
|
@@ -190,7 +194,7 @@ function Toggle({ checked, onChange, label, disabled, error = false, layout = "f
|
|
|
190
194
|
type: "button",
|
|
191
195
|
role: "switch",
|
|
192
196
|
"aria-checked": checked,
|
|
193
|
-
"aria-label": !label && !ariaLabelledBy ? "Toggle" : void 0,
|
|
197
|
+
"aria-label": ariaLabel ?? (!label && !ariaLabelledBy && !rest.id ? "Toggle" : void 0),
|
|
194
198
|
"aria-labelledby": ariaLabelledBy,
|
|
195
199
|
"aria-invalid": error || void 0,
|
|
196
200
|
disabled,
|
|
@@ -277,10 +281,24 @@ function useFieldControl(own, options = {}) {
|
|
|
277
281
|
const required = !!own.required || own["aria-required"] === true || own["aria-required"] === "true" || !!field?.required;
|
|
278
282
|
const noteId = options.requiredInDescription && required && id ? `${id}-required` : void 0;
|
|
279
283
|
const ids = [own["aria-describedby"], field?.describedBy, noteId].filter(Boolean).join(" ").split(/\s+/).filter(Boolean);
|
|
284
|
+
const { ownLabel, component } = options;
|
|
285
|
+
const fieldShown = !!field && !field.labelHidden;
|
|
286
|
+
const shown = !!ownLabel || fieldShown;
|
|
287
|
+
const pageLabel = own["aria-label"], pageLabelledBy = own["aria-labelledby"];
|
|
288
|
+
useEffect(() => {
|
|
289
|
+
if (!shown) return;
|
|
290
|
+
const whose = ownLabel ? "its own label" : "its Field's label";
|
|
291
|
+
for (const [attr, value] of [["aria-label", pageLabel], ["aria-labelledby", pageLabelledBy]]) {
|
|
292
|
+
if (value) warnNameLeftOut(component ?? "A Field's control", attr, value, whose);
|
|
293
|
+
}
|
|
294
|
+
}, [shown, ownLabel, component, pageLabel, pageLabelledBy]);
|
|
280
295
|
return {
|
|
281
296
|
id,
|
|
282
|
-
label:
|
|
283
|
-
|
|
297
|
+
label: shown ? void 0 : pageLabel,
|
|
298
|
+
// with none on screen, ARIA's own order: the page's aria-labelledby, its aria-label, then a Field label that isn't shown
|
|
299
|
+
// (the final review of 0.31.0, 3: a hidden "Select" had named every row's box, over the page's "Select Alice")
|
|
300
|
+
labelId: shown ? typeof ownLabel === "string" ? ownLabel : ownLabel ? void 0 : field?.labelId : pageLabelledBy ?? (pageLabel ? void 0 : field?.labelId),
|
|
301
|
+
shown,
|
|
284
302
|
describedBy: [...new Set(ids)].join(" ") || void 0,
|
|
285
303
|
invalid,
|
|
286
304
|
required,
|
|
@@ -333,10 +351,32 @@ function Field({ label, children, hint, error, required, labelAction, hideLabel
|
|
|
333
351
|
const hasError = typeof shownError === "string" && shownError.length > 0;
|
|
334
352
|
const describedBy = [hint ? hintId : null, hasError ? errorId : null].filter(Boolean).join(" ") || void 0;
|
|
335
353
|
const labelId = `${id}-label`;
|
|
336
|
-
const
|
|
354
|
+
const [rowShowsLabel, setRowShowsLabel] = useState(false);
|
|
355
|
+
useLayoutEffect(() => {
|
|
356
|
+
const el = document.getElementById(labelId);
|
|
357
|
+
if (!hideLabel || !inGroupRow || !el || typeof ResizeObserver === "undefined") return;
|
|
358
|
+
const ro = new ResizeObserver(() => {
|
|
359
|
+
const box = el.getBoundingClientRect();
|
|
360
|
+
setRowShowsLabel(box.width > 1 && box.height > 1);
|
|
361
|
+
});
|
|
362
|
+
ro.observe(el);
|
|
363
|
+
return () => ro.disconnect();
|
|
364
|
+
}, [hideLabel, inGroupRow, labelId]);
|
|
365
|
+
const labelShown = !hideLabel || inGroupRow && rowShowsLabel;
|
|
366
|
+
const fieldContext = useMemo(() => ({ controlId: id, labelId, describedBy, invalid: hasError, required: !!required, labelHidden: !labelShown }), [id, labelId, describedBy, hasError, required, labelShown]);
|
|
367
|
+
const childType = child && isValidElement(child) ? child.type : null;
|
|
368
|
+
const childName = typeof childType === "string" ? childType : childType ? childType.displayName ?? childType.name ?? "A Field's control" : "A Field's control";
|
|
369
|
+
const pageLabel = labelShown ? childProps?.["aria-label"] : void 0;
|
|
370
|
+
const pageLabelledBy = labelShown ? childProps?.["aria-labelledby"] : void 0;
|
|
371
|
+
useEffect(() => {
|
|
372
|
+
if (pageLabel) warnNameLeftOut(childName, "aria-label", pageLabel, "its Field's label");
|
|
373
|
+
if (pageLabelledBy) warnNameLeftOut(childName, "aria-labelledby", pageLabelledBy, "its Field's label");
|
|
374
|
+
}, [childName, pageLabel, pageLabelledBy]);
|
|
375
|
+
const ownDescribedBy = childProps?.["aria-describedby"];
|
|
376
|
+
const allDescribedBy = [...new Set([ownDescribedBy, describedBy].filter(Boolean).join(" ").split(/\s+/).filter(Boolean))].join(" ") || void 0;
|
|
337
377
|
const controlProps = {
|
|
338
378
|
id,
|
|
339
|
-
"aria-describedby":
|
|
379
|
+
"aria-describedby": allDescribedBy,
|
|
340
380
|
...hasError ? { "aria-invalid": true, error: true } : {},
|
|
341
381
|
...required ? { "aria-required": true, required: true } : {}
|
|
342
382
|
};
|
|
@@ -352,7 +392,8 @@ function Field({ label, children, hint, error, required, labelAction, hideLabel
|
|
|
352
392
|
ownOnChange?.(...args);
|
|
353
393
|
recheck();
|
|
354
394
|
} } : {};
|
|
355
|
-
|
|
395
|
+
const names = labelShown ? { "aria-label": void 0, "aria-labelledby": void 0 } : {};
|
|
396
|
+
control = cloneElement(child, isNative ? { ...ariaOnly, ...names } : { ...ariaOnly, ...names, ...errorFlag ? { error: true } : {}, ...onChange });
|
|
356
397
|
}
|
|
357
398
|
const labelNode = /* @__PURE__ */ jsxs(Label, { id: labelId, htmlFor: id, className: cn(hideLabel && "sr-only", hideLabel && inGroupRow && "@max-[30rem]:not-sr-only"), children: [
|
|
358
399
|
label,
|
|
@@ -405,11 +446,11 @@ function Dropdown({
|
|
|
405
446
|
useInputModality();
|
|
406
447
|
const contextSize = useContext(ControlSizeContext);
|
|
407
448
|
const generatedId = useId();
|
|
408
|
-
const
|
|
449
|
+
const labelSpanId = `${generatedId}-label`;
|
|
450
|
+
const nameSpanId = `${generatedId}-name`;
|
|
451
|
+
const control = useFieldControl({ ...wiring, "aria-label": ariaLabel, error: error || void 0 }, { requiredInDescription: true, component: "Dropdown", ownLabel: label ? labelSpanId : void 0 });
|
|
409
452
|
const triggerId = control.id ?? generatedId;
|
|
410
|
-
const
|
|
411
|
-
const labelledBy = control.labelId ? `${control.labelId} ${triggerId}` : label && !ariaLabel ? `${labelSpanId} ${triggerId}` : void 0;
|
|
412
|
-
const listLabelledBy = control.labelId ?? (label && !ariaLabel ? labelSpanId : void 0);
|
|
453
|
+
const listLabelledBy = control.labelId;
|
|
413
454
|
const invalid = control.invalid;
|
|
414
455
|
const size = sizeProp ?? contextSize ?? "md";
|
|
415
456
|
const [open, setOpen] = useState(false);
|
|
@@ -581,9 +622,15 @@ function Dropdown({
|
|
|
581
622
|
});
|
|
582
623
|
const displayLabel = multiple ? selectedValues.length > 0 ? `${selectedValues.length} selected` : placeholder : options.find((o) => o.id === selectedValues[0])?.label ?? placeholder;
|
|
583
624
|
const { ref: overflowRef, isTruncated } = useTextOverflow({ lines: 1, content: displayLabel });
|
|
625
|
+
const shows = String(displayLabel).trim();
|
|
626
|
+
const name = control.label?.trim() ?? "";
|
|
627
|
+
const repeats = !!name && new RegExp(`^${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?!\\p{L})`, "iu").test(shows);
|
|
628
|
+
const nameSpan = !control.labelId && !!control.label && !repeats;
|
|
629
|
+
const labelledBy = control.labelId ? `${control.labelId} ${triggerId}` : nameSpan ? `${nameSpanId} ${triggerId}` : void 0;
|
|
584
630
|
return /* @__PURE__ */ jsxs("div", { ref, className: cn("relative", className), children: [
|
|
585
631
|
label && /* @__PURE__ */ jsx("span", { id: labelSpanId, className: "block mb-ds-1.5 text-sm font-medium text-[var(--ds-text-secondary)]", children: label }),
|
|
586
632
|
control.requiredNote,
|
|
633
|
+
nameSpan && /* @__PURE__ */ jsx("span", { id: nameSpanId, hidden: true, children: shows && !/[,.:;!?]$/.test(control.label.trim()) ? `${control.label},` : control.label }),
|
|
587
634
|
/* @__PURE__ */ jsxs(
|
|
588
635
|
"button",
|
|
589
636
|
{
|
|
@@ -602,7 +649,6 @@ function Dropdown({
|
|
|
602
649
|
"aria-invalid": invalid || void 0,
|
|
603
650
|
"aria-haspopup": "listbox",
|
|
604
651
|
"aria-expanded": open,
|
|
605
|
-
"aria-label": labelledBy ? void 0 : ariaLabel,
|
|
606
652
|
"aria-labelledby": labelledBy,
|
|
607
653
|
"aria-describedby": control.describedBy,
|
|
608
654
|
className: cn(
|
|
@@ -655,7 +701,7 @@ function Dropdown({
|
|
|
655
701
|
className: cn("w-full bg-transparent text-[var(--ds-text-primary)] placeholder:text-[var(--ds-text-muted)] outline-none", size === "sm" ? "text-xs" : "text-sm")
|
|
656
702
|
}
|
|
657
703
|
) }),
|
|
658
|
-
/* @__PURE__ */ jsx("div", { ref: setListbox, role: "listbox", "aria-labelledby": listLabelledBy, "aria-label": listLabelledBy ? void 0 :
|
|
704
|
+
/* @__PURE__ */ jsx("div", { ref: setListbox, role: "listbox", "aria-labelledby": listLabelledBy, "aria-label": listLabelledBy ? void 0 : control.label, "aria-multiselectable": multiple || void 0, className: "overflow-y-auto scroll-p-[4px]", style: { padding: "max(var(--ds-space-1), 4px)", maxHeight: "var(--ds-dropdown-max-height)" }, children: filtered.length === 0 ? /* @__PURE__ */ jsx("p", { className: cn("text-[var(--ds-text-muted)] text-center py-ds-4", size === "sm" ? "text-xs" : "text-sm"), children: "No options found" }) : hasGroups ? Array.from(groups.entries()).map(([group, opts]) => /* @__PURE__ */ jsxs("div", { children: [
|
|
659
705
|
group && /* @__PURE__ */ jsx("p", { className: "text-2xs font-medium uppercase tracking-wider text-[var(--ds-text-muted)] px-ds-2 pt-ds-2 pb-ds-1", children: group }),
|
|
660
706
|
opts.map((opt) => /* @__PURE__ */ jsx(
|
|
661
707
|
OptionButton,
|
|
@@ -757,679 +803,7 @@ function OptionButton({
|
|
|
757
803
|
}
|
|
758
804
|
);
|
|
759
805
|
}
|
|
760
|
-
var sizes = {
|
|
761
|
-
sm: { box: "h-[var(--ds-control-height-sm)]", input: "text-xs self-stretch", btn: "w-7 text-xs" },
|
|
762
|
-
md: { box: "h-[var(--ds-control-height-md)]", input: "text-sm self-stretch", btn: "w-9 text-sm" },
|
|
763
|
-
lg: { box: "h-[var(--ds-control-height-lg)]", input: "text-base self-stretch", btn: "w-10 text-base" }
|
|
764
|
-
};
|
|
765
|
-
function NumberInput({
|
|
766
|
-
value,
|
|
767
|
-
onChange: onChangeProp,
|
|
768
|
-
min = -Infinity,
|
|
769
|
-
max = Infinity,
|
|
770
|
-
step = 1,
|
|
771
|
-
disabled = false,
|
|
772
|
-
placeholder = "0",
|
|
773
|
-
prefix,
|
|
774
|
-
suffix,
|
|
775
|
-
size: sizeProp,
|
|
776
|
-
className,
|
|
777
|
-
error = false,
|
|
778
|
-
allowDecimals,
|
|
779
|
-
id,
|
|
780
|
-
"aria-label": ariaLabel
|
|
781
|
-
}) {
|
|
782
|
-
const onChange = useReportedChange(onChangeProp, value);
|
|
783
|
-
useInputModality();
|
|
784
|
-
const contextSize = useContext(ControlSizeContext);
|
|
785
|
-
const size = sizeProp ?? contextSize ?? "md";
|
|
786
|
-
const intervalRef = useRef(void 0);
|
|
787
|
-
const timeoutRef = useRef(void 0);
|
|
788
|
-
const inputRef = useRef(null);
|
|
789
|
-
const [focused, setFocused] = useState(false);
|
|
790
|
-
const [draft, setDraft] = useState(String(value));
|
|
791
|
-
const decimals = allowDecimals ?? step % 1 !== 0;
|
|
792
|
-
const [prevSync, setPrevSync] = useState({ value, focused });
|
|
793
|
-
if (value !== prevSync.value || focused !== prevSync.focused) {
|
|
794
|
-
setPrevSync({ value, focused });
|
|
795
|
-
if (!focused) setDraft(String(value));
|
|
796
|
-
}
|
|
797
|
-
const clamp = useCallback(
|
|
798
|
-
(v) => Math.min(max, Math.max(min, v)),
|
|
799
|
-
[min, max]
|
|
800
|
-
);
|
|
801
|
-
const valueRef = useLatest(value);
|
|
802
|
-
const commitValue = useCallback((v, own = false) => {
|
|
803
|
-
const clamped = clamp(v);
|
|
804
|
-
valueRef.current = clamped;
|
|
805
|
-
setDraft(String(clamped));
|
|
806
|
-
(own ? onChangeProp : onChange)(clamped);
|
|
807
|
-
return clamped;
|
|
808
|
-
}, [onChange, onChangeProp, clamp, valueRef]);
|
|
809
|
-
const increment = useCallback(() => commitValue(valueRef.current + step), [commitValue, step, valueRef]);
|
|
810
|
-
const decrement = useCallback(() => commitValue(valueRef.current - step), [commitValue, step, valueRef]);
|
|
811
|
-
const startRepeat = useCallback(
|
|
812
|
-
(fn) => {
|
|
813
|
-
fn();
|
|
814
|
-
timeoutRef.current = setTimeout(() => {
|
|
815
|
-
intervalRef.current = setInterval(fn, 75);
|
|
816
|
-
}, 400);
|
|
817
|
-
},
|
|
818
|
-
[]
|
|
819
|
-
);
|
|
820
|
-
const stopRepeat = useCallback(() => {
|
|
821
|
-
clearTimeout(timeoutRef.current);
|
|
822
|
-
clearInterval(intervalRef.current);
|
|
823
|
-
}, []);
|
|
824
|
-
const handleInput = useCallback(
|
|
825
|
-
(e) => {
|
|
826
|
-
const raw = e.target.value;
|
|
827
|
-
if (raw === "" || raw === "-") {
|
|
828
|
-
setDraft(raw);
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
if (!decimals && raw.includes(".")) return;
|
|
832
|
-
const validPattern = decimals ? /^-?\d*\.?\d*$/ : /^-?\d*$/;
|
|
833
|
-
if (!validPattern.test(raw)) return;
|
|
834
|
-
setDraft(raw);
|
|
835
|
-
const parsed = parseFloat(raw);
|
|
836
|
-
if (!isNaN(parsed) && isFinite(parsed)) {
|
|
837
|
-
onChange(clamp(parsed));
|
|
838
|
-
}
|
|
839
|
-
},
|
|
840
|
-
[onChange, clamp, decimals]
|
|
841
|
-
);
|
|
842
|
-
const handleBlur = useCallback(() => {
|
|
843
|
-
setFocused(false);
|
|
844
|
-
const parsed = parseFloat(draft);
|
|
845
|
-
if (!isNaN(parsed) && isFinite(parsed)) {
|
|
846
|
-
const committed = commitValue(parsed, draft === String(value));
|
|
847
|
-
setDraft(String(committed));
|
|
848
|
-
} else {
|
|
849
|
-
const fallback = isFinite(min) ? min : 0;
|
|
850
|
-
const committed = commitValue(value !== void 0 ? value : fallback, true);
|
|
851
|
-
setDraft(String(committed));
|
|
852
|
-
}
|
|
853
|
-
}, [draft, commitValue, min, value]);
|
|
854
|
-
const handleFocus = useCallback(() => {
|
|
855
|
-
setFocused(true);
|
|
856
|
-
}, []);
|
|
857
|
-
const handleKeyDown = useCallback(
|
|
858
|
-
(e) => {
|
|
859
|
-
if (e.key === "ArrowUp") {
|
|
860
|
-
e.preventDefault();
|
|
861
|
-
increment();
|
|
862
|
-
}
|
|
863
|
-
if (e.key === "ArrowDown") {
|
|
864
|
-
e.preventDefault();
|
|
865
|
-
decrement();
|
|
866
|
-
}
|
|
867
|
-
const plain = !e.shiftKey && !e.ctrlKey && !e.metaKey && !e.altKey;
|
|
868
|
-
if (e.key === "Home" && plain && Number.isFinite(min)) {
|
|
869
|
-
e.preventDefault();
|
|
870
|
-
commitValue(min);
|
|
871
|
-
}
|
|
872
|
-
if (e.key === "End" && plain && Number.isFinite(max)) {
|
|
873
|
-
e.preventDefault();
|
|
874
|
-
commitValue(max);
|
|
875
|
-
}
|
|
876
|
-
},
|
|
877
|
-
[increment, decrement, commitValue, min, max]
|
|
878
|
-
);
|
|
879
|
-
const s = sizes[size];
|
|
880
|
-
const atMin = value <= min;
|
|
881
|
-
const atMax = value >= max;
|
|
882
|
-
const shown = focused ? draft : String(value);
|
|
883
|
-
const holdsNumber = Number.isFinite(parseFloat(shown)) && Number.isFinite(value);
|
|
884
|
-
return /* @__PURE__ */ jsxs(
|
|
885
|
-
"div",
|
|
886
|
-
{
|
|
887
|
-
className: cn(
|
|
888
|
-
"flex items-center w-full",
|
|
889
|
-
s.box,
|
|
890
|
-
"border-[length:var(--ds-border-width)]",
|
|
891
|
-
error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
|
|
892
|
-
"rounded-[var(--ds-radius-md)]",
|
|
893
|
-
"shadow-[var(--ds-input-shadow)]",
|
|
894
|
-
"bg-[var(--ds-input-bg)]",
|
|
895
|
-
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
896
|
-
!error && !disabled && "hover:border-[var(--ds-input-border-hover)]",
|
|
897
|
-
error ? ERROR_FOCUS_WITHIN_RING : FIELD_FOCUS_WITHIN_RING,
|
|
898
|
-
disabled && "opacity-disabled cursor-not-allowed",
|
|
899
|
-
className
|
|
900
|
-
),
|
|
901
|
-
children: [
|
|
902
|
-
/* @__PURE__ */ jsx(
|
|
903
|
-
"button",
|
|
904
|
-
{
|
|
905
|
-
type: "button",
|
|
906
|
-
tabIndex: -1,
|
|
907
|
-
disabled: disabled || atMin,
|
|
908
|
-
onMouseDown: (e) => {
|
|
909
|
-
e.preventDefault();
|
|
910
|
-
inputRef.current?.focus();
|
|
911
|
-
if (!disabled && !atMin) startRepeat(decrement);
|
|
912
|
-
},
|
|
913
|
-
onMouseUp: stopRepeat,
|
|
914
|
-
onMouseLeave: stopRepeat,
|
|
915
|
-
className: cn(
|
|
916
|
-
"inline-flex items-center justify-center shrink-0",
|
|
917
|
-
"border-r-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
918
|
-
"text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]",
|
|
919
|
-
"transition-colors cursor-pointer",
|
|
920
|
-
"rounded-l-[var(--ds-radius-md)]",
|
|
921
|
-
// kept out of the Tab order (the field's arrows step it) and never focused by a press: the outline all the same
|
|
922
|
-
// if anything focuses it (Sprint 30)
|
|
923
|
-
FOCUS_RING,
|
|
924
|
-
s.btn,
|
|
925
|
-
s.input,
|
|
926
|
-
(disabled || atMin) && "opacity-disabled cursor-not-allowed hover:bg-transparent hover:text-[var(--ds-text-muted)]"
|
|
927
|
-
),
|
|
928
|
-
"aria-label": "Decrease",
|
|
929
|
-
children: "\u2212"
|
|
930
|
-
}
|
|
931
|
-
),
|
|
932
|
-
/* @__PURE__ */ jsxs("div", { className: cn("flex items-center flex-1 min-w-0 px-ds-2 gap-ds-1", s.input), children: [
|
|
933
|
-
prefix && /* @__PURE__ */ jsx("span", { className: "text-[var(--ds-text-muted)] shrink-0 text-xs", children: prefix }),
|
|
934
|
-
/* @__PURE__ */ jsx(
|
|
935
|
-
"input",
|
|
936
|
-
{
|
|
937
|
-
ref: inputRef,
|
|
938
|
-
id,
|
|
939
|
-
type: "text",
|
|
940
|
-
role: "spinbutton",
|
|
941
|
-
"aria-label": ariaLabel,
|
|
942
|
-
"aria-valuenow": holdsNumber ? value : void 0,
|
|
943
|
-
"aria-valuemin": Number.isFinite(min) ? min : void 0,
|
|
944
|
-
"aria-valuemax": Number.isFinite(max) ? max : void 0,
|
|
945
|
-
inputMode: decimals ? "decimal" : "numeric",
|
|
946
|
-
"aria-invalid": error || void 0,
|
|
947
|
-
value: shown,
|
|
948
|
-
onChange: handleInput,
|
|
949
|
-
onFocus: handleFocus,
|
|
950
|
-
onBlur: handleBlur,
|
|
951
|
-
onKeyDown: handleKeyDown,
|
|
952
|
-
disabled,
|
|
953
|
-
placeholder,
|
|
954
|
-
className: cn(
|
|
955
|
-
"w-full min-w-0 bg-transparent text-center outline-none",
|
|
956
|
-
"text-[var(--ds-text-primary)]",
|
|
957
|
-
"placeholder:text-[var(--ds-text-muted)]",
|
|
958
|
-
"font-[family-name:var(--ds-font-mono)]",
|
|
959
|
-
s.input,
|
|
960
|
-
disabled && "cursor-not-allowed"
|
|
961
|
-
)
|
|
962
|
-
}
|
|
963
|
-
),
|
|
964
|
-
suffix && /* @__PURE__ */ jsx("span", { className: "text-[var(--ds-text-muted)] shrink-0 text-xs", children: suffix })
|
|
965
|
-
] }),
|
|
966
|
-
/* @__PURE__ */ jsx(
|
|
967
|
-
"button",
|
|
968
|
-
{
|
|
969
|
-
type: "button",
|
|
970
|
-
tabIndex: -1,
|
|
971
|
-
disabled: disabled || atMax,
|
|
972
|
-
onMouseDown: (e) => {
|
|
973
|
-
e.preventDefault();
|
|
974
|
-
inputRef.current?.focus();
|
|
975
|
-
if (!disabled && !atMax) startRepeat(increment);
|
|
976
|
-
},
|
|
977
|
-
onMouseUp: stopRepeat,
|
|
978
|
-
onMouseLeave: stopRepeat,
|
|
979
|
-
className: cn(
|
|
980
|
-
"inline-flex items-center justify-center shrink-0",
|
|
981
|
-
"border-l-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
982
|
-
"text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]",
|
|
983
|
-
"transition-colors cursor-pointer",
|
|
984
|
-
"rounded-r-[var(--ds-radius-md)]",
|
|
985
|
-
FOCUS_RING,
|
|
986
|
-
s.btn,
|
|
987
|
-
s.input,
|
|
988
|
-
(disabled || atMax) && "opacity-disabled cursor-not-allowed hover:bg-transparent hover:text-[var(--ds-text-muted)]"
|
|
989
|
-
),
|
|
990
|
-
"aria-label": "Increase",
|
|
991
|
-
children: "+"
|
|
992
|
-
}
|
|
993
|
-
)
|
|
994
|
-
]
|
|
995
|
-
}
|
|
996
|
-
);
|
|
997
|
-
}
|
|
998
|
-
function Popover({ open, onOpenChange, trigger, children, side = "bottom", align = "center", width, className, container }) {
|
|
999
|
-
const triggerRef = useRef(null);
|
|
1000
|
-
const floatingRef = useRef(null);
|
|
1001
|
-
const [triggerWidth, setTriggerWidth] = useState(void 0);
|
|
1002
|
-
const setTriggerNode = useCallback((node) => {
|
|
1003
|
-
triggerRef.current = node;
|
|
1004
|
-
setTriggerWidth(node?.offsetWidth);
|
|
1005
|
-
}, []);
|
|
1006
|
-
const pos = usePosition(triggerRef, floatingRef, open, { side, align, offset: 8, flip: true });
|
|
1007
|
-
const onOpenChangeRef = useLatest(onOpenChange);
|
|
1008
|
-
const { props: pressMarks, marked } = usePressMarks();
|
|
1009
|
-
const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: triggerRef, panel: floatingRef, close: () => onOpenChangeRef.current(false), tabExits: "edges", marked });
|
|
1010
|
-
useEffect(() => {
|
|
1011
|
-
if (!open) return;
|
|
1012
|
-
const onKeyDown = (e) => {
|
|
1013
|
-
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
1014
|
-
const active = document.activeElement;
|
|
1015
|
-
if (active && active !== document.body && !triggerRef.current?.contains(active) && !floatingRef.current?.contains(active)) return;
|
|
1016
|
-
e.preventDefault();
|
|
1017
|
-
returnFocus();
|
|
1018
|
-
onOpenChangeRef.current(false);
|
|
1019
|
-
};
|
|
1020
|
-
const onPress = (e) => {
|
|
1021
|
-
const target = e.target;
|
|
1022
|
-
if (marked(e) || triggerRef.current?.contains(target) || floatingRef.current?.contains(target)) return;
|
|
1023
|
-
onOpenChangeRef.current(false);
|
|
1024
|
-
};
|
|
1025
|
-
document.addEventListener("keydown", onKeyDown);
|
|
1026
|
-
document.addEventListener("mousedown", onPress);
|
|
1027
|
-
return () => {
|
|
1028
|
-
document.removeEventListener("keydown", onKeyDown);
|
|
1029
|
-
document.removeEventListener("mousedown", onPress);
|
|
1030
|
-
};
|
|
1031
|
-
}, [open, onOpenChangeRef, isTopLayer, returnFocus, marked]);
|
|
1032
|
-
useEffect(() => {
|
|
1033
|
-
if (open) focusInitial(floatingRef.current);
|
|
1034
|
-
}, [open]);
|
|
1035
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1036
|
-
/* @__PURE__ */ jsx(
|
|
1037
|
-
Slot,
|
|
1038
|
-
{
|
|
1039
|
-
...triggerType(trigger),
|
|
1040
|
-
ref: setTriggerNode,
|
|
1041
|
-
onClick: () => {
|
|
1042
|
-
setTriggerWidth(triggerRef.current?.offsetWidth);
|
|
1043
|
-
onOpenChange(!open);
|
|
1044
|
-
},
|
|
1045
|
-
children: trigger
|
|
1046
|
-
}
|
|
1047
|
-
),
|
|
1048
|
-
open && createPortal(
|
|
1049
|
-
/* @__PURE__ */ jsx(
|
|
1050
|
-
"div",
|
|
1051
|
-
{
|
|
1052
|
-
ref: floatingRef,
|
|
1053
|
-
"data-gs": "popover",
|
|
1054
|
-
...pressMarks,
|
|
1055
|
-
onClick: (e) => e.stopPropagation(),
|
|
1056
|
-
className: cn(
|
|
1057
|
-
"fixed z-[var(--ds-z-popover)]",
|
|
1058
|
-
"bg-[var(--ds-bg-surface-elevated)]",
|
|
1059
|
-
GLASS_DROPDOWN,
|
|
1060
|
-
"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
1061
|
-
"rounded-[var(--ds-radius-md)]",
|
|
1062
|
-
className
|
|
1063
|
-
),
|
|
1064
|
-
style: {
|
|
1065
|
-
top: pos.top,
|
|
1066
|
-
left: pos.left,
|
|
1067
|
-
width: width === "trigger" ? triggerWidth : width === "auto" ? void 0 : width
|
|
1068
|
-
},
|
|
1069
|
-
children: /* @__PURE__ */ jsx(RowsCtx, { value: false, children })
|
|
1070
|
-
}
|
|
1071
|
-
),
|
|
1072
|
-
container ?? document.body
|
|
1073
|
-
)
|
|
1074
|
-
] });
|
|
1075
|
-
}
|
|
1076
|
-
var controlsDrawn = /* @__PURE__ */ new WeakMap();
|
|
1077
|
-
function Pagination(props) {
|
|
1078
|
-
const { pagerToken } = props;
|
|
1079
|
-
useEffect(() => {
|
|
1080
|
-
if (!pagerToken) return;
|
|
1081
|
-
const count = (controlsDrawn.get(pagerToken) ?? 0) + 1;
|
|
1082
|
-
controlsDrawn.set(pagerToken, count);
|
|
1083
|
-
if (count > 1) warnOnce("Pagination", "this list's pager already draws its control: a list given the pager (<Table pager={pager}>) draws it beneath the rows, so remove the separate <Pagination {...pager.props} />.");
|
|
1084
|
-
return () => {
|
|
1085
|
-
controlsDrawn.set(pagerToken, (controlsDrawn.get(pagerToken) ?? 1) - 1);
|
|
1086
|
-
};
|
|
1087
|
-
}, [pagerToken]);
|
|
1088
|
-
if ((props.mode === "load-more" || props.mode === "infinite-scroll") && props.onLoadMore) return /* @__PURE__ */ jsx(LoadMore, { ...props, onLoadMore: props.onLoadMore, scroll: props.mode === "infinite-scroll" });
|
|
1089
|
-
return /* @__PURE__ */ jsx(PageNumbers, { ...props });
|
|
1090
|
-
}
|
|
1091
|
-
function scrollerOf(el) {
|
|
1092
|
-
for (let n = el.parentElement; n && n !== document.body && n !== document.documentElement; n = n.parentElement) {
|
|
1093
|
-
const { overflowY } = getComputedStyle(n);
|
|
1094
|
-
if ((overflowY === "auto" || overflowY === "scroll") && n.scrollHeight > n.clientHeight) return n;
|
|
1095
|
-
}
|
|
1096
|
-
return null;
|
|
1097
|
-
}
|
|
1098
|
-
function contentTop(el) {
|
|
1099
|
-
let top = el.getBoundingClientRect().top + window.scrollY;
|
|
1100
|
-
for (let n = el.parentElement; n && n !== document.documentElement; n = n.parentElement) top += n.scrollTop;
|
|
1101
|
-
return top;
|
|
1102
|
-
}
|
|
1103
|
-
function LoadMore({ hasMore, onLoadMore, loadMoreLabel, currentPage, scroll, className }) {
|
|
1104
|
-
const marker = useRef(null);
|
|
1105
|
-
const button = useRef(null);
|
|
1106
|
-
const pager = useRef(null);
|
|
1107
|
-
const watching = useRef(null);
|
|
1108
|
-
const press = () => {
|
|
1109
|
-
const b = button.current, root = pager.current;
|
|
1110
|
-
if (b && root && document.activeElement === b) {
|
|
1111
|
-
const list = root.previousElementSibling;
|
|
1112
|
-
const had = new Set(rowsOf(list));
|
|
1113
|
-
watching.current?.();
|
|
1114
|
-
watching.current = focusAfterSelfRemoval(b, {
|
|
1115
|
-
item: root,
|
|
1116
|
-
to: () => {
|
|
1117
|
-
const fresh = rowsOf(list).find((r) => !had.has(r));
|
|
1118
|
-
return fresh ? tabStops(fresh)[0] ?? fresh : null;
|
|
1119
|
-
}
|
|
1120
|
-
});
|
|
1121
|
-
}
|
|
1122
|
-
onLoadMore();
|
|
1123
|
-
};
|
|
1124
|
-
const load = useLatest(onLoadMore);
|
|
1125
|
-
const loadedAt = useRef(null);
|
|
1126
|
-
const [held, setHeld] = useState(false);
|
|
1127
|
-
useEffect(() => {
|
|
1128
|
-
const el = marker.current;
|
|
1129
|
-
if (!scroll || !hasMore || !el || held || typeof IntersectionObserver === "undefined") return;
|
|
1130
|
-
const observer = new IntersectionObserver((entries) => {
|
|
1131
|
-
if (!entries.some((e) => e.isIntersecting)) return;
|
|
1132
|
-
const at = contentTop(el);
|
|
1133
|
-
if (loadedAt.current !== null && Math.abs(at - loadedAt.current) < 1) return;
|
|
1134
|
-
loadedAt.current = at;
|
|
1135
|
-
load.current();
|
|
1136
|
-
}, { root: scrollerOf(el), rootMargin: "200px 0px" });
|
|
1137
|
-
observer.observe(el);
|
|
1138
|
-
const rearm = () => {
|
|
1139
|
-
loadedAt.current = null;
|
|
1140
|
-
};
|
|
1141
|
-
window.addEventListener("scroll", rearm, { capture: true, passive: true });
|
|
1142
|
-
return () => {
|
|
1143
|
-
observer.disconnect();
|
|
1144
|
-
window.removeEventListener("scroll", rearm, { capture: true });
|
|
1145
|
-
};
|
|
1146
|
-
}, [scroll, hasMore, currentPage, load, held]);
|
|
1147
|
-
useEffect(() => {
|
|
1148
|
-
const b = button.current;
|
|
1149
|
-
if (b && document.activeElement === b) b.scrollIntoView?.({ block: "nearest" });
|
|
1150
|
-
}, [currentPage]);
|
|
1151
|
-
if (!hasMore) return null;
|
|
1152
|
-
return /* @__PURE__ */ jsxs("div", { ref: pager, "data-ds-pager": "", "data-pagination-mode": scroll ? "infinite-scroll" : "load-more", className: cn("flex flex-col items-center", className), children: [
|
|
1153
|
-
scroll && /* @__PURE__ */ jsx("div", { ref: marker, "data-load-more-trigger": "", "aria-hidden": "true", className: "h-px w-full" }),
|
|
1154
|
-
/* @__PURE__ */ jsx(
|
|
1155
|
-
Button,
|
|
1156
|
-
{
|
|
1157
|
-
ref: button,
|
|
1158
|
-
type: "button",
|
|
1159
|
-
variant: "secondary",
|
|
1160
|
-
onClick: press,
|
|
1161
|
-
onFocus: (e) => setHeld(scroll && byKeyboard(e.currentTarget)),
|
|
1162
|
-
onBlur: () => setHeld(false),
|
|
1163
|
-
children: loadMoreLabel ?? "Load more"
|
|
1164
|
-
}
|
|
1165
|
-
)
|
|
1166
|
-
] });
|
|
1167
|
-
}
|
|
1168
|
-
function rowsOf(list) {
|
|
1169
|
-
if (!list) return [];
|
|
1170
|
-
const marked = list.querySelectorAll("[data-row-key], [data-row-index]");
|
|
1171
|
-
return Array.from(marked.length ? marked : list.querySelectorAll('tbody > tr, li, [role="row"], [data-card]'));
|
|
1172
|
-
}
|
|
1173
|
-
function byKeyboard(el) {
|
|
1174
|
-
try {
|
|
1175
|
-
return el.matches(":focus-visible");
|
|
1176
|
-
} catch {
|
|
1177
|
-
return true;
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
function PageNumbers({ currentPage, totalPages, onPageChange, pageSize: pageSizeProp, pageSizeOptions, onPageSizeChange, className }) {
|
|
1181
|
-
const pages = buildPageRange(currentPage, totalPages);
|
|
1182
|
-
const preferences = useShellPreferences();
|
|
1183
|
-
const pageSize = pageSizeProp ?? preferences.itemsPerPage;
|
|
1184
|
-
const sizeChoice = !!(pageSizeOptions && pageSize != null && onPageSizeChange);
|
|
1185
|
-
if (totalPages <= 1 && !sizeChoice) return null;
|
|
1186
|
-
return /* @__PURE__ */ jsxs("nav", { "aria-label": "Pagination", "data-ds-pager": "", className: cn("flex flex-wrap items-center gap-ds-3", className), children: [
|
|
1187
|
-
sizeChoice && pageSizeOptions && pageSize != null && onPageSizeChange && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
|
|
1188
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-[var(--ds-text-muted)] whitespace-nowrap", children: "Rows per page" }),
|
|
1189
|
-
/* @__PURE__ */ jsx(FormChangeCtx, { value: null, children: /* @__PURE__ */ jsx(
|
|
1190
|
-
Dropdown,
|
|
1191
|
-
{
|
|
1192
|
-
"aria-label": `Rows per page, ${pageSize}`,
|
|
1193
|
-
options: pageSizeOptions.map((n) => ({ id: String(n), label: String(n) })),
|
|
1194
|
-
value: String(pageSize),
|
|
1195
|
-
onChange: (v) => onPageSizeChange(Number(v)),
|
|
1196
|
-
size: "sm",
|
|
1197
|
-
className: "w-[4.5rem]"
|
|
1198
|
-
}
|
|
1199
|
-
) })
|
|
1200
|
-
] }),
|
|
1201
|
-
totalPages > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-1", children: [
|
|
1202
|
-
/* @__PURE__ */ jsx(AutoTooltip, { content: "Previous page", children: /* @__PURE__ */ jsx(
|
|
1203
|
-
"button",
|
|
1204
|
-
{
|
|
1205
|
-
type: "button",
|
|
1206
|
-
onClick: (e) => {
|
|
1207
|
-
focusAfterSelfRemoval(e.currentTarget);
|
|
1208
|
-
onPageChange(currentPage - 1);
|
|
1209
|
-
},
|
|
1210
|
-
disabled: currentPage <= 1,
|
|
1211
|
-
"aria-label": "Previous page",
|
|
1212
|
-
className: cn(
|
|
1213
|
-
"inline-flex items-center justify-center h-[var(--ds-control-height-sm)] w-[var(--ds-control-height-sm)] text-sm cursor-pointer",
|
|
1214
|
-
"rounded-[var(--ds-radius-md)]",
|
|
1215
|
-
"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
1216
|
-
"bg-[var(--ds-bg-surface)] text-[var(--ds-text-secondary)]",
|
|
1217
|
-
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
1218
|
-
"hover:bg-[var(--ds-bg-muted)] hover:text-[var(--ds-text-primary)]",
|
|
1219
|
-
"disabled:opacity-disabled disabled:cursor-not-allowed",
|
|
1220
|
-
FOCUS_RING
|
|
1221
|
-
),
|
|
1222
|
-
children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16, className: "rotate-180" })
|
|
1223
|
-
}
|
|
1224
|
-
) }),
|
|
1225
|
-
pages.map(
|
|
1226
|
-
(p, i) => p === "..." ? /* @__PURE__ */ jsx(EllipsisJumper, { totalPages, onPageChange }, `ellipsis-${i}`) : /* @__PURE__ */ jsx(
|
|
1227
|
-
"button",
|
|
1228
|
-
{
|
|
1229
|
-
type: "button",
|
|
1230
|
-
onClick: () => onPageChange(p),
|
|
1231
|
-
"aria-current": p === currentPage ? "page" : void 0,
|
|
1232
|
-
className: cn(
|
|
1233
|
-
"inline-flex items-center justify-center h-[var(--ds-control-height-sm)] w-[var(--ds-control-height-sm)] text-sm font-medium cursor-pointer",
|
|
1234
|
-
"rounded-[var(--ds-radius-md)]",
|
|
1235
|
-
"border-[length:var(--ds-border-width)]",
|
|
1236
|
-
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
1237
|
-
FOCUS_RING,
|
|
1238
|
-
p === currentPage ? "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] text-[var(--ds-text-on-primary)] border-[var(--ds-accent-primary)]" : "bg-[var(--ds-bg-surface)] text-[var(--ds-text-secondary)] border-[var(--ds-border-default)] hover:bg-[var(--ds-bg-muted)] hover:text-[var(--ds-text-primary)]"
|
|
1239
|
-
),
|
|
1240
|
-
children: p
|
|
1241
|
-
},
|
|
1242
|
-
p
|
|
1243
|
-
)
|
|
1244
|
-
),
|
|
1245
|
-
/* @__PURE__ */ jsx(AutoTooltip, { content: "Next page", children: /* @__PURE__ */ jsx(
|
|
1246
|
-
"button",
|
|
1247
|
-
{
|
|
1248
|
-
type: "button",
|
|
1249
|
-
onClick: (e) => {
|
|
1250
|
-
focusAfterSelfRemoval(e.currentTarget);
|
|
1251
|
-
onPageChange(currentPage + 1);
|
|
1252
|
-
},
|
|
1253
|
-
disabled: currentPage >= totalPages,
|
|
1254
|
-
"aria-label": "Next page",
|
|
1255
|
-
className: cn(
|
|
1256
|
-
"inline-flex items-center justify-center h-[var(--ds-control-height-sm)] w-[var(--ds-control-height-sm)] text-sm cursor-pointer",
|
|
1257
|
-
"rounded-[var(--ds-radius-md)]",
|
|
1258
|
-
"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
1259
|
-
"bg-[var(--ds-bg-surface)] text-[var(--ds-text-secondary)]",
|
|
1260
|
-
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
1261
|
-
"hover:bg-[var(--ds-bg-muted)] hover:text-[var(--ds-text-primary)]",
|
|
1262
|
-
"disabled:opacity-disabled disabled:cursor-not-allowed",
|
|
1263
|
-
FOCUS_RING
|
|
1264
|
-
),
|
|
1265
|
-
children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16 })
|
|
1266
|
-
}
|
|
1267
|
-
) })
|
|
1268
|
-
] })
|
|
1269
|
-
] });
|
|
1270
|
-
}
|
|
1271
|
-
function buildPageRange(current, total) {
|
|
1272
|
-
if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);
|
|
1273
|
-
const pages = [1];
|
|
1274
|
-
if (current > 3) pages.push("...");
|
|
1275
|
-
const start = Math.max(2, current - 1);
|
|
1276
|
-
const end = Math.min(total - 1, current + 1);
|
|
1277
|
-
for (let i = start; i <= end; i++) pages.push(i);
|
|
1278
|
-
if (current < total - 2) pages.push("...");
|
|
1279
|
-
pages.push(total);
|
|
1280
|
-
return pages;
|
|
1281
|
-
}
|
|
1282
|
-
function EllipsisJumper({ totalPages, onPageChange }) {
|
|
1283
|
-
const [open, setOpen] = useState(false);
|
|
1284
|
-
const [jumpPage, setJumpPage] = useState(1);
|
|
1285
|
-
const handleOpenChange = (next) => {
|
|
1286
|
-
if (next) setJumpPage(1);
|
|
1287
|
-
setOpen(next);
|
|
1288
|
-
};
|
|
1289
|
-
const handleGo = () => {
|
|
1290
|
-
const clamped = Math.min(totalPages, Math.max(1, jumpPage));
|
|
1291
|
-
onPageChange(clamped);
|
|
1292
|
-
setOpen(false);
|
|
1293
|
-
};
|
|
1294
|
-
return /* @__PURE__ */ jsx(
|
|
1295
|
-
Popover,
|
|
1296
|
-
{
|
|
1297
|
-
open,
|
|
1298
|
-
onOpenChange: handleOpenChange,
|
|
1299
|
-
trigger: /* @__PURE__ */ jsx(
|
|
1300
|
-
"button",
|
|
1301
|
-
{
|
|
1302
|
-
type: "button",
|
|
1303
|
-
"aria-label": "Go to page",
|
|
1304
|
-
onClick: () => handleOpenChange(true),
|
|
1305
|
-
className: cn(
|
|
1306
|
-
"inline-flex items-center justify-center h-[var(--ds-control-height-sm)] w-[var(--ds-control-height-sm)] text-sm cursor-pointer",
|
|
1307
|
-
"rounded-[var(--ds-radius-md)]",
|
|
1308
|
-
"text-[var(--ds-text-muted)]",
|
|
1309
|
-
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
1310
|
-
"hover:bg-[var(--ds-bg-muted)] hover:text-[var(--ds-text-primary)]",
|
|
1311
|
-
FOCUS_RING
|
|
1312
|
-
),
|
|
1313
|
-
children: "\u2026"
|
|
1314
|
-
}
|
|
1315
|
-
),
|
|
1316
|
-
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-ds-2 p-ds-3", children: [
|
|
1317
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-[var(--ds-text-secondary)]", children: "Go to page" }),
|
|
1318
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
|
|
1319
|
-
/* @__PURE__ */ jsx(
|
|
1320
|
-
NumberInput,
|
|
1321
|
-
{
|
|
1322
|
-
value: jumpPage,
|
|
1323
|
-
onChange: setJumpPage,
|
|
1324
|
-
min: 1,
|
|
1325
|
-
max: totalPages,
|
|
1326
|
-
step: 1,
|
|
1327
|
-
size: "sm",
|
|
1328
|
-
className: "w-[5rem]"
|
|
1329
|
-
}
|
|
1330
|
-
),
|
|
1331
|
-
/* @__PURE__ */ jsx(
|
|
1332
|
-
"button",
|
|
1333
|
-
{
|
|
1334
|
-
type: "button",
|
|
1335
|
-
onClick: handleGo,
|
|
1336
|
-
className: cn(
|
|
1337
|
-
"px-ds-2.5 h-[var(--ds-control-height-sm)] text-xs font-medium cursor-pointer",
|
|
1338
|
-
"rounded-[var(--ds-radius-md)]",
|
|
1339
|
-
"bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]",
|
|
1340
|
-
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
1341
|
-
"hover:opacity-90",
|
|
1342
|
-
FOCUS_RING
|
|
1343
|
-
),
|
|
1344
|
-
children: "Go"
|
|
1345
|
-
}
|
|
1346
|
-
)
|
|
1347
|
-
] })
|
|
1348
|
-
] })
|
|
1349
|
-
}
|
|
1350
|
-
);
|
|
1351
|
-
}
|
|
1352
|
-
function firstKey(item) {
|
|
1353
|
-
if (item !== null && typeof item === "object") return "id" in item ? item.id : void 0;
|
|
1354
|
-
return item;
|
|
1355
|
-
}
|
|
1356
|
-
function resetKey(resetOn) {
|
|
1357
|
-
try {
|
|
1358
|
-
return JSON.stringify(resetOn ?? null);
|
|
1359
|
-
} catch {
|
|
1360
|
-
return String(resetOn);
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
function signature(list, resetOn) {
|
|
1364
|
-
let first = "";
|
|
1365
|
-
try {
|
|
1366
|
-
first = JSON.stringify(firstKey(list[0]) ?? null);
|
|
1367
|
-
} catch {
|
|
1368
|
-
first = "";
|
|
1369
|
-
}
|
|
1370
|
-
return `${list.length}|${first}|${resetKey(resetOn)}`;
|
|
1371
|
-
}
|
|
1372
|
-
function grewAtAnEnd(seen, list, reset) {
|
|
1373
|
-
if (reset !== seen.reset || list.length <= seen.length || seen.first === void 0 || seen.length === 0) return false;
|
|
1374
|
-
return firstKey(list[0]) === seen.first || firstKey(list[list.length - seen.length]) === seen.first;
|
|
1375
|
-
}
|
|
1376
|
-
function usePagination(list, options = {}) {
|
|
1377
|
-
const shell = useShellPreferences();
|
|
1378
|
-
const style = options.style ?? shell.paginationStyle ?? PREFERENCE_HOUSE_DEFAULTS.paginationStyle;
|
|
1379
|
-
const [chosenSize, setChosenSize] = useState(null);
|
|
1380
|
-
const [pagerToken] = useState(() => ({}));
|
|
1381
|
-
const pageSize = Math.max(1, Math.floor(chosenSize ?? options.pageSize ?? shell.itemsPerPage ?? PREFERENCE_HOUSE_DEFAULTS.itemsPerPage));
|
|
1382
|
-
const [page, setPageState] = useState(1);
|
|
1383
|
-
const sig = signature(list, options.resetOn);
|
|
1384
|
-
const reset = resetKey(options.resetOn);
|
|
1385
|
-
const [seen, setSeen] = useState(() => ({ sig, length: list.length, first: firstKey(list[0]), reset }));
|
|
1386
|
-
const changed = seen.sig !== sig;
|
|
1387
|
-
const startsAgain = changed && !grewAtAnEnd(seen, list, reset);
|
|
1388
|
-
if (changed) {
|
|
1389
|
-
setSeen({ sig, length: list.length, first: firstKey(list[0]), reset });
|
|
1390
|
-
if (startsAgain && page !== 1) setPageState(1);
|
|
1391
|
-
}
|
|
1392
|
-
const totalItems = list.length;
|
|
1393
|
-
const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
|
|
1394
|
-
const current = Math.min(Math.max(1, startsAgain ? 1 : page), totalPages);
|
|
1395
|
-
const grows = style !== "pagination";
|
|
1396
|
-
const start = grows ? 0 : (current - 1) * pageSize;
|
|
1397
|
-
const items = list.slice(start, current * pageSize);
|
|
1398
|
-
const range = items.length === 0 ? [0, 0] : [start + 1, start + items.length];
|
|
1399
|
-
const hasMore = current < totalPages;
|
|
1400
|
-
const setPage = (next) => setPageState(Math.min(Math.max(1, Math.floor(next)), totalPages));
|
|
1401
|
-
const loadMore = () => setPageState((p) => Math.min(p + 1, totalPages));
|
|
1402
|
-
const setPageSize = (size) => {
|
|
1403
|
-
setChosenSize(Math.max(1, Math.floor(size)));
|
|
1404
|
-
setPageState(1);
|
|
1405
|
-
};
|
|
1406
|
-
return {
|
|
1407
|
-
style,
|
|
1408
|
-
items,
|
|
1409
|
-
page: current,
|
|
1410
|
-
pageSize,
|
|
1411
|
-
totalItems,
|
|
1412
|
-
totalPages,
|
|
1413
|
-
range,
|
|
1414
|
-
shown: items.length,
|
|
1415
|
-
hasMore,
|
|
1416
|
-
loadMore,
|
|
1417
|
-
setPage,
|
|
1418
|
-
setPageSize,
|
|
1419
|
-
props: { currentPage: current, totalPages, onPageChange: setPage, pageSize, onPageSizeChange: setPageSize, mode: style, hasMore, onLoadMore: loadMore, pagerToken, ...options.pageSizeOptions ? { pageSizeOptions: options.pageSizeOptions } : {} }
|
|
1420
|
-
};
|
|
1421
|
-
}
|
|
1422
|
-
function pagerPart(list, pager) {
|
|
1423
|
-
if (!pager) return list;
|
|
1424
|
-
return pager.range[0] === 0 ? [] : list.slice(pager.range[0] - 1, pager.range[1]);
|
|
1425
|
-
}
|
|
1426
|
-
function useWholeListCheck(component, length, pager) {
|
|
1427
|
-
const total = pager?.totalItems;
|
|
1428
|
-
useEffect(() => {
|
|
1429
|
-
if (total !== void 0 && length !== total) warnOnce(component, `the pager counts ${total} items but the list holds ${length}: give the list and usePagination the same items, every item of the list, and the list draws the pager's part (Sprint 25.13 A3).`);
|
|
1430
|
-
}, [component, length, total]);
|
|
1431
|
-
}
|
|
1432
806
|
|
|
1433
|
-
export { AddAnotherRowCtx, CHECK_FILL, Dropdown, Field, FilterRowContext, InlineFieldError, Input, Label,
|
|
1434
|
-
//# sourceMappingURL=chunk-
|
|
1435
|
-
//# sourceMappingURL=chunk-
|
|
807
|
+
export { AddAnotherRowCtx, CHECK_FILL, Dropdown, Field, FilterRowContext, InlineFieldError, Input, Label, OutsideField, SWITCH_KNOB_MOTION, SWITCH_KNOB_OFF, SWITCH_KNOB_ON, SWITCH_TRACK_OFF, SWITCH_TRACK_ON, Textarea, Toggle, useField, useFieldControl };
|
|
808
|
+
//# sourceMappingURL=chunk-IZW5XHKA.js.map
|
|
809
|
+
//# sourceMappingURL=chunk-IZW5XHKA.js.map
|