@tensor_1/react 2.46.0 → 2.48.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/README.md +509 -1
- package/dist/{SchedulingCalendar-B6TakO2h.d.ts → SchedulingCalendar-WWyFL3Eo.d.ts} +51 -2
- package/dist/announce.d.ts +10 -0
- package/dist/announce.js +30 -0
- package/dist/chart-analytic.cjs +33 -25
- package/dist/chart-preset.cjs +23 -15
- package/dist/chart.cjs +23 -15
- package/dist/combobox-model.d.ts +10 -1
- package/dist/combobox-model.js +15 -0
- package/dist/command-palette-model.d.ts +44 -0
- package/dist/command-palette-model.js +36 -0
- package/dist/components/AccountMenu.d.ts +44 -0
- package/dist/components/AccountMenu.js +43 -0
- package/dist/components/AppShell.d.ts +45 -2
- package/dist/components/AppShell.js +116 -5
- package/dist/components/AuditLog.d.ts +128 -0
- package/dist/components/AuditLog.js +196 -0
- package/dist/components/BrandProfiles.js +4 -3
- package/dist/components/Breadcrumbs.d.ts +9 -2
- package/dist/components/Breadcrumbs.js +3 -2
- package/dist/components/BusyBar.d.ts +13 -0
- package/dist/components/BusyBar.js +10 -0
- package/dist/components/BusyRegion.d.ts +18 -0
- package/dist/components/BusyRegion.js +12 -0
- package/dist/components/Button.d.ts +33 -5
- package/dist/components/Button.js +12 -6
- package/dist/components/ButtonFeedback.d.ts +30 -0
- package/dist/components/ButtonFeedback.js +78 -0
- package/dist/components/Card.d.ts +18 -1
- package/dist/components/Card.js +5 -1
- package/dist/components/ChangeHighlight.d.ts +30 -0
- package/dist/components/ChangeHighlight.js +21 -0
- package/dist/components/ChartCore.js +2 -1
- package/dist/components/Checkbox.d.ts +27 -1
- package/dist/components/Checkbox.js +31 -2
- package/dist/components/CodeDisplay.d.ts +65 -0
- package/dist/components/CodeDisplay.js +124 -0
- package/dist/components/Combobox.d.ts +27 -6
- package/dist/components/Combobox.js +195 -58
- package/dist/components/CommandPalette.d.ts +36 -1
- package/dist/components/CommandPalette.js +254 -2
- package/dist/components/ContactCard.d.ts +66 -0
- package/dist/components/ContactCard.js +74 -0
- package/dist/components/Count.d.ts +24 -0
- package/dist/components/Count.js +32 -0
- package/dist/components/DataWorkspace.d.ts +167 -0
- package/dist/components/DataWorkspace.js +383 -0
- package/dist/components/DateFieldParts.d.ts +2 -1
- package/dist/components/DateFieldParts.js +7 -0
- package/dist/components/DatePicker.d.ts +9 -2
- package/dist/components/DatePicker.js +67 -28
- package/dist/components/DateRangePicker.d.ts +12 -2
- package/dist/components/DateRangePicker.js +13 -12
- package/dist/components/DelayedRender.d.ts +15 -0
- package/dist/components/DelayedRender.js +10 -0
- package/dist/components/DetailPage.d.ts +7 -1
- package/dist/components/DetailPage.js +2 -1
- package/dist/components/Dialogs.d.ts +71 -10
- package/dist/components/Dialogs.js +130 -30
- package/dist/components/DiffViewer.d.ts +23 -3
- package/dist/components/DiffViewer.js +26 -12
- package/dist/components/DiscardPrompt.d.ts +1 -0
- package/dist/components/DiscardPrompt.js +16 -0
- package/dist/components/DisplayPreferences.d.ts +83 -0
- package/dist/components/DisplayPreferences.js +185 -0
- package/dist/components/Drawer.d.ts +1 -0
- package/dist/components/EditorialHome.d.ts +23 -3
- package/dist/components/EditorialHome.js +16 -11
- package/dist/components/EntityPicker.d.ts +20 -3
- package/dist/components/EntityPicker.js +32 -13
- package/dist/components/FilterEditor.d.ts +18 -4
- package/dist/components/FilterEditor.js +141 -19
- package/dist/components/InfoTip.js +11 -12
- package/dist/components/InlineEdit.d.ts +87 -20
- package/dist/components/InlineEdit.js +197 -54
- package/dist/components/Input.d.ts +12 -2
- package/dist/components/Input.js +5 -3
- package/dist/components/Kbd.d.ts +6 -1
- package/dist/components/Kbd.js +6 -6
- package/dist/components/Lightbox.d.ts +41 -5
- package/dist/components/Lightbox.js +95 -9
- package/dist/components/MatchReview.d.ts +40 -1
- package/dist/components/MatchReview.js +130 -4
- package/dist/components/MetricInput.js +3 -2
- package/dist/components/Modal.d.ts +3 -1
- package/dist/components/Modal.js +13 -4
- package/dist/components/NavFavourites.d.ts +5 -0
- package/dist/components/NavFavourites.js +7 -7
- package/dist/components/OtpInput.d.ts +33 -0
- package/dist/components/OtpInput.js +123 -0
- package/dist/components/PageHeader.d.ts +7 -1
- package/dist/components/PageHeader.js +2 -2
- package/dist/components/Pagination.js +6 -5
- package/dist/components/PasswordInput.d.ts +34 -0
- package/dist/components/PasswordInput.js +101 -0
- package/dist/components/Peek.js +3 -3
- package/dist/components/Popover.js +2 -0
- package/dist/components/PrintLayout.d.ts +63 -0
- package/dist/components/PrintLayout.js +114 -0
- package/dist/components/QrCode.d.ts +47 -0
- package/dist/components/QrCode.js +55 -0
- package/dist/components/QrScanner.d.ts +95 -0
- package/dist/components/QrScanner.js +266 -0
- package/dist/components/RelationshipGraph.d.ts +32 -1
- package/dist/components/RelationshipGraph.js +18 -5
- package/dist/components/RelationshipGraphLayered.d.ts +1 -1
- package/dist/components/RelationshipGraphLayered.js +190 -115
- package/dist/components/RelationshipGraphList.js +33 -6
- package/dist/components/RuleBuilder.js +39 -7
- package/dist/components/SaveStatus.d.ts +7 -1
- package/dist/components/SaveStatus.js +13 -4
- package/dist/components/SchedulingAgenda.d.ts +3 -1
- package/dist/components/SchedulingCalendar.d.ts +3 -1
- package/dist/components/SchedulingCalendar.js +43 -4
- package/dist/components/SchedulingResourceDay.d.ts +57 -0
- package/dist/components/SchedulingResourceDay.js +287 -0
- package/dist/components/SchedulingTimeGrid.d.ts +3 -1
- package/dist/components/SchedulingTimeline.d.ts +3 -1
- package/dist/components/SearchBar.d.ts +1 -0
- package/dist/components/SearchSuggest.d.ts +23 -3
- package/dist/components/SearchSuggest.js +48 -17
- package/dist/components/Segmented.d.ts +39 -4
- package/dist/components/Segmented.js +31 -3
- package/dist/components/Select.d.ts +3 -2
- package/dist/components/Select.js +1 -1
- package/dist/components/ShortcutHint.d.ts +11 -10
- package/dist/components/ShortcutHint.js +44 -38
- package/dist/components/Sidebar.d.ts +36 -4
- package/dist/components/Sidebar.js +37 -12
- package/dist/components/SortableList.d.ts +45 -0
- package/dist/components/SortableList.js +134 -0
- package/dist/components/Spinner.d.ts +7 -1
- package/dist/components/Spinner.js +7 -1
- package/dist/components/Stat.d.ts +28 -3
- package/dist/components/Stat.js +13 -3
- package/dist/components/States.d.ts +7 -1
- package/dist/components/States.js +7 -1
- package/dist/components/StatusPill.d.ts +14 -3
- package/dist/components/StatusPill.js +11 -3
- package/dist/components/Steps.js +2 -1
- package/dist/components/Table.d.ts +16 -2
- package/dist/components/Table.js +8 -4
- package/dist/components/TableControls.js +18 -58
- package/dist/components/TableOverflowProbe.d.ts +12 -0
- package/dist/components/TableOverflowProbe.js +27 -0
- package/dist/components/Tabs.d.ts +2 -1
- package/dist/components/Tabs.js +1 -0
- package/dist/components/TagInput.d.ts +67 -0
- package/dist/components/TagInput.js +342 -0
- package/dist/components/Textarea.d.ts +5 -0
- package/dist/components/Textarea.js +2 -2
- package/dist/components/TimePicker.d.ts +80 -0
- package/dist/components/TimePicker.js +347 -0
- package/dist/components/Toast.js +3 -2
- package/dist/components/Tooltip.d.ts +8 -4
- package/dist/components/Tooltip.js +24 -21
- package/dist/components/Tree.d.ts +8 -0
- package/dist/components/Tree.js +11 -5
- package/dist/display-preferences-model.d.ts +53 -0
- package/dist/display-preferences-model.js +64 -0
- package/dist/emoji.cjs +28 -35
- package/dist/filter-model.d.ts +59 -3
- package/dist/filter-model.js +67 -2
- package/dist/hooks/useChangeHighlight.d.ts +33 -0
- package/dist/hooks/useChangeHighlight.js +44 -0
- package/dist/hooks/useDelayedPending.d.ts +22 -0
- package/dist/hooks/useDelayedPending.js +35 -0
- package/dist/hooks/useDialog.d.ts +5 -0
- package/dist/hooks/useDialog.js +3 -2
- package/dist/hooks/useOptimisticValue.d.ts +51 -0
- package/dist/hooks/useOptimisticValue.js +61 -0
- package/dist/hooks/useReducedMotion.d.ts +4 -1
- package/dist/hooks/useReducedMotion.js +7 -1
- package/dist/hooks/useResolvedFilterLabels.d.ts +15 -0
- package/dist/hooks/useResolvedFilterLabels.js +34 -0
- package/dist/hooks/useVirtualRows.d.ts +21 -2
- package/dist/hooks/useVirtualRows.js +30 -3
- package/dist/icons/Icon.d.ts +1 -1
- package/dist/index.cjs +18977 -13378
- package/dist/index.d.cts +3065 -664
- package/dist/index.d.ts +46 -16
- package/dist/index.js +149 -7
- package/dist/labels-context.d.ts +93 -3
- package/dist/motion-timing.d.ts +8 -0
- package/dist/motion-timing.js +8 -0
- package/dist/overlay-stack.d.ts +6 -0
- package/dist/overlay-stack.js +13 -0
- package/dist/qr-model.d.ts +65 -0
- package/dist/qr-model.js +323 -0
- package/dist/relationship-graph-model.d.ts +78 -3
- package/dist/relationship-graph-model.js +112 -5
- package/dist/reorder.d.ts +30 -0
- package/dist/reorder.js +74 -0
- package/dist/rich-text.cjs +4 -2
- package/dist/scheduling-resource-model.d.ts +76 -0
- package/dist/scheduling-resource-model.js +74 -0
- package/dist/search-suggest-model.js +3 -1
- package/dist/shell-context.d.ts +11 -0
- package/dist/shell-context.js +7 -0
- package/dist/time-field-model.d.ts +46 -0
- package/dist/time-field-model.js +152 -0
- package/dist/toast-store.d.ts +17 -1
- package/dist/toast-store.js +23 -7
- package/package.json +2 -1
package/dist/hooks/useDialog.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useRef, useEffect } from "react";
|
|
2
|
+
import { useRef, useEffect, useLayoutEffect } from "react";
|
|
3
|
+
const useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
|
|
3
4
|
let lockCount = 0;
|
|
4
5
|
let saved = null;
|
|
5
6
|
function lockBodyScroll() {
|
|
@@ -36,7 +37,7 @@ function useDialog(open, onClose, keepOpen) {
|
|
|
36
37
|
onCloseRef.current = onClose;
|
|
37
38
|
const keepOpenRef = useRef(keepOpen);
|
|
38
39
|
keepOpenRef.current = keepOpen;
|
|
39
|
-
|
|
40
|
+
useIsomorphicLayoutEffect(() => {
|
|
40
41
|
const el = ref.current;
|
|
41
42
|
if (!el || !open) {
|
|
42
43
|
if (el && el.open) el.close();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
interface OptimisticRollback<T> {
|
|
2
|
+
/** The value the person asked for. */
|
|
3
|
+
attempted: T;
|
|
4
|
+
/** The value shown again. */
|
|
5
|
+
restored: T;
|
|
6
|
+
error: unknown;
|
|
7
|
+
}
|
|
8
|
+
interface UseOptimisticValueOptions<T> {
|
|
9
|
+
/** Called when a change fails and the value is put back, e.g. to show a toast that says what was restored. */
|
|
10
|
+
onRollback?: (rollback: OptimisticRollback<T>) => void;
|
|
11
|
+
}
|
|
12
|
+
interface UseOptimisticValueResult<T> {
|
|
13
|
+
/** What to show: the person's change at once, the server's value otherwise. */
|
|
14
|
+
value: T;
|
|
15
|
+
/** Show `next` now and save it; resolves when the save settles (it never rejects). */
|
|
16
|
+
set: (next: T) => Promise<void>;
|
|
17
|
+
/** A save is in flight. */
|
|
18
|
+
pending: boolean;
|
|
19
|
+
/** The last rollback, until the next change; `null` when the last change saved. */
|
|
20
|
+
rollback: OptimisticRollback<T> | null;
|
|
21
|
+
/**
|
|
22
|
+
* Counts rollbacks. Pass it to `ChangeHighlight value={rollbacks}` (or `useChangeHighlight`)
|
|
23
|
+
* so the restored element flashes; the person's own change does not.
|
|
24
|
+
*/
|
|
25
|
+
rollbacks: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* An optimistic value with a visible rollback (HAR-2583, motion standard §2.6). Only for
|
|
29
|
+
* reversible, personal, nearly-always-successful actions: star, pin, a reaction, mark read, a
|
|
30
|
+
* display preference, the order of one's own saved views. Never for anything the server can
|
|
31
|
+
* refuse or an auditor reads (a state transition, an assignment, an approval, a priority):
|
|
32
|
+
* those stay pessimistic, with `Button loading` and `result`.
|
|
33
|
+
*
|
|
34
|
+
* `set(next)` shows `next` at once and calls `commit(next)`. If the commit fails, the last
|
|
35
|
+
* confirmed value comes back, `rollbacks` counts up (flash the element with it), `rollback`
|
|
36
|
+
* says what happened, and `onRollback` runs so the product can say it in words. When changes
|
|
37
|
+
* overlap, only the latest one's outcome counts. While nothing is pending, `value` follows
|
|
38
|
+
* `serverValue` when it changes; a change that saved stays shown until then.
|
|
39
|
+
*
|
|
40
|
+
* ```tsx
|
|
41
|
+
* const star = useOptimisticValue(view.starred, (next) => api.star(view.id, next), {
|
|
42
|
+
* onRollback: () => toast.error(t('starNotSaved')),
|
|
43
|
+
* });
|
|
44
|
+
* <ChangeHighlight value={star.rollbacks} variant="ring" announce={star.rollback ? t('starRestored') : undefined}>
|
|
45
|
+
* <StarButton starred={star.value} onToggle={() => star.set(!star.value)} />
|
|
46
|
+
* </ChangeHighlight>
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
declare function useOptimisticValue<T>(serverValue: T, commit: (next: T) => Promise<unknown>, { onRollback }?: UseOptimisticValueOptions<T>): UseOptimisticValueResult<T>;
|
|
50
|
+
|
|
51
|
+
export { type OptimisticRollback, type UseOptimisticValueOptions, type UseOptimisticValueResult, useOptimisticValue };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
function useOptimisticValue(serverValue, commit, { onRollback } = {}) {
|
|
4
|
+
const [shown, setShown] = useState(serverValue);
|
|
5
|
+
const [pending, setPending] = useState(false);
|
|
6
|
+
const [rollback, setRollback] = useState(null);
|
|
7
|
+
const [rollbacks, setRollbacks] = useState(0);
|
|
8
|
+
const confirmed = useRef(serverValue);
|
|
9
|
+
const latest = useRef(0);
|
|
10
|
+
const confirmedId = useRef(0);
|
|
11
|
+
const mounted = useRef(true);
|
|
12
|
+
const commitRef = useRef(commit);
|
|
13
|
+
const onRollbackRef = useRef(onRollback);
|
|
14
|
+
commitRef.current = commit;
|
|
15
|
+
onRollbackRef.current = onRollback;
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
mounted.current = true;
|
|
18
|
+
return () => {
|
|
19
|
+
mounted.current = false;
|
|
20
|
+
};
|
|
21
|
+
}, []);
|
|
22
|
+
const pendingRef = useRef(false);
|
|
23
|
+
pendingRef.current = pending;
|
|
24
|
+
const firstServer = useRef(true);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (firstServer.current) {
|
|
27
|
+
firstServer.current = false;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
confirmed.current = serverValue;
|
|
31
|
+
if (!pendingRef.current) setShown(serverValue);
|
|
32
|
+
}, [serverValue]);
|
|
33
|
+
const set = useCallback(async (next) => {
|
|
34
|
+
const id = ++latest.current;
|
|
35
|
+
setShown(next);
|
|
36
|
+
setPending(true);
|
|
37
|
+
setRollback(null);
|
|
38
|
+
try {
|
|
39
|
+
await commitRef.current(next);
|
|
40
|
+
if (id > confirmedId.current) {
|
|
41
|
+
confirmedId.current = id;
|
|
42
|
+
confirmed.current = next;
|
|
43
|
+
}
|
|
44
|
+
if (!mounted.current || id !== latest.current) return;
|
|
45
|
+
setPending(false);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (!mounted.current || id !== latest.current) return;
|
|
48
|
+
const restored = confirmed.current;
|
|
49
|
+
const event = { attempted: next, restored, error };
|
|
50
|
+
setShown(restored);
|
|
51
|
+
setPending(false);
|
|
52
|
+
setRollback(event);
|
|
53
|
+
setRollbacks((n) => n + 1);
|
|
54
|
+
onRollbackRef.current?.(event);
|
|
55
|
+
}
|
|
56
|
+
}, []);
|
|
57
|
+
return { value: shown, set, pending, rollback, rollbacks };
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
useOptimisticValue
|
|
61
|
+
};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Non-hook read for event handlers and effects: the OS setting, or the in-app preference
|
|
3
|
+
* `<html data-motion="reduce">` (HAR-1372). `false` where there is no `matchMedia` (SSR, tests).
|
|
4
|
+
*/
|
|
2
5
|
declare function prefersReducedMotion(): boolean;
|
|
3
6
|
/** True while the user asks for reduced motion; re-renders when the setting changes. `false` on the server. */
|
|
4
7
|
declare function useReducedMotion(): boolean;
|
|
@@ -6,9 +6,15 @@ function subscribe(onChange) {
|
|
|
6
6
|
};
|
|
7
7
|
const mql = matchMedia(QUERY);
|
|
8
8
|
mql.addEventListener("change", onChange);
|
|
9
|
-
|
|
9
|
+
const observer = typeof MutationObserver === "function" && typeof document !== "undefined" ? new MutationObserver(onChange) : null;
|
|
10
|
+
observer?.observe(document.documentElement, { attributes: true, attributeFilter: ["data-motion"] });
|
|
11
|
+
return () => {
|
|
12
|
+
mql.removeEventListener("change", onChange);
|
|
13
|
+
observer?.disconnect();
|
|
14
|
+
};
|
|
10
15
|
}
|
|
11
16
|
function prefersReducedMotion() {
|
|
17
|
+
if (typeof document !== "undefined" && document.documentElement.getAttribute("data-motion") === "reduce") return true;
|
|
12
18
|
return typeof matchMedia !== "undefined" && matchMedia(QUERY).matches;
|
|
13
19
|
}
|
|
14
20
|
function useReducedMotion() {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { FilterField, FilterValue } from '../filter-model.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A filter chip's field and value with the labels of ids restored from a URL or a saved view
|
|
5
|
+
* (PR #168 review: a chip read "Assignee: usr_8f3a"). A `reference` value's choices without a
|
|
6
|
+
* label, and an `enum` value's ids that are not among `field.options`, are looked up once with
|
|
7
|
+
* `field.resolveLabels`. Until the answer arrives, and when the lookup fails, the ids show.
|
|
8
|
+
* Internal: DataWorkspace and AuditLog summarise their chips through it.
|
|
9
|
+
*/
|
|
10
|
+
declare function useResolvedFilterLabels(field: FilterField, value: FilterValue | undefined): {
|
|
11
|
+
field: FilterField;
|
|
12
|
+
value: FilterValue | undefined;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export { useResolvedFilterLabels };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { isUnresolvedChoice } from "../filter-model.js";
|
|
4
|
+
function useResolvedFilterLabels(field, value) {
|
|
5
|
+
const [known, setKnown] = useState(() => /* @__PURE__ */ new Map());
|
|
6
|
+
const asked = useRef(/* @__PURE__ */ new Set());
|
|
7
|
+
const resolver = useRef(field.resolveLabels);
|
|
8
|
+
resolver.current = field.resolveLabels;
|
|
9
|
+
const optionIds = new Set((field.options ?? []).map((o) => o.value));
|
|
10
|
+
const missing = value?.kind === "reference" ? value.values.filter(isUnresolvedChoice).map((v) => v.value) : value?.kind === "enum" ? value.values.filter((v) => !optionIds.has(v)) : [];
|
|
11
|
+
const key = missing.filter((id) => !known.has(id) && !asked.current.has(id)).join("\0");
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!key || !resolver.current) return;
|
|
14
|
+
const ids = key.split("\0");
|
|
15
|
+
for (const id of ids) asked.current.add(id);
|
|
16
|
+
resolver.current(ids).then(
|
|
17
|
+
(choices) => setKnown((prev) => new Map([...prev, ...choices.map((c) => [c.value, c.label])])),
|
|
18
|
+
() => {
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
}, [key]);
|
|
22
|
+
if (known.size === 0 || !value) return { field, value };
|
|
23
|
+
if (value.kind === "reference") {
|
|
24
|
+
return { field, value: { kind: "reference", values: value.values.map((v) => isUnresolvedChoice(v) && known.has(v.value) ? { value: v.value, label: known.get(v.value) } : v) } };
|
|
25
|
+
}
|
|
26
|
+
if (value.kind === "enum") {
|
|
27
|
+
const extra = value.values.filter((v) => !optionIds.has(v) && known.has(v)).map((v) => ({ value: v, label: known.get(v) }));
|
|
28
|
+
if (extra.length) return { field: { ...field, options: [...field.options ?? [], ...extra] }, value };
|
|
29
|
+
}
|
|
30
|
+
return { field, value };
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
useResolvedFilterLabels
|
|
34
|
+
};
|
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
2
|
|
|
3
3
|
interface UseVirtualRowsOptions {
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Rendered row height in pixels. With `measureRows` it is the estimate for the first paint,
|
|
6
|
+
* until the mounted rows have been measured.
|
|
7
|
+
*/
|
|
5
8
|
rowHeight: number;
|
|
9
|
+
/**
|
|
10
|
+
* A selector for the mounted data rows inside the container, e.g. `'tbody > tr[data-row]'`
|
|
11
|
+
* (HAR-1829). With it the hook sizes its spacers and window from the rows' real pitch (their
|
|
12
|
+
* mean `offsetHeight`) instead of trusting `rowHeight`, which only has to be close: a row's
|
|
13
|
+
* height comes from CSS and content (kit padding, a second line, density), and a wrong number
|
|
14
|
+
* gives a short scrollbar and a window that drifts away from the rows on screen. It measures
|
|
15
|
+
* when windowing starts, when `rowHeight` changes (a density switch) and when the container
|
|
16
|
+
* resizes, never while scrolling, so the spacers do not move under the reader. Spacer rows
|
|
17
|
+
* must not match it.
|
|
18
|
+
*/
|
|
19
|
+
measureRows?: string;
|
|
6
20
|
/** Rows rendered beyond each viewport edge. Default: 6. */
|
|
7
21
|
overscan?: number;
|
|
8
22
|
/**
|
|
@@ -30,6 +44,11 @@ interface UseVirtualRowsResult<T> {
|
|
|
30
44
|
padBottom: number;
|
|
31
45
|
totalHeight: number;
|
|
32
46
|
virtualized: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* The row height in use: the measured pitch with `measureRows`, else the `rowHeight` option.
|
|
49
|
+
* Scroll a keyboard-focused row into view with it (`index * rowHeight`).
|
|
50
|
+
*/
|
|
51
|
+
rowHeight: number;
|
|
33
52
|
}
|
|
34
53
|
/**
|
|
35
54
|
* Owns scroll/resize observation and exposes the small visible row window.
|
|
@@ -37,6 +56,6 @@ interface UseVirtualRowsResult<T> {
|
|
|
37
56
|
* When the row count shrinks under a large scroll position the window is clamped to the new
|
|
38
57
|
* count in the same render, so `padTop` never exceeds the list (HAR-1616).
|
|
39
58
|
*/
|
|
40
|
-
declare function useVirtualRows<T>(rows: readonly T[], { rowHeight, overscan, threshold, estimatedViewportHeight, enabled }: UseVirtualRowsOptions): UseVirtualRowsResult<T>;
|
|
59
|
+
declare function useVirtualRows<T>(rows: readonly T[], { rowHeight: estimate, overscan, threshold, estimatedViewportHeight, enabled, measureRows }: UseVirtualRowsOptions): UseVirtualRowsResult<T>;
|
|
41
60
|
|
|
42
61
|
export { type UseVirtualRowsOptions, type UseVirtualRowsResult, useVirtualRows };
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { shouldVirtualize, virtualWindow } from "../table-engine.js";
|
|
4
|
-
function useVirtualRows(rows, { rowHeight, overscan = 6, threshold = 100, estimatedViewportHeight = 0, enabled = true }) {
|
|
4
|
+
function useVirtualRows(rows, { rowHeight: estimate, overscan = 6, threshold = 100, estimatedViewportHeight = 0, enabled = true, measureRows }) {
|
|
5
5
|
const containerRef = useRef(null);
|
|
6
6
|
const [viewport, setViewport] = useState({ scrollTop: 0, height: 0 });
|
|
7
|
+
const [measured, setMeasured] = useState(null);
|
|
8
|
+
const measureRowsRef = useRef(() => {
|
|
9
|
+
});
|
|
7
10
|
useLayoutEffect(() => {
|
|
8
11
|
const element = containerRef.current;
|
|
9
12
|
if (!element) return;
|
|
@@ -21,7 +24,10 @@ function useVirtualRows(rows, { rowHeight, overscan = 6, threshold = 100, estima
|
|
|
21
24
|
};
|
|
22
25
|
commitMeasurement();
|
|
23
26
|
element.addEventListener("scroll", measure, { passive: true });
|
|
24
|
-
const observer = new ResizeObserver(
|
|
27
|
+
const observer = new ResizeObserver(() => {
|
|
28
|
+
measure();
|
|
29
|
+
measureRowsRef.current();
|
|
30
|
+
});
|
|
25
31
|
observer.observe(element);
|
|
26
32
|
return () => {
|
|
27
33
|
element.removeEventListener("scroll", measure);
|
|
@@ -31,6 +37,26 @@ function useVirtualRows(rows, { rowHeight, overscan = 6, threshold = 100, estima
|
|
|
31
37
|
}, []);
|
|
32
38
|
const height = viewport.height > 0 ? viewport.height : Math.max(0, estimatedViewportHeight);
|
|
33
39
|
const virtualized = enabled && height > 0 && shouldVirtualize(rows.length, threshold);
|
|
40
|
+
const rowHeight = measureRows && measured && measured.estimate === estimate ? measured.height : estimate;
|
|
41
|
+
measureRowsRef.current = () => {
|
|
42
|
+
const element = containerRef.current;
|
|
43
|
+
if (!element || !measureRows || !virtualized) return;
|
|
44
|
+
let total = 0;
|
|
45
|
+
let count = 0;
|
|
46
|
+
for (const row of Array.from(element.querySelectorAll(measureRows))) {
|
|
47
|
+
const h = row.offsetHeight;
|
|
48
|
+
if (h > 0) {
|
|
49
|
+
total += h;
|
|
50
|
+
count += 1;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (count === 0) return;
|
|
54
|
+
const next = Math.round(total / count * 100) / 100;
|
|
55
|
+
setMeasured((current) => current && current.estimate === estimate && current.height === next ? current : { height: next, estimate });
|
|
56
|
+
};
|
|
57
|
+
useLayoutEffect(() => {
|
|
58
|
+
measureRowsRef.current();
|
|
59
|
+
}, [virtualized, estimate, measureRows]);
|
|
34
60
|
const window = useMemo(
|
|
35
61
|
() => virtualized ? virtualWindow(viewport.scrollTop, height, rowHeight, rows.length, overscan) : { start: 0, end: rows.length, padTop: 0, padBottom: 0, total: rows.length * rowHeight },
|
|
36
62
|
[virtualized, overscan, rowHeight, rows.length, height, viewport.scrollTop]
|
|
@@ -43,7 +69,8 @@ function useVirtualRows(rows, { rowHeight, overscan = 6, threshold = 100, estima
|
|
|
43
69
|
padTop: window.padTop,
|
|
44
70
|
padBottom: window.padBottom,
|
|
45
71
|
totalHeight: window.total,
|
|
46
|
-
virtualized
|
|
72
|
+
virtualized,
|
|
73
|
+
rowHeight
|
|
47
74
|
};
|
|
48
75
|
}
|
|
49
76
|
export {
|
package/dist/icons/Icon.d.ts
CHANGED
|
@@ -269,6 +269,7 @@ declare function renderIcon(node: IconNode, { size, tone, label, strokeWidth, cl
|
|
|
269
269
|
clip?: number | string | undefined | undefined;
|
|
270
270
|
stroke: string;
|
|
271
271
|
href?: string | undefined | undefined;
|
|
272
|
+
d?: string | undefined | undefined;
|
|
272
273
|
capHeight?: number | string | undefined | undefined;
|
|
273
274
|
dy?: number | string | undefined | undefined;
|
|
274
275
|
dx?: number | string | undefined | undefined;
|
|
@@ -306,7 +307,6 @@ declare function renderIcon(node: IconNode, { size, tone, label, strokeWidth, cl
|
|
|
306
307
|
contentStyleType?: number | string | undefined | undefined;
|
|
307
308
|
cx?: number | string | undefined | undefined;
|
|
308
309
|
cy?: number | string | undefined | undefined;
|
|
309
|
-
d?: string | undefined | undefined;
|
|
310
310
|
decelerate?: number | string | undefined | undefined;
|
|
311
311
|
descent?: number | string | undefined | undefined;
|
|
312
312
|
diffuseConstant?: number | string | undefined | undefined;
|