@comitor/ui 1.10.0 → 2.0.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 +24 -5
- package/dist/data-grid/compose-refs.d.ts +19 -0
- package/dist/data-grid/compose-refs.js +41 -0
- package/dist/data-grid/compose-refs.js.map +1 -0
- package/dist/data-grid/data-grid-cell-variants.d.ts +15 -0
- package/dist/data-grid/data-grid-cell-variants.js +1738 -0
- package/dist/data-grid/data-grid-cell-variants.js.map +1 -0
- package/dist/data-grid/data-grid-cell-wrapper.d.ts +9 -0
- package/dist/data-grid/data-grid-cell-wrapper.js +179 -0
- package/dist/data-grid/data-grid-cell-wrapper.js.map +1 -0
- package/dist/data-grid/data-grid-cell.d.ts +8 -0
- package/dist/data-grid/data-grid-cell.js +101 -0
- package/dist/data-grid/data-grid-cell.js.map +1 -0
- package/dist/data-grid/data-grid-column-header.d.ts +12 -0
- package/dist/data-grid/data-grid-column-header.js +207 -0
- package/dist/data-grid/data-grid-column-header.js.map +1 -0
- package/dist/data-grid/data-grid-context-menu.d.ts +12 -0
- package/dist/data-grid/data-grid-context-menu.js +161 -0
- package/dist/data-grid/data-grid-context-menu.js.map +1 -0
- package/dist/data-grid/data-grid-filter-menu.d.ts +23 -0
- package/dist/data-grid/data-grid-filter-menu.js +725 -0
- package/dist/data-grid/data-grid-filter-menu.js.map +1 -0
- package/dist/data-grid/data-grid-keyboard-shortcuts.d.ts +31 -0
- package/dist/data-grid/data-grid-keyboard-shortcuts.js +229 -0
- package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -0
- package/dist/data-grid/data-grid-locale.d.ts +24 -0
- package/dist/data-grid/data-grid-locale.js +25 -0
- package/dist/data-grid/data-grid-locale.js.map +1 -0
- package/dist/data-grid/data-grid-paste-dialog.d.ts +11 -0
- package/dist/data-grid/data-grid-paste-dialog.js +104 -0
- package/dist/data-grid/data-grid-paste-dialog.js.map +1 -0
- package/dist/data-grid/data-grid-presence.d.ts +14 -0
- package/dist/data-grid/data-grid-presence.js +16 -0
- package/dist/data-grid/data-grid-presence.js.map +1 -0
- package/dist/data-grid/data-grid-row-height-menu.d.ts +25 -0
- package/dist/data-grid/data-grid-row-height-menu.js +53 -0
- package/dist/data-grid/data-grid-row-height-menu.js.map +1 -0
- package/dist/data-grid/data-grid-row.d.ts +28 -0
- package/dist/data-grid/data-grid-row.js +206 -0
- package/dist/data-grid/data-grid-row.js.map +1 -0
- package/dist/data-grid/data-grid-search.d.ts +10 -0
- package/dist/data-grid/data-grid-search.js +184 -0
- package/dist/data-grid/data-grid-search.js.map +1 -0
- package/dist/data-grid/data-grid-select-column.d.ts +12 -0
- package/dist/data-grid/data-grid-select-column.js +179 -0
- package/dist/data-grid/data-grid-select-column.js.map +1 -0
- package/dist/data-grid/data-grid-skeleton.d.ts +13 -0
- package/dist/data-grid/data-grid-skeleton.js +52 -0
- package/dist/data-grid/data-grid-skeleton.js.map +1 -0
- package/dist/data-grid/data-grid-sort-menu.d.ts +23 -0
- package/dist/data-grid/data-grid-sort-menu.js +283 -0
- package/dist/data-grid/data-grid-sort-menu.js.map +1 -0
- package/dist/data-grid/data-grid-view-menu.d.ts +24 -0
- package/dist/data-grid/data-grid-view-menu.js +74 -0
- package/dist/data-grid/data-grid-view-menu.js.map +1 -0
- package/dist/data-grid/data-grid.d.ts +44 -0
- package/dist/data-grid/data-grid.js +238 -0
- package/dist/data-grid/data-grid.js.map +1 -0
- package/dist/data-grid/filters.d.ts +27 -0
- package/dist/data-grid/filters.js +233 -0
- package/dist/data-grid/filters.js.map +1 -0
- package/dist/data-grid/index.d.ts +25 -0
- package/dist/data-grid/index.js +95 -0
- package/dist/data-grid/index.js.map +1 -0
- package/dist/data-grid/messages-en.d.ts +294 -0
- package/dist/data-grid/messages-en.js +279 -0
- package/dist/data-grid/messages-en.js.map +1 -0
- package/dist/data-grid/messages-vi.d.ts +12 -0
- package/dist/data-grid/messages-vi.js +279 -0
- package/dist/data-grid/messages-vi.js.map +1 -0
- package/dist/data-grid/messages.d.ts +567 -0
- package/dist/data-grid/messages.js +20 -0
- package/dist/data-grid/messages.js.map +1 -0
- package/dist/data-grid/sortable.d.ts +50 -0
- package/dist/data-grid/sortable.js +361 -0
- package/dist/data-grid/sortable.js.map +1 -0
- package/dist/data-grid/toast.d.ts +10 -0
- package/dist/data-grid/toast.js +21 -0
- package/dist/data-grid/toast.js.map +1 -0
- package/dist/data-grid/types.d.ts +189 -0
- package/dist/data-grid/types.js +1 -0
- package/dist/data-grid/types.js.map +1 -0
- package/dist/data-grid/use-as-ref.d.ts +5 -0
- package/dist/data-grid/use-as-ref.js +13 -0
- package/dist/data-grid/use-as-ref.js.map +1 -0
- package/dist/data-grid/use-badge-overflow.d.ts +24 -0
- package/dist/data-grid/use-badge-overflow.js +123 -0
- package/dist/data-grid/use-badge-overflow.js.map +1 -0
- package/dist/data-grid/use-callback-ref.d.ts +10 -0
- package/dist/data-grid/use-callback-ref.js +12 -0
- package/dist/data-grid/use-callback-ref.js.map +1 -0
- package/dist/data-grid/use-data-grid-undo-redo.d.ts +45 -0
- package/dist/data-grid/use-data-grid-undo-redo.js +348 -0
- package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -0
- package/dist/data-grid/use-data-grid.d.ts +372 -0
- package/dist/data-grid/use-data-grid.js +2697 -0
- package/dist/data-grid/use-data-grid.js.map +1 -0
- package/dist/data-grid/use-debounced-callback.d.ts +6 -0
- package/dist/data-grid/use-debounced-callback.js +19 -0
- package/dist/data-grid/use-debounced-callback.js.map +1 -0
- package/dist/data-grid/use-isomorphic-layout-effect.d.ts +5 -0
- package/dist/data-grid/use-isomorphic-layout-effect.js +6 -0
- package/dist/data-grid/use-isomorphic-layout-effect.js.map +1 -0
- package/dist/data-grid/use-lazy-ref.d.ts +5 -0
- package/dist/data-grid/use-lazy-ref.js +12 -0
- package/dist/data-grid/use-lazy-ref.js.map +1 -0
- package/dist/data-grid/utils.d.ts +81 -0
- package/dist/data-grid/utils.js +409 -0
- package/dist/data-grid/utils.js.map +1 -0
- package/package.json +33 -15
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/data-grid/use-as-ref.ts"],"sourcesContent":["import * as React from \"react\";\n\nimport { useIsomorphicLayoutEffect } from \"./use-isomorphic-layout-effect.js\";\n\nfunction useAsRef<T>(props: T) {\n const ref = React.useRef<T>(props);\n\n useIsomorphicLayoutEffect(() => {\n ref.current = props;\n });\n\n return ref;\n}\n\nexport { useAsRef };\n"],"mappings":"AAAA,YAAY,WAAW;AAEvB,SAAS,iCAAiC;AAE1C,SAAS,SAAY,OAAU;AAC7B,QAAM,MAAM,MAAM,OAAU,KAAK;AAEjC,4BAA0B,MAAM;AAC9B,QAAI,UAAU;AAAA,EAChB,CAAC;AAED,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface UseBadgeOverflowProps<T> {
|
|
4
|
+
items: T[];
|
|
5
|
+
getLabel: (item: T) => string;
|
|
6
|
+
containerRef: React.RefObject<HTMLElement | null>;
|
|
7
|
+
lineCount: number;
|
|
8
|
+
cacheKeyPrefix?: string;
|
|
9
|
+
iconSize?: number;
|
|
10
|
+
maxWidth?: number;
|
|
11
|
+
className?: string;
|
|
12
|
+
containerPadding?: number;
|
|
13
|
+
badgeGap?: number;
|
|
14
|
+
overflowBadgeWidth?: number;
|
|
15
|
+
}
|
|
16
|
+
interface UseBadgeOverflowReturn<T> {
|
|
17
|
+
visibleItems: T[];
|
|
18
|
+
hiddenCount: number;
|
|
19
|
+
containerWidth: number;
|
|
20
|
+
}
|
|
21
|
+
declare function useBadgeOverflow<T>({ items, getLabel, containerRef, lineCount, cacheKeyPrefix, containerPadding, badgeGap, overflowBadgeWidth, iconSize, maxWidth, className }: UseBadgeOverflowProps<T>): UseBadgeOverflowReturn<T>;
|
|
22
|
+
declare function clearBadgeWidthCache(): void;
|
|
23
|
+
|
|
24
|
+
export { clearBadgeWidthCache, useBadgeOverflow };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
const badgeWidthCache = /* @__PURE__ */ new Map();
|
|
3
|
+
const DEFAULT_CONTAINER_PADDING = 16;
|
|
4
|
+
const DEFAULT_BADGE_GAP = 4;
|
|
5
|
+
const DEFAULT_OVERFLOW_BADGE_WIDTH = 40;
|
|
6
|
+
function measureBadgeWidth({ label, cacheKey, iconSize, maxWidth, className }) {
|
|
7
|
+
const cached = badgeWidthCache.get(cacheKey);
|
|
8
|
+
if (cached !== void 0) {
|
|
9
|
+
return cached;
|
|
10
|
+
}
|
|
11
|
+
const measureEl = document.createElement("div");
|
|
12
|
+
measureEl.className = `inline-flex items-center rounded-md border px-1.5 text-xs font-semibold h-5 gap-1 shrink-0 absolute invisible pointer-events-none ${className ?? ""}`;
|
|
13
|
+
measureEl.style.whiteSpace = "nowrap";
|
|
14
|
+
if (iconSize) {
|
|
15
|
+
const icon = document.createElement("span");
|
|
16
|
+
icon.className = "shrink-0";
|
|
17
|
+
icon.style.width = `${iconSize}px`;
|
|
18
|
+
icon.style.height = `${iconSize}px`;
|
|
19
|
+
measureEl.appendChild(icon);
|
|
20
|
+
}
|
|
21
|
+
if (maxWidth) {
|
|
22
|
+
const text = document.createElement("span");
|
|
23
|
+
text.className = "truncate";
|
|
24
|
+
text.style.maxWidth = `${maxWidth}px`;
|
|
25
|
+
text.textContent = label;
|
|
26
|
+
measureEl.appendChild(text);
|
|
27
|
+
} else {
|
|
28
|
+
measureEl.textContent = label;
|
|
29
|
+
}
|
|
30
|
+
document.body.appendChild(measureEl);
|
|
31
|
+
const width = measureEl.offsetWidth;
|
|
32
|
+
document.body.removeChild(measureEl);
|
|
33
|
+
badgeWidthCache.set(cacheKey, width);
|
|
34
|
+
return width;
|
|
35
|
+
}
|
|
36
|
+
function useBadgeOverflow({
|
|
37
|
+
items,
|
|
38
|
+
getLabel,
|
|
39
|
+
containerRef,
|
|
40
|
+
lineCount,
|
|
41
|
+
cacheKeyPrefix = "",
|
|
42
|
+
containerPadding = DEFAULT_CONTAINER_PADDING,
|
|
43
|
+
badgeGap = DEFAULT_BADGE_GAP,
|
|
44
|
+
overflowBadgeWidth = DEFAULT_OVERFLOW_BADGE_WIDTH,
|
|
45
|
+
iconSize,
|
|
46
|
+
maxWidth,
|
|
47
|
+
className
|
|
48
|
+
}) {
|
|
49
|
+
const [containerWidth, setContainerWidth] = React.useState(0);
|
|
50
|
+
React.useEffect(() => {
|
|
51
|
+
if (!containerRef.current) return;
|
|
52
|
+
function measureWidth() {
|
|
53
|
+
if (containerRef.current) {
|
|
54
|
+
const width = containerRef.current.clientWidth - containerPadding;
|
|
55
|
+
setContainerWidth(width);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
measureWidth();
|
|
59
|
+
const resizeObserver = new ResizeObserver(measureWidth);
|
|
60
|
+
resizeObserver.observe(containerRef.current);
|
|
61
|
+
return () => {
|
|
62
|
+
resizeObserver.disconnect();
|
|
63
|
+
};
|
|
64
|
+
}, [containerRef, containerPadding]);
|
|
65
|
+
const result = React.useMemo(() => {
|
|
66
|
+
if (!containerWidth || items.length === 0) {
|
|
67
|
+
return { visibleItems: items, hiddenCount: 0, containerWidth };
|
|
68
|
+
}
|
|
69
|
+
let currentLineWidth = 0;
|
|
70
|
+
let currentLine = 1;
|
|
71
|
+
const visible = [];
|
|
72
|
+
for (const item of items) {
|
|
73
|
+
const label = getLabel(item);
|
|
74
|
+
const cacheKey = cacheKeyPrefix ? `${cacheKeyPrefix}:${label}` : label;
|
|
75
|
+
const badgeWidth = measureBadgeWidth({
|
|
76
|
+
label,
|
|
77
|
+
cacheKey,
|
|
78
|
+
iconSize,
|
|
79
|
+
maxWidth,
|
|
80
|
+
className
|
|
81
|
+
});
|
|
82
|
+
const widthWithGap = badgeWidth + badgeGap;
|
|
83
|
+
if (currentLineWidth + widthWithGap <= containerWidth) {
|
|
84
|
+
currentLineWidth += widthWithGap;
|
|
85
|
+
visible.push(item);
|
|
86
|
+
} else if (currentLine < lineCount) {
|
|
87
|
+
currentLine++;
|
|
88
|
+
currentLineWidth = widthWithGap;
|
|
89
|
+
visible.push(item);
|
|
90
|
+
} else {
|
|
91
|
+
if (currentLineWidth + overflowBadgeWidth > containerWidth && visible.length > 0) {
|
|
92
|
+
visible.pop();
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
visibleItems: visible,
|
|
99
|
+
hiddenCount: Math.max(0, items.length - visible.length),
|
|
100
|
+
containerWidth
|
|
101
|
+
};
|
|
102
|
+
}, [
|
|
103
|
+
items,
|
|
104
|
+
getLabel,
|
|
105
|
+
containerWidth,
|
|
106
|
+
lineCount,
|
|
107
|
+
cacheKeyPrefix,
|
|
108
|
+
iconSize,
|
|
109
|
+
maxWidth,
|
|
110
|
+
className,
|
|
111
|
+
badgeGap,
|
|
112
|
+
overflowBadgeWidth
|
|
113
|
+
]);
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
function clearBadgeWidthCache() {
|
|
117
|
+
badgeWidthCache.clear();
|
|
118
|
+
}
|
|
119
|
+
export {
|
|
120
|
+
clearBadgeWidthCache,
|
|
121
|
+
useBadgeOverflow
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=use-badge-overflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/data-grid/use-badge-overflow.ts"],"sourcesContent":["import * as React from \"react\";\n\nconst badgeWidthCache = new Map<string, number>();\n\nconst DEFAULT_CONTAINER_PADDING = 16; // px-2 = 8px * 2\nconst DEFAULT_BADGE_GAP = 4; // gap-1 = 4px\nconst DEFAULT_OVERFLOW_BADGE_WIDTH = 40; // Approximate width of \"+N\" badge\n\ninterface MeasureBadgeWidthProps {\n label: string;\n cacheKey: string;\n iconSize?: number;\n maxWidth?: number;\n className?: string;\n}\n\nfunction measureBadgeWidth({ label, cacheKey, iconSize, maxWidth, className }: MeasureBadgeWidthProps): number {\n const cached = badgeWidthCache.get(cacheKey);\n if (cached !== undefined) {\n return cached;\n }\n\n const measureEl = document.createElement(\"div\");\n measureEl.className = `inline-flex items-center rounded-md border px-1.5 text-xs font-semibold h-5 gap-1 shrink-0 absolute invisible pointer-events-none ${\n className ?? \"\"\n }`;\n measureEl.style.whiteSpace = \"nowrap\";\n\n if (iconSize) {\n const icon = document.createElement(\"span\");\n icon.className = \"shrink-0\";\n icon.style.width = `${iconSize}px`;\n icon.style.height = `${iconSize}px`;\n measureEl.appendChild(icon);\n }\n\n if (maxWidth) {\n const text = document.createElement(\"span\");\n text.className = \"truncate\";\n text.style.maxWidth = `${maxWidth}px`;\n text.textContent = label;\n measureEl.appendChild(text);\n } else {\n measureEl.textContent = label;\n }\n\n document.body.appendChild(measureEl);\n const width = measureEl.offsetWidth;\n document.body.removeChild(measureEl);\n\n badgeWidthCache.set(cacheKey, width);\n return width;\n}\n\ninterface UseBadgeOverflowProps<T> {\n items: T[];\n getLabel: (item: T) => string;\n containerRef: React.RefObject<HTMLElement | null>;\n lineCount: number;\n cacheKeyPrefix?: string;\n iconSize?: number;\n maxWidth?: number;\n className?: string;\n containerPadding?: number;\n badgeGap?: number;\n overflowBadgeWidth?: number;\n}\n\ninterface UseBadgeOverflowReturn<T> {\n visibleItems: T[];\n hiddenCount: number;\n containerWidth: number;\n}\n\nexport function useBadgeOverflow<T>({\n items,\n getLabel,\n containerRef,\n lineCount,\n cacheKeyPrefix = \"\",\n containerPadding = DEFAULT_CONTAINER_PADDING,\n badgeGap = DEFAULT_BADGE_GAP,\n overflowBadgeWidth = DEFAULT_OVERFLOW_BADGE_WIDTH,\n iconSize,\n maxWidth,\n className\n}: UseBadgeOverflowProps<T>): UseBadgeOverflowReturn<T> {\n const [containerWidth, setContainerWidth] = React.useState(0);\n\n React.useEffect(() => {\n if (!containerRef.current) return;\n\n function measureWidth() {\n if (containerRef.current) {\n const width = containerRef.current.clientWidth - containerPadding;\n setContainerWidth(width);\n }\n }\n\n measureWidth();\n\n const resizeObserver = new ResizeObserver(measureWidth);\n resizeObserver.observe(containerRef.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, [containerRef, containerPadding]);\n\n const result = React.useMemo(() => {\n if (!containerWidth || items.length === 0) {\n return { visibleItems: items, hiddenCount: 0, containerWidth };\n }\n\n let currentLineWidth = 0;\n let currentLine = 1;\n const visible: T[] = [];\n\n for (const item of items) {\n const label = getLabel(item);\n const cacheKey = cacheKeyPrefix ? `${cacheKeyPrefix}:${label}` : label;\n const badgeWidth = measureBadgeWidth({\n label,\n cacheKey,\n iconSize,\n maxWidth,\n className\n });\n const widthWithGap = badgeWidth + badgeGap;\n\n if (currentLineWidth + widthWithGap <= containerWidth) {\n currentLineWidth += widthWithGap;\n visible.push(item);\n } else if (currentLine < lineCount) {\n currentLine++;\n currentLineWidth = widthWithGap;\n visible.push(item);\n } else {\n if (currentLineWidth + overflowBadgeWidth > containerWidth && visible.length > 0) {\n visible.pop();\n }\n\n break;\n }\n }\n\n return {\n visibleItems: visible,\n hiddenCount: Math.max(0, items.length - visible.length),\n containerWidth\n };\n }, [\n items,\n getLabel,\n containerWidth,\n lineCount,\n cacheKeyPrefix,\n iconSize,\n maxWidth,\n className,\n badgeGap,\n overflowBadgeWidth\n ]);\n\n return result;\n}\n\nexport function clearBadgeWidthCache(): void {\n badgeWidthCache.clear();\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see https://github.com/radix-ui/primitives/blob/main/packages/react/use-callback-ref/src/useCallbackRef.tsx
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* A custom hook that converts a callback to a ref to avoid triggering re-renders when passed as a
|
|
6
|
+
* prop or avoid re-executing effects when passed as a dependency
|
|
7
|
+
*/
|
|
8
|
+
declare function useCallbackRef<T extends (...args: never[]) => unknown>(callback: T | undefined): T;
|
|
9
|
+
|
|
10
|
+
export { useCallbackRef };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
function useCallbackRef(callback) {
|
|
3
|
+
const callbackRef = React.useRef(callback);
|
|
4
|
+
React.useEffect(() => {
|
|
5
|
+
callbackRef.current = callback;
|
|
6
|
+
});
|
|
7
|
+
return React.useMemo(() => ((...args) => callbackRef.current?.(...args)), []);
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
useCallbackRef
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=use-callback-ref.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/data-grid/use-callback-ref.ts"],"sourcesContent":["import * as React from \"react\";\n\n/**\n * @see https://github.com/radix-ui/primitives/blob/main/packages/react/use-callback-ref/src/useCallbackRef.tsx\n */\n\n/**\n * A custom hook that converts a callback to a ref to avoid triggering re-renders when passed as a\n * prop or avoid re-executing effects when passed as a dependency\n */\nfunction useCallbackRef<T extends (...args: never[]) => unknown>(callback: T | undefined): T {\n const callbackRef = React.useRef(callback);\n\n React.useEffect(() => {\n callbackRef.current = callback;\n });\n\n // https://github.com/facebook/react/issues/19240\n return React.useMemo(() => ((...args) => callbackRef.current?.(...args)) as T, []);\n}\n\nexport { useCallbackRef };\n"],"mappings":"AAAA,YAAY,WAAW;AAUvB,SAAS,eAAwD,UAA4B;AAC3F,QAAM,cAAc,MAAM,OAAO,QAAQ;AAEzC,QAAM,UAAU,MAAM;AACpB,gBAAY,UAAU;AAAA,EACxB,CAAC;AAGD,SAAO,MAAM,QAAQ,OAAO,IAAI,SAAS,YAAY,UAAU,GAAG,IAAI,IAAS,CAAC,CAAC;AACnF;","names":[]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { DataGridLocale } from './messages.js';
|
|
2
|
+
import { DataGridMessages } from './messages-en.js';
|
|
3
|
+
import './messages-vi.js';
|
|
4
|
+
|
|
5
|
+
interface UndoRedoCellUpdate {
|
|
6
|
+
rowId: string;
|
|
7
|
+
columnId: string;
|
|
8
|
+
previousValue: unknown;
|
|
9
|
+
newValue: unknown;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Tuỳ chọn của {@link useDataGridUndoRedo}. Lịch sử nằm hoàn toàn ở phía trình duyệt:
|
|
13
|
+
* hook tính ra mảng dữ liệu trước/sau rồi trả về qua `onDataChange` — ứng dụng tự
|
|
14
|
+
* đồng bộ lên máy chủ (hoặc bỏ qua nếu chỉ làm việc với state cục bộ).
|
|
15
|
+
*/
|
|
16
|
+
interface UseDataGridUndoRedoProps<TData> {
|
|
17
|
+
data: TData[];
|
|
18
|
+
onDataChange: (data: TData[]) => void;
|
|
19
|
+
/** Khoá định danh bản ghi — phải khớp với `getRowId` của lưới */
|
|
20
|
+
getRowId: (row: TData) => string;
|
|
21
|
+
/** Số thao tác giữ trong lịch sử (mặc định 100) */
|
|
22
|
+
maxHistory?: number;
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Ngôn ngữ của toast mà hook bắn ra (`"en"` mặc định · `"vi"`, hoặc cả bảng chuỗi tự soạn).
|
|
26
|
+
*
|
|
27
|
+
* Hook giữ bảng chuỗi của RIÊNG nó chứ không đọc `DataGridLocaleProvider`: toast render ở gốc
|
|
28
|
+
* app qua `<Toaster />`, ngoài hẳn cây DOM của lưới, nên không có context nào để đọc. Truyền
|
|
29
|
+
* cùng giá trị đã truyền cho `useDataGrid`.
|
|
30
|
+
*/
|
|
31
|
+
locale?: DataGridLocale | DataGridMessages;
|
|
32
|
+
}
|
|
33
|
+
interface UseDataGridUndoRedoReturn<TData> {
|
|
34
|
+
canUndo: boolean;
|
|
35
|
+
canRedo: boolean;
|
|
36
|
+
onUndo: () => void;
|
|
37
|
+
onRedo: () => void;
|
|
38
|
+
onClear: () => void;
|
|
39
|
+
trackCellsUpdate: (updates: UndoRedoCellUpdate[]) => void;
|
|
40
|
+
trackRowsAdd: (rows: TData[]) => void;
|
|
41
|
+
trackRowsDelete: (rows: TData[]) => void;
|
|
42
|
+
}
|
|
43
|
+
declare function useDataGridUndoRedo<TData>({ data, onDataChange, getRowId, maxHistory, enabled, locale }: UseDataGridUndoRedoProps<TData>): UseDataGridUndoRedoReturn<TData>;
|
|
44
|
+
|
|
45
|
+
export { type UndoRedoCellUpdate, useDataGridUndoRedo };
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { getDataGridMessages } from "./messages.js";
|
|
4
|
+
import { toastInfo, toastSuccess } from "./toast.js";
|
|
5
|
+
import { useAsRef } from "./use-as-ref.js";
|
|
6
|
+
import { useLazyRef } from "./use-lazy-ref.js";
|
|
7
|
+
import { getIsInPopover } from "./utils.js";
|
|
8
|
+
const DEFAULT_MAX_HISTORY = 100;
|
|
9
|
+
const BATCH_TIMEOUT = 300;
|
|
10
|
+
function useStore(store, selector) {
|
|
11
|
+
const getSnapshot = React.useCallback(() => selector(store.getState()), [store, selector]);
|
|
12
|
+
return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
13
|
+
}
|
|
14
|
+
function buildIndexById(data, getRowId) {
|
|
15
|
+
const map = /* @__PURE__ */ new Map();
|
|
16
|
+
for (let i = 0; i < data.length; i++) {
|
|
17
|
+
const row = data[i];
|
|
18
|
+
if (row) {
|
|
19
|
+
map.set(getRowId(row), i);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return map;
|
|
23
|
+
}
|
|
24
|
+
function getPendingKey(rowId, columnId) {
|
|
25
|
+
return `${rowId}\0${columnId}`;
|
|
26
|
+
}
|
|
27
|
+
function useDataGridUndoRedo({
|
|
28
|
+
data,
|
|
29
|
+
onDataChange,
|
|
30
|
+
getRowId,
|
|
31
|
+
maxHistory = DEFAULT_MAX_HISTORY,
|
|
32
|
+
enabled = true,
|
|
33
|
+
locale
|
|
34
|
+
}) {
|
|
35
|
+
const msg = React.useMemo(() => getDataGridMessages(locale), [locale]);
|
|
36
|
+
const msgRef = useAsRef(msg);
|
|
37
|
+
const propsRef = useAsRef({
|
|
38
|
+
data,
|
|
39
|
+
onDataChange,
|
|
40
|
+
getRowId,
|
|
41
|
+
maxHistory,
|
|
42
|
+
enabled
|
|
43
|
+
});
|
|
44
|
+
const listenersRef = useLazyRef(() => /* @__PURE__ */ new Set());
|
|
45
|
+
const stateRef = useLazyRef(() => ({
|
|
46
|
+
undoStack: [],
|
|
47
|
+
redoStack: [],
|
|
48
|
+
hasPendingChanges: false
|
|
49
|
+
}));
|
|
50
|
+
const pendingBatchRef = React.useRef({
|
|
51
|
+
byKey: /* @__PURE__ */ new Map(),
|
|
52
|
+
timeoutId: null
|
|
53
|
+
});
|
|
54
|
+
const pendingNotifyRef = React.useRef(false);
|
|
55
|
+
const store = React.useMemo(() => {
|
|
56
|
+
return {
|
|
57
|
+
subscribe: (callback) => {
|
|
58
|
+
listenersRef.current.add(callback);
|
|
59
|
+
return () => listenersRef.current.delete(callback);
|
|
60
|
+
},
|
|
61
|
+
getState: () => stateRef.current,
|
|
62
|
+
push: (entry) => {
|
|
63
|
+
const state = stateRef.current;
|
|
64
|
+
const newUndoStack = [...state.undoStack, entry];
|
|
65
|
+
if (newUndoStack.length > propsRef.current.maxHistory) {
|
|
66
|
+
newUndoStack.shift();
|
|
67
|
+
}
|
|
68
|
+
stateRef.current = {
|
|
69
|
+
undoStack: newUndoStack,
|
|
70
|
+
redoStack: [],
|
|
71
|
+
hasPendingChanges: false
|
|
72
|
+
};
|
|
73
|
+
store.notify();
|
|
74
|
+
},
|
|
75
|
+
undo: () => {
|
|
76
|
+
const state = stateRef.current;
|
|
77
|
+
if (state.undoStack.length === 0) return null;
|
|
78
|
+
const entry = state.undoStack[state.undoStack.length - 1];
|
|
79
|
+
if (!entry) return null;
|
|
80
|
+
stateRef.current = {
|
|
81
|
+
undoStack: state.undoStack.slice(0, -1),
|
|
82
|
+
redoStack: [...state.redoStack, entry],
|
|
83
|
+
hasPendingChanges: false
|
|
84
|
+
};
|
|
85
|
+
store.notify();
|
|
86
|
+
return entry;
|
|
87
|
+
},
|
|
88
|
+
redo: () => {
|
|
89
|
+
const state = stateRef.current;
|
|
90
|
+
if (state.redoStack.length === 0) return null;
|
|
91
|
+
const entry = state.redoStack[state.redoStack.length - 1];
|
|
92
|
+
if (!entry) return null;
|
|
93
|
+
stateRef.current = {
|
|
94
|
+
undoStack: [...state.undoStack, entry],
|
|
95
|
+
redoStack: state.redoStack.slice(0, -1),
|
|
96
|
+
hasPendingChanges: false
|
|
97
|
+
};
|
|
98
|
+
store.notify();
|
|
99
|
+
return entry;
|
|
100
|
+
},
|
|
101
|
+
clear: () => {
|
|
102
|
+
stateRef.current = {
|
|
103
|
+
undoStack: [],
|
|
104
|
+
redoStack: [],
|
|
105
|
+
hasPendingChanges: false
|
|
106
|
+
};
|
|
107
|
+
store.notify();
|
|
108
|
+
},
|
|
109
|
+
setPendingChanges: (value) => {
|
|
110
|
+
if (stateRef.current.hasPendingChanges === value) return;
|
|
111
|
+
stateRef.current = {
|
|
112
|
+
...stateRef.current,
|
|
113
|
+
hasPendingChanges: value
|
|
114
|
+
};
|
|
115
|
+
if (!pendingNotifyRef.current) {
|
|
116
|
+
pendingNotifyRef.current = true;
|
|
117
|
+
queueMicrotask(() => {
|
|
118
|
+
pendingNotifyRef.current = false;
|
|
119
|
+
store.notify();
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
notify: () => {
|
|
124
|
+
for (const listener of listenersRef.current) {
|
|
125
|
+
listener();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}, [listenersRef, stateRef, propsRef]);
|
|
130
|
+
const canUndo = useStore(store, (state) => state.undoStack.length > 0 || state.hasPendingChanges);
|
|
131
|
+
const canRedo = useStore(store, (state) => state.redoStack.length > 0);
|
|
132
|
+
const onCommit = React.useCallback(() => {
|
|
133
|
+
const pending = pendingBatchRef.current;
|
|
134
|
+
if (pending.byKey.size === 0) return;
|
|
135
|
+
if (pending.timeoutId) {
|
|
136
|
+
clearTimeout(pending.timeoutId);
|
|
137
|
+
pending.timeoutId = null;
|
|
138
|
+
}
|
|
139
|
+
const updates = Array.from(pending.byKey.values());
|
|
140
|
+
pending.byKey.clear();
|
|
141
|
+
const { getRowId: getRowId2 } = propsRef.current;
|
|
142
|
+
const entry = {
|
|
143
|
+
variant: "cells_update",
|
|
144
|
+
count: updates.length,
|
|
145
|
+
timestamp: Date.now(),
|
|
146
|
+
undo: (currentData) => {
|
|
147
|
+
const newData = [...currentData];
|
|
148
|
+
const indexById = buildIndexById(newData, getRowId2);
|
|
149
|
+
for (const update of updates) {
|
|
150
|
+
const index = indexById.get(update.rowId);
|
|
151
|
+
if (index !== void 0) {
|
|
152
|
+
const row = newData[index];
|
|
153
|
+
if (row) {
|
|
154
|
+
newData[index] = {
|
|
155
|
+
...row,
|
|
156
|
+
[update.columnId]: update.previousValue
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return newData;
|
|
162
|
+
},
|
|
163
|
+
redo: (currentData) => {
|
|
164
|
+
const newData = [...currentData];
|
|
165
|
+
const indexById = buildIndexById(newData, getRowId2);
|
|
166
|
+
for (const update of updates) {
|
|
167
|
+
const index = indexById.get(update.rowId);
|
|
168
|
+
if (index !== void 0) {
|
|
169
|
+
const row = newData[index];
|
|
170
|
+
if (row) {
|
|
171
|
+
newData[index] = { ...row, [update.columnId]: update.newValue };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return newData;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
store.push(entry);
|
|
179
|
+
}, [store, propsRef]);
|
|
180
|
+
const onUndo = React.useCallback(() => {
|
|
181
|
+
if (!propsRef.current.enabled) return;
|
|
182
|
+
onCommit();
|
|
183
|
+
const entry = store.undo();
|
|
184
|
+
if (!entry) {
|
|
185
|
+
toastInfo(msgRef.current.toast.nothingToUndo);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const newData = entry.undo(propsRef.current.data);
|
|
189
|
+
propsRef.current.onDataChange(newData);
|
|
190
|
+
toastSuccess(msgRef.current.toast.undone(entry.count));
|
|
191
|
+
}, [store, propsRef, onCommit, msgRef]);
|
|
192
|
+
const onRedo = React.useCallback(() => {
|
|
193
|
+
if (!propsRef.current.enabled) return;
|
|
194
|
+
onCommit();
|
|
195
|
+
const entry = store.redo();
|
|
196
|
+
if (!entry) {
|
|
197
|
+
toastInfo(msgRef.current.toast.nothingToRedo);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const newData = entry.redo(propsRef.current.data);
|
|
201
|
+
propsRef.current.onDataChange(newData);
|
|
202
|
+
toastSuccess(msgRef.current.toast.redone(entry.count));
|
|
203
|
+
}, [store, propsRef, onCommit, msgRef]);
|
|
204
|
+
const onClear = React.useCallback(() => {
|
|
205
|
+
const pending = pendingBatchRef.current;
|
|
206
|
+
if (pending.timeoutId) {
|
|
207
|
+
clearTimeout(pending.timeoutId);
|
|
208
|
+
pending.timeoutId = null;
|
|
209
|
+
}
|
|
210
|
+
pending.byKey.clear();
|
|
211
|
+
store.clear();
|
|
212
|
+
}, [store]);
|
|
213
|
+
const trackCellsUpdate = React.useCallback(
|
|
214
|
+
(updates) => {
|
|
215
|
+
if (!propsRef.current.enabled || updates.length === 0) return;
|
|
216
|
+
const filteredUpdates = updates.filter((u) => !Object.is(u.previousValue, u.newValue));
|
|
217
|
+
if (filteredUpdates.length === 0) return;
|
|
218
|
+
const pending = pendingBatchRef.current;
|
|
219
|
+
for (const update of filteredUpdates) {
|
|
220
|
+
const key = getPendingKey(update.rowId, update.columnId);
|
|
221
|
+
const existing = pending.byKey.get(key);
|
|
222
|
+
if (existing) {
|
|
223
|
+
pending.byKey.set(key, { ...existing, newValue: update.newValue });
|
|
224
|
+
} else {
|
|
225
|
+
pending.byKey.set(key, update);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
store.setPendingChanges(true);
|
|
229
|
+
if (pending.timeoutId) {
|
|
230
|
+
clearTimeout(pending.timeoutId);
|
|
231
|
+
}
|
|
232
|
+
pending.timeoutId = setTimeout(onCommit, BATCH_TIMEOUT);
|
|
233
|
+
},
|
|
234
|
+
[store, propsRef, onCommit]
|
|
235
|
+
);
|
|
236
|
+
const trackRowsAdd = React.useCallback(
|
|
237
|
+
(rows) => {
|
|
238
|
+
if (!propsRef.current.enabled || rows.length === 0) return;
|
|
239
|
+
onCommit();
|
|
240
|
+
const { getRowId: getRowId2 } = propsRef.current;
|
|
241
|
+
const rowIds = new Set(rows.map((row) => getRowId2(row)));
|
|
242
|
+
const rowsCopy = rows.map((row) => ({ ...row }));
|
|
243
|
+
const entry = {
|
|
244
|
+
variant: "rows_add",
|
|
245
|
+
count: rows.length,
|
|
246
|
+
timestamp: Date.now(),
|
|
247
|
+
undo: (currentData) => {
|
|
248
|
+
return currentData.filter((row) => !rowIds.has(getRowId2(row)));
|
|
249
|
+
},
|
|
250
|
+
redo: (currentData) => {
|
|
251
|
+
return [...currentData, ...rowsCopy.map((row) => ({ ...row }))];
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
store.push(entry);
|
|
255
|
+
},
|
|
256
|
+
[store, propsRef, onCommit]
|
|
257
|
+
);
|
|
258
|
+
const trackRowsDelete = React.useCallback(
|
|
259
|
+
(rows) => {
|
|
260
|
+
if (!propsRef.current.enabled || rows.length === 0) return;
|
|
261
|
+
onCommit();
|
|
262
|
+
const { getRowId: getRowId2, data: currentData } = propsRef.current;
|
|
263
|
+
const indexById = buildIndexById(currentData, getRowId2);
|
|
264
|
+
const rowsWithPositions = [];
|
|
265
|
+
for (const row of rows) {
|
|
266
|
+
const rowId = getRowId2(row);
|
|
267
|
+
const currentIndex = indexById.get(rowId);
|
|
268
|
+
if (currentIndex !== void 0) {
|
|
269
|
+
rowsWithPositions.push({
|
|
270
|
+
index: currentIndex,
|
|
271
|
+
row: { ...row }
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
rowsWithPositions.sort((a, b) => a.index - b.index);
|
|
276
|
+
const rowIds = new Set(rows.map((row) => getRowId2(row)));
|
|
277
|
+
const entry = {
|
|
278
|
+
variant: "rows_delete",
|
|
279
|
+
count: rows.length,
|
|
280
|
+
timestamp: Date.now(),
|
|
281
|
+
undo: (currentData2) => {
|
|
282
|
+
const newData = [...currentData2];
|
|
283
|
+
for (const { index, row } of rowsWithPositions) {
|
|
284
|
+
const insertIndex = Math.min(index, newData.length);
|
|
285
|
+
newData.splice(insertIndex, 0, { ...row });
|
|
286
|
+
}
|
|
287
|
+
return newData;
|
|
288
|
+
},
|
|
289
|
+
redo: (currentData2) => {
|
|
290
|
+
return currentData2.filter((row) => !rowIds.has(getRowId2(row)));
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
store.push(entry);
|
|
294
|
+
},
|
|
295
|
+
[store, propsRef, onCommit]
|
|
296
|
+
);
|
|
297
|
+
React.useEffect(() => {
|
|
298
|
+
const pending = pendingBatchRef.current;
|
|
299
|
+
return () => {
|
|
300
|
+
if (pending.timeoutId) {
|
|
301
|
+
clearTimeout(pending.timeoutId);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
}, []);
|
|
305
|
+
React.useEffect(() => {
|
|
306
|
+
if (!enabled) return;
|
|
307
|
+
function onKeyDown(event) {
|
|
308
|
+
const isCtrlOrCmd = event.ctrlKey || event.metaKey;
|
|
309
|
+
const key = event.key.toLowerCase();
|
|
310
|
+
if (!isCtrlOrCmd || key !== "z" && key !== "y") return;
|
|
311
|
+
const activeElement = document.activeElement;
|
|
312
|
+
if (activeElement) {
|
|
313
|
+
const isInput = activeElement.tagName === "INPUT" || activeElement.tagName === "TEXTAREA";
|
|
314
|
+
const isContentEditable = activeElement.getAttribute("contenteditable") === "true";
|
|
315
|
+
const isInPopover = getIsInPopover(activeElement);
|
|
316
|
+
if (isInput || isContentEditable || isInPopover) return;
|
|
317
|
+
}
|
|
318
|
+
if (key === "z" && !event.shiftKey) {
|
|
319
|
+
event.preventDefault();
|
|
320
|
+
onUndo();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (key === "z" && event.shiftKey || key === "y") {
|
|
324
|
+
event.preventDefault();
|
|
325
|
+
onRedo();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
document.addEventListener("keydown", onKeyDown);
|
|
329
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
330
|
+
}, [enabled, onUndo, onRedo]);
|
|
331
|
+
return React.useMemo(
|
|
332
|
+
() => ({
|
|
333
|
+
canUndo,
|
|
334
|
+
canRedo,
|
|
335
|
+
onUndo,
|
|
336
|
+
onRedo,
|
|
337
|
+
onClear,
|
|
338
|
+
trackCellsUpdate,
|
|
339
|
+
trackRowsAdd,
|
|
340
|
+
trackRowsDelete
|
|
341
|
+
}),
|
|
342
|
+
[canUndo, canRedo, onUndo, onRedo, onClear, trackCellsUpdate, trackRowsAdd, trackRowsDelete]
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
export {
|
|
346
|
+
useDataGridUndoRedo
|
|
347
|
+
};
|
|
348
|
+
//# sourceMappingURL=use-data-grid-undo-redo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/data-grid/use-data-grid-undo-redo.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { type DataGridLocale, type DataGridMessages, getDataGridMessages } from \"./messages.js\";\nimport { toastInfo, toastSuccess } from \"./toast.js\";\nimport { useAsRef } from \"./use-as-ref.js\";\nimport { useLazyRef } from \"./use-lazy-ref.js\";\nimport { getIsInPopover } from \"./utils.js\";\n\nconst DEFAULT_MAX_HISTORY = 100;\nconst BATCH_TIMEOUT = 300;\n\ninterface HistoryEntry<TData> {\n variant: \"cells_update\" | \"rows_add\" | \"rows_delete\";\n count: number;\n timestamp: number;\n undo: (currentData: TData[]) => TData[];\n redo: (currentData: TData[]) => TData[];\n}\n\ninterface UndoRedoCellUpdate {\n rowId: string;\n columnId: string;\n previousValue: unknown;\n newValue: unknown;\n}\n\ninterface StoreState<TData> {\n undoStack: HistoryEntry<TData>[];\n redoStack: HistoryEntry<TData>[];\n hasPendingChanges: boolean;\n}\n\ninterface Store<TData> {\n subscribe: (callback: () => void) => () => void;\n getState: () => StoreState<TData>;\n push: (entry: HistoryEntry<TData>) => void;\n undo: () => HistoryEntry<TData> | null;\n redo: () => HistoryEntry<TData> | null;\n clear: () => void;\n setPendingChanges: (value: boolean) => void;\n notify: () => void;\n}\n\nfunction useStore<T>(store: Store<T>, selector: (state: StoreState<T>) => boolean): boolean {\n const getSnapshot = React.useCallback(() => selector(store.getState()), [store, selector]);\n\n return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\nfunction buildIndexById<TData>(data: TData[], getRowId: (row: TData) => string): Map<string, number> {\n const map = new Map<string, number>();\n for (let i = 0; i < data.length; i++) {\n const row = data[i];\n if (row) {\n map.set(getRowId(row), i);\n }\n }\n return map;\n}\n\nfunction getPendingKey(rowId: string, columnId: string): string {\n return `${rowId}\\0${columnId}`;\n}\n\n/**\n * Tuỳ chọn của {@link useDataGridUndoRedo}. Lịch sử nằm hoàn toàn ở phía trình duyệt:\n * hook tính ra mảng dữ liệu trước/sau rồi trả về qua `onDataChange` — ứng dụng tự\n * đồng bộ lên máy chủ (hoặc bỏ qua nếu chỉ làm việc với state cục bộ).\n */\ninterface UseDataGridUndoRedoProps<TData> {\n data: TData[];\n onDataChange: (data: TData[]) => void;\n /** Khoá định danh bản ghi — phải khớp với `getRowId` của lưới */\n getRowId: (row: TData) => string;\n /** Số thao tác giữ trong lịch sử (mặc định 100) */\n maxHistory?: number;\n enabled?: boolean;\n /**\n * Ngôn ngữ của toast mà hook bắn ra (`\"en\"` mặc định · `\"vi\"`, hoặc cả bảng chuỗi tự soạn).\n *\n * Hook giữ bảng chuỗi của RIÊNG nó chứ không đọc `DataGridLocaleProvider`: toast render ở gốc\n * app qua `<Toaster />`, ngoài hẳn cây DOM của lưới, nên không có context nào để đọc. Truyền\n * cùng giá trị đã truyền cho `useDataGrid`.\n */\n locale?: DataGridLocale | DataGridMessages;\n}\n\ninterface UseDataGridUndoRedoReturn<TData> {\n canUndo: boolean;\n canRedo: boolean;\n onUndo: () => void;\n onRedo: () => void;\n onClear: () => void;\n trackCellsUpdate: (updates: UndoRedoCellUpdate[]) => void;\n trackRowsAdd: (rows: TData[]) => void;\n trackRowsDelete: (rows: TData[]) => void;\n}\n\nfunction useDataGridUndoRedo<TData>({\n data,\n onDataChange,\n getRowId,\n maxHistory = DEFAULT_MAX_HISTORY,\n enabled = true,\n locale\n}: UseDataGridUndoRedoProps<TData>): UseDataGridUndoRedoReturn<TData> {\n const msg = React.useMemo(() => getDataGridMessages(locale), [locale]);\n const msgRef = useAsRef(msg);\n const propsRef = useAsRef({\n data,\n onDataChange,\n getRowId,\n maxHistory,\n enabled\n });\n\n const listenersRef = useLazyRef(() => new Set<() => void>());\n\n const stateRef = useLazyRef<StoreState<TData>>(() => ({\n undoStack: [],\n redoStack: [],\n hasPendingChanges: false\n }));\n\n const pendingBatchRef = React.useRef<{\n byKey: Map<string, UndoRedoCellUpdate>;\n timeoutId: ReturnType<typeof setTimeout> | null;\n }>({\n byKey: new Map(),\n timeoutId: null\n });\n\n const pendingNotifyRef = React.useRef(false);\n\n const store = React.useMemo<Store<TData>>(() => {\n return {\n subscribe: (callback) => {\n listenersRef.current.add(callback);\n return () => listenersRef.current.delete(callback);\n },\n getState: () => stateRef.current,\n push: (entry) => {\n const state = stateRef.current;\n const newUndoStack = [...state.undoStack, entry];\n\n if (newUndoStack.length > propsRef.current.maxHistory) {\n newUndoStack.shift();\n }\n\n stateRef.current = {\n undoStack: newUndoStack,\n redoStack: [],\n hasPendingChanges: false\n };\n store.notify();\n },\n undo: () => {\n const state = stateRef.current;\n if (state.undoStack.length === 0) return null;\n\n const entry = state.undoStack[state.undoStack.length - 1];\n if (!entry) return null;\n\n stateRef.current = {\n undoStack: state.undoStack.slice(0, -1),\n redoStack: [...state.redoStack, entry],\n hasPendingChanges: false\n };\n store.notify();\n return entry;\n },\n redo: () => {\n const state = stateRef.current;\n if (state.redoStack.length === 0) return null;\n\n const entry = state.redoStack[state.redoStack.length - 1];\n if (!entry) return null;\n\n stateRef.current = {\n undoStack: [...state.undoStack, entry],\n redoStack: state.redoStack.slice(0, -1),\n hasPendingChanges: false\n };\n store.notify();\n return entry;\n },\n clear: () => {\n stateRef.current = {\n undoStack: [],\n redoStack: [],\n hasPendingChanges: false\n };\n store.notify();\n },\n setPendingChanges: (value) => {\n if (stateRef.current.hasPendingChanges === value) return;\n stateRef.current = {\n ...stateRef.current,\n hasPendingChanges: value\n };\n\n if (!pendingNotifyRef.current) {\n pendingNotifyRef.current = true;\n queueMicrotask(() => {\n pendingNotifyRef.current = false;\n store.notify();\n });\n }\n },\n notify: () => {\n for (const listener of listenersRef.current) {\n listener();\n }\n }\n };\n }, [listenersRef, stateRef, propsRef]);\n\n const canUndo = useStore(store, (state) => state.undoStack.length > 0 || state.hasPendingChanges);\n const canRedo = useStore(store, (state) => state.redoStack.length > 0);\n\n const onCommit = React.useCallback(() => {\n const pending = pendingBatchRef.current;\n if (pending.byKey.size === 0) return;\n\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n pending.timeoutId = null;\n }\n\n const updates = Array.from(pending.byKey.values());\n pending.byKey.clear();\n\n const { getRowId } = propsRef.current;\n\n const entry: HistoryEntry<TData> = {\n variant: \"cells_update\",\n count: updates.length,\n timestamp: Date.now(),\n undo: (currentData) => {\n const newData = [...currentData];\n const indexById = buildIndexById(newData, getRowId);\n\n for (const update of updates) {\n const index = indexById.get(update.rowId);\n if (index !== undefined) {\n const row = newData[index];\n if (row) {\n newData[index] = {\n ...row,\n [update.columnId]: update.previousValue\n };\n }\n }\n }\n return newData;\n },\n redo: (currentData) => {\n const newData = [...currentData];\n const indexById = buildIndexById(newData, getRowId);\n\n for (const update of updates) {\n const index = indexById.get(update.rowId);\n if (index !== undefined) {\n const row = newData[index];\n if (row) {\n newData[index] = { ...row, [update.columnId]: update.newValue };\n }\n }\n }\n return newData;\n }\n };\n\n store.push(entry);\n }, [store, propsRef]);\n\n const onUndo = React.useCallback(() => {\n if (!propsRef.current.enabled) return;\n\n onCommit();\n\n const entry = store.undo();\n if (!entry) {\n toastInfo(msgRef.current.toast.nothingToUndo);\n return;\n }\n\n const newData = entry.undo(propsRef.current.data);\n propsRef.current.onDataChange(newData);\n\n toastSuccess(msgRef.current.toast.undone(entry.count));\n }, [store, propsRef, onCommit, msgRef]);\n\n const onRedo = React.useCallback(() => {\n if (!propsRef.current.enabled) return;\n\n onCommit();\n\n const entry = store.redo();\n if (!entry) {\n toastInfo(msgRef.current.toast.nothingToRedo);\n return;\n }\n\n const newData = entry.redo(propsRef.current.data);\n propsRef.current.onDataChange(newData);\n\n toastSuccess(msgRef.current.toast.redone(entry.count));\n }, [store, propsRef, onCommit, msgRef]);\n\n const onClear = React.useCallback(() => {\n const pending = pendingBatchRef.current;\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n pending.timeoutId = null;\n }\n pending.byKey.clear();\n\n store.clear();\n }, [store]);\n\n const trackCellsUpdate = React.useCallback(\n (updates: UndoRedoCellUpdate[]) => {\n if (!propsRef.current.enabled || updates.length === 0) return;\n\n const filteredUpdates = updates.filter((u) => !Object.is(u.previousValue, u.newValue));\n if (filteredUpdates.length === 0) return;\n\n const pending = pendingBatchRef.current;\n\n for (const update of filteredUpdates) {\n const key = getPendingKey(update.rowId, update.columnId);\n const existing = pending.byKey.get(key);\n\n if (existing) {\n pending.byKey.set(key, { ...existing, newValue: update.newValue });\n } else {\n pending.byKey.set(key, update);\n }\n }\n\n store.setPendingChanges(true);\n\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n }\n pending.timeoutId = setTimeout(onCommit, BATCH_TIMEOUT);\n },\n [store, propsRef, onCommit]\n );\n\n const trackRowsAdd = React.useCallback(\n (rows: TData[]) => {\n if (!propsRef.current.enabled || rows.length === 0) return;\n\n onCommit();\n\n const { getRowId } = propsRef.current;\n\n const rowIds = new Set(rows.map((row) => getRowId(row)));\n const rowsCopy = rows.map((row) => ({ ...row }));\n\n const entry: HistoryEntry<TData> = {\n variant: \"rows_add\",\n count: rows.length,\n timestamp: Date.now(),\n undo: (currentData) => {\n return currentData.filter((row) => !rowIds.has(getRowId(row)));\n },\n redo: (currentData) => {\n return [...currentData, ...rowsCopy.map((row) => ({ ...row }))];\n }\n };\n\n store.push(entry);\n },\n [store, propsRef, onCommit]\n );\n\n const trackRowsDelete = React.useCallback(\n (rows: TData[]) => {\n if (!propsRef.current.enabled || rows.length === 0) return;\n\n onCommit();\n\n const { getRowId, data: currentData } = propsRef.current;\n\n const indexById = buildIndexById(currentData, getRowId);\n\n const rowsWithPositions: Array<{ index: number; row: TData }> = [];\n for (const row of rows) {\n const rowId = getRowId(row);\n const currentIndex = indexById.get(rowId);\n if (currentIndex !== undefined) {\n rowsWithPositions.push({\n index: currentIndex,\n row: { ...row }\n });\n }\n }\n\n rowsWithPositions.sort((a, b) => a.index - b.index);\n\n const rowIds = new Set(rows.map((row) => getRowId(row)));\n\n const entry: HistoryEntry<TData> = {\n variant: \"rows_delete\",\n count: rows.length,\n timestamp: Date.now(),\n undo: (currentData) => {\n const newData = [...currentData];\n for (const { index, row } of rowsWithPositions) {\n const insertIndex = Math.min(index, newData.length);\n newData.splice(insertIndex, 0, { ...row });\n }\n return newData;\n },\n redo: (currentData) => {\n return currentData.filter((row) => !rowIds.has(getRowId(row)));\n }\n };\n\n store.push(entry);\n },\n [store, propsRef, onCommit]\n );\n\n React.useEffect(() => {\n const pending = pendingBatchRef.current;\n return () => {\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n }\n };\n }, []);\n\n React.useEffect(() => {\n if (!enabled) return;\n\n function onKeyDown(event: KeyboardEvent) {\n const isCtrlOrCmd = event.ctrlKey || event.metaKey;\n const key = event.key.toLowerCase();\n\n if (!isCtrlOrCmd || (key !== \"z\" && key !== \"y\")) return;\n\n const activeElement = document.activeElement;\n if (activeElement) {\n const isInput = activeElement.tagName === \"INPUT\" || activeElement.tagName === \"TEXTAREA\";\n const isContentEditable = activeElement.getAttribute(\"contenteditable\") === \"true\";\n const isInPopover = getIsInPopover(activeElement);\n\n if (isInput || isContentEditable || isInPopover) return;\n }\n\n if (key === \"z\" && !event.shiftKey) {\n event.preventDefault();\n onUndo();\n return;\n }\n\n if ((key === \"z\" && event.shiftKey) || key === \"y\") {\n event.preventDefault();\n onRedo();\n }\n }\n\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [enabled, onUndo, onRedo]);\n\n return React.useMemo(\n () => ({\n canUndo,\n canRedo,\n onUndo,\n onRedo,\n onClear,\n trackCellsUpdate,\n trackRowsAdd,\n trackRowsDelete\n }),\n [canUndo, canRedo, onUndo, onRedo, onClear, trackCellsUpdate, trackRowsAdd, trackRowsDelete]\n );\n}\n\nexport {\n //\n type UndoRedoCellUpdate,\n useDataGridUndoRedo\n};\n"],"mappings":";AAEA,YAAY,WAAW;AAEvB,SAAqD,2BAA2B;AAChF,SAAS,WAAW,oBAAoB;AACxC,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB;AAE/B,MAAM,sBAAsB;AAC5B,MAAM,gBAAgB;AAkCtB,SAAS,SAAY,OAAiB,UAAsD;AAC1F,QAAM,cAAc,MAAM,YAAY,MAAM,SAAS,MAAM,SAAS,CAAC,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEzF,SAAO,MAAM,qBAAqB,MAAM,WAAW,aAAa,WAAW;AAC7E;AAEA,SAAS,eAAsB,MAAe,UAAuD;AACnG,QAAM,MAAM,oBAAI,IAAoB;AACpC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,KAAK;AACP,UAAI,IAAI,SAAS,GAAG,GAAG,CAAC;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,cAAc,OAAe,UAA0B;AAC9D,SAAO,GAAG,KAAK,KAAK,QAAQ;AAC9B;AAoCA,SAAS,oBAA2B;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,UAAU;AAAA,EACV;AACF,GAAsE;AACpE,QAAM,MAAM,MAAM,QAAQ,MAAM,oBAAoB,MAAM,GAAG,CAAC,MAAM,CAAC;AACrE,QAAM,SAAS,SAAS,GAAG;AAC3B,QAAM,WAAW,SAAS;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,eAAe,WAAW,MAAM,oBAAI,IAAgB,CAAC;AAE3D,QAAM,WAAW,WAA8B,OAAO;AAAA,IACpD,WAAW,CAAC;AAAA,IACZ,WAAW,CAAC;AAAA,IACZ,mBAAmB;AAAA,EACrB,EAAE;AAEF,QAAM,kBAAkB,MAAM,OAG3B;AAAA,IACD,OAAO,oBAAI,IAAI;AAAA,IACf,WAAW;AAAA,EACb,CAAC;AAED,QAAM,mBAAmB,MAAM,OAAO,KAAK;AAE3C,QAAM,QAAQ,MAAM,QAAsB,MAAM;AAC9C,WAAO;AAAA,MACL,WAAW,CAAC,aAAa;AACvB,qBAAa,QAAQ,IAAI,QAAQ;AACjC,eAAO,MAAM,aAAa,QAAQ,OAAO,QAAQ;AAAA,MACnD;AAAA,MACA,UAAU,MAAM,SAAS;AAAA,MACzB,MAAM,CAAC,UAAU;AACf,cAAM,QAAQ,SAAS;AACvB,cAAM,eAAe,CAAC,GAAG,MAAM,WAAW,KAAK;AAE/C,YAAI,aAAa,SAAS,SAAS,QAAQ,YAAY;AACrD,uBAAa,MAAM;AAAA,QACrB;AAEA,iBAAS,UAAU;AAAA,UACjB,WAAW;AAAA,UACX,WAAW,CAAC;AAAA,UACZ,mBAAmB;AAAA,QACrB;AACA,cAAM,OAAO;AAAA,MACf;AAAA,MACA,MAAM,MAAM;AACV,cAAM,QAAQ,SAAS;AACvB,YAAI,MAAM,UAAU,WAAW,EAAG,QAAO;AAEzC,cAAM,QAAQ,MAAM,UAAU,MAAM,UAAU,SAAS,CAAC;AACxD,YAAI,CAAC,MAAO,QAAO;AAEnB,iBAAS,UAAU;AAAA,UACjB,WAAW,MAAM,UAAU,MAAM,GAAG,EAAE;AAAA,UACtC,WAAW,CAAC,GAAG,MAAM,WAAW,KAAK;AAAA,UACrC,mBAAmB;AAAA,QACrB;AACA,cAAM,OAAO;AACb,eAAO;AAAA,MACT;AAAA,MACA,MAAM,MAAM;AACV,cAAM,QAAQ,SAAS;AACvB,YAAI,MAAM,UAAU,WAAW,EAAG,QAAO;AAEzC,cAAM,QAAQ,MAAM,UAAU,MAAM,UAAU,SAAS,CAAC;AACxD,YAAI,CAAC,MAAO,QAAO;AAEnB,iBAAS,UAAU;AAAA,UACjB,WAAW,CAAC,GAAG,MAAM,WAAW,KAAK;AAAA,UACrC,WAAW,MAAM,UAAU,MAAM,GAAG,EAAE;AAAA,UACtC,mBAAmB;AAAA,QACrB;AACA,cAAM,OAAO;AACb,eAAO;AAAA,MACT;AAAA,MACA,OAAO,MAAM;AACX,iBAAS,UAAU;AAAA,UACjB,WAAW,CAAC;AAAA,UACZ,WAAW,CAAC;AAAA,UACZ,mBAAmB;AAAA,QACrB;AACA,cAAM,OAAO;AAAA,MACf;AAAA,MACA,mBAAmB,CAAC,UAAU;AAC5B,YAAI,SAAS,QAAQ,sBAAsB,MAAO;AAClD,iBAAS,UAAU;AAAA,UACjB,GAAG,SAAS;AAAA,UACZ,mBAAmB;AAAA,QACrB;AAEA,YAAI,CAAC,iBAAiB,SAAS;AAC7B,2BAAiB,UAAU;AAC3B,yBAAe,MAAM;AACnB,6BAAiB,UAAU;AAC3B,kBAAM,OAAO;AAAA,UACf,CAAC;AAAA,QACH;AAAA,MACF;AAAA,MACA,QAAQ,MAAM;AACZ,mBAAW,YAAY,aAAa,SAAS;AAC3C,mBAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,cAAc,UAAU,QAAQ,CAAC;AAErC,QAAM,UAAU,SAAS,OAAO,CAAC,UAAU,MAAM,UAAU,SAAS,KAAK,MAAM,iBAAiB;AAChG,QAAM,UAAU,SAAS,OAAO,CAAC,UAAU,MAAM,UAAU,SAAS,CAAC;AAErE,QAAM,WAAW,MAAM,YAAY,MAAM;AACvC,UAAM,UAAU,gBAAgB;AAChC,QAAI,QAAQ,MAAM,SAAS,EAAG;AAE9B,QAAI,QAAQ,WAAW;AACrB,mBAAa,QAAQ,SAAS;AAC9B,cAAQ,YAAY;AAAA,IACtB;AAEA,UAAM,UAAU,MAAM,KAAK,QAAQ,MAAM,OAAO,CAAC;AACjD,YAAQ,MAAM,MAAM;AAEpB,UAAM,EAAE,UAAAA,UAAS,IAAI,SAAS;AAE9B,UAAM,QAA6B;AAAA,MACjC,SAAS;AAAA,MACT,OAAO,QAAQ;AAAA,MACf,WAAW,KAAK,IAAI;AAAA,MACpB,MAAM,CAAC,gBAAgB;AACrB,cAAM,UAAU,CAAC,GAAG,WAAW;AAC/B,cAAM,YAAY,eAAe,SAASA,SAAQ;AAElD,mBAAW,UAAU,SAAS;AAC5B,gBAAM,QAAQ,UAAU,IAAI,OAAO,KAAK;AACxC,cAAI,UAAU,QAAW;AACvB,kBAAM,MAAM,QAAQ,KAAK;AACzB,gBAAI,KAAK;AACP,sBAAQ,KAAK,IAAI;AAAA,gBACf,GAAG;AAAA,gBACH,CAAC,OAAO,QAAQ,GAAG,OAAO;AAAA,cAC5B;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAAA,MACA,MAAM,CAAC,gBAAgB;AACrB,cAAM,UAAU,CAAC,GAAG,WAAW;AAC/B,cAAM,YAAY,eAAe,SAASA,SAAQ;AAElD,mBAAW,UAAU,SAAS;AAC5B,gBAAM,QAAQ,UAAU,IAAI,OAAO,KAAK;AACxC,cAAI,UAAU,QAAW;AACvB,kBAAM,MAAM,QAAQ,KAAK;AACzB,gBAAI,KAAK;AACP,sBAAQ,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC,OAAO,QAAQ,GAAG,OAAO,SAAS;AAAA,YAChE;AAAA,UACF;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,KAAK,KAAK;AAAA,EAClB,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,QAAM,SAAS,MAAM,YAAY,MAAM;AACrC,QAAI,CAAC,SAAS,QAAQ,QAAS;AAE/B,aAAS;AAET,UAAM,QAAQ,MAAM,KAAK;AACzB,QAAI,CAAC,OAAO;AACV,gBAAU,OAAO,QAAQ,MAAM,aAAa;AAC5C;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,KAAK,SAAS,QAAQ,IAAI;AAChD,aAAS,QAAQ,aAAa,OAAO;AAErC,iBAAa,OAAO,QAAQ,MAAM,OAAO,MAAM,KAAK,CAAC;AAAA,EACvD,GAAG,CAAC,OAAO,UAAU,UAAU,MAAM,CAAC;AAEtC,QAAM,SAAS,MAAM,YAAY,MAAM;AACrC,QAAI,CAAC,SAAS,QAAQ,QAAS;AAE/B,aAAS;AAET,UAAM,QAAQ,MAAM,KAAK;AACzB,QAAI,CAAC,OAAO;AACV,gBAAU,OAAO,QAAQ,MAAM,aAAa;AAC5C;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,KAAK,SAAS,QAAQ,IAAI;AAChD,aAAS,QAAQ,aAAa,OAAO;AAErC,iBAAa,OAAO,QAAQ,MAAM,OAAO,MAAM,KAAK,CAAC;AAAA,EACvD,GAAG,CAAC,OAAO,UAAU,UAAU,MAAM,CAAC;AAEtC,QAAM,UAAU,MAAM,YAAY,MAAM;AACtC,UAAM,UAAU,gBAAgB;AAChC,QAAI,QAAQ,WAAW;AACrB,mBAAa,QAAQ,SAAS;AAC9B,cAAQ,YAAY;AAAA,IACtB;AACA,YAAQ,MAAM,MAAM;AAEpB,UAAM,MAAM;AAAA,EACd,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,mBAAmB,MAAM;AAAA,IAC7B,CAAC,YAAkC;AACjC,UAAI,CAAC,SAAS,QAAQ,WAAW,QAAQ,WAAW,EAAG;AAEvD,YAAM,kBAAkB,QAAQ,OAAO,CAAC,MAAM,CAAC,OAAO,GAAG,EAAE,eAAe,EAAE,QAAQ,CAAC;AACrF,UAAI,gBAAgB,WAAW,EAAG;AAElC,YAAM,UAAU,gBAAgB;AAEhC,iBAAW,UAAU,iBAAiB;AACpC,cAAM,MAAM,cAAc,OAAO,OAAO,OAAO,QAAQ;AACvD,cAAM,WAAW,QAAQ,MAAM,IAAI,GAAG;AAEtC,YAAI,UAAU;AACZ,kBAAQ,MAAM,IAAI,KAAK,EAAE,GAAG,UAAU,UAAU,OAAO,SAAS,CAAC;AAAA,QACnE,OAAO;AACL,kBAAQ,MAAM,IAAI,KAAK,MAAM;AAAA,QAC/B;AAAA,MACF;AAEA,YAAM,kBAAkB,IAAI;AAE5B,UAAI,QAAQ,WAAW;AACrB,qBAAa,QAAQ,SAAS;AAAA,MAChC;AACA,cAAQ,YAAY,WAAW,UAAU,aAAa;AAAA,IACxD;AAAA,IACA,CAAC,OAAO,UAAU,QAAQ;AAAA,EAC5B;AAEA,QAAM,eAAe,MAAM;AAAA,IACzB,CAAC,SAAkB;AACjB,UAAI,CAAC,SAAS,QAAQ,WAAW,KAAK,WAAW,EAAG;AAEpD,eAAS;AAET,YAAM,EAAE,UAAAA,UAAS,IAAI,SAAS;AAE9B,YAAM,SAAS,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQA,UAAS,GAAG,CAAC,CAAC;AACvD,YAAM,WAAW,KAAK,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,EAAE;AAE/C,YAAM,QAA6B;AAAA,QACjC,SAAS;AAAA,QACT,OAAO,KAAK;AAAA,QACZ,WAAW,KAAK,IAAI;AAAA,QACpB,MAAM,CAAC,gBAAgB;AACrB,iBAAO,YAAY,OAAO,CAAC,QAAQ,CAAC,OAAO,IAAIA,UAAS,GAAG,CAAC,CAAC;AAAA,QAC/D;AAAA,QACA,MAAM,CAAC,gBAAgB;AACrB,iBAAO,CAAC,GAAG,aAAa,GAAG,SAAS,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,EAAE,CAAC;AAAA,QAChE;AAAA,MACF;AAEA,YAAM,KAAK,KAAK;AAAA,IAClB;AAAA,IACA,CAAC,OAAO,UAAU,QAAQ;AAAA,EAC5B;AAEA,QAAM,kBAAkB,MAAM;AAAA,IAC5B,CAAC,SAAkB;AACjB,UAAI,CAAC,SAAS,QAAQ,WAAW,KAAK,WAAW,EAAG;AAEpD,eAAS;AAET,YAAM,EAAE,UAAAA,WAAU,MAAM,YAAY,IAAI,SAAS;AAEjD,YAAM,YAAY,eAAe,aAAaA,SAAQ;AAEtD,YAAM,oBAA0D,CAAC;AACjE,iBAAW,OAAO,MAAM;AACtB,cAAM,QAAQA,UAAS,GAAG;AAC1B,cAAM,eAAe,UAAU,IAAI,KAAK;AACxC,YAAI,iBAAiB,QAAW;AAC9B,4BAAkB,KAAK;AAAA,YACrB,OAAO;AAAA,YACP,KAAK,EAAE,GAAG,IAAI;AAAA,UAChB,CAAC;AAAA,QACH;AAAA,MACF;AAEA,wBAAkB,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAElD,YAAM,SAAS,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQA,UAAS,GAAG,CAAC,CAAC;AAEvD,YAAM,QAA6B;AAAA,QACjC,SAAS;AAAA,QACT,OAAO,KAAK;AAAA,QACZ,WAAW,KAAK,IAAI;AAAA,QACpB,MAAM,CAACC,iBAAgB;AACrB,gBAAM,UAAU,CAAC,GAAGA,YAAW;AAC/B,qBAAW,EAAE,OAAO,IAAI,KAAK,mBAAmB;AAC9C,kBAAM,cAAc,KAAK,IAAI,OAAO,QAAQ,MAAM;AAClD,oBAAQ,OAAO,aAAa,GAAG,EAAE,GAAG,IAAI,CAAC;AAAA,UAC3C;AACA,iBAAO;AAAA,QACT;AAAA,QACA,MAAM,CAACA,iBAAgB;AACrB,iBAAOA,aAAY,OAAO,CAAC,QAAQ,CAAC,OAAO,IAAID,UAAS,GAAG,CAAC,CAAC;AAAA,QAC/D;AAAA,MACF;AAEA,YAAM,KAAK,KAAK;AAAA,IAClB;AAAA,IACA,CAAC,OAAO,UAAU,QAAQ;AAAA,EAC5B;AAEA,QAAM,UAAU,MAAM;AACpB,UAAM,UAAU,gBAAgB;AAChC,WAAO,MAAM;AACX,UAAI,QAAQ,WAAW;AACrB,qBAAa,QAAQ,SAAS;AAAA,MAChC;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAS;AAEd,aAAS,UAAU,OAAsB;AACvC,YAAM,cAAc,MAAM,WAAW,MAAM;AAC3C,YAAM,MAAM,MAAM,IAAI,YAAY;AAElC,UAAI,CAAC,eAAgB,QAAQ,OAAO,QAAQ,IAAM;AAElD,YAAM,gBAAgB,SAAS;AAC/B,UAAI,eAAe;AACjB,cAAM,UAAU,cAAc,YAAY,WAAW,cAAc,YAAY;AAC/E,cAAM,oBAAoB,cAAc,aAAa,iBAAiB,MAAM;AAC5E,cAAM,cAAc,eAAe,aAAa;AAEhD,YAAI,WAAW,qBAAqB,YAAa;AAAA,MACnD;AAEA,UAAI,QAAQ,OAAO,CAAC,MAAM,UAAU;AAClC,cAAM,eAAe;AACrB,eAAO;AACP;AAAA,MACF;AAEA,UAAK,QAAQ,OAAO,MAAM,YAAa,QAAQ,KAAK;AAClD,cAAM,eAAe;AACrB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,SAAS;AAC9C,WAAO,MAAM,SAAS,oBAAoB,WAAW,SAAS;AAAA,EAChE,GAAG,CAAC,SAAS,QAAQ,MAAM,CAAC;AAE5B,SAAO,MAAM;AAAA,IACX,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,SAAS,SAAS,QAAQ,QAAQ,SAAS,kBAAkB,cAAc,eAAe;AAAA,EAC7F;AACF;","names":["getRowId","currentData"]}
|