@comitor/ui 2.0.0 → 2.1.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.
Files changed (131) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +14 -6
  3. package/dist/components/ui/action-bar-constants.d.ts +31 -0
  4. package/dist/components/ui/action-bar-constants.js +9 -0
  5. package/dist/components/ui/action-bar-constants.js.map +1 -0
  6. package/dist/components/ui/action-bar.d.ts +67 -0
  7. package/dist/components/ui/action-bar.js +522 -0
  8. package/dist/components/ui/action-bar.js.map +1 -0
  9. package/dist/components/ui/badge-overflow.d.ts +30 -0
  10. package/dist/components/ui/badge-overflow.js +179 -0
  11. package/dist/components/ui/badge-overflow.js.map +1 -0
  12. package/dist/components/ui/banner.d.ts +79 -0
  13. package/dist/components/ui/banner.js +518 -0
  14. package/dist/components/ui/banner.js.map +1 -0
  15. package/dist/components/ui/circular-progress.d.ts +50 -0
  16. package/dist/components/ui/circular-progress.js +266 -0
  17. package/dist/components/ui/circular-progress.js.map +1 -0
  18. package/dist/components/ui/cropper-constants.d.ts +34 -0
  19. package/dist/components/ui/cropper-constants.js +10 -0
  20. package/dist/components/ui/cropper-constants.js.map +1 -0
  21. package/dist/components/ui/cropper.d.ts +106 -0
  22. package/dist/components/ui/cropper.js +1269 -0
  23. package/dist/components/ui/cropper.js.map +1 -0
  24. package/dist/components/ui/drawer.d.ts +1 -1
  25. package/dist/components/ui/drawer.js +2 -2
  26. package/dist/components/ui/drawer.js.map +1 -1
  27. package/dist/components/ui/kanban-constants.d.ts +61 -0
  28. package/dist/components/ui/kanban-constants.js +21 -0
  29. package/dist/components/ui/kanban-constants.js.map +1 -0
  30. package/dist/components/ui/kanban.d.ts +72 -0
  31. package/dist/components/ui/kanban.js +787 -0
  32. package/dist/components/ui/kanban.js.map +1 -0
  33. package/dist/components/ui/key-value-constants.d.ts +33 -0
  34. package/dist/components/ui/key-value-constants.js +11 -0
  35. package/dist/components/ui/key-value-constants.js.map +1 -0
  36. package/dist/components/ui/key-value.d.ts +96 -0
  37. package/dist/components/ui/key-value.js +641 -0
  38. package/dist/components/ui/key-value.js.map +1 -0
  39. package/dist/components/ui/mask-input-constants.d.ts +99 -0
  40. package/dist/components/ui/mask-input-constants.js +601 -0
  41. package/dist/components/ui/mask-input-constants.js.map +1 -0
  42. package/dist/components/ui/mask-input.d.ts +36 -0
  43. package/dist/components/ui/mask-input.js +558 -0
  44. package/dist/components/ui/mask-input.js.map +1 -0
  45. package/dist/components/ui/mention.d.ts +31 -0
  46. package/dist/components/ui/mention.js +128 -0
  47. package/dist/components/ui/mention.js.map +1 -0
  48. package/dist/components/ui/phone-input-constants.d.ts +54 -0
  49. package/dist/components/ui/phone-input-constants.js +9 -0
  50. package/dist/components/ui/phone-input-constants.js.map +1 -0
  51. package/dist/components/ui/phone-input.d.ts +85 -0
  52. package/dist/components/ui/phone-input.js +743 -0
  53. package/dist/components/ui/phone-input.js.map +1 -0
  54. package/dist/components/ui/rating.d.ts +56 -0
  55. package/dist/components/ui/rating.js +646 -0
  56. package/dist/components/ui/rating.js.map +1 -0
  57. package/dist/components/ui/responsive-dialog.d.ts +35 -0
  58. package/dist/components/ui/responsive-dialog.js +193 -0
  59. package/dist/components/ui/responsive-dialog.js.map +1 -0
  60. package/dist/components/ui/scroll-spy.d.ts +42 -0
  61. package/dist/components/ui/scroll-spy.js +334 -0
  62. package/dist/components/ui/scroll-spy.js.map +1 -0
  63. package/dist/components/ui/stepper-constants.d.ts +29 -0
  64. package/dist/components/ui/stepper-constants.js +9 -0
  65. package/dist/components/ui/stepper-constants.js.map +1 -0
  66. package/dist/components/ui/stepper.d.ts +80 -0
  67. package/dist/components/ui/stepper.js +902 -0
  68. package/dist/components/ui/stepper.js.map +1 -0
  69. package/dist/components/ui/tags-input.d.ts +41 -0
  70. package/dist/components/ui/tags-input.js +127 -0
  71. package/dist/components/ui/tags-input.js.map +1 -0
  72. package/dist/components/ui/time-picker-constants.d.ts +48 -0
  73. package/dist/components/ui/time-picker-constants.js +14 -0
  74. package/dist/components/ui/time-picker-constants.js.map +1 -0
  75. package/dist/components/ui/time-picker.d.ts +115 -0
  76. package/dist/components/ui/time-picker.js +1620 -0
  77. package/dist/components/ui/time-picker.js.map +1 -0
  78. package/dist/components/ui/timeline.d.ts +32 -0
  79. package/dist/components/ui/timeline.js +559 -0
  80. package/dist/components/ui/timeline.js.map +1 -0
  81. package/dist/components/ui/tour-constants.d.ts +39 -0
  82. package/dist/components/ui/tour-constants.js +12 -0
  83. package/dist/components/ui/tour-constants.js.map +1 -0
  84. package/dist/components/ui/tour.d.ts +124 -0
  85. package/dist/components/ui/tour.js +1207 -0
  86. package/dist/components/ui/tour.js.map +1 -0
  87. package/dist/data-grid/data-grid-cell-wrapper.js +1 -1
  88. package/dist/data-grid/data-grid-cell-wrapper.js.map +1 -1
  89. package/dist/data-grid/data-grid-context-menu.js +1 -1
  90. package/dist/data-grid/data-grid-context-menu.js.map +1 -1
  91. package/dist/data-grid/data-grid-paste-dialog.js +1 -1
  92. package/dist/data-grid/data-grid-paste-dialog.js.map +1 -1
  93. package/dist/data-grid/data-grid-row.js +1 -1
  94. package/dist/data-grid/data-grid-row.js.map +1 -1
  95. package/dist/data-grid/data-grid-search.js +1 -1
  96. package/dist/data-grid/data-grid-search.js.map +1 -1
  97. package/dist/data-grid/data-grid.js +1 -1
  98. package/dist/data-grid/data-grid.js.map +1 -1
  99. package/dist/data-grid/sortable.js +1 -1
  100. package/dist/data-grid/sortable.js.map +1 -1
  101. package/dist/data-grid/use-data-grid-undo-redo.js +2 -2
  102. package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -1
  103. package/dist/data-grid/use-data-grid.js +3 -3
  104. package/dist/data-grid/use-data-grid.js.map +1 -1
  105. package/dist/index.d.ts +31 -0
  106. package/dist/index.js +42 -0
  107. package/dist/index.js.map +1 -1
  108. package/dist/lib/compose-refs.js.map +1 -0
  109. package/dist/lib/use-as-ref.js.map +1 -0
  110. package/dist/lib/use-is-mobile.d.ts +10 -0
  111. package/dist/lib/use-is-mobile.js +21 -0
  112. package/dist/lib/use-is-mobile.js.map +1 -0
  113. package/dist/lib/use-isomorphic-layout-effect.js.map +1 -0
  114. package/dist/lib/use-lazy-ref.js.map +1 -0
  115. package/dist/lib/visually-hidden-input.d.ts +12 -0
  116. package/dist/lib/visually-hidden-input.js +105 -0
  117. package/dist/lib/visually-hidden-input.js.map +1 -0
  118. package/package.json +5 -1
  119. package/styles.css +19 -0
  120. package/dist/data-grid/compose-refs.js.map +0 -1
  121. package/dist/data-grid/use-as-ref.js.map +0 -1
  122. package/dist/data-grid/use-isomorphic-layout-effect.js.map +0 -1
  123. package/dist/data-grid/use-lazy-ref.js.map +0 -1
  124. /package/dist/{data-grid → lib}/compose-refs.d.ts +0 -0
  125. /package/dist/{data-grid → lib}/compose-refs.js +0 -0
  126. /package/dist/{data-grid → lib}/use-as-ref.d.ts +0 -0
  127. /package/dist/{data-grid → lib}/use-as-ref.js +0 -0
  128. /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.d.ts +0 -0
  129. /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.js +0 -0
  130. /package/dist/{data-grid → lib}/use-lazy-ref.d.ts +0 -0
  131. /package/dist/{data-grid → lib}/use-lazy-ref.js +0 -0
@@ -0,0 +1,787 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import {
4
+ closestCenter,
5
+ closestCorners,
6
+ DndContext,
7
+ DragOverlay,
8
+ defaultDropAnimationSideEffects,
9
+ getFirstCollision,
10
+ KeyboardCode,
11
+ KeyboardSensor,
12
+ MeasuringStrategy,
13
+ MouseSensor,
14
+ pointerWithin,
15
+ rectIntersection,
16
+ TouchSensor,
17
+ useSensor,
18
+ useSensors
19
+ } from "@dnd-kit/core";
20
+ import {
21
+ arrayMove,
22
+ defaultAnimateLayoutChanges,
23
+ horizontalListSortingStrategy,
24
+ SortableContext,
25
+ useSortable,
26
+ verticalListSortingStrategy
27
+ } from "@dnd-kit/sortable";
28
+ import { CSS, isKeyboardEvent } from "@dnd-kit/utilities";
29
+ import { Slot } from "@radix-ui/react-slot";
30
+ import * as React from "react";
31
+ import * as ReactDOM from "react-dom";
32
+ import { cn } from "../../lib/cn.js";
33
+ import { useComposedRefs } from "../../lib/compose-refs.js";
34
+ import { useIsomorphicLayoutEffect } from "../../lib/use-isomorphic-layout-effect.js";
35
+ import { DEFAULT_KANBAN_LABELS } from "./kanban-constants.js";
36
+ const directions = /* @__PURE__ */ new Set([KeyboardCode.Down, KeyboardCode.Right, KeyboardCode.Up, KeyboardCode.Left]);
37
+ function getIsDirectionCode(code) {
38
+ return directions.has(code);
39
+ }
40
+ const coordinateGetter = (event, { context }) => {
41
+ const { active, droppableRects, droppableContainers, collisionRect } = context;
42
+ if (getIsDirectionCode(event.code)) {
43
+ event.preventDefault();
44
+ if (!active || !collisionRect) return;
45
+ const filteredContainers = [];
46
+ for (const entry of droppableContainers.getEnabled()) {
47
+ if (!entry || entry?.disabled) continue;
48
+ const rect = droppableRects.get(entry.id);
49
+ if (!rect) continue;
50
+ const data = entry.data.current;
51
+ if (data) {
52
+ const { type, children } = data;
53
+ if (type === "container" && children?.length > 0) {
54
+ if (active.data.current?.type !== "container") {
55
+ continue;
56
+ }
57
+ }
58
+ }
59
+ switch (event.code) {
60
+ case KeyboardCode.Down:
61
+ if (collisionRect.top < rect.top) {
62
+ filteredContainers.push(entry);
63
+ }
64
+ break;
65
+ case KeyboardCode.Up:
66
+ if (collisionRect.top > rect.top) {
67
+ filteredContainers.push(entry);
68
+ }
69
+ break;
70
+ case KeyboardCode.Left:
71
+ if (collisionRect.left >= rect.left + rect.width) {
72
+ filteredContainers.push(entry);
73
+ }
74
+ break;
75
+ case KeyboardCode.Right:
76
+ if (collisionRect.left + collisionRect.width <= rect.left) {
77
+ filteredContainers.push(entry);
78
+ }
79
+ break;
80
+ }
81
+ }
82
+ const collisions = closestCorners({
83
+ active,
84
+ collisionRect,
85
+ droppableRects,
86
+ droppableContainers: filteredContainers,
87
+ pointerCoordinates: null
88
+ });
89
+ const closestId = getFirstCollision(collisions, "id");
90
+ if (closestId != null) {
91
+ const newDroppable = droppableContainers.get(closestId);
92
+ const newNode = newDroppable?.node.current;
93
+ const newRect = newDroppable?.rect.current;
94
+ if (newNode && newRect) {
95
+ if (newDroppable.id === "placeholder") {
96
+ return {
97
+ x: newRect.left + (newRect.width - collisionRect.width) / 2,
98
+ y: newRect.top + (newRect.height - collisionRect.height) / 2
99
+ };
100
+ }
101
+ if (newDroppable.data.current?.type === "container") {
102
+ return {
103
+ x: newRect.left + 20,
104
+ y: newRect.top + 74
105
+ };
106
+ }
107
+ return {
108
+ x: newRect.left,
109
+ y: newRect.top
110
+ };
111
+ }
112
+ }
113
+ }
114
+ return void 0;
115
+ };
116
+ function isDragPrevented(event) {
117
+ return event.activatorEvent.defaultPrevented && !isKeyboardEvent(event.activatorEvent);
118
+ }
119
+ const ROOT_NAME = "Kanban";
120
+ const BOARD_NAME = "KanbanBoard";
121
+ const COLUMN_NAME = "KanbanColumn";
122
+ const COLUMN_HANDLE_NAME = "KanbanColumnHandle";
123
+ const ITEM_NAME = "KanbanItem";
124
+ const ITEM_HANDLE_NAME = "KanbanItemHandle";
125
+ const OVERLAY_NAME = "KanbanOverlay";
126
+ const KanbanContext = React.createContext(null);
127
+ function useKanbanContext(consumerName) {
128
+ const context = React.useContext(KanbanContext);
129
+ if (!context) {
130
+ throw new Error(`\`${consumerName}\` ph\u1EA3i n\u1EB1m b\xEAn trong \`${ROOT_NAME}\``);
131
+ }
132
+ return context;
133
+ }
134
+ function Kanban(props) {
135
+ const {
136
+ value,
137
+ onValueChange,
138
+ modifiers,
139
+ strategy = verticalListSortingStrategy,
140
+ orientation = "horizontal",
141
+ onMove,
142
+ getItemValue: getItemValueProp,
143
+ accessibility,
144
+ flatCursor = false,
145
+ labels: labelsProp,
146
+ ...kanbanProps
147
+ } = props;
148
+ const id = React.useId();
149
+ const [activeId, setActiveId] = React.useState(null);
150
+ const lastOverIdRef = React.useRef(null);
151
+ const hasMovedRef = React.useRef(false);
152
+ const valueAtDragStartRef = React.useRef(null);
153
+ const sensors = useSensors(
154
+ useSensor(MouseSensor),
155
+ useSensor(TouchSensor),
156
+ useSensor(KeyboardSensor, {
157
+ coordinateGetter
158
+ })
159
+ );
160
+ const labels = React.useMemo(() => ({ ...DEFAULT_KANBAN_LABELS, ...labelsProp }), [labelsProp]);
161
+ const getItemValue = React.useCallback(
162
+ (item) => {
163
+ if (typeof item === "object" && !getItemValueProp) {
164
+ throw new Error("`getItemValue` l\xE0 b\u1EAFt bu\u1ED9c khi th\u1EBB l\xE0 object");
165
+ }
166
+ return getItemValueProp ? getItemValueProp(item) : item;
167
+ },
168
+ [getItemValueProp]
169
+ );
170
+ const getColumn = React.useCallback(
171
+ (targetId) => {
172
+ if (targetId in value) return targetId;
173
+ for (const [columnId, columnItems] of Object.entries(value)) {
174
+ if (columnItems.some((item) => getItemValue(item) === targetId)) {
175
+ return columnId;
176
+ }
177
+ }
178
+ return null;
179
+ },
180
+ [value, getItemValue]
181
+ );
182
+ const collisionDetection = React.useCallback(
183
+ (args) => {
184
+ if (activeId && activeId in value) {
185
+ return closestCenter({
186
+ ...args,
187
+ droppableContainers: args.droppableContainers.filter((container) => container.id in value)
188
+ });
189
+ }
190
+ const pointerIntersections = pointerWithin(args);
191
+ const intersections = pointerIntersections.length > 0 ? pointerIntersections : rectIntersection(args);
192
+ let overId = getFirstCollision(intersections, "id");
193
+ if (!overId) {
194
+ if (hasMovedRef.current) {
195
+ lastOverIdRef.current = activeId;
196
+ }
197
+ return lastOverIdRef.current ? [{ id: lastOverIdRef.current }] : [];
198
+ }
199
+ if (overId in value) {
200
+ const containerItems = value[overId];
201
+ if (containerItems && containerItems.length > 0) {
202
+ const closestItem = closestCenter({
203
+ ...args,
204
+ droppableContainers: args.droppableContainers.filter(
205
+ (container) => container.id !== overId && containerItems.some((item) => getItemValue(item) === container.id)
206
+ )
207
+ });
208
+ if (closestItem.length > 0) {
209
+ overId = closestItem[0]?.id ?? overId;
210
+ }
211
+ }
212
+ }
213
+ lastOverIdRef.current = overId;
214
+ return [{ id: overId }];
215
+ },
216
+ [activeId, value, getItemValue]
217
+ );
218
+ const onDragStart = React.useCallback(
219
+ (event) => {
220
+ kanbanProps.onDragStart?.(event);
221
+ if (isDragPrevented(event)) return;
222
+ setActiveId(event.active.id);
223
+ valueAtDragStartRef.current = value;
224
+ },
225
+ [value, kanbanProps.onDragStart]
226
+ );
227
+ const onDragOver = React.useCallback(
228
+ (event) => {
229
+ kanbanProps.onDragOver?.(event);
230
+ if (isDragPrevented(event)) return;
231
+ const { active, over } = event;
232
+ if (!over) return;
233
+ const activeColumn = getColumn(active.id);
234
+ const overColumn = getColumn(over.id);
235
+ if (!activeColumn || !overColumn) return;
236
+ if (activeColumn === overColumn) {
237
+ const items = value[activeColumn];
238
+ if (!items) return;
239
+ const activeIndex = items.findIndex((item) => getItemValue(item) === active.id);
240
+ const overIndex = items.findIndex((item) => getItemValue(item) === over.id);
241
+ if (activeIndex !== overIndex) {
242
+ const newColumns = { ...value };
243
+ newColumns[activeColumn] = arrayMove(items, activeIndex, overIndex);
244
+ onValueChange?.(newColumns);
245
+ }
246
+ } else {
247
+ const activeItems = value[activeColumn];
248
+ const overItems = value[overColumn];
249
+ if (!activeItems || !overItems) return;
250
+ const activeIndex = activeItems.findIndex((item) => getItemValue(item) === active.id);
251
+ if (activeIndex === -1) return;
252
+ const activeItem = activeItems[activeIndex];
253
+ if (!activeItem) return;
254
+ const updatedItems = {
255
+ ...value,
256
+ [activeColumn]: activeItems.filter((item) => getItemValue(item) !== active.id),
257
+ [overColumn]: [...overItems, activeItem]
258
+ };
259
+ onValueChange?.(updatedItems);
260
+ hasMovedRef.current = true;
261
+ }
262
+ },
263
+ [value, getColumn, getItemValue, onValueChange, kanbanProps.onDragOver]
264
+ );
265
+ const onDragEnd = React.useCallback(
266
+ (event) => {
267
+ kanbanProps.onDragEnd?.(event);
268
+ if (isDragPrevented(event)) return;
269
+ valueAtDragStartRef.current = null;
270
+ const { active, over } = event;
271
+ if (!over) {
272
+ setActiveId(null);
273
+ return;
274
+ }
275
+ if (active.id in value && over.id in value) {
276
+ const activeIndex = Object.keys(value).indexOf(active.id);
277
+ const overIndex = Object.keys(value).indexOf(over.id);
278
+ if (activeIndex !== overIndex) {
279
+ const orderedColumns = Object.keys(value);
280
+ const newOrder = arrayMove(orderedColumns, activeIndex, overIndex);
281
+ const newColumns = {};
282
+ for (const key of newOrder) {
283
+ const items = value[key];
284
+ if (items) {
285
+ newColumns[key] = items;
286
+ }
287
+ }
288
+ if (onMove) {
289
+ onMove({ ...event, activeIndex, overIndex });
290
+ } else {
291
+ onValueChange?.(newColumns);
292
+ }
293
+ }
294
+ } else {
295
+ const activeColumn = getColumn(active.id);
296
+ const overColumn = getColumn(over.id);
297
+ if (!activeColumn || !overColumn) {
298
+ setActiveId(null);
299
+ return;
300
+ }
301
+ if (activeColumn === overColumn) {
302
+ const items = value[activeColumn];
303
+ if (!items) {
304
+ setActiveId(null);
305
+ return;
306
+ }
307
+ const activeIndex = items.findIndex((item) => getItemValue(item) === active.id);
308
+ const overIndex = items.findIndex((item) => getItemValue(item) === over.id);
309
+ if (activeIndex !== overIndex) {
310
+ const newColumns = { ...value };
311
+ newColumns[activeColumn] = arrayMove(items, activeIndex, overIndex);
312
+ if (onMove) {
313
+ onMove({
314
+ ...event,
315
+ activeIndex,
316
+ overIndex
317
+ });
318
+ } else {
319
+ onValueChange?.(newColumns);
320
+ }
321
+ }
322
+ }
323
+ }
324
+ setActiveId(null);
325
+ hasMovedRef.current = false;
326
+ },
327
+ [value, getColumn, getItemValue, onValueChange, onMove, kanbanProps.onDragEnd]
328
+ );
329
+ const onDragCancel = React.useCallback(
330
+ (event) => {
331
+ kanbanProps.onDragCancel?.(event);
332
+ if (isDragPrevented(event)) return;
333
+ const valueAtDragStart = valueAtDragStartRef.current;
334
+ valueAtDragStartRef.current = null;
335
+ if (valueAtDragStart && valueAtDragStart !== value) {
336
+ onValueChange?.(valueAtDragStart);
337
+ }
338
+ setActiveId(null);
339
+ hasMovedRef.current = false;
340
+ },
341
+ [value, onValueChange, kanbanProps.onDragCancel]
342
+ );
343
+ const announcements = React.useMemo(
344
+ () => ({
345
+ onDragStart({ active }) {
346
+ const isColumn = active.id in value;
347
+ const variant = isColumn ? "column" : "item";
348
+ const position = isColumn ? Object.keys(value).indexOf(active.id) + 1 : (() => {
349
+ const column = getColumn(active.id);
350
+ if (!column || !value[column]) return 1;
351
+ return value[column].findIndex((item) => getItemValue(item) === active.id) + 1;
352
+ })();
353
+ const total = isColumn ? Object.keys(value).length : (() => {
354
+ const column = getColumn(active.id);
355
+ return column ? value[column]?.length ?? 0 : 0;
356
+ })();
357
+ return labels.pickedUp({ variant, value: active.id, position, total });
358
+ },
359
+ onDragOver({ active, over }) {
360
+ if (!over) return;
361
+ const isColumn = active.id in value;
362
+ const variant = isColumn ? "column" : "item";
363
+ const position = isColumn ? Object.keys(value).indexOf(over.id) + 1 : (() => {
364
+ const column = getColumn(over.id);
365
+ if (!column || !value[column]) return 1;
366
+ return value[column].findIndex((item) => getItemValue(item) === over.id) + 1;
367
+ })();
368
+ const total = isColumn ? Object.keys(value).length : (() => {
369
+ const column = getColumn(over.id);
370
+ return column ? value[column]?.length ?? 0 : 0;
371
+ })();
372
+ const overColumn = getColumn(over.id);
373
+ const activeColumn = getColumn(active.id);
374
+ if (isColumn) {
375
+ return labels.moved({ variant, value: active.id, position, total });
376
+ }
377
+ if (activeColumn !== overColumn) {
378
+ return labels.moved({ variant, value: active.id, position, total, column: overColumn ?? void 0 });
379
+ }
380
+ return labels.moved({ variant, value: active.id, position, total });
381
+ },
382
+ onDragEnd({ active, over }) {
383
+ if (!over) return;
384
+ const isColumn = active.id in value;
385
+ const variant = isColumn ? "column" : "item";
386
+ const position = isColumn ? Object.keys(value).indexOf(over.id) + 1 : (() => {
387
+ const column = getColumn(over.id);
388
+ if (!column || !value[column]) return 1;
389
+ return value[column].findIndex((item) => getItemValue(item) === over.id) + 1;
390
+ })();
391
+ const total = isColumn ? Object.keys(value).length : (() => {
392
+ const column = getColumn(over.id);
393
+ return column ? value[column]?.length ?? 0 : 0;
394
+ })();
395
+ const overColumn = getColumn(over.id);
396
+ const activeColumn = getColumn(active.id);
397
+ if (isColumn) {
398
+ return labels.dropped({ variant, value: active.id, position, total });
399
+ }
400
+ if (activeColumn !== overColumn) {
401
+ return labels.dropped({ variant, value: active.id, position, total, column: overColumn ?? void 0 });
402
+ }
403
+ return labels.dropped({ variant, value: active.id, position, total });
404
+ },
405
+ onDragCancel({ active }) {
406
+ const isColumn = active.id in value;
407
+ return labels.cancelled({ variant: isColumn ? "column" : "item", value: active.id });
408
+ }
409
+ }),
410
+ [value, getColumn, getItemValue, labels]
411
+ );
412
+ const contextValue = React.useMemo(
413
+ () => ({
414
+ id,
415
+ items: value,
416
+ modifiers,
417
+ strategy,
418
+ orientation,
419
+ activeId,
420
+ setActiveId,
421
+ getItemValue,
422
+ flatCursor,
423
+ labels
424
+ }),
425
+ [id, value, activeId, modifiers, strategy, orientation, getItemValue, flatCursor, labels]
426
+ );
427
+ return /* @__PURE__ */ jsx(KanbanContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
428
+ DndContext,
429
+ {
430
+ collisionDetection,
431
+ modifiers,
432
+ sensors,
433
+ ...kanbanProps,
434
+ id,
435
+ measuring: {
436
+ droppable: {
437
+ strategy: MeasuringStrategy.Always
438
+ }
439
+ },
440
+ onDragStart,
441
+ onDragOver,
442
+ onDragEnd,
443
+ onDragCancel,
444
+ accessibility: {
445
+ announcements,
446
+ screenReaderInstructions: {
447
+ draggable: labels.instructions
448
+ },
449
+ ...accessibility
450
+ }
451
+ }
452
+ ) });
453
+ }
454
+ const KanbanBoardContext = React.createContext(false);
455
+ function KanbanBoard(props) {
456
+ const { asChild, className, ref, ...boardProps } = props;
457
+ const context = useKanbanContext(BOARD_NAME);
458
+ const columns = React.useMemo(() => {
459
+ return Object.keys(context.items);
460
+ }, [context.items]);
461
+ const BoardPrimitive = asChild ? Slot : "div";
462
+ return /* @__PURE__ */ jsx(KanbanBoardContext.Provider, { value: true, children: /* @__PURE__ */ jsx(
463
+ SortableContext,
464
+ {
465
+ items: columns,
466
+ strategy: context.orientation === "horizontal" ? horizontalListSortingStrategy : verticalListSortingStrategy,
467
+ children: /* @__PURE__ */ jsx(
468
+ BoardPrimitive,
469
+ {
470
+ "aria-orientation": context.orientation,
471
+ "data-orientation": context.orientation,
472
+ "data-slot": "kanban-board",
473
+ ...boardProps,
474
+ ref,
475
+ className: cn(
476
+ "flex size-full gap-4",
477
+ context.orientation === "horizontal" ? "flex-row" : "flex-col",
478
+ className
479
+ )
480
+ }
481
+ )
482
+ }
483
+ ) });
484
+ }
485
+ const KanbanColumnContext = React.createContext(null);
486
+ function useKanbanColumnContext(consumerName) {
487
+ const context = React.useContext(KanbanColumnContext);
488
+ if (!context) {
489
+ throw new Error(`\`${consumerName}\` ph\u1EA3i n\u1EB1m b\xEAn trong \`${COLUMN_NAME}\``);
490
+ }
491
+ return context;
492
+ }
493
+ const animateLayoutChanges = (args) => defaultAnimateLayoutChanges({ ...args, wasDragging: true });
494
+ function KanbanColumn(props) {
495
+ const { value, asChild, asHandle, disabled, className, style, ref, ...columnProps } = props;
496
+ const id = React.useId();
497
+ const context = useKanbanContext(COLUMN_NAME);
498
+ const inBoard = React.useContext(KanbanBoardContext);
499
+ const inOverlay = React.useContext(KanbanOverlayContext);
500
+ if (!inBoard && !inOverlay) {
501
+ throw new Error(`\`${COLUMN_NAME}\` ph\u1EA3i n\u1EB1m b\xEAn trong \`${BOARD_NAME}\` ho\u1EB7c \`${OVERLAY_NAME}\``);
502
+ }
503
+ if (value === "") {
504
+ throw new Error(`\`value\` c\u1EE7a \`${COLUMN_NAME}\` kh\xF4ng \u0111\u01B0\u1EE3c l\xE0 chu\u1ED7i r\u1ED7ng`);
505
+ }
506
+ const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
507
+ id: value,
508
+ disabled,
509
+ animateLayoutChanges,
510
+ // ⚠ Khác thượng nguồn: dnd-kit mặc định đọc vai trò là "sortable" (tiếng Anh).
511
+ attributes: { roleDescription: context.labels.columnRoleDescription }
512
+ });
513
+ const onNodeRefChange = React.useCallback(
514
+ (node) => {
515
+ if (disabled) return;
516
+ setNodeRef(node);
517
+ },
518
+ [disabled, setNodeRef]
519
+ );
520
+ const composedRef = useComposedRefs(ref, onNodeRefChange);
521
+ const composedStyle = React.useMemo(() => {
522
+ return {
523
+ transform: CSS.Transform.toString(transform),
524
+ transition,
525
+ ...style
526
+ };
527
+ }, [transform, transition, style]);
528
+ const items = React.useMemo(() => {
529
+ const columnItems = context.items[value] ?? [];
530
+ return columnItems.map((item) => context.getItemValue(item));
531
+ }, [context.items, value, context.getItemValue]);
532
+ const columnContext = React.useMemo(
533
+ () => ({
534
+ id,
535
+ attributes,
536
+ listeners,
537
+ setActivatorNodeRef,
538
+ isDragging,
539
+ disabled
540
+ }),
541
+ [id, attributes, listeners, setActivatorNodeRef, isDragging, disabled]
542
+ );
543
+ const ColumnPrimitive = asChild ? Slot : "div";
544
+ return /* @__PURE__ */ jsx(KanbanColumnContext.Provider, { value: columnContext, children: /* @__PURE__ */ jsx(
545
+ SortableContext,
546
+ {
547
+ items,
548
+ strategy: context.orientation === "horizontal" ? horizontalListSortingStrategy : verticalListSortingStrategy,
549
+ children: /* @__PURE__ */ jsx(
550
+ ColumnPrimitive,
551
+ {
552
+ id,
553
+ "data-disabled": disabled ? "" : void 0,
554
+ "data-dragging": isDragging ? "" : void 0,
555
+ "data-slot": "kanban-column",
556
+ ...columnProps,
557
+ ...asHandle && !disabled ? attributes : {},
558
+ ...asHandle && !disabled ? listeners : {},
559
+ ref: composedRef,
560
+ style: composedStyle,
561
+ className: cn(
562
+ /*
563
+ * ⚠ Khác thượng nguồn: `bg-zinc-100 dark:bg-zinc-900` là thang mặc định của Tailwind
564
+ * (cổng màu chặn) → `bg-muted`, tự lật theo theme. Khung cột là viền TRANG TRÍ (không
565
+ * bấm được, không mang trạng thái) nên `border-border` là đúng vai trò.
566
+ */
567
+ "flex size-full flex-col gap-2 rounded-lg border border-border bg-muted p-2.5 aria-disabled:pointer-events-none aria-disabled:opacity-50",
568
+ {
569
+ "touch-none select-none": asHandle,
570
+ "cursor-default": context.flatCursor,
571
+ "data-dragging:cursor-grabbing": !context.flatCursor,
572
+ "cursor-grab": !isDragging && asHandle && !context.flatCursor,
573
+ "opacity-50": isDragging,
574
+ "pointer-events-none opacity-50": disabled,
575
+ // ⚠ Khác thượng nguồn: bản sao trong `KanbanOverlay` được nâng lên bằng bóng nổi.
576
+ "shadow-overlay": inOverlay
577
+ },
578
+ className
579
+ )
580
+ }
581
+ )
582
+ }
583
+ ) });
584
+ }
585
+ function KanbanColumnHandle(props) {
586
+ const { asChild, disabled, className, ref, ...columnHandleProps } = props;
587
+ const context = useKanbanContext(COLUMN_HANDLE_NAME);
588
+ const columnContext = useKanbanColumnContext(COLUMN_HANDLE_NAME);
589
+ const isDisabled = disabled ?? columnContext.disabled;
590
+ const { setActivatorNodeRef } = columnContext;
591
+ const onActivatorNodeRef = React.useCallback(
592
+ (node) => {
593
+ if (isDisabled) return;
594
+ setActivatorNodeRef(node);
595
+ },
596
+ [isDisabled, setActivatorNodeRef]
597
+ );
598
+ const composedRef = useComposedRefs(ref, onActivatorNodeRef);
599
+ const HandlePrimitive = asChild ? Slot : "button";
600
+ return /* @__PURE__ */ jsx(
601
+ HandlePrimitive,
602
+ {
603
+ type: "button",
604
+ "aria-label": context.labels.columnHandleLabel,
605
+ "aria-controls": columnContext.id,
606
+ "data-disabled": isDisabled ? "" : void 0,
607
+ "data-dragging": columnContext.isDragging ? "" : void 0,
608
+ "data-slot": "kanban-column-handle",
609
+ ...columnHandleProps,
610
+ ...isDisabled ? {} : columnContext.attributes,
611
+ ...isDisabled ? {} : columnContext.listeners,
612
+ ref: composedRef,
613
+ className: cn(
614
+ "select-none disabled:pointer-events-none disabled:opacity-50",
615
+ context.flatCursor ? "cursor-default" : "cursor-grab data-dragging:cursor-grabbing",
616
+ className
617
+ ),
618
+ disabled: isDisabled
619
+ }
620
+ );
621
+ }
622
+ const KanbanItemContext = React.createContext(null);
623
+ function useKanbanItemContext(consumerName) {
624
+ const context = React.useContext(KanbanItemContext);
625
+ if (!context) {
626
+ throw new Error(`\`${consumerName}\` ph\u1EA3i n\u1EB1m b\xEAn trong \`${ITEM_NAME}\``);
627
+ }
628
+ return context;
629
+ }
630
+ function KanbanItem(props) {
631
+ const { value, style, asHandle, asChild, disabled, className, ref, ...itemProps } = props;
632
+ const id = React.useId();
633
+ const context = useKanbanContext(ITEM_NAME);
634
+ const inBoard = React.useContext(KanbanBoardContext);
635
+ const inOverlay = React.useContext(KanbanOverlayContext);
636
+ if (!inBoard && !inOverlay) {
637
+ throw new Error(`\`${ITEM_NAME}\` ph\u1EA3i n\u1EB1m b\xEAn trong \`${BOARD_NAME}\``);
638
+ }
639
+ const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
640
+ id: value,
641
+ disabled,
642
+ // ⚠ Khác thượng nguồn: dnd-kit mặc định đọc vai trò là "sortable" (tiếng Anh).
643
+ attributes: { roleDescription: context.labels.itemRoleDescription }
644
+ });
645
+ if (value === "") {
646
+ throw new Error(`\`value\` c\u1EE7a \`${ITEM_NAME}\` kh\xF4ng \u0111\u01B0\u1EE3c l\xE0 chu\u1ED7i r\u1ED7ng`);
647
+ }
648
+ const onNodeRef = React.useCallback(
649
+ (node) => {
650
+ if (disabled) return;
651
+ setNodeRef(node);
652
+ },
653
+ [disabled, setNodeRef]
654
+ );
655
+ const composedRef = useComposedRefs(ref, onNodeRef);
656
+ const composedStyle = React.useMemo(() => {
657
+ return {
658
+ transform: CSS.Transform.toString(transform),
659
+ transition,
660
+ ...style
661
+ };
662
+ }, [transform, transition, style]);
663
+ const itemContext = React.useMemo(
664
+ () => ({
665
+ id,
666
+ attributes,
667
+ listeners,
668
+ setActivatorNodeRef,
669
+ isDragging,
670
+ disabled
671
+ }),
672
+ [id, attributes, listeners, setActivatorNodeRef, isDragging, disabled]
673
+ );
674
+ const ItemPrimitive = asChild ? Slot : "div";
675
+ return /* @__PURE__ */ jsx(KanbanItemContext.Provider, { value: itemContext, children: /* @__PURE__ */ jsx(
676
+ ItemPrimitive,
677
+ {
678
+ id,
679
+ "data-disabled": disabled ? "" : void 0,
680
+ "data-dragging": isDragging ? "" : void 0,
681
+ "data-slot": "kanban-item",
682
+ ...itemProps,
683
+ ...asHandle && !disabled ? attributes : {},
684
+ ...asHandle && !disabled ? listeners : {},
685
+ ref: composedRef,
686
+ style: composedStyle,
687
+ className: cn(
688
+ // Viền focus cùng khuôn với `Checkbox`/`RadioGroup` của gói.
689
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
690
+ {
691
+ "touch-none select-none": asHandle,
692
+ "cursor-default": context.flatCursor,
693
+ "data-dragging:cursor-grabbing": !context.flatCursor,
694
+ "cursor-grab": !isDragging && asHandle && !context.flatCursor,
695
+ "opacity-50": isDragging,
696
+ "pointer-events-none opacity-50": disabled,
697
+ // ⚠ Khác thượng nguồn: bản sao trong `KanbanOverlay` được nâng lên bằng bóng nổi.
698
+ // Bóng theo bo góc của CHÍNH phần tử — dùng `asChild` lên thẻ để bóng ôm đúng góc.
699
+ "shadow-overlay": inOverlay
700
+ },
701
+ className
702
+ )
703
+ }
704
+ ) });
705
+ }
706
+ function KanbanItemHandle(props) {
707
+ const { asChild, disabled, className, ref, ...itemHandleProps } = props;
708
+ const context = useKanbanContext(ITEM_HANDLE_NAME);
709
+ const itemContext = useKanbanItemContext(ITEM_HANDLE_NAME);
710
+ const isDisabled = disabled ?? itemContext.disabled;
711
+ const { setActivatorNodeRef } = itemContext;
712
+ const onActivatorNodeRef = React.useCallback(
713
+ (node) => {
714
+ if (isDisabled) return;
715
+ setActivatorNodeRef(node);
716
+ },
717
+ [isDisabled, setActivatorNodeRef]
718
+ );
719
+ const composedRef = useComposedRefs(ref, onActivatorNodeRef);
720
+ const HandlePrimitive = asChild ? Slot : "button";
721
+ return /* @__PURE__ */ jsx(
722
+ HandlePrimitive,
723
+ {
724
+ type: "button",
725
+ "aria-label": context.labels.itemHandleLabel,
726
+ "aria-controls": itemContext.id,
727
+ "data-disabled": isDisabled ? "" : void 0,
728
+ "data-dragging": itemContext.isDragging ? "" : void 0,
729
+ "data-slot": "kanban-item-handle",
730
+ ...itemHandleProps,
731
+ ...isDisabled ? {} : itemContext.attributes,
732
+ ...isDisabled ? {} : itemContext.listeners,
733
+ ref: composedRef,
734
+ className: cn(
735
+ "select-none disabled:pointer-events-none disabled:opacity-50",
736
+ context.flatCursor ? "cursor-default" : "cursor-grab data-dragging:cursor-grabbing",
737
+ className
738
+ ),
739
+ disabled: isDisabled
740
+ }
741
+ );
742
+ }
743
+ const KanbanOverlayContext = React.createContext(false);
744
+ const dropAnimation = {
745
+ sideEffects: defaultDropAnimationSideEffects({
746
+ styles: {
747
+ active: {
748
+ opacity: "0.4"
749
+ }
750
+ }
751
+ })
752
+ };
753
+ function KanbanOverlay(props) {
754
+ const { container: containerProp, children, ...overlayProps } = props;
755
+ const context = useKanbanContext(OVERLAY_NAME);
756
+ const [mounted, setMounted] = React.useState(false);
757
+ useIsomorphicLayoutEffect(() => setMounted(true), []);
758
+ const container = containerProp ?? (mounted ? globalThis.document?.body : null);
759
+ if (!container) return null;
760
+ const variant = context.activeId && context.activeId in context.items ? "column" : "item";
761
+ return ReactDOM.createPortal(
762
+ /* @__PURE__ */ jsx(
763
+ DragOverlay,
764
+ {
765
+ dropAnimation,
766
+ modifiers: context.modifiers,
767
+ className: cn(!context.flatCursor && "cursor-grabbing"),
768
+ ...overlayProps,
769
+ children: /* @__PURE__ */ jsx(KanbanOverlayContext.Provider, { value: true, children: context.activeId && children ? typeof children === "function" ? children({
770
+ value: context.activeId,
771
+ variant
772
+ }) : children : null })
773
+ }
774
+ ),
775
+ container
776
+ );
777
+ }
778
+ export {
779
+ Kanban,
780
+ KanbanBoard,
781
+ KanbanColumn,
782
+ KanbanColumnHandle,
783
+ KanbanItem,
784
+ KanbanItemHandle,
785
+ KanbanOverlay
786
+ };
787
+ //# sourceMappingURL=kanban.js.map