@comitor/ui 3.11.0 → 3.12.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 +10 -5
- package/dist/chat/chat-composer.d.ts +36 -0
- package/dist/chat/chat-composer.js +85 -0
- package/dist/chat/chat-composer.js.map +1 -0
- package/dist/chat/chat-constants.d.ts +63 -0
- package/dist/chat/chat-constants.js +1 -0
- package/dist/chat/chat-constants.js.map +1 -0
- package/dist/chat/chat-message.d.ts +51 -0
- package/dist/chat/chat-message.js +137 -0
- package/dist/chat/chat-message.js.map +1 -0
- package/dist/chat/chat-sources.d.ts +25 -0
- package/dist/chat/chat-sources.js +97 -0
- package/dist/chat/chat-sources.js.map +1 -0
- package/dist/chat/chat-steps.d.ts +20 -0
- package/dist/chat/chat-steps.js +84 -0
- package/dist/chat/chat-steps.js.map +1 -0
- package/dist/chat/chat-thread.d.ts +21 -0
- package/dist/chat/chat-thread.js +87 -0
- package/dist/chat/chat-thread.js.map +1 -0
- package/dist/chat/index.d.ts +12 -0
- package/dist/chat/index.js +14 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/components/composite/gantt-constants.d.ts +127 -0
- package/dist/components/composite/gantt-constants.js +5 -0
- package/dist/components/composite/gantt-constants.js.map +1 -0
- package/dist/components/composite/gantt-utils.d.ts +106 -0
- package/dist/components/composite/gantt-utils.js +196 -0
- package/dist/components/composite/gantt-utils.js.map +1 -0
- package/dist/components/composite/gantt.d.ts +93 -0
- package/dist/components/composite/gantt.js +1137 -0
- package/dist/components/composite/gantt.js.map +1 -0
- package/dist/components/composite/stat-card.d.ts +1 -1
- package/dist/components/composite/tree-view-constants.d.ts +96 -0
- package/dist/components/composite/tree-view-constants.js +85 -0
- package/dist/components/composite/tree-view-constants.js.map +1 -0
- package/dist/components/composite/tree-view.d.ts +58 -0
- package/dist/components/composite/tree-view.js +385 -0
- package/dist/components/composite/tree-view.js.map +1 -0
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/data-grid/data-grid-cell-wrapper.d.ts +1 -1
- package/dist/data-grid/data-grid-cell-wrapper.js +11 -5
- package/dist/data-grid/data-grid-cell-wrapper.js.map +1 -1
- package/dist/data-grid/data-grid-column-header.js +12 -12
- package/dist/data-grid/data-grid-column-header.js.map +1 -1
- package/dist/data-grid/messages-en.d.ts +2 -0
- package/dist/data-grid/messages-en.js +3 -1
- package/dist/data-grid/messages-en.js.map +1 -1
- package/dist/data-grid/messages-vi.js +2 -1
- package/dist/data-grid/messages-vi.js.map +1 -1
- package/dist/data-grid/messages.d.ts +2 -0
- package/dist/data-grid/use-data-grid.d.ts +1 -0
- package/dist/data-grid/utils.d.ts +13 -1
- package/dist/data-grid/utils.js +13 -0
- package/dist/data-grid/utils.js.map +1 -1
- package/dist/editor/bubble-toolbar.d.ts +1 -1
- package/dist/editor/editor-core.d.ts +2 -2
- package/dist/editor/editor-core.js +6 -0
- package/dist/editor/editor-core.js.map +1 -1
- package/dist/editor/editor-types.d.ts +1 -1
- package/dist/editor/fixed-toolbar.d.ts +1 -1
- package/dist/editor/image-upload.d.ts +1 -1
- package/dist/editor/index.d.ts +1 -1
- package/dist/editor/markdown-editor.d.ts +1 -1
- package/dist/editor/markdown-extensions.d.ts +3 -1
- package/dist/editor/markdown-extensions.js +3 -1
- package/dist/editor/markdown-extensions.js.map +1 -1
- package/dist/editor/rich-text-editor.d.ts +1 -1
- package/dist/editor/slash-commands.d.ts +1 -1
- package/dist/editor/toolbar-actions.d.ts +1 -1
- package/dist/{editor-types-CSrMeuSw.d.ts → editor-types-BacXD2VN.d.ts} +7 -1
- package/dist/i18n/locale-provider.d.ts +3 -0
- package/dist/i18n/messages.d.ts +6 -0
- package/dist/i18n/messages.js +11 -2
- package/dist/i18n/messages.js.map +1 -1
- package/dist/i18n/namespaces/chat.d.ts +8 -0
- package/dist/i18n/namespaces/chat.js +47 -0
- package/dist/i18n/namespaces/chat.js.map +1 -0
- package/dist/i18n/namespaces/gantt.d.ts +10 -0
- package/dist/i18n/namespaces/gantt.js +57 -0
- package/dist/i18n/namespaces/gantt.js.map +1 -0
- package/dist/i18n/namespaces/tree-view.d.ts +8 -0
- package/dist/i18n/namespaces/tree-view.js +21 -0
- package/dist/i18n/namespaces/tree-view.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +9 -2
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createElement } from "react";
|
|
4
|
+
import {
|
|
5
|
+
dragAndDropFeature,
|
|
6
|
+
hotkeysCoreFeature,
|
|
7
|
+
isOrderedDragTarget,
|
|
8
|
+
selectionFeature,
|
|
9
|
+
syncDataLoaderFeature
|
|
10
|
+
} from "@headless-tree/core";
|
|
11
|
+
import { useTree } from "@headless-tree/react";
|
|
12
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
13
|
+
import { ChevronRight } from "lucide-react";
|
|
14
|
+
import * as React from "react";
|
|
15
|
+
import { useComitorLabels, useComitorLocale } from "../../i18n/locale-provider.js";
|
|
16
|
+
import { cn } from "../../lib/cn.js";
|
|
17
|
+
import {
|
|
18
|
+
computeTreeNumbering,
|
|
19
|
+
computeTreeRollup,
|
|
20
|
+
groupTreeChildren
|
|
21
|
+
} from "./tree-view-constants.js";
|
|
22
|
+
const ROOT_ID = "__comitor_tree_root__";
|
|
23
|
+
function useCoarsePointer() {
|
|
24
|
+
const subscribe = React.useCallback((onChange) => {
|
|
25
|
+
const mql = window.matchMedia("(pointer: coarse)");
|
|
26
|
+
mql.addEventListener("change", onChange);
|
|
27
|
+
return () => mql.removeEventListener("change", onChange);
|
|
28
|
+
}, []);
|
|
29
|
+
return React.useSyncExternalStore(
|
|
30
|
+
subscribe,
|
|
31
|
+
() => window.matchMedia("(pointer: coarse)").matches,
|
|
32
|
+
() => false
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
function useSemiControlled(value, defaultValue, onChange) {
|
|
36
|
+
const [inner, setInner] = React.useState(defaultValue ?? []);
|
|
37
|
+
const current = value ?? inner;
|
|
38
|
+
const currentRef = React.useRef(current);
|
|
39
|
+
currentRef.current = current;
|
|
40
|
+
const onChangeRef = React.useRef(onChange);
|
|
41
|
+
onChangeRef.current = onChange;
|
|
42
|
+
const controlled = value !== void 0;
|
|
43
|
+
const set = React.useCallback(
|
|
44
|
+
(updater) => {
|
|
45
|
+
const next = typeof updater === "function" ? updater(currentRef.current) : updater;
|
|
46
|
+
currentRef.current = next;
|
|
47
|
+
if (!controlled) setInner(next);
|
|
48
|
+
onChangeRef.current?.(next);
|
|
49
|
+
},
|
|
50
|
+
[controlled]
|
|
51
|
+
);
|
|
52
|
+
return [current, set];
|
|
53
|
+
}
|
|
54
|
+
const FEATURES = [syncDataLoaderFeature, selectionFeature, hotkeysCoreFeature, dragAndDropFeature];
|
|
55
|
+
function TreeView({
|
|
56
|
+
items,
|
|
57
|
+
expandedIds,
|
|
58
|
+
defaultExpandedIds,
|
|
59
|
+
onExpandedIdsChange,
|
|
60
|
+
selectedIds,
|
|
61
|
+
defaultSelectedIds,
|
|
62
|
+
onSelectedIdsChange,
|
|
63
|
+
onMove,
|
|
64
|
+
onItemOpen,
|
|
65
|
+
columns,
|
|
66
|
+
numbering = false,
|
|
67
|
+
renderItem,
|
|
68
|
+
rowHeight: rowHeightProp,
|
|
69
|
+
indent = 20,
|
|
70
|
+
readOnly = false,
|
|
71
|
+
labels,
|
|
72
|
+
className,
|
|
73
|
+
"aria-label": ariaLabel,
|
|
74
|
+
...props
|
|
75
|
+
}) {
|
|
76
|
+
const t = useComitorLabels("treeView", labels);
|
|
77
|
+
const { locale } = useComitorLocale();
|
|
78
|
+
const coarse = useCoarsePointer();
|
|
79
|
+
const rowHeight = rowHeightProp ?? (coarse ? 44 : 32);
|
|
80
|
+
const editable = !readOnly && !!onMove;
|
|
81
|
+
const [expanded, setExpanded] = useSemiControlled(expandedIds, defaultExpandedIds, onExpandedIdsChange);
|
|
82
|
+
const [selected, setSelected] = useSemiControlled(selectedIds, defaultSelectedIds, onSelectedIdsChange);
|
|
83
|
+
const children = React.useMemo(() => groupTreeChildren(items), [items]);
|
|
84
|
+
const byId = React.useMemo(() => new Map(items.map((item) => [item.id, item])), [items]);
|
|
85
|
+
const numbers = React.useMemo(() => numbering ? computeTreeNumbering(items) : null, [numbering, items]);
|
|
86
|
+
const rollups = React.useMemo(
|
|
87
|
+
() => (columns ?? []).map((column) => column.value ? computeTreeRollup(items, column) : null),
|
|
88
|
+
[columns, items]
|
|
89
|
+
);
|
|
90
|
+
const numberFormat = React.useMemo(() => new Intl.NumberFormat(locale, { maximumFractionDigits: 1 }), [locale]);
|
|
91
|
+
const dataRef = React.useRef({ children, byId });
|
|
92
|
+
dataRef.current = { children, byId };
|
|
93
|
+
const dataLoader = React.useMemo(
|
|
94
|
+
() => ({
|
|
95
|
+
getItem: (id) => dataRef.current.byId.get(id) ?? { id, parentId: null, name: "" },
|
|
96
|
+
getChildren: (id) => (dataRef.current.children.get(id === ROOT_ID ? null : id) ?? []).map((child) => child.id)
|
|
97
|
+
}),
|
|
98
|
+
[]
|
|
99
|
+
);
|
|
100
|
+
const isFolder = (id) => (children.get(id)?.length ?? 0) > 0 || !!byId.get(id)?.isFolder;
|
|
101
|
+
const [announcement, setAnnouncement] = React.useState("");
|
|
102
|
+
const pendingFocusRef = React.useRef(null);
|
|
103
|
+
const emitMove = (move) => {
|
|
104
|
+
if (!onMove) return;
|
|
105
|
+
const first = byId.get(move.ids[0] ?? "");
|
|
106
|
+
const remaining = (children.get(move.parentId) ?? []).filter((item) => !move.ids.includes(item.id));
|
|
107
|
+
const total = remaining.length + move.ids.length;
|
|
108
|
+
const position = Math.min(move.index, remaining.length) + 1;
|
|
109
|
+
onMove(move);
|
|
110
|
+
if (move.parentId) setExpanded((old) => old.includes(move.parentId) ? old : [...old, move.parentId]);
|
|
111
|
+
if (first) {
|
|
112
|
+
const sameParent = move.ids.length === 1 && first.parentId === move.parentId;
|
|
113
|
+
setAnnouncement(
|
|
114
|
+
sameParent ? t.movedTo(first.name, position, total) : t.movedInto(first.name, move.parentId ? byId.get(move.parentId)?.name ?? t.root : t.root, position, total)
|
|
115
|
+
);
|
|
116
|
+
pendingFocusRef.current = first.id;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const siblingsOf = (item) => children.get(item.parentId != null && byId.has(item.parentId) ? item.parentId : null) ?? [];
|
|
120
|
+
const canMoveItem = (id) => !!id && id !== ROOT_ID && editable && !byId.get(id)?.readOnly;
|
|
121
|
+
const outline = {
|
|
122
|
+
canIndent: (id) => {
|
|
123
|
+
const item = byId.get(id);
|
|
124
|
+
return !!item && canMoveItem(id) && siblingsOf(item).indexOf(item) > 0;
|
|
125
|
+
},
|
|
126
|
+
indent: (id) => {
|
|
127
|
+
const item = byId.get(id);
|
|
128
|
+
if (!item) return;
|
|
129
|
+
const siblings = siblingsOf(item);
|
|
130
|
+
const prev = siblings[siblings.indexOf(item) - 1];
|
|
131
|
+
if (!prev) return;
|
|
132
|
+
emitMove({ ids: [id], parentId: prev.id, index: children.get(prev.id)?.length ?? 0, kind: "indent" });
|
|
133
|
+
},
|
|
134
|
+
canOutdent: (id) => {
|
|
135
|
+
const item = byId.get(id);
|
|
136
|
+
return !!item && canMoveItem(id) && item.parentId != null && byId.has(item.parentId);
|
|
137
|
+
},
|
|
138
|
+
outdent: (id) => {
|
|
139
|
+
const item = byId.get(id);
|
|
140
|
+
const parent = item?.parentId != null ? byId.get(item.parentId) : void 0;
|
|
141
|
+
if (!item || !parent) return;
|
|
142
|
+
const grand = parent.parentId != null && byId.has(parent.parentId) ? parent.parentId : null;
|
|
143
|
+
const index = (children.get(grand) ?? []).indexOf(parent) + 1;
|
|
144
|
+
emitMove({ ids: [id], parentId: grand, index, kind: "outdent" });
|
|
145
|
+
},
|
|
146
|
+
canShift: (id, direction) => {
|
|
147
|
+
const item = byId.get(id);
|
|
148
|
+
if (!item || !canMoveItem(id)) return false;
|
|
149
|
+
const siblings = siblingsOf(item);
|
|
150
|
+
const index = siblings.indexOf(item) + direction;
|
|
151
|
+
return index >= 0 && index < siblings.length;
|
|
152
|
+
},
|
|
153
|
+
shift: (id, direction) => {
|
|
154
|
+
const item = byId.get(id);
|
|
155
|
+
if (!item) return;
|
|
156
|
+
const index = siblingsOf(item).indexOf(item) + direction;
|
|
157
|
+
emitMove({
|
|
158
|
+
ids: [id],
|
|
159
|
+
parentId: siblingsOf(item) === children.get(null) ? null : item.parentId,
|
|
160
|
+
index,
|
|
161
|
+
kind: direction < 0 ? "move-up" : "move-down"
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
const onDrop = (dragged, target) => {
|
|
166
|
+
const ids = dragged.map((item) => item.getId());
|
|
167
|
+
const targetId = target.item.getId();
|
|
168
|
+
const parentId = targetId === ROOT_ID ? null : targetId;
|
|
169
|
+
const index = isOrderedDragTarget(target) ? target.insertionIndex : (children.get(parentId) ?? []).filter((item) => !ids.includes(item.id)).length;
|
|
170
|
+
emitMove({ ids, parentId, index, kind: "drag" });
|
|
171
|
+
};
|
|
172
|
+
const scrollRef = React.useRef(null);
|
|
173
|
+
const headerHeight = columns?.length ? 32 : 0;
|
|
174
|
+
const virtualizerRef = React.useRef(null);
|
|
175
|
+
const tree = useTree({
|
|
176
|
+
rootItemId: ROOT_ID,
|
|
177
|
+
dataLoader,
|
|
178
|
+
getItemName: (item) => item.getItemData()?.name ?? "",
|
|
179
|
+
isItemFolder: (item) => isFolder(item.getId()),
|
|
180
|
+
state: { expandedItems: expanded, selectedItems: selected },
|
|
181
|
+
setExpandedItems: setExpanded,
|
|
182
|
+
setSelectedItems: setSelected,
|
|
183
|
+
indent,
|
|
184
|
+
canReorder: editable,
|
|
185
|
+
canDrag: (dragged) => editable && dragged.every((item) => !item.getItemData()?.readOnly),
|
|
186
|
+
canDrop: () => editable,
|
|
187
|
+
onDrop,
|
|
188
|
+
scrollToItem: (item) => virtualizerRef.current?.scrollToIndex(item.getItemMeta().index, { align: "auto" }),
|
|
189
|
+
features: FEATURES
|
|
190
|
+
});
|
|
191
|
+
const onKeyDownCapture = (event) => {
|
|
192
|
+
if (event.target.getAttribute("role") !== "treeitem") return;
|
|
193
|
+
if (event.ctrlKey || event.metaKey) return;
|
|
194
|
+
const id = tree.getFocusedItem().getId();
|
|
195
|
+
const run = (action) => {
|
|
196
|
+
event.preventDefault();
|
|
197
|
+
event.stopPropagation();
|
|
198
|
+
action();
|
|
199
|
+
};
|
|
200
|
+
if (event.key === "Tab" && !event.altKey) {
|
|
201
|
+
if (event.shiftKey ? outline.canOutdent(id) : outline.canIndent(id)) {
|
|
202
|
+
run(() => event.shiftKey ? outline.outdent(id) : outline.indent(id));
|
|
203
|
+
}
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (editable && event.altKey && !event.shiftKey && (event.key === "ArrowUp" || event.key === "ArrowDown")) {
|
|
207
|
+
const direction = event.key === "ArrowUp" ? -1 : 1;
|
|
208
|
+
run(() => {
|
|
209
|
+
if (outline.canShift(id, direction)) outline.shift(id, direction);
|
|
210
|
+
});
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (event.key === "Enter" && !event.shiftKey && !event.altKey && onItemOpen) {
|
|
214
|
+
const data = byId.get(id);
|
|
215
|
+
if (data) run(() => onItemOpen(data));
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
React.useEffect(() => {
|
|
219
|
+
tree.rebuildTree();
|
|
220
|
+
const id = pendingFocusRef.current;
|
|
221
|
+
if (id) {
|
|
222
|
+
pendingFocusRef.current = null;
|
|
223
|
+
tree.getItemInstance(id)?.setFocused();
|
|
224
|
+
tree.updateDomFocus();
|
|
225
|
+
}
|
|
226
|
+
}, [items]);
|
|
227
|
+
const treeItems = tree.getItems();
|
|
228
|
+
const virtualizer = useVirtualizer({
|
|
229
|
+
count: treeItems.length,
|
|
230
|
+
getScrollElement: () => scrollRef.current,
|
|
231
|
+
estimateSize: () => rowHeight,
|
|
232
|
+
overscan: 10,
|
|
233
|
+
scrollMargin: headerHeight
|
|
234
|
+
});
|
|
235
|
+
virtualizerRef.current = virtualizer;
|
|
236
|
+
React.useEffect(() => {
|
|
237
|
+
virtualizer.measure();
|
|
238
|
+
}, [virtualizer, rowHeight]);
|
|
239
|
+
const instructionsId = React.useId();
|
|
240
|
+
const columnText = (column, index, item) => {
|
|
241
|
+
const value = rollups[index]?.get(item.id);
|
|
242
|
+
if (value == null) return null;
|
|
243
|
+
return column.formatText ? column.formatText(value, item) : numberFormat.format(value);
|
|
244
|
+
};
|
|
245
|
+
return /* @__PURE__ */ jsxs(
|
|
246
|
+
"div",
|
|
247
|
+
{
|
|
248
|
+
"data-slot": "tree-view",
|
|
249
|
+
className: cn("flex min-h-0 flex-col overflow-hidden rounded-lg border bg-card text-card-foreground", className),
|
|
250
|
+
...props,
|
|
251
|
+
children: [
|
|
252
|
+
/* @__PURE__ */ jsxs("div", { ref: scrollRef, className: "relative min-h-0 flex-1 overflow-auto overscroll-contain", children: [
|
|
253
|
+
columns && columns.length > 0 && // Tiêu đề cột là của MẮT: cây ARIA không có hàng tiêu đề, nên giá trị cột đi vào tên của
|
|
254
|
+
// từng mục ("Thiết kế, Ước lượng: 12").
|
|
255
|
+
/* @__PURE__ */ jsxs(
|
|
256
|
+
"div",
|
|
257
|
+
{
|
|
258
|
+
"aria-hidden": "true",
|
|
259
|
+
"data-slot": "tree-view-header",
|
|
260
|
+
className: "sticky top-0 z-10 flex items-center gap-2 border-b bg-card pe-3 font-medium text-muted-foreground text-xs",
|
|
261
|
+
style: { height: headerHeight, paddingInlineStart: 8 + 20 },
|
|
262
|
+
children: [
|
|
263
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 truncate", children: t.nameHeader }),
|
|
264
|
+
columns.map((column) => /* @__PURE__ */ jsx(
|
|
265
|
+
"div",
|
|
266
|
+
{
|
|
267
|
+
className: cn("shrink-0 truncate", column.align === "start" ? "text-start" : "text-end"),
|
|
268
|
+
style: { width: column.width ?? 96 },
|
|
269
|
+
children: column.header
|
|
270
|
+
},
|
|
271
|
+
column.id
|
|
272
|
+
))
|
|
273
|
+
]
|
|
274
|
+
}
|
|
275
|
+
),
|
|
276
|
+
/* @__PURE__ */ jsxs(
|
|
277
|
+
"div",
|
|
278
|
+
{
|
|
279
|
+
...tree.getContainerProps(ariaLabel ?? t.label),
|
|
280
|
+
"aria-describedby": editable ? instructionsId : void 0,
|
|
281
|
+
className: "relative outline-hidden",
|
|
282
|
+
onKeyDownCapture,
|
|
283
|
+
style: { height: virtualizer.getTotalSize() },
|
|
284
|
+
children: [
|
|
285
|
+
virtualizer.getVirtualItems().map((virtualItem) => {
|
|
286
|
+
const item = treeItems[virtualItem.index];
|
|
287
|
+
if (!item) return null;
|
|
288
|
+
const data = item.getItemData();
|
|
289
|
+
if (!data) return null;
|
|
290
|
+
const meta = item.getItemMeta();
|
|
291
|
+
const folder = item.isFolder();
|
|
292
|
+
const isExpanded = folder && item.isExpanded();
|
|
293
|
+
const number = numbers?.get(data.id) ?? "";
|
|
294
|
+
const itemProps = item.getProps();
|
|
295
|
+
const label = [
|
|
296
|
+
number ? `${number} ${data.name}` : data.name,
|
|
297
|
+
...(columns ?? []).flatMap((column, index) => {
|
|
298
|
+
const text = columnText(column, index, data);
|
|
299
|
+
return text != null && typeof column.header === "string" ? [`${column.header}: ${text}`] : [];
|
|
300
|
+
})
|
|
301
|
+
].join(", ");
|
|
302
|
+
return /* @__PURE__ */ createElement(
|
|
303
|
+
"div",
|
|
304
|
+
{
|
|
305
|
+
...itemProps,
|
|
306
|
+
key: item.getKey(),
|
|
307
|
+
role: "treeitem",
|
|
308
|
+
tabIndex: item.isFocused() ? 0 : -1,
|
|
309
|
+
"aria-label": label,
|
|
310
|
+
"data-slot": "tree-view-item",
|
|
311
|
+
"data-folder": folder || void 0,
|
|
312
|
+
className: cn(
|
|
313
|
+
"absolute inset-x-0 flex cursor-default select-none items-center gap-2 pe-3 text-foreground text-sm outline-hidden",
|
|
314
|
+
"hover:bg-muted focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-2 focus-visible:outline-ring",
|
|
315
|
+
item.isSelected() && "bg-accent text-accent-foreground hover:bg-accent",
|
|
316
|
+
item.isDragTarget() && "bg-accent"
|
|
317
|
+
),
|
|
318
|
+
style: {
|
|
319
|
+
top: virtualItem.start - headerHeight,
|
|
320
|
+
height: rowHeight,
|
|
321
|
+
paddingInlineStart: 8 + meta.level * indent
|
|
322
|
+
},
|
|
323
|
+
onDoubleClick: () => onItemOpen?.(data)
|
|
324
|
+
},
|
|
325
|
+
/* @__PURE__ */ jsx(
|
|
326
|
+
"span",
|
|
327
|
+
{
|
|
328
|
+
"aria-hidden": "true",
|
|
329
|
+
className: "flex size-5 shrink-0 items-center justify-center text-muted-foreground",
|
|
330
|
+
children: folder && /* @__PURE__ */ jsx(
|
|
331
|
+
ChevronRight,
|
|
332
|
+
{
|
|
333
|
+
className: cn(
|
|
334
|
+
"size-3.5 transition-transform rtl:rotate-180",
|
|
335
|
+
isExpanded && "rotate-90 rtl:rotate-90"
|
|
336
|
+
)
|
|
337
|
+
}
|
|
338
|
+
)
|
|
339
|
+
}
|
|
340
|
+
),
|
|
341
|
+
number && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "shrink-0 text-muted-foreground text-xs tabular-nums", children: number }),
|
|
342
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("min-w-0 flex-1 truncate", folder && "font-medium"), children: renderItem ? renderItem(data, { level: meta.level, expanded: isExpanded, folder, number }) : data.name }),
|
|
343
|
+
columns?.map((column, index) => {
|
|
344
|
+
const value = rollups[index]?.get(data.id);
|
|
345
|
+
const rolledUp = !!column.rollup && (children.get(data.id)?.length ?? 0) > 0;
|
|
346
|
+
return /* @__PURE__ */ jsx(
|
|
347
|
+
"span",
|
|
348
|
+
{
|
|
349
|
+
"aria-hidden": "true",
|
|
350
|
+
className: cn(
|
|
351
|
+
"shrink-0 truncate tabular-nums",
|
|
352
|
+
column.align === "start" ? "text-start" : "text-end",
|
|
353
|
+
rolledUp ? "font-medium" : "text-muted-foreground"
|
|
354
|
+
),
|
|
355
|
+
style: { width: column.width ?? 96 },
|
|
356
|
+
children: column.render ? column.render(data) : value == null ? null : column.format ? column.format(value, data, { rolledUp }) : numberFormat.format(value)
|
|
357
|
+
},
|
|
358
|
+
column.id
|
|
359
|
+
);
|
|
360
|
+
})
|
|
361
|
+
);
|
|
362
|
+
}),
|
|
363
|
+
/* @__PURE__ */ jsx(
|
|
364
|
+
"div",
|
|
365
|
+
{
|
|
366
|
+
"aria-hidden": "true",
|
|
367
|
+
"data-slot": "tree-view-drag-line",
|
|
368
|
+
className: "pointer-events-none -mt-px h-0.5 rounded-full bg-primary-ink",
|
|
369
|
+
style: tree.getDragLineStyle()
|
|
370
|
+
}
|
|
371
|
+
)
|
|
372
|
+
]
|
|
373
|
+
}
|
|
374
|
+
)
|
|
375
|
+
] }),
|
|
376
|
+
editable && /* @__PURE__ */ jsx("p", { id: instructionsId, className: "sr-only", children: t.instructions }),
|
|
377
|
+
/* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: "sr-only", children: announcement })
|
|
378
|
+
]
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
export {
|
|
383
|
+
TreeView
|
|
384
|
+
};
|
|
385
|
+
//# sourceMappingURL=tree-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/composite/tree-view.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Lõi cây: Headless Tree (https://headless-tree.lukasbach.com) — MIT License, Copyright (c) 2023\n * Lukas Bach. Gói dùng nó làm THƯ VIỆN (dependency), không chép mã: Headless Tree lo trạng thái\n * focus/mở/chọn, ARIA `tree`/`treeitem`, phím điều hướng theo W3C và kéo thả có đổi cha; phần vẽ,\n * phím dàn ý, đánh số, cột cộng dồn và ảo hoá là của gói. Lý do chọn: docs/tree-view.md.\n */\nimport {\n type DragTarget,\n dragAndDropFeature,\n hotkeysCoreFeature,\n type ItemInstance,\n isOrderedDragTarget,\n selectionFeature,\n syncDataLoaderFeature,\n type TreeInstance\n} from \"@headless-tree/core\";\nimport { useTree } from \"@headless-tree/react\";\nimport { useVirtualizer } from \"@tanstack/react-virtual\";\nimport { ChevronRight } from \"lucide-react\";\nimport * as React from \"react\";\nimport { useComitorLabels, useComitorLocale } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport {\n computeTreeNumbering,\n computeTreeRollup,\n groupTreeChildren,\n type TreeViewColumn,\n type TreeViewItem,\n type TreeViewLabels,\n type TreeViewMove\n} from \"./tree-view-constants.js\";\n\nexport interface TreeViewProps<T extends TreeViewItem>\n extends Omit<React.ComponentProps<\"div\">, \"children\" | \"onChange\" | \"onSelect\"> {\n /** Danh sách PHẲNG — `parentId` + thứ tự trong mảng (xem `TreeViewItem`). */\n items: T[];\n /** Nhánh đang mở (controlled). Bỏ trống thì cây tự giữ, bắt đầu từ `defaultExpandedIds`. */\n expandedIds?: string[];\n defaultExpandedIds?: string[];\n onExpandedIdsChange?: (ids: string[]) => void;\n /** Mục đang chọn (controlled). Bấm = chọn một; Ctrl/⌘ = thêm/bớt; Shift = chọn dải. */\n selectedIds?: string[];\n defaultSelectedIds?: string[];\n onSelectedIdsChange?: (ids: string[]) => void;\n /**\n * Người dùng đổi chỗ (kéo thả, Tab, Shift+Tab, Alt+↑/↓). Cây KHÔNG tự ghi: app áp bằng\n * `moveTreeItems` (hoặc gọi API) rồi truyền `items` mới. Không truyền = thứ tự chỉ đọc.\n */\n onMove?: (move: TreeViewMove) => void;\n /** Mở một mục: Enter, hoặc bấm đúp. */\n onItemOpen?: (item: T) => void;\n /** Cột phụ bên phải tên — số liệu có cộng dồn ở nhánh (giờ ước lượng, tiến độ…). */\n columns?: TreeViewColumn<T>[];\n /** Hiện số hiệu dàn ý `1.2.3` trước tên (WBS). */\n numbering?: boolean;\n /** Vẽ phần tên của một dòng — thay cho tên trần (icon, nhãn trạng thái…). */\n renderItem?: (\n item: T,\n state: { level: number; expanded: boolean; folder: boolean; number: string }\n ) => React.ReactNode;\n /** Chiều cao dòng, px (mặc định 32 — và 44 trên thiết bị chạm). */\n rowHeight?: number;\n /** Thụt lề mỗi bậc, px (mặc định 20). */\n indent?: number;\n /** Khoá mọi thao tác đổi chỗ dù có `onMove`. */\n readOnly?: boolean;\n labels?: Partial<TreeViewLabels>;\n}\n\nconst ROOT_ID = \"__comitor_tree_root__\";\n\nfunction useCoarsePointer(): boolean {\n const subscribe = React.useCallback((onChange: () => void) => {\n const mql = window.matchMedia(\"(pointer: coarse)\");\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n return React.useSyncExternalStore(\n subscribe,\n () => window.matchMedia(\"(pointer: coarse)\").matches,\n () => false\n );\n}\n\n/** State nửa controlled: có `value` thì theo `value`, không thì tự giữ — cùng báo `onChange`. */\nfunction useSemiControlled(\n value: string[] | undefined,\n defaultValue: string[] | undefined,\n onChange: ((next: string[]) => void) | undefined\n) {\n const [inner, setInner] = React.useState<string[]>(defaultValue ?? []);\n const current = value ?? inner;\n const currentRef = React.useRef(current);\n currentRef.current = current;\n const onChangeRef = React.useRef(onChange);\n onChangeRef.current = onChange;\n const controlled = value !== undefined;\n const set = React.useCallback(\n (updater: string[] | ((old: string[]) => string[])) => {\n const next = typeof updater === \"function\" ? updater(currentRef.current) : updater;\n currentRef.current = next;\n if (!controlled) setInner(next);\n onChangeRef.current?.(next);\n },\n [controlled]\n );\n return [current, set] as const;\n}\n\nconst FEATURES = [syncDataLoaderFeature, selectionFeature, hotkeysCoreFeature, dragAndDropFeature];\n\n/**\n * Cây có thao tác DÀN Ý — dựng cho WBS (dự án → giai đoạn → hạng mục → việc), dùng được cho mọi\n * cây có thứ tự (danh mục, sơ đồ tổ chức, thư mục).\n *\n * ─ CONTROLLED ─\n * Dữ liệu và thứ tự là của app. Mọi lần đổi chỗ đi ra qua MỘT callback `onMove` (cha mới + vị trí\n * mới), kể cả khi nó đến từ kéo thả hay từ phím; `moveTreeItems` áp nó lên danh sách phẳng.\n *\n * ─ BÀN PHÍM ─ (một điểm dừng Tab — mục đang focus; mũi tên theo mẫu Treeview của W3C)\n * ↑ ↓ — chuyển mục · → — mở nhánh / xuống con đầu · ← — thu nhánh / lên cha · Home End\n * Tab — thụt vào (thành con cuối của mục đứng trên) · Shift+Tab — lùi ra (đứng sau cha cũ)\n * Alt+↑ / Alt+↓ — đổi chỗ với anh em · Enter — `onItemOpen` · Shift+↑/↓, Ctrl+A — chọn\n * Tab chỉ bị giữ lại khi thật sự thụt được; ở mục đầu tiên của một cấp nó đi tiếp ra ngoài như\n * thường — cây không thành cái bẫy bàn phím.\n */\nexport function TreeView<T extends TreeViewItem>({\n items,\n expandedIds,\n defaultExpandedIds,\n onExpandedIdsChange,\n selectedIds,\n defaultSelectedIds,\n onSelectedIdsChange,\n onMove,\n onItemOpen,\n columns,\n numbering = false,\n renderItem,\n rowHeight: rowHeightProp,\n indent = 20,\n readOnly = false,\n labels,\n className,\n \"aria-label\": ariaLabel,\n ...props\n}: TreeViewProps<T>) {\n const t = useComitorLabels(\"treeView\", labels);\n const { locale } = useComitorLocale();\n const coarse = useCoarsePointer();\n const rowHeight = rowHeightProp ?? (coarse ? 44 : 32);\n const editable = !readOnly && !!onMove;\n\n const [expanded, setExpanded] = useSemiControlled(expandedIds, defaultExpandedIds, onExpandedIdsChange);\n const [selected, setSelected] = useSemiControlled(selectedIds, defaultSelectedIds, onSelectedIdsChange);\n\n // ── Dữ liệu ──────────────────────────────────────────────────────────────────────────────────────\n const children = React.useMemo(() => groupTreeChildren(items), [items]);\n const byId = React.useMemo(() => new Map(items.map((item) => [item.id, item])), [items]);\n const numbers = React.useMemo(() => (numbering ? computeTreeNumbering(items) : null), [numbering, items]);\n const rollups = React.useMemo(\n () => (columns ?? []).map((column) => (column.value ? computeTreeRollup(items, column) : null)),\n [columns, items]\n );\n const numberFormat = React.useMemo(() => new Intl.NumberFormat(locale, { maximumFractionDigits: 1 }), [locale]);\n\n // Headless Tree đọc dữ liệu qua `dataLoader` — đi qua ref để loader ổn định giữa các lần render.\n const dataRef = React.useRef({ children, byId });\n dataRef.current = { children, byId };\n const dataLoader = React.useMemo(\n () => ({\n getItem: (id: string) => dataRef.current.byId.get(id) ?? ({ id, parentId: null, name: \"\" } as unknown as T),\n getChildren: (id: string) =>\n (dataRef.current.children.get(id === ROOT_ID ? null : id) ?? []).map((child) => child.id)\n }),\n []\n );\n\n const isFolder = (id: string) => (children.get(id)?.length ?? 0) > 0 || !!byId.get(id)?.isFolder;\n\n // ── Lời xướng + chuyển chỗ ───────────────────────────────────────────────────────────────────────\n const [announcement, setAnnouncement] = React.useState(\"\");\n const pendingFocusRef = React.useRef<string | null>(null);\n\n const emitMove = (move: TreeViewMove) => {\n if (!onMove) return;\n const first = byId.get(move.ids[0] ?? \"\");\n const remaining = (children.get(move.parentId) ?? []).filter((item) => !move.ids.includes(item.id));\n const total = remaining.length + move.ids.length;\n const position = Math.min(move.index, remaining.length) + 1;\n onMove(move);\n if (move.parentId) setExpanded((old) => (old.includes(move.parentId!) ? old : [...old, move.parentId!]));\n if (first) {\n const sameParent = move.ids.length === 1 && first.parentId === move.parentId;\n setAnnouncement(\n sameParent\n ? t.movedTo(first.name, position, total)\n : t.movedInto(first.name, move.parentId ? (byId.get(move.parentId)?.name ?? t.root) : t.root, position, total)\n );\n pendingFocusRef.current = first.id;\n }\n };\n\n const siblingsOf = (item: T) =>\n children.get(item.parentId != null && byId.has(item.parentId) ? item.parentId : null) ?? [];\n const canMoveItem = (id: string | undefined): id is string =>\n !!id && id !== ROOT_ID && editable && !byId.get(id)?.readOnly;\n\n const outline = {\n canIndent: (id: string) => {\n const item = byId.get(id);\n return !!item && canMoveItem(id) && siblingsOf(item).indexOf(item) > 0;\n },\n indent: (id: string) => {\n const item = byId.get(id);\n if (!item) return;\n const siblings = siblingsOf(item);\n const prev = siblings[siblings.indexOf(item) - 1];\n if (!prev) return;\n emitMove({ ids: [id], parentId: prev.id, index: children.get(prev.id)?.length ?? 0, kind: \"indent\" });\n },\n canOutdent: (id: string) => {\n const item = byId.get(id);\n return !!item && canMoveItem(id) && item.parentId != null && byId.has(item.parentId);\n },\n outdent: (id: string) => {\n const item = byId.get(id);\n const parent = item?.parentId != null ? byId.get(item.parentId) : undefined;\n if (!item || !parent) return;\n const grand = parent.parentId != null && byId.has(parent.parentId) ? parent.parentId : null;\n const index = (children.get(grand) ?? []).indexOf(parent) + 1;\n emitMove({ ids: [id], parentId: grand, index, kind: \"outdent\" });\n },\n canShift: (id: string, direction: -1 | 1) => {\n const item = byId.get(id);\n if (!item || !canMoveItem(id)) return false;\n const siblings = siblingsOf(item);\n const index = siblings.indexOf(item) + direction;\n return index >= 0 && index < siblings.length;\n },\n shift: (id: string, direction: -1 | 1) => {\n const item = byId.get(id);\n if (!item) return;\n const index = siblingsOf(item).indexOf(item) + direction;\n emitMove({\n ids: [id],\n parentId: siblingsOf(item) === children.get(null) ? null : item.parentId,\n index,\n kind: direction < 0 ? \"move-up\" : \"move-down\"\n });\n }\n };\n\n const onDrop = (dragged: ItemInstance<T>[], target: DragTarget<T>) => {\n const ids = dragged.map((item) => item.getId());\n const targetId = target.item.getId();\n const parentId = targetId === ROOT_ID ? null : targetId;\n const index = isOrderedDragTarget(target)\n ? target.insertionIndex\n : (children.get(parentId) ?? []).filter((item) => !ids.includes(item.id)).length;\n emitMove({ ids, parentId, index, kind: \"drag\" });\n };\n\n // ── Ảo hoá ───────────────────────────────────────────────────────────────────────────────────────\n const scrollRef = React.useRef<HTMLDivElement>(null);\n const headerHeight = columns?.length ? 32 : 0;\n const virtualizerRef = React.useRef<ReturnType<typeof useVirtualizer<HTMLDivElement, Element>> | null>(null);\n\n const tree: TreeInstance<T> = useTree<T>({\n rootItemId: ROOT_ID,\n dataLoader,\n getItemName: (item) => item.getItemData()?.name ?? \"\",\n isItemFolder: (item) => isFolder(item.getId()),\n state: { expandedItems: expanded, selectedItems: selected },\n setExpandedItems: setExpanded,\n setSelectedItems: setSelected,\n indent,\n canReorder: editable,\n canDrag: (dragged) => editable && dragged.every((item) => !item.getItemData()?.readOnly),\n canDrop: () => editable,\n onDrop,\n scrollToItem: (item) => virtualizerRef.current?.scrollToIndex(item.getItemMeta().index, { align: \"auto\" }),\n features: FEATURES\n });\n\n /*\n * Phím dàn ý + Enter tự xử lý ở pha CAPTURE, KHÔNG đăng ký làm hotkey của Headless Tree: bộ\n * hotkey của nó khớp tổ hợp bằng cách tự đếm `keydown` của từng phím đang giữ, nên Shift/Alt nhấn\n * TRƯỚC khi focus vào cây (hoặc sự kiện không kèm `keydown` riêng của phím bổ trợ) là tổ hợp hụt —\n * đo ở harness: Shift+Tab rơi về Tab trần, Alt+↓ thành ↓ (chuyển mục thay vì dời mục). Đọc cờ\n * `shiftKey`/`altKey` của chính sự kiện thì không có trạng thái nào để lệch. React chạy capture ở\n * gốc, trước listener của Headless Tree trên khung cây, nên `stopPropagation` chặn được nó.\n */\n const onKeyDownCapture = (event: React.KeyboardEvent) => {\n if ((event.target as HTMLElement).getAttribute(\"role\") !== \"treeitem\") return;\n if (event.ctrlKey || event.metaKey) return;\n const id = tree.getFocusedItem().getId();\n const run = (action: () => void) => {\n event.preventDefault();\n event.stopPropagation();\n action();\n };\n if (event.key === \"Tab\" && !event.altKey) {\n if (event.shiftKey ? outline.canOutdent(id) : outline.canIndent(id)) {\n run(() => (event.shiftKey ? outline.outdent(id) : outline.indent(id)));\n }\n return;\n }\n if (editable && event.altKey && !event.shiftKey && (event.key === \"ArrowUp\" || event.key === \"ArrowDown\")) {\n const direction = event.key === \"ArrowUp\" ? -1 : 1;\n // Không dời được (đầu/cuối hàng anh em) vẫn nuốt phím: để rơi xuống thì Alt+↓ thành ↓ trần —\n // focus nhảy đi, người dùng tưởng mục đã dời.\n run(() => {\n if (outline.canShift(id, direction)) outline.shift(id, direction);\n });\n return;\n }\n if (event.key === \"Enter\" && !event.shiftKey && !event.altKey && onItemOpen) {\n const data = byId.get(id);\n if (data) run(() => onItemOpen(data));\n }\n };\n\n // Dữ liệu đổi (sau một lần chuyển chỗ, hoặc app nạp thêm) → dựng lại cây, rồi trả focus về mục\n // vừa chuyển: mục đổi cha là một phần tử mới trong DOM, focus cũ đã rơi khỏi cây.\n // biome-ignore lint/correctness/useExhaustiveDependencies: chỉ chạy khi DỮ LIỆU đổi\n React.useEffect(() => {\n tree.rebuildTree();\n const id = pendingFocusRef.current;\n if (id) {\n pendingFocusRef.current = null;\n tree.getItemInstance(id)?.setFocused();\n tree.updateDomFocus();\n }\n }, [items]);\n\n const treeItems = tree.getItems();\n const virtualizer = useVirtualizer({\n count: treeItems.length,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => rowHeight,\n overscan: 10,\n scrollMargin: headerHeight\n });\n virtualizerRef.current = virtualizer;\n // biome-ignore lint/correctness/useExhaustiveDependencies: đo lại khi cỡ dòng đổi (chạm ⇄ chuột)\n React.useEffect(() => {\n virtualizer.measure();\n }, [virtualizer, rowHeight]);\n\n const instructionsId = React.useId();\n\n const columnText = (column: TreeViewColumn<T>, index: number, item: T): string | null => {\n const value = rollups[index]?.get(item.id);\n if (value == null) return null;\n return column.formatText ? column.formatText(value, item) : numberFormat.format(value);\n };\n\n return (\n <div\n data-slot=\"tree-view\"\n className={cn(\"flex min-h-0 flex-col overflow-hidden rounded-lg border bg-card text-card-foreground\", className)}\n {...props}\n >\n <div ref={scrollRef} className=\"relative min-h-0 flex-1 overflow-auto overscroll-contain\">\n {columns && columns.length > 0 && (\n // Tiêu đề cột là của MẮT: cây ARIA không có hàng tiêu đề, nên giá trị cột đi vào tên của\n // từng mục (\"Thiết kế, Ước lượng: 12\").\n <div\n aria-hidden=\"true\"\n data-slot=\"tree-view-header\"\n className=\"sticky top-0 z-10 flex items-center gap-2 border-b bg-card pe-3 font-medium text-muted-foreground text-xs\"\n style={{ height: headerHeight, paddingInlineStart: 8 + 20 }}\n >\n <div className=\"min-w-0 flex-1 truncate\">{t.nameHeader}</div>\n {columns.map((column) => (\n <div\n key={column.id}\n className={cn(\"shrink-0 truncate\", column.align === \"start\" ? \"text-start\" : \"text-end\")}\n style={{ width: column.width ?? 96 }}\n >\n {column.header}\n </div>\n ))}\n </div>\n )}\n\n <div\n {...tree.getContainerProps(ariaLabel ?? t.label)}\n aria-describedby={editable ? instructionsId : undefined}\n className=\"relative outline-hidden\"\n onKeyDownCapture={onKeyDownCapture}\n style={{ height: virtualizer.getTotalSize() }}\n >\n {virtualizer.getVirtualItems().map((virtualItem) => {\n const item = treeItems[virtualItem.index];\n if (!item) return null;\n const data = item.getItemData();\n if (!data) return null;\n const meta = item.getItemMeta();\n const folder = item.isFolder();\n const isExpanded = folder && item.isExpanded();\n const number = numbers?.get(data.id) ?? \"\";\n const itemProps = item.getProps();\n const label = [\n number ? `${number} ${data.name}` : data.name,\n ...(columns ?? []).flatMap((column, index) => {\n const text = columnText(column, index, data);\n return text != null && typeof column.header === \"string\" ? [`${column.header}: ${text}`] : [];\n })\n ].join(\", \");\n\n return (\n <div\n {...itemProps}\n key={item.getKey()}\n // Lặp lại hai thuộc tính Headless Tree đã rải — để biome đọc được vai trò của thẻ.\n role=\"treeitem\"\n tabIndex={item.isFocused() ? 0 : -1}\n aria-label={label}\n data-slot=\"tree-view-item\"\n data-folder={folder || undefined}\n className={cn(\n \"absolute inset-x-0 flex cursor-default select-none items-center gap-2 pe-3 text-foreground text-sm outline-hidden\",\n \"hover:bg-muted focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-2 focus-visible:outline-ring\",\n item.isSelected() && \"bg-accent text-accent-foreground hover:bg-accent\",\n item.isDragTarget() && \"bg-accent\"\n )}\n style={{\n top: virtualItem.start - headerHeight,\n height: rowHeight,\n paddingInlineStart: 8 + meta.level * indent\n }}\n onDoubleClick={() => onItemOpen?.(data)}\n >\n <span\n aria-hidden=\"true\"\n className=\"flex size-5 shrink-0 items-center justify-center text-muted-foreground\"\n >\n {folder && (\n <ChevronRight\n className={cn(\n \"size-3.5 transition-transform rtl:rotate-180\",\n isExpanded && \"rotate-90 rtl:rotate-90\"\n )}\n />\n )}\n </span>\n {number && (\n <span aria-hidden=\"true\" className=\"shrink-0 text-muted-foreground text-xs tabular-nums\">\n {number}\n </span>\n )}\n <span aria-hidden=\"true\" className={cn(\"min-w-0 flex-1 truncate\", folder && \"font-medium\")}>\n {renderItem\n ? renderItem(data, { level: meta.level, expanded: isExpanded, folder, number })\n : data.name}\n </span>\n {columns?.map((column, index) => {\n const value = rollups[index]?.get(data.id);\n const rolledUp = !!column.rollup && (children.get(data.id)?.length ?? 0) > 0;\n return (\n <span\n key={column.id}\n aria-hidden=\"true\"\n className={cn(\n \"shrink-0 truncate tabular-nums\",\n column.align === \"start\" ? \"text-start\" : \"text-end\",\n rolledUp ? \"font-medium\" : \"text-muted-foreground\"\n )}\n style={{ width: column.width ?? 96 }}\n >\n {column.render\n ? column.render(data)\n : value == null\n ? null\n : column.format\n ? column.format(value, data, { rolledUp })\n : numberFormat.format(value)}\n </span>\n );\n })}\n </div>\n );\n })}\n <div\n aria-hidden=\"true\"\n data-slot=\"tree-view-drag-line\"\n className=\"pointer-events-none -mt-px h-0.5 rounded-full bg-primary-ink\"\n style={tree.getDragLineStyle()}\n />\n </div>\n </div>\n\n {editable && (\n <p id={instructionsId} className=\"sr-only\">\n {t.instructions}\n </p>\n )}\n <div role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {announcement}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
|
|
5
5
|
declare const badgeVariants: (props?: ({
|
|
6
|
-
variant?: "
|
|
6
|
+
variant?: "primary" | "default" | "destructive" | "warning" | "success" | "secondary" | "outline" | null | undefined;
|
|
7
7
|
size?: "sm" | "md" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
interface BadgeProps extends React.ComponentProps<"span">, VariantProps<typeof badgeVariants> {
|
|
@@ -3,7 +3,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
|
|
5
5
|
declare const buttonVariants: (props?: ({
|
|
6
|
-
variant?: "
|
|
6
|
+
variant?: "primary" | "link" | "default" | "destructive" | "secondary" | "outline" | "ghost" | null | undefined;
|
|
7
7
|
size?: "default" | "sm" | "md" | "lg" | "icon" | "icon-sm" | "icon-lg" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
interface ButtonProps extends React.ComponentProps<"button">, VariantProps<typeof buttonVariants> {
|
|
@@ -4,6 +4,6 @@ import '@tanstack/react-table';
|
|
|
4
4
|
|
|
5
5
|
interface DataGridCellWrapperProps<TData> extends DataGridCellProps<TData>, React.ComponentProps<"div"> {
|
|
6
6
|
}
|
|
7
|
-
declare function DataGridCellWrapper<TData>({ cell
|
|
7
|
+
declare function DataGridCellWrapper<TData>({ cell, tableMeta, rowIndex, columnId, isEditing, isFocused, isSelected, isSearchMatch, isActiveSearchMatch, readOnly, rowHeight, className, onClick: onClickProp, onKeyDown: onKeyDownProp, ref, ...props }: DataGridCellWrapperProps<TData>): React.JSX.Element;
|
|
8
8
|
|
|
9
9
|
export { DataGridCellWrapper };
|
|
@@ -3,15 +3,17 @@ import { jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../lib/cn.js";
|
|
5
5
|
import { useComposedRefs } from "../lib/compose-refs.js";
|
|
6
|
+
import { useDataGridMessages } from "./data-grid-locale.js";
|
|
6
7
|
import { useDataGridPresence } from "./data-grid-presence.js";
|
|
7
|
-
import {
|
|
8
|
+
import { messagesEn } from "./messages-en.js";
|
|
9
|
+
import { getCellKey, getColumnLabel, isEmptyCellValue } from "./utils.js";
|
|
8
10
|
function DataGridCellWrapper({
|
|
9
11
|
/*
|
|
10
|
-
* ⚠ Khác bản gốc (3.1.0): tách `cell` ra
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* ⚠ Khác bản gốc (3.1.0): tách `cell` ra. Mọi kiểu ô truyền nguyên `cell` (object của TanStack)
|
|
13
|
+
* xuống wrapper, và bản gốc để nó rơi vào `...props` rồi rải lên thẻ `<div>` — DOM của mỗi ô mang
|
|
14
|
+
* thuộc tính rác `cell="[object Object]"`. Từ 3.11.1 nó còn cấp tên truy cập cho ô rỗng (bên dưới).
|
|
13
15
|
*/
|
|
14
|
-
cell
|
|
16
|
+
cell,
|
|
15
17
|
tableMeta,
|
|
16
18
|
rowIndex,
|
|
17
19
|
columnId,
|
|
@@ -28,7 +30,10 @@ function DataGridCellWrapper({
|
|
|
28
30
|
ref,
|
|
29
31
|
...props
|
|
30
32
|
}) {
|
|
33
|
+
const msg = useDataGridMessages();
|
|
31
34
|
const cellMapRef = tableMeta?.cellMapRef;
|
|
35
|
+
const emptyMessage = msg.cell.empty ?? messagesEn.cell.empty;
|
|
36
|
+
const emptyLabel = isEmptyCellValue(cell.getValue()) ? emptyMessage(getColumnLabel(cell.column)) : void 0;
|
|
32
37
|
const cellPresence = useDataGridPresence(getCellKey(rowIndex, columnId));
|
|
33
38
|
const onCellChange = React.useCallback(
|
|
34
39
|
(node) => {
|
|
@@ -130,6 +135,7 @@ function DataGridCellWrapper({
|
|
|
130
135
|
"data-focused": isFocused ? "" : void 0,
|
|
131
136
|
"data-selected": isSelected ? "" : void 0,
|
|
132
137
|
tabIndex: isFocused && !isEditing ? 0 : -1,
|
|
138
|
+
"aria-label": emptyLabel,
|
|
133
139
|
...props,
|
|
134
140
|
ref: composedRef,
|
|
135
141
|
className: cn(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/data-grid/data-grid-cell-wrapper.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn.js\";\nimport { useComposedRefs } from \"../lib/compose-refs.js\";\nimport { useDataGridPresence } from \"./data-grid-presence.js\";\nimport type { DataGridCellProps } from \"./types.js\";\nimport { getCellKey } from \"./utils.js\";\n\ninterface DataGridCellWrapperProps<TData> extends DataGridCellProps<TData>, React.ComponentProps<\"div\"> {}\n\nexport function DataGridCellWrapper<TData>({\n /*\n * ⚠ Khác bản gốc (3.1.0): tách `cell` ra dù không dùng. Mọi kiểu ô truyền nguyên `cell` (object của\n * TanStack) xuống wrapper, và bản gốc để nó rơi vào `...props` rồi rải lên thẻ `<div>` — DOM của\n * mỗi ô mang thuộc tính rác `cell=\"[object Object]\"`.\n */\n cell: _cell,\n tableMeta,\n rowIndex,\n columnId,\n isEditing,\n isFocused,\n isSelected,\n isSearchMatch,\n isActiveSearchMatch,\n readOnly,\n rowHeight,\n className,\n onClick: onClickProp,\n onKeyDown: onKeyDownProp,\n ref,\n ...props\n}: DataGridCellWrapperProps<TData>) {\n const cellMapRef = tableMeta?.cellMapRef;\n const cellPresence = useDataGridPresence(getCellKey(rowIndex, columnId));\n\n const onCellChange = React.useCallback(\n (node: HTMLDivElement | null) => {\n if (!cellMapRef) return;\n\n const cellKey = getCellKey(rowIndex, columnId);\n\n if (node) {\n cellMapRef.current.set(cellKey, node);\n } else {\n cellMapRef.current.delete(cellKey);\n }\n },\n [rowIndex, columnId, cellMapRef]\n );\n\n const composedRef = useComposedRefs(ref, onCellChange);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n if (!isEditing) {\n event.preventDefault();\n onClickProp?.(event);\n if (isFocused && !readOnly) {\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n } else {\n tableMeta?.onCellClick?.(rowIndex, columnId, event);\n }\n }\n },\n [tableMeta, rowIndex, columnId, isEditing, isFocused, readOnly, onClickProp]\n );\n\n const onContextMenu = React.useCallback(\n (event: React.MouseEvent) => {\n if (!isEditing) {\n tableMeta?.onCellContextMenu?.(rowIndex, columnId, event);\n }\n },\n [tableMeta, rowIndex, columnId, isEditing]\n );\n\n const onDoubleClick = React.useCallback(\n (event: React.MouseEvent) => {\n if (!isEditing) {\n event.preventDefault();\n tableMeta?.onCellDoubleClick?.(rowIndex, columnId);\n }\n },\n [tableMeta, rowIndex, columnId, isEditing]\n );\n\n const onKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n onKeyDownProp?.(event);\n\n if (event.defaultPrevented) return;\n\n if (\n event.key === \"ArrowUp\" ||\n event.key === \"ArrowDown\" ||\n event.key === \"ArrowLeft\" ||\n event.key === \"ArrowRight\" ||\n event.key === \"Home\" ||\n event.key === \"End\" ||\n event.key === \"PageUp\" ||\n event.key === \"PageDown\" ||\n event.key === \"Tab\"\n ) {\n return;\n }\n\n if (isFocused && !isEditing && !readOnly) {\n if (event.key === \"F2\" || event.key === \"Enter\") {\n event.preventDefault();\n event.stopPropagation();\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n return;\n }\n\n if (event.key === \" \") {\n event.preventDefault();\n event.stopPropagation();\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n return;\n }\n\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {\n event.preventDefault();\n event.stopPropagation();\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n }\n }\n },\n [onKeyDownProp, isFocused, isEditing, readOnly, tableMeta, rowIndex, columnId]\n );\n\n const onMouseDown = React.useCallback(\n (event: React.MouseEvent) => {\n if (!isEditing) {\n tableMeta?.onCellMouseDown?.(rowIndex, columnId, event);\n }\n },\n [tableMeta, rowIndex, columnId, isEditing]\n );\n\n const onMouseEnter = React.useCallback(() => {\n if (!isEditing) {\n tableMeta?.onCellMouseEnter?.(rowIndex, columnId);\n }\n }, [tableMeta, rowIndex, columnId, isEditing]);\n\n const onMouseUp = React.useCallback(() => {\n if (!isEditing) {\n tableMeta?.onCellMouseUp?.();\n }\n }, [tableMeta, isEditing]);\n\n return (\n <div\n role=\"button\"\n data-slot=\"grid-cell-wrapper\"\n data-editing={isEditing ? \"\" : undefined}\n data-focused={isFocused ? \"\" : undefined}\n data-selected={isSelected ? \"\" : undefined}\n tabIndex={isFocused && !isEditing ? 0 : -1}\n {...props}\n ref={composedRef}\n className={cn(\n /*\n * ─ BA TÍN HIỆU MÀU CỦA MỘT Ô, VÀ VÌ SAO CHÚNG KHÔNG PHẢI TINT CỦA `--primary`/`--warning` ─\n *\n * Bản gốc tô `bg-primary/10` cho vùng chọn và `bg-warning/20`÷`/50` cho kết quả tìm kiếm.\n * Cả hai đều là bậc TÔ NỀN pha loãng, và `pnpm lint:a11y` bác đúng chỗ: 1,05:1 (light) ·\n * 1,21:1 (dark) cho vùng chọn — dưới xa ngưỡng 3:1 của WCAG 1.4.11 cho thông tin thị giác.\n * Một dấu hiệu không nhìn thấy là một dấu hiệu GIẢ.\n *\n * Thay bằng đúng hai bậc mà hợp đồng màu cấp cho việc này:\n * • vùng chọn → `--accent`, bậc tint TRẠNG THÁI (Tabs/Switch/Progress nói \"đang bật\"\n * bằng chính nó). Là token BỀ MẶT nên nó được phép nhạt: nghĩa vụ 3:1\n * không rơi lên tint, mà rơi lên NÉT đi kèm.\n * • tìm kiếm → viền `--warning-ink` (6,51:1 light · 11,60:1 dark trên nền ô) — ĐÂY mới\n * là thứ mang thông tin; ô đang đứng thì thêm nền `--warning` đặc + chữ\n * `--warning-foreground` (12,26:1) cho khác hẳn các ô còn lại.\n *\n * `border-transparent` ở bậc nghỉ để bật/tắt viền KHÔNG xô layout 1px (cùng cách\n * `TableRow` giữ bề rộng viền không đổi giữa hai trạng thái).\n */\n // has-[[role=checkbox]] = ô kiểu checkbox (Radix render <button role=\"checkbox\">)\n \"size-full border border-transparent px-2 py-1.5 text-start text-sm outline-none focus-visible:outline-none has-[[role=checkbox]]:pt-2.5\",\n {\n \"ring-1 ring-inset\": isFocused || !!cellPresence,\n \"ring-ring\": isFocused && !cellPresence,\n \"border-warning-ink\": isSearchMatch || isActiveSearchMatch,\n \"bg-warning text-warning-foreground\": isActiveSearchMatch,\n \"bg-accent\": isSelected && !isEditing && !isActiveSearchMatch,\n \"cursor-default\": !isEditing,\n \"[&_[data-slot=grid-cell-content]]:line-clamp-1\": !isEditing && rowHeight === \"short\",\n \"[&_[data-slot=grid-cell-content]]:line-clamp-2\": !isEditing && rowHeight === \"medium\",\n \"[&_[data-slot=grid-cell-content]]:line-clamp-3\": !isEditing && rowHeight === \"tall\",\n \"[&_[data-slot=grid-cell-content]]:line-clamp-4\": !isEditing && rowHeight === \"extra-tall\"\n },\n className\n )}\n style={cellPresence ? ({ \"--tw-ring-color\": cellPresence.color } as React.CSSProperties) : undefined}\n onClick={onClick}\n onContextMenu={onContextMenu}\n onDoubleClick={onDoubleClick}\n onMouseDown={onMouseDown}\n onMouseEnter={onMouseEnter}\n onMouseUp={onMouseUp}\n onKeyDown={onKeyDown}\n />\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/data-grid/data-grid-cell-wrapper.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn.js\";\nimport { useComposedRefs } from \"../lib/compose-refs.js\";\nimport { useDataGridMessages } from \"./data-grid-locale.js\";\nimport { useDataGridPresence } from \"./data-grid-presence.js\";\nimport { messagesEn } from \"./messages-en.js\";\nimport type { DataGridCellProps } from \"./types.js\";\nimport { getCellKey, getColumnLabel, isEmptyCellValue } from \"./utils.js\";\n\ninterface DataGridCellWrapperProps<TData> extends DataGridCellProps<TData>, React.ComponentProps<\"div\"> {}\n\nexport function DataGridCellWrapper<TData>({\n /*\n * ⚠ Khác bản gốc (3.1.0): tách `cell` ra. Mọi kiểu ô truyền nguyên `cell` (object của TanStack)\n * xuống wrapper, và bản gốc để nó rơi vào `...props` rồi rải lên thẻ `<div>` — DOM của mỗi ô mang\n * thuộc tính rác `cell=\"[object Object]\"`. Từ 3.11.1 nó còn cấp tên truy cập cho ô rỗng (bên dưới).\n */\n cell,\n tableMeta,\n rowIndex,\n columnId,\n isEditing,\n isFocused,\n isSelected,\n isSearchMatch,\n isActiveSearchMatch,\n readOnly,\n rowHeight,\n className,\n onClick: onClickProp,\n onKeyDown: onKeyDownProp,\n ref,\n ...props\n}: DataGridCellWrapperProps<TData>) {\n const msg = useDataGridMessages();\n const cellMapRef = tableMeta?.cellMapRef;\n /*\n * ⚠ Khác bản gốc (3.11.1, inv-07): wrapper là `role=\"button\"`, và tên của nó CHỈ đến từ chữ bên\n * trong — ô rỗng (select chưa chọn, ngày chưa có, chữ trống…) là một NÚT KHÔNG TÊN. Chỉ gắn\n * `aria-label` khi ô rỗng: ô có giá trị thì để chữ bên trong làm tên, khỏi đè lên nội dung mà\n * `renderValue` của app vẽ ra. `aria-label` app truyền thẳng (qua `...props`) vẫn thắng.\n * Đường lui về bảng tiếng Anh: `cell.empty` là khoá MỚI, và bảng chuỗi app soạn tay trước 3.11.1 (truyền\n * qua `locale`) không có nó — thiếu thì mọi ô rỗng ném \"not a function\" lúc render.\n */\n const emptyMessage = msg.cell.empty ?? messagesEn.cell.empty;\n const emptyLabel = isEmptyCellValue(cell.getValue()) ? emptyMessage(getColumnLabel(cell.column)) : undefined;\n const cellPresence = useDataGridPresence(getCellKey(rowIndex, columnId));\n\n const onCellChange = React.useCallback(\n (node: HTMLDivElement | null) => {\n if (!cellMapRef) return;\n\n const cellKey = getCellKey(rowIndex, columnId);\n\n if (node) {\n cellMapRef.current.set(cellKey, node);\n } else {\n cellMapRef.current.delete(cellKey);\n }\n },\n [rowIndex, columnId, cellMapRef]\n );\n\n const composedRef = useComposedRefs(ref, onCellChange);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n if (!isEditing) {\n event.preventDefault();\n onClickProp?.(event);\n if (isFocused && !readOnly) {\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n } else {\n tableMeta?.onCellClick?.(rowIndex, columnId, event);\n }\n }\n },\n [tableMeta, rowIndex, columnId, isEditing, isFocused, readOnly, onClickProp]\n );\n\n const onContextMenu = React.useCallback(\n (event: React.MouseEvent) => {\n if (!isEditing) {\n tableMeta?.onCellContextMenu?.(rowIndex, columnId, event);\n }\n },\n [tableMeta, rowIndex, columnId, isEditing]\n );\n\n const onDoubleClick = React.useCallback(\n (event: React.MouseEvent) => {\n if (!isEditing) {\n event.preventDefault();\n tableMeta?.onCellDoubleClick?.(rowIndex, columnId);\n }\n },\n [tableMeta, rowIndex, columnId, isEditing]\n );\n\n const onKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n onKeyDownProp?.(event);\n\n if (event.defaultPrevented) return;\n\n if (\n event.key === \"ArrowUp\" ||\n event.key === \"ArrowDown\" ||\n event.key === \"ArrowLeft\" ||\n event.key === \"ArrowRight\" ||\n event.key === \"Home\" ||\n event.key === \"End\" ||\n event.key === \"PageUp\" ||\n event.key === \"PageDown\" ||\n event.key === \"Tab\"\n ) {\n return;\n }\n\n if (isFocused && !isEditing && !readOnly) {\n if (event.key === \"F2\" || event.key === \"Enter\") {\n event.preventDefault();\n event.stopPropagation();\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n return;\n }\n\n if (event.key === \" \") {\n event.preventDefault();\n event.stopPropagation();\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n return;\n }\n\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {\n event.preventDefault();\n event.stopPropagation();\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n }\n }\n },\n [onKeyDownProp, isFocused, isEditing, readOnly, tableMeta, rowIndex, columnId]\n );\n\n const onMouseDown = React.useCallback(\n (event: React.MouseEvent) => {\n if (!isEditing) {\n tableMeta?.onCellMouseDown?.(rowIndex, columnId, event);\n }\n },\n [tableMeta, rowIndex, columnId, isEditing]\n );\n\n const onMouseEnter = React.useCallback(() => {\n if (!isEditing) {\n tableMeta?.onCellMouseEnter?.(rowIndex, columnId);\n }\n }, [tableMeta, rowIndex, columnId, isEditing]);\n\n const onMouseUp = React.useCallback(() => {\n if (!isEditing) {\n tableMeta?.onCellMouseUp?.();\n }\n }, [tableMeta, isEditing]);\n\n return (\n <div\n role=\"button\"\n data-slot=\"grid-cell-wrapper\"\n data-editing={isEditing ? \"\" : undefined}\n data-focused={isFocused ? \"\" : undefined}\n data-selected={isSelected ? \"\" : undefined}\n tabIndex={isFocused && !isEditing ? 0 : -1}\n aria-label={emptyLabel}\n {...props}\n ref={composedRef}\n className={cn(\n /*\n * ─ BA TÍN HIỆU MÀU CỦA MỘT Ô, VÀ VÌ SAO CHÚNG KHÔNG PHẢI TINT CỦA `--primary`/`--warning` ─\n *\n * Bản gốc tô `bg-primary/10` cho vùng chọn và `bg-warning/20`÷`/50` cho kết quả tìm kiếm.\n * Cả hai đều là bậc TÔ NỀN pha loãng, và `pnpm lint:a11y` bác đúng chỗ: 1,05:1 (light) ·\n * 1,21:1 (dark) cho vùng chọn — dưới xa ngưỡng 3:1 của WCAG 1.4.11 cho thông tin thị giác.\n * Một dấu hiệu không nhìn thấy là một dấu hiệu GIẢ.\n *\n * Thay bằng đúng hai bậc mà hợp đồng màu cấp cho việc này:\n * • vùng chọn → `--accent`, bậc tint TRẠNG THÁI (Tabs/Switch/Progress nói \"đang bật\"\n * bằng chính nó). Là token BỀ MẶT nên nó được phép nhạt: nghĩa vụ 3:1\n * không rơi lên tint, mà rơi lên NÉT đi kèm.\n * • tìm kiếm → viền `--warning-ink` (6,51:1 light · 11,60:1 dark trên nền ô) — ĐÂY mới\n * là thứ mang thông tin; ô đang đứng thì thêm nền `--warning` đặc + chữ\n * `--warning-foreground` (12,26:1) cho khác hẳn các ô còn lại.\n *\n * `border-transparent` ở bậc nghỉ để bật/tắt viền KHÔNG xô layout 1px (cùng cách\n * `TableRow` giữ bề rộng viền không đổi giữa hai trạng thái).\n */\n // has-[[role=checkbox]] = ô kiểu checkbox (Radix render <button role=\"checkbox\">)\n \"size-full border border-transparent px-2 py-1.5 text-start text-sm outline-none focus-visible:outline-none has-[[role=checkbox]]:pt-2.5\",\n {\n \"ring-1 ring-inset\": isFocused || !!cellPresence,\n \"ring-ring\": isFocused && !cellPresence,\n \"border-warning-ink\": isSearchMatch || isActiveSearchMatch,\n \"bg-warning text-warning-foreground\": isActiveSearchMatch,\n \"bg-accent\": isSelected && !isEditing && !isActiveSearchMatch,\n \"cursor-default\": !isEditing,\n \"[&_[data-slot=grid-cell-content]]:line-clamp-1\": !isEditing && rowHeight === \"short\",\n \"[&_[data-slot=grid-cell-content]]:line-clamp-2\": !isEditing && rowHeight === \"medium\",\n \"[&_[data-slot=grid-cell-content]]:line-clamp-3\": !isEditing && rowHeight === \"tall\",\n \"[&_[data-slot=grid-cell-content]]:line-clamp-4\": !isEditing && rowHeight === \"extra-tall\"\n },\n className\n )}\n style={cellPresence ? ({ \"--tw-ring-color\": cellPresence.color } as React.CSSProperties) : undefined}\n onClick={onClick}\n onContextMenu={onContextMenu}\n onDoubleClick={onDoubleClick}\n onMouseDown={onMouseDown}\n onMouseEnter={onMouseEnter}\n onMouseUp={onMouseUp}\n onKeyDown={onKeyDown}\n />\n );\n}\n"],"mappings":"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","names":[]}
|