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