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
|
@@ -0,0 +1,755 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useShellPreferences, PREFERENCE_HOUSE_DEFAULTS } from './chunk-QOMP4D6V.js';
|
|
3
|
+
import { Dropdown } from './chunk-BKYWKMUN.js';
|
|
4
|
+
import { useInputModality } from './chunk-7RKC5C7G.js';
|
|
5
|
+
import { useReportedChange, FormChangeCtx } from './chunk-72SDAX54.js';
|
|
6
|
+
import { RowsCtx } from './chunk-V73LHNRL.js';
|
|
7
|
+
import { ControlSizeContext, triggerType, Button } from './chunk-2Q4HOGEI.js';
|
|
8
|
+
import { Slot, AutoTooltip } from './chunk-XSQD3UGY.js';
|
|
9
|
+
import { warnOnce, Icon } from './chunk-53F7GVBQ.js';
|
|
10
|
+
import { useLatest, cn, FOCUS_RING, ERROR_FOCUS_WITHIN_RING, FIELD_FOCUS_WITHIN_RING, usePosition, usePressMarks, usePopupFocus, focusInitial, GLASS_DROPDOWN, focusAfterSelfRemoval, tabStops, isTabStop } from './chunk-OWQ7M6R7.js';
|
|
11
|
+
import { useContext, useRef, useState, useCallback, useEffect } from 'react';
|
|
12
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
13
|
+
import { createPortal } from 'react-dom';
|
|
14
|
+
|
|
15
|
+
var sizes = {
|
|
16
|
+
sm: { box: "h-[var(--ds-control-height-sm)]", input: "text-xs self-stretch", btn: "w-7 text-xs" },
|
|
17
|
+
md: { box: "h-[var(--ds-control-height-md)]", input: "text-sm self-stretch", btn: "w-9 text-sm" },
|
|
18
|
+
lg: { box: "h-[var(--ds-control-height-lg)]", input: "text-base self-stretch", btn: "w-10 text-base" }
|
|
19
|
+
};
|
|
20
|
+
function NumberInput({
|
|
21
|
+
value,
|
|
22
|
+
onChange: onChangeProp,
|
|
23
|
+
min = -Infinity,
|
|
24
|
+
max = Infinity,
|
|
25
|
+
step = 1,
|
|
26
|
+
disabled = false,
|
|
27
|
+
placeholder = "0",
|
|
28
|
+
prefix,
|
|
29
|
+
suffix,
|
|
30
|
+
size: sizeProp,
|
|
31
|
+
className,
|
|
32
|
+
error = false,
|
|
33
|
+
allowDecimals,
|
|
34
|
+
id,
|
|
35
|
+
"aria-label": ariaLabel
|
|
36
|
+
}) {
|
|
37
|
+
const onChange = useReportedChange(onChangeProp, value);
|
|
38
|
+
useInputModality();
|
|
39
|
+
const contextSize = useContext(ControlSizeContext);
|
|
40
|
+
const size = sizeProp ?? contextSize ?? "md";
|
|
41
|
+
const intervalRef = useRef(void 0);
|
|
42
|
+
const timeoutRef = useRef(void 0);
|
|
43
|
+
const inputRef = useRef(null);
|
|
44
|
+
const [focused, setFocused] = useState(false);
|
|
45
|
+
const [draft, setDraft] = useState(String(value));
|
|
46
|
+
const decimals = allowDecimals ?? step % 1 !== 0;
|
|
47
|
+
const [prevSync, setPrevSync] = useState({ value, focused });
|
|
48
|
+
if (value !== prevSync.value || focused !== prevSync.focused) {
|
|
49
|
+
setPrevSync({ value, focused });
|
|
50
|
+
if (!focused) setDraft(String(value));
|
|
51
|
+
}
|
|
52
|
+
const clamp = useCallback(
|
|
53
|
+
(v) => Math.min(max, Math.max(min, v)),
|
|
54
|
+
[min, max]
|
|
55
|
+
);
|
|
56
|
+
const valueRef = useLatest(value);
|
|
57
|
+
const commitValue = useCallback((v, own = false) => {
|
|
58
|
+
const clamped = clamp(v);
|
|
59
|
+
valueRef.current = clamped;
|
|
60
|
+
setDraft(String(clamped));
|
|
61
|
+
(own ? onChangeProp : onChange)(clamped);
|
|
62
|
+
return clamped;
|
|
63
|
+
}, [onChange, onChangeProp, clamp, valueRef]);
|
|
64
|
+
const increment = useCallback(() => commitValue(valueRef.current + step), [commitValue, step, valueRef]);
|
|
65
|
+
const decrement = useCallback(() => commitValue(valueRef.current - step), [commitValue, step, valueRef]);
|
|
66
|
+
const startRepeat = useCallback(
|
|
67
|
+
(fn) => {
|
|
68
|
+
fn();
|
|
69
|
+
timeoutRef.current = setTimeout(() => {
|
|
70
|
+
intervalRef.current = setInterval(fn, 75);
|
|
71
|
+
}, 400);
|
|
72
|
+
},
|
|
73
|
+
[]
|
|
74
|
+
);
|
|
75
|
+
const stopRepeat = useCallback(() => {
|
|
76
|
+
clearTimeout(timeoutRef.current);
|
|
77
|
+
clearInterval(intervalRef.current);
|
|
78
|
+
}, []);
|
|
79
|
+
const handleInput = useCallback(
|
|
80
|
+
(e) => {
|
|
81
|
+
const raw = e.target.value;
|
|
82
|
+
if (raw === "" || raw === "-") {
|
|
83
|
+
setDraft(raw);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!decimals && raw.includes(".")) return;
|
|
87
|
+
const validPattern = decimals ? /^-?\d*\.?\d*$/ : /^-?\d*$/;
|
|
88
|
+
if (!validPattern.test(raw)) return;
|
|
89
|
+
setDraft(raw);
|
|
90
|
+
const parsed = parseFloat(raw);
|
|
91
|
+
if (!isNaN(parsed) && isFinite(parsed)) {
|
|
92
|
+
onChange(clamp(parsed));
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
[onChange, clamp, decimals]
|
|
96
|
+
);
|
|
97
|
+
const handleBlur = useCallback(() => {
|
|
98
|
+
setFocused(false);
|
|
99
|
+
const parsed = parseFloat(draft);
|
|
100
|
+
if (!isNaN(parsed) && isFinite(parsed)) {
|
|
101
|
+
const committed = commitValue(parsed, draft === String(value));
|
|
102
|
+
setDraft(String(committed));
|
|
103
|
+
} else {
|
|
104
|
+
const fallback = isFinite(min) ? min : 0;
|
|
105
|
+
const committed = commitValue(value !== void 0 ? value : fallback, true);
|
|
106
|
+
setDraft(String(committed));
|
|
107
|
+
}
|
|
108
|
+
}, [draft, commitValue, min, value]);
|
|
109
|
+
const handleFocus = useCallback(() => {
|
|
110
|
+
setFocused(true);
|
|
111
|
+
}, []);
|
|
112
|
+
const handleKeyDown = useCallback(
|
|
113
|
+
(e) => {
|
|
114
|
+
if (e.key === "ArrowUp") {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
increment();
|
|
117
|
+
}
|
|
118
|
+
if (e.key === "ArrowDown") {
|
|
119
|
+
e.preventDefault();
|
|
120
|
+
decrement();
|
|
121
|
+
}
|
|
122
|
+
const plain = !e.shiftKey && !e.ctrlKey && !e.metaKey && !e.altKey;
|
|
123
|
+
if (e.key === "Home" && plain && Number.isFinite(min)) {
|
|
124
|
+
e.preventDefault();
|
|
125
|
+
commitValue(min);
|
|
126
|
+
}
|
|
127
|
+
if (e.key === "End" && plain && Number.isFinite(max)) {
|
|
128
|
+
e.preventDefault();
|
|
129
|
+
commitValue(max);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
[increment, decrement, commitValue, min, max]
|
|
133
|
+
);
|
|
134
|
+
const s = sizes[size];
|
|
135
|
+
const atMin = value <= min;
|
|
136
|
+
const atMax = value >= max;
|
|
137
|
+
const shown = focused ? draft : String(value);
|
|
138
|
+
const holdsNumber = Number.isFinite(parseFloat(shown)) && Number.isFinite(value);
|
|
139
|
+
return /* @__PURE__ */ jsxs(
|
|
140
|
+
"div",
|
|
141
|
+
{
|
|
142
|
+
className: cn(
|
|
143
|
+
"flex items-center w-full",
|
|
144
|
+
s.box,
|
|
145
|
+
"border-[length:var(--ds-border-width)]",
|
|
146
|
+
error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
|
|
147
|
+
"rounded-[var(--ds-radius-md)]",
|
|
148
|
+
"shadow-[var(--ds-input-shadow)]",
|
|
149
|
+
"bg-[var(--ds-input-bg)]",
|
|
150
|
+
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
151
|
+
!error && !disabled && "hover:border-[var(--ds-input-border-hover)]",
|
|
152
|
+
error ? ERROR_FOCUS_WITHIN_RING : FIELD_FOCUS_WITHIN_RING,
|
|
153
|
+
disabled && "opacity-disabled cursor-not-allowed",
|
|
154
|
+
className
|
|
155
|
+
),
|
|
156
|
+
children: [
|
|
157
|
+
/* @__PURE__ */ jsx(
|
|
158
|
+
"button",
|
|
159
|
+
{
|
|
160
|
+
type: "button",
|
|
161
|
+
tabIndex: -1,
|
|
162
|
+
disabled: disabled || atMin,
|
|
163
|
+
onMouseDown: (e) => {
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
inputRef.current?.focus();
|
|
166
|
+
if (!disabled && !atMin) startRepeat(decrement);
|
|
167
|
+
},
|
|
168
|
+
onMouseUp: stopRepeat,
|
|
169
|
+
onMouseLeave: stopRepeat,
|
|
170
|
+
className: cn(
|
|
171
|
+
"inline-flex items-center justify-center shrink-0",
|
|
172
|
+
"border-r-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
173
|
+
"text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]",
|
|
174
|
+
"transition-colors cursor-pointer",
|
|
175
|
+
"rounded-l-[var(--ds-radius-md)]",
|
|
176
|
+
// kept out of the Tab order (the field's arrows step it) and never focused by a press: the outline all the same
|
|
177
|
+
// if anything focuses it (Sprint 30)
|
|
178
|
+
FOCUS_RING,
|
|
179
|
+
s.btn,
|
|
180
|
+
s.input,
|
|
181
|
+
(disabled || atMin) && "opacity-disabled cursor-not-allowed hover:bg-transparent hover:text-[var(--ds-text-muted)]"
|
|
182
|
+
),
|
|
183
|
+
"aria-label": "Decrease",
|
|
184
|
+
children: "\u2212"
|
|
185
|
+
}
|
|
186
|
+
),
|
|
187
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex items-center flex-1 min-w-0 px-ds-2 gap-ds-1", s.input), children: [
|
|
188
|
+
prefix && /* @__PURE__ */ jsx("span", { className: "text-[var(--ds-text-muted)] shrink-0 text-xs", children: prefix }),
|
|
189
|
+
/* @__PURE__ */ jsx(
|
|
190
|
+
"input",
|
|
191
|
+
{
|
|
192
|
+
ref: inputRef,
|
|
193
|
+
id,
|
|
194
|
+
type: "text",
|
|
195
|
+
role: "spinbutton",
|
|
196
|
+
"aria-label": ariaLabel,
|
|
197
|
+
"aria-valuenow": holdsNumber ? value : void 0,
|
|
198
|
+
"aria-valuemin": Number.isFinite(min) ? min : void 0,
|
|
199
|
+
"aria-valuemax": Number.isFinite(max) ? max : void 0,
|
|
200
|
+
inputMode: decimals ? "decimal" : "numeric",
|
|
201
|
+
"aria-invalid": error || void 0,
|
|
202
|
+
value: shown,
|
|
203
|
+
onChange: handleInput,
|
|
204
|
+
onFocus: handleFocus,
|
|
205
|
+
onBlur: handleBlur,
|
|
206
|
+
onKeyDown: handleKeyDown,
|
|
207
|
+
disabled,
|
|
208
|
+
placeholder,
|
|
209
|
+
className: cn(
|
|
210
|
+
"w-full min-w-0 bg-transparent text-center outline-none",
|
|
211
|
+
"text-[var(--ds-text-primary)]",
|
|
212
|
+
"placeholder:text-[var(--ds-text-muted)]",
|
|
213
|
+
"font-[family-name:var(--ds-font-mono)]",
|
|
214
|
+
s.input,
|
|
215
|
+
disabled && "cursor-not-allowed"
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
),
|
|
219
|
+
suffix && /* @__PURE__ */ jsx("span", { className: "text-[var(--ds-text-muted)] shrink-0 text-xs", children: suffix })
|
|
220
|
+
] }),
|
|
221
|
+
/* @__PURE__ */ jsx(
|
|
222
|
+
"button",
|
|
223
|
+
{
|
|
224
|
+
type: "button",
|
|
225
|
+
tabIndex: -1,
|
|
226
|
+
disabled: disabled || atMax,
|
|
227
|
+
onMouseDown: (e) => {
|
|
228
|
+
e.preventDefault();
|
|
229
|
+
inputRef.current?.focus();
|
|
230
|
+
if (!disabled && !atMax) startRepeat(increment);
|
|
231
|
+
},
|
|
232
|
+
onMouseUp: stopRepeat,
|
|
233
|
+
onMouseLeave: stopRepeat,
|
|
234
|
+
className: cn(
|
|
235
|
+
"inline-flex items-center justify-center shrink-0",
|
|
236
|
+
"border-l-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
237
|
+
"text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-muted)]",
|
|
238
|
+
"transition-colors cursor-pointer",
|
|
239
|
+
"rounded-r-[var(--ds-radius-md)]",
|
|
240
|
+
FOCUS_RING,
|
|
241
|
+
s.btn,
|
|
242
|
+
s.input,
|
|
243
|
+
(disabled || atMax) && "opacity-disabled cursor-not-allowed hover:bg-transparent hover:text-[var(--ds-text-muted)]"
|
|
244
|
+
),
|
|
245
|
+
"aria-label": "Increase",
|
|
246
|
+
children: "+"
|
|
247
|
+
}
|
|
248
|
+
)
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
function Popover({ open, onOpenChange, trigger, children, side = "bottom", align = "center", width, className, container }) {
|
|
254
|
+
const triggerRef = useRef(null);
|
|
255
|
+
const floatingRef = useRef(null);
|
|
256
|
+
const [triggerWidth, setTriggerWidth] = useState(void 0);
|
|
257
|
+
const setTriggerNode = useCallback((node) => {
|
|
258
|
+
triggerRef.current = node;
|
|
259
|
+
setTriggerWidth(node?.offsetWidth);
|
|
260
|
+
}, []);
|
|
261
|
+
const pos = usePosition(triggerRef, floatingRef, open, { side, align, offset: 8, flip: true });
|
|
262
|
+
const onOpenChangeRef = useLatest(onOpenChange);
|
|
263
|
+
const { props: pressMarks, marked } = usePressMarks();
|
|
264
|
+
const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: triggerRef, panel: floatingRef, close: () => onOpenChangeRef.current(false), tabExits: "edges", marked });
|
|
265
|
+
useEffect(() => {
|
|
266
|
+
if (!open) return;
|
|
267
|
+
const onKeyDown = (e) => {
|
|
268
|
+
if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
|
|
269
|
+
const active = document.activeElement;
|
|
270
|
+
if (active && active !== document.body && !triggerRef.current?.contains(active) && !floatingRef.current?.contains(active)) return;
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
returnFocus();
|
|
273
|
+
onOpenChangeRef.current(false);
|
|
274
|
+
};
|
|
275
|
+
const onPress = (e) => {
|
|
276
|
+
const target = e.target;
|
|
277
|
+
if (marked(e) || triggerRef.current?.contains(target) || floatingRef.current?.contains(target)) return;
|
|
278
|
+
onOpenChangeRef.current(false);
|
|
279
|
+
};
|
|
280
|
+
document.addEventListener("keydown", onKeyDown);
|
|
281
|
+
document.addEventListener("mousedown", onPress);
|
|
282
|
+
return () => {
|
|
283
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
284
|
+
document.removeEventListener("mousedown", onPress);
|
|
285
|
+
};
|
|
286
|
+
}, [open, onOpenChangeRef, isTopLayer, returnFocus, marked]);
|
|
287
|
+
useEffect(() => {
|
|
288
|
+
if (open) focusInitial(floatingRef.current);
|
|
289
|
+
}, [open]);
|
|
290
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
291
|
+
/* @__PURE__ */ jsx(
|
|
292
|
+
Slot,
|
|
293
|
+
{
|
|
294
|
+
...triggerType(trigger),
|
|
295
|
+
ref: setTriggerNode,
|
|
296
|
+
onClick: () => {
|
|
297
|
+
setTriggerWidth(triggerRef.current?.offsetWidth);
|
|
298
|
+
onOpenChange(!open);
|
|
299
|
+
},
|
|
300
|
+
children: trigger
|
|
301
|
+
}
|
|
302
|
+
),
|
|
303
|
+
open && createPortal(
|
|
304
|
+
/* @__PURE__ */ jsx(
|
|
305
|
+
"div",
|
|
306
|
+
{
|
|
307
|
+
ref: floatingRef,
|
|
308
|
+
"data-gs": "popover",
|
|
309
|
+
...pressMarks,
|
|
310
|
+
onClick: (e) => e.stopPropagation(),
|
|
311
|
+
className: cn(
|
|
312
|
+
"fixed z-[var(--ds-z-popover)]",
|
|
313
|
+
"bg-[var(--ds-bg-surface-elevated)]",
|
|
314
|
+
GLASS_DROPDOWN,
|
|
315
|
+
"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
316
|
+
"rounded-[var(--ds-radius-md)]",
|
|
317
|
+
className
|
|
318
|
+
),
|
|
319
|
+
style: {
|
|
320
|
+
top: pos.top,
|
|
321
|
+
left: pos.left,
|
|
322
|
+
width: width === "trigger" ? triggerWidth : width === "auto" ? void 0 : width
|
|
323
|
+
},
|
|
324
|
+
children: /* @__PURE__ */ jsx(RowsCtx, { value: false, children })
|
|
325
|
+
}
|
|
326
|
+
),
|
|
327
|
+
container ?? document.body
|
|
328
|
+
)
|
|
329
|
+
] });
|
|
330
|
+
}
|
|
331
|
+
var controlsDrawn = /* @__PURE__ */ new WeakMap();
|
|
332
|
+
function Pagination(props) {
|
|
333
|
+
const { pagerToken } = props;
|
|
334
|
+
useEffect(() => {
|
|
335
|
+
if (!pagerToken) return;
|
|
336
|
+
const count = (controlsDrawn.get(pagerToken) ?? 0) + 1;
|
|
337
|
+
controlsDrawn.set(pagerToken, count);
|
|
338
|
+
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} />.");
|
|
339
|
+
return () => {
|
|
340
|
+
controlsDrawn.set(pagerToken, (controlsDrawn.get(pagerToken) ?? 1) - 1);
|
|
341
|
+
};
|
|
342
|
+
}, [pagerToken]);
|
|
343
|
+
if ((props.mode === "load-more" || props.mode === "infinite-scroll") && props.onLoadMore) return /* @__PURE__ */ jsx(LoadMore, { ...props, onLoadMore: props.onLoadMore, scroll: props.mode === "infinite-scroll" });
|
|
344
|
+
return /* @__PURE__ */ jsx(PageNumbers, { ...props });
|
|
345
|
+
}
|
|
346
|
+
function scrollerOf(el) {
|
|
347
|
+
for (let n = el.parentElement; n && n !== document.body && n !== document.documentElement; n = n.parentElement) {
|
|
348
|
+
const { overflowY } = getComputedStyle(n);
|
|
349
|
+
if ((overflowY === "auto" || overflowY === "scroll") && n.scrollHeight > n.clientHeight) return n;
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
function contentTop(el) {
|
|
354
|
+
let top = el.getBoundingClientRect().top + window.scrollY;
|
|
355
|
+
for (let n = el.parentElement; n && n !== document.documentElement; n = n.parentElement) top += n.scrollTop;
|
|
356
|
+
return top;
|
|
357
|
+
}
|
|
358
|
+
function LoadMore({ hasMore, onLoadMore, loadMoreLabel, currentPage, scroll, className }) {
|
|
359
|
+
const marker = useRef(null);
|
|
360
|
+
const button = useRef(null);
|
|
361
|
+
const pager = useRef(null);
|
|
362
|
+
const watching = useRef(null);
|
|
363
|
+
const endWatch = () => {
|
|
364
|
+
watching.current?.();
|
|
365
|
+
watching.current = null;
|
|
366
|
+
};
|
|
367
|
+
const [held, setHeld] = useState(false);
|
|
368
|
+
const press = () => {
|
|
369
|
+
const b = button.current, root = pager.current;
|
|
370
|
+
if (b && root && document.activeElement === b) {
|
|
371
|
+
const byKey = scroll ? held : byKeyboard(b);
|
|
372
|
+
const place = placeOf(root);
|
|
373
|
+
const had = before(place);
|
|
374
|
+
const hadRows = had.flatMap(rowsOf);
|
|
375
|
+
const hadStops = had.flatMap(stopsIn);
|
|
376
|
+
endWatch();
|
|
377
|
+
const handOn = focusAfterSelfRemoval(b, {
|
|
378
|
+
item: root,
|
|
379
|
+
group: null,
|
|
380
|
+
to: () => {
|
|
381
|
+
const content = before(place);
|
|
382
|
+
const fresh = appended(content.flatMap(rowsOf), hadRows);
|
|
383
|
+
if (fresh) return tabStops(fresh)[0] ?? fresh;
|
|
384
|
+
const stops = content.flatMap(stopsIn);
|
|
385
|
+
return appended(stops, hadStops) ?? stops.at(-1);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
const backInView = byKey ? whenRowsCome(place, hadRows, hadStops, b, () => {
|
|
389
|
+
if (document.activeElement === b) b.scrollIntoView?.({ block: "nearest" });
|
|
390
|
+
}) : () => {
|
|
391
|
+
};
|
|
392
|
+
watching.current = () => {
|
|
393
|
+
handOn();
|
|
394
|
+
backInView();
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
onLoadMore();
|
|
398
|
+
};
|
|
399
|
+
const load = useLatest(onLoadMore);
|
|
400
|
+
const loadedAt = useRef(null);
|
|
401
|
+
if (held && !hasMore) setHeld(false);
|
|
402
|
+
useEffect(() => {
|
|
403
|
+
const el = marker.current;
|
|
404
|
+
if (!scroll || !hasMore || !el || held || typeof IntersectionObserver === "undefined") return;
|
|
405
|
+
const observer = new IntersectionObserver((entries) => {
|
|
406
|
+
if (!entries.some((e) => e.isIntersecting)) return;
|
|
407
|
+
const at = contentTop(el);
|
|
408
|
+
if (loadedAt.current !== null && Math.abs(at - loadedAt.current) < 1) return;
|
|
409
|
+
loadedAt.current = at;
|
|
410
|
+
watching.current?.();
|
|
411
|
+
watching.current = null;
|
|
412
|
+
load.current();
|
|
413
|
+
}, { root: scrollerOf(el), rootMargin: "200px 0px" });
|
|
414
|
+
observer.observe(el);
|
|
415
|
+
const rearm = () => {
|
|
416
|
+
loadedAt.current = null;
|
|
417
|
+
};
|
|
418
|
+
window.addEventListener("scroll", rearm, { capture: true, passive: true });
|
|
419
|
+
return () => {
|
|
420
|
+
observer.disconnect();
|
|
421
|
+
window.removeEventListener("scroll", rearm, { capture: true });
|
|
422
|
+
};
|
|
423
|
+
}, [scroll, hasMore, currentPage, load, held]);
|
|
424
|
+
if (!hasMore) return null;
|
|
425
|
+
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: [
|
|
426
|
+
scroll && /* @__PURE__ */ jsx("div", { ref: marker, "data-load-more-trigger": "", "aria-hidden": "true", className: "h-px w-full" }),
|
|
427
|
+
/* @__PURE__ */ jsx(
|
|
428
|
+
Button,
|
|
429
|
+
{
|
|
430
|
+
ref: button,
|
|
431
|
+
type: "button",
|
|
432
|
+
variant: "secondary",
|
|
433
|
+
onClick: press,
|
|
434
|
+
onFocus: (e) => setHeld(scroll && byKeyboard(e.currentTarget)),
|
|
435
|
+
onBlur: () => setHeld(false),
|
|
436
|
+
children: loadMoreLabel ?? "Load more"
|
|
437
|
+
}
|
|
438
|
+
)
|
|
439
|
+
] });
|
|
440
|
+
}
|
|
441
|
+
function placeOf(el) {
|
|
442
|
+
for (let n = el; n && n !== document.body; n = n.parentElement) {
|
|
443
|
+
if (n.previousElementSibling && n.parentElement) return { at: n, parent: n.parentElement, next: n.nextElementSibling };
|
|
444
|
+
}
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
function before(place) {
|
|
448
|
+
if (!place) return [];
|
|
449
|
+
const { at, parent, next } = place;
|
|
450
|
+
const kids = Array.from(parent.children);
|
|
451
|
+
const end = at.parentElement === parent ? kids.indexOf(at) : next?.parentElement === parent ? kids.indexOf(next) : kids.length;
|
|
452
|
+
return kids.slice(0, end);
|
|
453
|
+
}
|
|
454
|
+
var withSelf = (el, selector) => [...el.matches(selector) ? [el] : [], ...el.querySelectorAll(selector)];
|
|
455
|
+
function rowsOf(list) {
|
|
456
|
+
const marked = withSelf(list, "[data-row-key], [data-row-index]");
|
|
457
|
+
return marked.length ? marked : withSelf(list, 'tbody > tr, li, [role="row"], [data-card]');
|
|
458
|
+
}
|
|
459
|
+
var stopsIn = (el) => [...isTabStop(el) ? [el] : [], ...tabStops(el)];
|
|
460
|
+
function appended(now, had) {
|
|
461
|
+
const seen = new Set(had);
|
|
462
|
+
const fresh = now.filter((el) => !seen.has(el));
|
|
463
|
+
const last = had.at(-1);
|
|
464
|
+
return (last?.isConnected ? fresh.find((el) => last.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING) : void 0) ?? fresh[0];
|
|
465
|
+
}
|
|
466
|
+
function whenRowsCome(place, hadRows, hadStops, button, then) {
|
|
467
|
+
if (!place || typeof MutationObserver === "undefined") return () => {
|
|
468
|
+
};
|
|
469
|
+
const rows = new Set(hadRows), stops = new Set(hadStops);
|
|
470
|
+
const observer = new MutationObserver((records) => {
|
|
471
|
+
if (!button.isConnected) return stop();
|
|
472
|
+
const content = before(place);
|
|
473
|
+
const fresh = hadRows.length ? content.flatMap(rowsOf).some((r) => !rows.has(r)) || content.flatMap(stopsIn).some((s) => !stops.has(s)) : records.some((r) => Array.from(r.addedNodes).some((n) => n instanceof Element && content.some((c) => c === n || c.contains(n))));
|
|
474
|
+
if (!fresh) return;
|
|
475
|
+
stop();
|
|
476
|
+
then();
|
|
477
|
+
});
|
|
478
|
+
const timer = setTimeout(stop, 1e4);
|
|
479
|
+
function stop() {
|
|
480
|
+
observer.disconnect();
|
|
481
|
+
clearTimeout(timer);
|
|
482
|
+
}
|
|
483
|
+
observer.observe(place.parent, { childList: true, subtree: true });
|
|
484
|
+
return stop;
|
|
485
|
+
}
|
|
486
|
+
function byKeyboard(el) {
|
|
487
|
+
try {
|
|
488
|
+
return el.matches(":focus-visible");
|
|
489
|
+
} catch {
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
function PageNumbers({ currentPage, totalPages, onPageChange, pageSize: pageSizeProp, pageSizeOptions, onPageSizeChange, className }) {
|
|
494
|
+
const pages = buildPageRange(currentPage, totalPages);
|
|
495
|
+
const preferences = useShellPreferences();
|
|
496
|
+
const pageSize = pageSizeProp ?? preferences.itemsPerPage;
|
|
497
|
+
const sizeChoice = !!(pageSizeOptions && pageSize != null && onPageSizeChange);
|
|
498
|
+
if (totalPages <= 1 && !sizeChoice) return null;
|
|
499
|
+
const sizes2 = pageSizeOptions && pageSize != null ? withSize(pageSizeOptions, pageSize) : pageSizeOptions;
|
|
500
|
+
return /* @__PURE__ */ jsxs("nav", { "aria-label": "Pagination", "data-ds-pager": "", className: cn("flex flex-wrap items-center gap-ds-3", className), children: [
|
|
501
|
+
sizeChoice && pageSizeOptions && pageSize != null && onPageSizeChange && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
|
|
502
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-[var(--ds-text-muted)] whitespace-nowrap", children: "Rows per page" }),
|
|
503
|
+
/* @__PURE__ */ jsx(FormChangeCtx, { value: null, children: /* @__PURE__ */ jsx(
|
|
504
|
+
Dropdown,
|
|
505
|
+
{
|
|
506
|
+
"aria-label": "Rows per page",
|
|
507
|
+
options: (sizes2 ?? pageSizeOptions).map((n) => ({ id: String(n), label: String(n) })),
|
|
508
|
+
value: String(pageSize),
|
|
509
|
+
onChange: (v) => onPageSizeChange(Number(v)),
|
|
510
|
+
size: "sm",
|
|
511
|
+
className: "w-[4.5rem]"
|
|
512
|
+
}
|
|
513
|
+
) })
|
|
514
|
+
] }),
|
|
515
|
+
totalPages > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-1", children: [
|
|
516
|
+
/* @__PURE__ */ jsx(AutoTooltip, { content: "Previous page", children: /* @__PURE__ */ jsx(
|
|
517
|
+
"button",
|
|
518
|
+
{
|
|
519
|
+
type: "button",
|
|
520
|
+
onClick: (e) => {
|
|
521
|
+
focusAfterSelfRemoval(e.currentTarget);
|
|
522
|
+
onPageChange(currentPage - 1);
|
|
523
|
+
},
|
|
524
|
+
disabled: currentPage <= 1,
|
|
525
|
+
"aria-label": "Previous page",
|
|
526
|
+
className: cn(
|
|
527
|
+
"inline-flex items-center justify-center h-[var(--ds-control-height-sm)] w-[var(--ds-control-height-sm)] text-sm cursor-pointer",
|
|
528
|
+
"rounded-[var(--ds-radius-md)]",
|
|
529
|
+
"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
530
|
+
"bg-[var(--ds-bg-surface)] text-[var(--ds-text-secondary)]",
|
|
531
|
+
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
532
|
+
"hover:bg-[var(--ds-bg-muted)] hover:text-[var(--ds-text-primary)]",
|
|
533
|
+
"disabled:opacity-disabled disabled:cursor-not-allowed",
|
|
534
|
+
FOCUS_RING
|
|
535
|
+
),
|
|
536
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16, className: "rotate-180" })
|
|
537
|
+
}
|
|
538
|
+
) }),
|
|
539
|
+
pages.map(
|
|
540
|
+
(p, i) => p === "..." ? /* @__PURE__ */ jsx(EllipsisJumper, { totalPages, onPageChange }, `ellipsis-${i}`) : /* @__PURE__ */ jsx(
|
|
541
|
+
"button",
|
|
542
|
+
{
|
|
543
|
+
type: "button",
|
|
544
|
+
onClick: () => onPageChange(p),
|
|
545
|
+
"aria-current": p === currentPage ? "page" : void 0,
|
|
546
|
+
className: cn(
|
|
547
|
+
"inline-flex items-center justify-center h-[var(--ds-control-height-sm)] w-[var(--ds-control-height-sm)] text-sm font-medium cursor-pointer",
|
|
548
|
+
"rounded-[var(--ds-radius-md)]",
|
|
549
|
+
"border-[length:var(--ds-border-width)]",
|
|
550
|
+
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
551
|
+
FOCUS_RING,
|
|
552
|
+
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)]"
|
|
553
|
+
),
|
|
554
|
+
children: p
|
|
555
|
+
},
|
|
556
|
+
p
|
|
557
|
+
)
|
|
558
|
+
),
|
|
559
|
+
/* @__PURE__ */ jsx(AutoTooltip, { content: "Next page", children: /* @__PURE__ */ jsx(
|
|
560
|
+
"button",
|
|
561
|
+
{
|
|
562
|
+
type: "button",
|
|
563
|
+
onClick: (e) => {
|
|
564
|
+
focusAfterSelfRemoval(e.currentTarget);
|
|
565
|
+
onPageChange(currentPage + 1);
|
|
566
|
+
},
|
|
567
|
+
disabled: currentPage >= totalPages,
|
|
568
|
+
"aria-label": "Next page",
|
|
569
|
+
className: cn(
|
|
570
|
+
"inline-flex items-center justify-center h-[var(--ds-control-height-sm)] w-[var(--ds-control-height-sm)] text-sm cursor-pointer",
|
|
571
|
+
"rounded-[var(--ds-radius-md)]",
|
|
572
|
+
"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
|
|
573
|
+
"bg-[var(--ds-bg-surface)] text-[var(--ds-text-secondary)]",
|
|
574
|
+
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
575
|
+
"hover:bg-[var(--ds-bg-muted)] hover:text-[var(--ds-text-primary)]",
|
|
576
|
+
"disabled:opacity-disabled disabled:cursor-not-allowed",
|
|
577
|
+
FOCUS_RING
|
|
578
|
+
),
|
|
579
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16 })
|
|
580
|
+
}
|
|
581
|
+
) })
|
|
582
|
+
] })
|
|
583
|
+
] });
|
|
584
|
+
}
|
|
585
|
+
function withSize(options, size) {
|
|
586
|
+
if (options.includes(size)) return options;
|
|
587
|
+
const descending = options.length > 1 && options.every((n, i) => i === 0 || options[i - 1] >= n);
|
|
588
|
+
const at = options.findIndex((n) => descending ? n < size : n > size);
|
|
589
|
+
return at < 0 ? [...options, size] : [...options.slice(0, at), size, ...options.slice(at)];
|
|
590
|
+
}
|
|
591
|
+
function buildPageRange(current, total) {
|
|
592
|
+
if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);
|
|
593
|
+
const pages = [1];
|
|
594
|
+
if (current > 3) pages.push("...");
|
|
595
|
+
const start = Math.max(2, current - 1);
|
|
596
|
+
const end = Math.min(total - 1, current + 1);
|
|
597
|
+
for (let i = start; i <= end; i++) pages.push(i);
|
|
598
|
+
if (current < total - 2) pages.push("...");
|
|
599
|
+
pages.push(total);
|
|
600
|
+
return pages;
|
|
601
|
+
}
|
|
602
|
+
function EllipsisJumper({ totalPages, onPageChange }) {
|
|
603
|
+
const [open, setOpen] = useState(false);
|
|
604
|
+
const [jumpPage, setJumpPage] = useState(1);
|
|
605
|
+
const handleOpenChange = (next) => {
|
|
606
|
+
if (next) setJumpPage(1);
|
|
607
|
+
setOpen(next);
|
|
608
|
+
};
|
|
609
|
+
const handleGo = () => {
|
|
610
|
+
const clamped = Math.min(totalPages, Math.max(1, jumpPage));
|
|
611
|
+
onPageChange(clamped);
|
|
612
|
+
setOpen(false);
|
|
613
|
+
};
|
|
614
|
+
return /* @__PURE__ */ jsx(
|
|
615
|
+
Popover,
|
|
616
|
+
{
|
|
617
|
+
open,
|
|
618
|
+
onOpenChange: handleOpenChange,
|
|
619
|
+
trigger: /* @__PURE__ */ jsx(
|
|
620
|
+
"button",
|
|
621
|
+
{
|
|
622
|
+
type: "button",
|
|
623
|
+
"aria-label": "Go to page",
|
|
624
|
+
onClick: () => handleOpenChange(true),
|
|
625
|
+
className: cn(
|
|
626
|
+
"inline-flex items-center justify-center h-[var(--ds-control-height-sm)] w-[var(--ds-control-height-sm)] text-sm cursor-pointer",
|
|
627
|
+
"rounded-[var(--ds-radius-md)]",
|
|
628
|
+
"text-[var(--ds-text-muted)]",
|
|
629
|
+
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
630
|
+
"hover:bg-[var(--ds-bg-muted)] hover:text-[var(--ds-text-primary)]",
|
|
631
|
+
FOCUS_RING
|
|
632
|
+
),
|
|
633
|
+
children: "\u2026"
|
|
634
|
+
}
|
|
635
|
+
),
|
|
636
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-ds-2 p-ds-3", children: [
|
|
637
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-[var(--ds-text-secondary)]", children: "Go to page" }),
|
|
638
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
|
|
639
|
+
/* @__PURE__ */ jsx(
|
|
640
|
+
NumberInput,
|
|
641
|
+
{
|
|
642
|
+
value: jumpPage,
|
|
643
|
+
onChange: setJumpPage,
|
|
644
|
+
min: 1,
|
|
645
|
+
max: totalPages,
|
|
646
|
+
step: 1,
|
|
647
|
+
size: "sm",
|
|
648
|
+
className: "w-[5rem]"
|
|
649
|
+
}
|
|
650
|
+
),
|
|
651
|
+
/* @__PURE__ */ jsx(
|
|
652
|
+
"button",
|
|
653
|
+
{
|
|
654
|
+
type: "button",
|
|
655
|
+
onClick: handleGo,
|
|
656
|
+
className: cn(
|
|
657
|
+
"px-ds-2.5 h-[var(--ds-control-height-sm)] text-xs font-medium cursor-pointer",
|
|
658
|
+
"rounded-[var(--ds-radius-md)]",
|
|
659
|
+
"bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]",
|
|
660
|
+
"transition-colors duration-[var(--ds-transition-speed)]",
|
|
661
|
+
"hover:opacity-90",
|
|
662
|
+
FOCUS_RING
|
|
663
|
+
),
|
|
664
|
+
children: "Go"
|
|
665
|
+
}
|
|
666
|
+
)
|
|
667
|
+
] })
|
|
668
|
+
] })
|
|
669
|
+
}
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
function firstKey(item) {
|
|
673
|
+
if (item !== null && typeof item === "object") return "id" in item ? item.id : void 0;
|
|
674
|
+
return item;
|
|
675
|
+
}
|
|
676
|
+
function resetKey(resetOn) {
|
|
677
|
+
try {
|
|
678
|
+
return JSON.stringify(resetOn ?? null);
|
|
679
|
+
} catch {
|
|
680
|
+
return String(resetOn);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
function signature(list, resetOn) {
|
|
684
|
+
let first = "";
|
|
685
|
+
try {
|
|
686
|
+
first = JSON.stringify(firstKey(list[0]) ?? null);
|
|
687
|
+
} catch {
|
|
688
|
+
first = "";
|
|
689
|
+
}
|
|
690
|
+
return `${list.length}|${first}|${resetKey(resetOn)}`;
|
|
691
|
+
}
|
|
692
|
+
function grewAtAnEnd(seen, list, reset) {
|
|
693
|
+
if (reset !== seen.reset || list.length <= seen.length || seen.first === void 0 || seen.length === 0) return false;
|
|
694
|
+
return firstKey(list[0]) === seen.first || firstKey(list[list.length - seen.length]) === seen.first;
|
|
695
|
+
}
|
|
696
|
+
function usePagination(list, options = {}) {
|
|
697
|
+
const shell = useShellPreferences();
|
|
698
|
+
const style = options.style ?? shell.paginationStyle ?? PREFERENCE_HOUSE_DEFAULTS.paginationStyle;
|
|
699
|
+
const [chosenSize, setChosenSize] = useState(null);
|
|
700
|
+
const [pagerToken] = useState(() => ({}));
|
|
701
|
+
const pageSize = Math.max(1, Math.floor(chosenSize ?? options.pageSize ?? shell.itemsPerPage ?? PREFERENCE_HOUSE_DEFAULTS.itemsPerPage));
|
|
702
|
+
const [page, setPageState] = useState(1);
|
|
703
|
+
const sig = signature(list, options.resetOn);
|
|
704
|
+
const reset = resetKey(options.resetOn);
|
|
705
|
+
const [seen, setSeen] = useState(() => ({ sig, length: list.length, first: firstKey(list[0]), reset }));
|
|
706
|
+
const changed = seen.sig !== sig;
|
|
707
|
+
const startsAgain = changed && !grewAtAnEnd(seen, list, reset);
|
|
708
|
+
if (changed) {
|
|
709
|
+
setSeen({ sig, length: list.length, first: firstKey(list[0]), reset });
|
|
710
|
+
if (startsAgain && page !== 1) setPageState(1);
|
|
711
|
+
}
|
|
712
|
+
const totalItems = list.length;
|
|
713
|
+
const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
|
|
714
|
+
const current = Math.min(Math.max(1, startsAgain ? 1 : page), totalPages);
|
|
715
|
+
const grows = style !== "pagination";
|
|
716
|
+
const start = grows ? 0 : (current - 1) * pageSize;
|
|
717
|
+
const items = list.slice(start, current * pageSize);
|
|
718
|
+
const range = items.length === 0 ? [0, 0] : [start + 1, start + items.length];
|
|
719
|
+
const hasMore = current < totalPages;
|
|
720
|
+
const setPage = (next) => setPageState(Math.min(Math.max(1, Math.floor(next)), totalPages));
|
|
721
|
+
const loadMore = () => setPageState((p) => Math.min(p + 1, totalPages));
|
|
722
|
+
const setPageSize = (size) => {
|
|
723
|
+
setChosenSize(Math.max(1, Math.floor(size)));
|
|
724
|
+
setPageState(1);
|
|
725
|
+
};
|
|
726
|
+
return {
|
|
727
|
+
style,
|
|
728
|
+
items,
|
|
729
|
+
page: current,
|
|
730
|
+
pageSize,
|
|
731
|
+
totalItems,
|
|
732
|
+
totalPages,
|
|
733
|
+
range,
|
|
734
|
+
shown: items.length,
|
|
735
|
+
hasMore,
|
|
736
|
+
loadMore,
|
|
737
|
+
setPage,
|
|
738
|
+
setPageSize,
|
|
739
|
+
props: { currentPage: current, totalPages, onPageChange: setPage, pageSize, onPageSizeChange: setPageSize, mode: style, hasMore, onLoadMore: loadMore, pagerToken, ...options.pageSizeOptions ? { pageSizeOptions: options.pageSizeOptions } : {} }
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
function pagerPart(list, pager) {
|
|
743
|
+
if (!pager) return list;
|
|
744
|
+
return pager.range[0] === 0 ? [] : list.slice(pager.range[0] - 1, pager.range[1]);
|
|
745
|
+
}
|
|
746
|
+
function useWholeListCheck(component, length, pager) {
|
|
747
|
+
const total = pager?.totalItems;
|
|
748
|
+
useEffect(() => {
|
|
749
|
+
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).`);
|
|
750
|
+
}, [component, length, total]);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
export { NumberInput, Pagination, Popover, pagerPart, usePagination, useWholeListCheck };
|
|
754
|
+
//# sourceMappingURL=chunk-V2ARGHJN.js.map
|
|
755
|
+
//# sourceMappingURL=chunk-V2ARGHJN.js.map
|