@composy/kanban-core 0.0.1 → 0.0.2

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 (147) hide show
  1. package/README.md +171 -37
  2. package/dist/index.css +114 -114
  3. package/dist/index.js +221 -126
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.min.css +1 -1
  6. package/dist/index.min.js +758 -1
  7. package/dist/index.min.js.map +1 -0
  8. package/es/.dts-output-manifest.json +1 -0
  9. package/es/.dts.tsbuildinfo +1 -0
  10. package/es/core/ColumnManager.d.ts +4 -3
  11. package/es/core/ColumnManager.js +43 -18
  12. package/es/core/ColumnManager.js.map +1 -1
  13. package/es/core/DragController.d.ts +1 -2
  14. package/es/core/DragController.js +11 -1
  15. package/es/core/DragController.js.map +1 -1
  16. package/es/core/EventEmitter.d.ts +0 -1
  17. package/es/core/EventEmitter.js +8 -3
  18. package/es/core/EventEmitter.js.map +1 -1
  19. package/es/core/FilterEngine.d.ts +1 -2
  20. package/es/core/FilterEngine.js +10 -5
  21. package/es/core/FilterEngine.js.map +1 -1
  22. package/es/core/KanbanBoard.d.ts +3 -3
  23. package/es/core/KanbanBoard.js +61 -15
  24. package/es/core/KanbanBoard.js.map +1 -1
  25. package/es/core/Renderer.d.ts +2 -3
  26. package/es/core/Renderer.js +11 -7
  27. package/es/core/Renderer.js.map +1 -1
  28. package/es/core/index.d.ts +2 -3
  29. package/es/core/index.js +1 -1
  30. package/es/index.d.ts +3 -4
  31. package/es/index.js +2 -2
  32. package/es/styles/kanban.css +114 -114
  33. package/es/types/index.d.ts +3 -4
  34. package/es/types/index.js +1 -1
  35. package/es/utils/dom.d.ts +0 -1
  36. package/es/utils/dom.js +1 -1
  37. package/es/utils/dom.js.map +1 -1
  38. package/es/utils/id.d.ts +0 -1
  39. package/es/utils/id.js +1 -1
  40. package/es/utils/id.js.map +1 -1
  41. package/es/utils/index.d.ts +1 -2
  42. package/es/utils/index.js +1 -1
  43. package/es/utils/index.js.map +1 -1
  44. package/es/version.d.ts +0 -1
  45. package/es/version.js +1 -1
  46. package/es/version.js.map +1 -1
  47. package/esm/core/ColumnManager.js +43 -18
  48. package/esm/core/ColumnManager.js.map +1 -1
  49. package/esm/core/DragController.js +11 -1
  50. package/esm/core/DragController.js.map +1 -1
  51. package/esm/core/EventEmitter.js +8 -3
  52. package/esm/core/EventEmitter.js.map +1 -1
  53. package/esm/core/FilterEngine.js +10 -5
  54. package/esm/core/FilterEngine.js.map +1 -1
  55. package/esm/core/KanbanBoard.js +63 -17
  56. package/esm/core/KanbanBoard.js.map +1 -1
  57. package/esm/core/Renderer.js +11 -7
  58. package/esm/core/Renderer.js.map +1 -1
  59. package/esm/index.js +4 -4
  60. package/esm/styles/kanban.less +357 -339
  61. package/esm/utils/dom.js +1 -1
  62. package/esm/utils/dom.js.map +1 -1
  63. package/esm/utils/id.js +1 -1
  64. package/esm/utils/id.js.map +1 -1
  65. package/esm/utils/index.js +2 -2
  66. package/esm/utils/index.js.map +1 -1
  67. package/esm/version.js +1 -1
  68. package/esm/version.js.map +1 -1
  69. package/lib/core/ColumnManager.cjs +43 -18
  70. package/lib/core/ColumnManager.cjs.map +1 -1
  71. package/{esm/core/ColumnManager.d.ts → lib/core/ColumnManager.d.cts} +4 -3
  72. package/lib/core/DragController.cjs +11 -1
  73. package/lib/core/DragController.cjs.map +1 -1
  74. package/{esm/core/DragController.d.ts → lib/core/DragController.d.cts} +1 -2
  75. package/lib/core/EventEmitter.cjs +8 -3
  76. package/lib/core/EventEmitter.cjs.map +1 -1
  77. package/lib/core/{EventEmitter.d.ts → EventEmitter.d.cts} +0 -1
  78. package/lib/core/FilterEngine.cjs +10 -5
  79. package/lib/core/FilterEngine.cjs.map +1 -1
  80. package/{esm/core/FilterEngine.d.ts → lib/core/FilterEngine.d.cts} +1 -2
  81. package/lib/core/KanbanBoard.cjs +61 -15
  82. package/lib/core/KanbanBoard.cjs.map +1 -1
  83. package/{esm/core/KanbanBoard.d.ts → lib/core/KanbanBoard.d.cts} +3 -3
  84. package/lib/core/Renderer.cjs +11 -7
  85. package/lib/core/Renderer.cjs.map +1 -1
  86. package/{esm/core/Renderer.d.ts → lib/core/Renderer.d.cts} +2 -3
  87. package/lib/core/index.cjs +1 -1
  88. package/{esm/core/index.d.ts → lib/core/index.d.cts} +2 -3
  89. package/lib/index.cjs +2 -2
  90. package/lib/{index.d.ts → index.d.cts} +3 -4
  91. package/lib/styles/kanban.css +114 -114
  92. package/lib/types/index.cjs +1 -1
  93. package/{esm/types/index.d.ts → lib/types/index.d.cts} +3 -4
  94. package/lib/utils/dom.cjs +1 -1
  95. package/lib/utils/dom.cjs.map +1 -1
  96. package/lib/utils/{dom.d.ts → dom.d.cts} +0 -1
  97. package/lib/utils/id.cjs +1 -1
  98. package/lib/utils/id.cjs.map +1 -1
  99. package/{esm/utils/id.d.ts → lib/utils/id.d.cts} +0 -1
  100. package/lib/utils/index.cjs +1 -1
  101. package/lib/utils/index.cjs.map +1 -1
  102. package/lib/utils/{index.d.ts → index.d.cts} +1 -2
  103. package/lib/version.cjs +1 -1
  104. package/lib/version.cjs.map +1 -1
  105. package/{esm/version.d.ts → lib/version.d.cts} +0 -1
  106. package/package.json +23 -21
  107. package/es/core/ColumnManager.d.ts.map +0 -1
  108. package/es/core/DragController.d.ts.map +0 -1
  109. package/es/core/EventEmitter.d.ts.map +0 -1
  110. package/es/core/FilterEngine.d.ts.map +0 -1
  111. package/es/core/KanbanBoard.d.ts.map +0 -1
  112. package/es/core/Renderer.d.ts.map +0 -1
  113. package/es/core/index.d.ts.map +0 -1
  114. package/es/index.d.ts.map +0 -1
  115. package/es/types/index.d.ts.map +0 -1
  116. package/es/utils/dom.d.ts.map +0 -1
  117. package/es/utils/id.d.ts.map +0 -1
  118. package/es/utils/index.d.ts.map +0 -1
  119. package/es/version.d.ts.map +0 -1
  120. package/esm/core/ColumnManager.d.ts.map +0 -1
  121. package/esm/core/DragController.d.ts.map +0 -1
  122. package/esm/core/EventEmitter.d.ts +0 -35
  123. package/esm/core/EventEmitter.d.ts.map +0 -1
  124. package/esm/core/FilterEngine.d.ts.map +0 -1
  125. package/esm/core/KanbanBoard.d.ts.map +0 -1
  126. package/esm/core/Renderer.d.ts.map +0 -1
  127. package/esm/core/index.d.ts.map +0 -1
  128. package/esm/core/index.js +0 -17
  129. package/esm/index.d.ts +0 -15
  130. package/esm/index.d.ts.map +0 -1
  131. package/esm/types/index.d.ts.map +0 -1
  132. package/esm/types/index.js +0 -10
  133. package/esm/utils/dom.d.ts +0 -24
  134. package/esm/utils/dom.d.ts.map +0 -1
  135. package/esm/utils/id.d.ts.map +0 -1
  136. package/esm/utils/index.d.ts +0 -25
  137. package/esm/utils/index.d.ts.map +0 -1
  138. package/esm/version.d.ts.map +0 -1
  139. package/lib/core/ColumnManager.d.ts +0 -99
  140. package/lib/core/DragController.d.ts +0 -55
  141. package/lib/core/FilterEngine.d.ts +0 -45
  142. package/lib/core/KanbanBoard.d.ts +0 -152
  143. package/lib/core/Renderer.d.ts +0 -59
  144. package/lib/core/index.d.ts +0 -11
  145. package/lib/types/index.d.ts +0 -209
  146. package/lib/utils/id.d.ts +0 -9
  147. package/lib/version.d.ts +0 -6
package/dist/index.js CHANGED
@@ -2,96 +2,15 @@
2
2
  * ***********************************
3
3
  * @composy/kanban-core v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:33:25 *
5
+ * Build time: 2024-10-12 17:29:01 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
9
9
  */
10
10
  (function(global, factory) {
11
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.LDesignKanban = {}));
11
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.LDesignKanbanCore = {}));
12
12
  })(this, function(exports) {
13
13
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
14
- //#region src/utils/dom.ts
15
- function createElement(tag, className, attributes) {
16
- const el = document.createElement(tag);
17
- if (className) el.className = className;
18
- if (attributes) Object.entries(attributes).forEach(([key, value]) => {
19
- el.setAttribute(key, value);
20
- });
21
- return el;
22
- }
23
- function resolveElement(target) {
24
- if (typeof target === "string") return document.querySelector(target);
25
- return target;
26
- }
27
- function clearElement(el) {
28
- el.innerHTML = "";
29
- }
30
- function removeElement(el) {
31
- el?.parentNode?.removeChild(el);
32
- }
33
- function insertAt(parent, child, index) {
34
- const ref = parent.children[index];
35
- if (ref) parent.insertBefore(child, ref);
36
- else parent.appendChild(child);
37
- }
38
- //#endregion
39
- //#region src/core/EventEmitter.ts
40
- var EventEmitter = class {
41
- constructor() {
42
- this.listeners = /* @__PURE__ */ new Map();
43
- }
44
- /**
45
- * 监听事件
46
- */
47
- on(event, handler) {
48
- if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
49
- this.listeners.get(event).add(handler);
50
- return () => this.off(event, handler);
51
- }
52
- /**
53
- * 监听一次性事件
54
- */
55
- once(event, handler) {
56
- const wrapper = (...args) => {
57
- this.off(event, wrapper);
58
- handler(...args);
59
- };
60
- return this.on(event, wrapper);
61
- }
62
- /**
63
- * 移除监听器
64
- */
65
- off(event, handler) {
66
- this.listeners.get(event)?.delete(handler);
67
- }
68
- /**
69
- * 触发事件
70
- */
71
- emit(event, payload) {
72
- const handlers = this.listeners.get(event);
73
- if (handlers) handlers.forEach((handler) => {
74
- try {
75
- handler(payload);
76
- } catch (err) {
77
- console.error(`[KanbanBoard] Error in event handler for "${event}":`, err);
78
- }
79
- });
80
- }
81
- /**
82
- * 移除所有监听器
83
- */
84
- removeAll() {
85
- this.listeners.clear();
86
- }
87
- /**
88
- * 获取监听器数量
89
- */
90
- listenerCount(event) {
91
- return this.listeners.get(event)?.size ?? 0;
92
- }
93
- };
94
- //#endregion
95
14
  //#region src/utils/id.ts
96
15
  let counter = 0;
97
16
  function generateId(prefix = "kb") {
@@ -113,15 +32,25 @@
113
32
  */
114
33
  init(columns) {
115
34
  this.columns = columns.map((col, index) => this.normalizeColumn(col, index));
35
+ this.sortColumns();
116
36
  }
117
37
  /**
118
38
  * 标准化列数据
119
39
  */
120
40
  normalizeColumn(partial, index) {
41
+ const id = partial.id || generateId("col");
42
+ const cards = (partial.cards ?? []).map((card, cardIndex) => this.normalizeCard({
43
+ ...card,
44
+ columnId: id
45
+ }, id, cardIndex));
46
+ cards.sort((a, b) => a.order - b.order);
47
+ cards.forEach((card, cardIndex) => {
48
+ card.order = cardIndex;
49
+ });
121
50
  return {
122
- id: partial.id || generateId("col"),
123
- title: partial.title || `Column ${index + 1}`,
124
- cards: (partial.cards || []).map((card, cardIndex) => this.normalizeCard(card, partial.id || "", cardIndex)),
51
+ id,
52
+ title: partial.title ?? `Column ${index + 1}`,
53
+ cards,
125
54
  wipLimit: partial.wipLimit,
126
55
  collapsed: partial.collapsed ?? false,
127
56
  color: partial.color,
@@ -134,16 +63,16 @@
134
63
  normalizeCard(partial, columnId, index) {
135
64
  return {
136
65
  id: partial.id || generateId("card"),
137
- title: partial.title || "",
66
+ title: partial.title ?? "",
138
67
  description: partial.description,
139
- labels: partial.labels || [],
140
- assignees: partial.assignees || [],
68
+ labels: partial.labels?.map((label) => ({ ...label })) ?? [],
69
+ assignees: partial.assignees ? [...partial.assignees] : [],
141
70
  dueDate: partial.dueDate,
142
71
  priority: partial.priority,
143
72
  order: partial.order ?? index,
144
- columnId: partial.columnId || columnId,
73
+ columnId: partial.columnId ?? columnId,
145
74
  data: partial.data,
146
- createdAt: partial.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
75
+ createdAt: partial.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
147
76
  updatedAt: partial.updatedAt
148
77
  };
149
78
  }
@@ -164,11 +93,8 @@
164
93
  */
165
94
  addColumn(partial) {
166
95
  const column = this.normalizeColumn(partial, this.columns.length);
167
- column.cards = column.cards.map((card) => ({
168
- ...card,
169
- columnId: column.id
170
- }));
171
96
  this.columns.push(column);
97
+ this.sortColumns();
172
98
  this.events.emit("column:add", { column });
173
99
  return column;
174
100
  }
@@ -190,6 +116,12 @@
190
116
  const column = this.columns.find((c) => c.id === id);
191
117
  if (!column) return void 0;
192
118
  Object.assign(column, changes, { id });
119
+ if (changes.cards) {
120
+ column.cards = changes.cards.map((card, index) => this.normalizeCard(card, id, index));
121
+ column.cards.sort((a, b) => a.order - b.order);
122
+ this.reorderCards(column);
123
+ }
124
+ if (changes.order !== void 0) this.sortColumns();
193
125
  this.events.emit("column:update", {
194
126
  column,
195
127
  changes
@@ -200,15 +132,17 @@
200
132
  * 移动列
201
133
  */
202
134
  moveColumn(id, toIndex) {
135
+ this.sortColumns();
203
136
  const fromIndex = this.columns.findIndex((c) => c.id === id);
204
137
  if (fromIndex === -1) return;
205
138
  const [column] = this.columns.splice(fromIndex, 1);
206
- this.columns.splice(toIndex, 0, column);
139
+ const targetIndex = this.clampIndex(toIndex, this.columns.length);
140
+ this.columns.splice(targetIndex, 0, column);
207
141
  this.reorderColumns();
208
142
  this.events.emit("column:move", {
209
143
  column,
210
144
  fromIndex,
211
- toIndex
145
+ toIndex: targetIndex
212
146
  });
213
147
  }
214
148
  /**
@@ -320,14 +254,15 @@
320
254
  }
321
255
  const [card] = fromColumn.cards.splice(cardIndex, 1);
322
256
  card.columnId = toColumnId;
323
- toColumn.cards.splice(toIndex, 0, card);
257
+ const targetIndex = this.clampIndex(toIndex, toColumn.cards.length);
258
+ toColumn.cards.splice(targetIndex, 0, card);
324
259
  this.reorderCards(fromColumn);
325
260
  this.reorderCards(toColumn);
326
261
  this.events.emit("card:move", {
327
262
  card,
328
263
  fromColumnId: fromColumn.id,
329
264
  toColumnId,
330
- toIndex
265
+ toIndex: targetIndex
331
266
  });
332
267
  }
333
268
  /**
@@ -364,7 +299,8 @@
364
299
  * 设置列数据(用于导入)
365
300
  */
366
301
  setColumns(columns) {
367
- this.columns = columns;
302
+ this.columns = columns.map((column, index) => this.normalizeColumn(column, index));
303
+ this.sortColumns();
368
304
  }
369
305
  /**
370
306
  * 清空所有数据
@@ -372,6 +308,14 @@
372
308
  clear() {
373
309
  this.columns = [];
374
310
  }
311
+ clampIndex(index, length) {
312
+ if (!Number.isFinite(index)) return length;
313
+ return Math.min(Math.max(Math.trunc(index), 0), length);
314
+ }
315
+ sortColumns() {
316
+ this.columns.sort((a, b) => a.order - b.order);
317
+ this.reorderColumns();
318
+ }
375
319
  };
376
320
  //#endregion
377
321
  //#region src/core/DragController.ts
@@ -403,6 +347,8 @@
403
347
  this.state.dragging = true;
404
348
  this.state.cardId = cardId;
405
349
  this.state.sourceColumnId = columnId;
350
+ this.state.targetColumnId = columnId;
351
+ this.state.targetIndex = 0;
406
352
  cardEl.classList.add(`${this.prefix}-card--dragging`);
407
353
  this.events.emit("card:drag-start", {
408
354
  cardId,
@@ -416,6 +362,8 @@
416
362
  this.state.dragging = false;
417
363
  this.state.cardId = null;
418
364
  this.state.sourceColumnId = null;
365
+ this.state.targetColumnId = null;
366
+ this.state.targetIndex = 0;
419
367
  this.events.emit("card:drag-end", { cardId });
420
368
  };
421
369
  cardEl.addEventListener("dragstart", onDragStart);
@@ -465,6 +413,7 @@
465
413
  if (child.hasAttribute("data-card-id") && !child.classList.contains(`${this.prefix}-card--dragging`)) dropIndex++;
466
414
  }
467
415
  } else dropIndex = columnBodyEl.querySelectorAll(`[data-card-id]:not(.${this.prefix}-card--dragging)`).length;
416
+ this.state.targetIndex = dropIndex;
468
417
  this.events.emit("card:drop", {
469
418
  cardId,
470
419
  fromColumnId: this.state.sourceColumnId,
@@ -584,6 +533,72 @@
584
533
  this.cleanupFns.forEach((fn) => fn());
585
534
  this.cleanupFns = [];
586
535
  this.removePlaceholder();
536
+ this.state.dragging = false;
537
+ this.state.cardId = null;
538
+ this.state.sourceColumnId = null;
539
+ this.state.targetColumnId = null;
540
+ this.state.targetIndex = 0;
541
+ }
542
+ };
543
+ //#endregion
544
+ //#region src/core/EventEmitter.ts
545
+ var EventEmitter = class {
546
+ constructor() {
547
+ this.listeners = /* @__PURE__ */ new Map();
548
+ }
549
+ /**
550
+ * 监听事件
551
+ */
552
+ on(event, handler) {
553
+ if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
554
+ const handlers = this.listeners.get(event);
555
+ if (!handlers) return () => this.off(event, handler);
556
+ handlers.add(handler);
557
+ return () => this.off(event, handler);
558
+ }
559
+ /**
560
+ * 监听一次性事件
561
+ */
562
+ once(event, handler) {
563
+ const wrapper = (...args) => {
564
+ this.off(event, wrapper);
565
+ handler(...args);
566
+ };
567
+ return this.on(event, wrapper);
568
+ }
569
+ /**
570
+ * 移除监听器
571
+ */
572
+ off(event, handler) {
573
+ const handlers = this.listeners.get(event);
574
+ if (!handlers) return;
575
+ handlers.delete(handler);
576
+ if (handlers.size === 0) this.listeners.delete(event);
577
+ }
578
+ /**
579
+ * 触发事件
580
+ */
581
+ emit(event, payload) {
582
+ const handlers = this.listeners.get(event);
583
+ if (handlers) handlers.forEach((handler) => {
584
+ try {
585
+ handler(payload);
586
+ } catch (err) {
587
+ console.error(`[KanbanBoard] Error in event handler for "${event}":`, err);
588
+ }
589
+ });
590
+ }
591
+ /**
592
+ * 移除所有监听器
593
+ */
594
+ removeAll() {
595
+ this.listeners.clear();
596
+ }
597
+ /**
598
+ * 获取监听器数量
599
+ */
600
+ listenerCount(event) {
601
+ return this.listeners.get(event)?.size ?? 0;
587
602
  }
588
603
  };
589
604
  //#endregion
@@ -611,11 +626,13 @@
611
626
  */
612
627
  search(keyword, columns) {
613
628
  this.events.emit("search:change", { keyword });
614
- if (!keyword.trim()) {
629
+ const normalizedKeyword = keyword.trim();
630
+ this.currentCriteria = normalizedKeyword ? { keyword: normalizedKeyword } : null;
631
+ if (!normalizedKeyword) {
615
632
  this.clearFilter();
616
633
  return columns.flatMap((col) => col.cards);
617
634
  }
618
- const lowerKeyword = keyword.toLowerCase();
635
+ const lowerKeyword = normalizedKeyword.toLowerCase();
619
636
  const allCards = columns.flatMap((col) => col.cards);
620
637
  const matched = [];
621
638
  this.hiddenCardIds.clear();
@@ -681,11 +698,14 @@
681
698
  if (criteria.dueDateRange) {
682
699
  if (!card.dueDate) return false;
683
700
  const dueDate = new Date(card.dueDate).getTime();
701
+ if (Number.isNaN(dueDate)) return false;
684
702
  if (criteria.dueDateRange.start) {
685
- if (dueDate < new Date(criteria.dueDateRange.start).getTime()) return false;
703
+ const start = new Date(criteria.dueDateRange.start).getTime();
704
+ if (Number.isNaN(start) || dueDate < start) return false;
686
705
  }
687
706
  if (criteria.dueDateRange.end) {
688
- if (dueDate > new Date(criteria.dueDateRange.end).getTime()) return false;
707
+ const end = new Date(criteria.dueDateRange.end).getTime();
708
+ if (Number.isNaN(end) || dueDate > end) return false;
689
709
  }
690
710
  }
691
711
  if (criteria.custom) {
@@ -702,6 +722,34 @@
702
722
  }
703
723
  };
704
724
  //#endregion
725
+ //#region src/utils/dom.ts
726
+ function createElement(tag, className, attributes) {
727
+ const el = document.createElement(tag);
728
+ if (className) el.className = className;
729
+ if (attributes) Object.entries(attributes).forEach(([key, value]) => {
730
+ el.setAttribute(key, value);
731
+ });
732
+ return el;
733
+ }
734
+ function resolveElement(target) {
735
+ if (typeof target === "string") return document.querySelector(target);
736
+ return target;
737
+ }
738
+ function clearElement(el) {
739
+ el.innerHTML = "";
740
+ }
741
+ function removeElement(el) {
742
+ el?.parentNode?.removeChild(el);
743
+ }
744
+ function insertAt(parent, child, index) {
745
+ const ref = parent.children[index];
746
+ if (ref) parent.insertBefore(child, ref);
747
+ else parent.appendChild(child);
748
+ }
749
+ //#endregion
750
+ //#region src/version.ts
751
+ const VERSION = "0.1.0";
752
+ //#endregion
705
753
  //#region src/core/Renderer.ts
706
754
  var Renderer = class {
707
755
  constructor(container, events, dragController, options) {
@@ -732,7 +780,7 @@
732
780
  if (this.options.showAddColumn !== false) {
733
781
  const addColBtn = createElement("div", `${p}-add-column`);
734
782
  addColBtn.innerHTML = `<button class="${p}-add-column-btn">+ \u6DFB\u52A0\u5217</button>`;
735
- addColBtn.querySelector("button").addEventListener("click", () => {
783
+ addColBtn.querySelector("button")?.addEventListener("click", () => {
736
784
  this.events.emit("ui:add-column");
737
785
  });
738
786
  columnsContainer.appendChild(addColBtn);
@@ -760,7 +808,7 @@
760
808
  if (this.options.showAddCard !== false) {
761
809
  const addBtn = createElement("div", `${p}-add-card`);
762
810
  addBtn.innerHTML = `<button class="${p}-add-card-btn">+ \u6DFB\u52A0\u5361\u7247</button>`;
763
- addBtn.querySelector("button").addEventListener("click", () => {
811
+ addBtn.querySelector("button")?.addEventListener("click", () => {
764
812
  this.events.emit("ui:add-card", { columnId: column.id });
765
813
  });
766
814
  body.appendChild(addBtn);
@@ -768,9 +816,10 @@
768
816
  if (this.options.draggable !== false) this.dragController.enableColumnDrop(body, column.id);
769
817
  }
770
818
  colEl.appendChild(body);
771
- if (this.options.columnDraggable !== false && this.boardEl) {
819
+ const boardEl = this.boardEl;
820
+ if (this.options.columnDraggable !== false && boardEl) {
772
821
  const dragHandle = header.querySelector(`.${p}-column-drag-handle`);
773
- if (dragHandle) this.dragController.enableColumnDrag(colEl, dragHandle, column.id, this.boardEl);
822
+ if (dragHandle) this.dragController.enableColumnDrag(colEl, dragHandle, column.id, boardEl);
774
823
  }
775
824
  this.columnElements.set(column.id, colEl);
776
825
  this.columnBodyElements.set(column.id, body);
@@ -891,8 +940,11 @@
891
940
  if (card.dueDate) {
892
941
  const dueEl = createElement("span", `${p}-card-due`);
893
942
  const date = new Date(card.dueDate);
894
- dueEl.textContent = `${date.getMonth() + 1}/${date.getDate()}`;
895
- if (date < /* @__PURE__ */ new Date()) dueEl.classList.add(`${p}-card-due--overdue`);
943
+ if (Number.isNaN(date.getTime())) dueEl.textContent = "Invalid date";
944
+ else {
945
+ dueEl.textContent = `${date.getMonth() + 1}/${date.getDate()}`;
946
+ if (date < /* @__PURE__ */ new Date()) dueEl.classList.add(`${p}-card-due--overdue`);
947
+ }
896
948
  footer.appendChild(dueEl);
897
949
  }
898
950
  cardEl.appendChild(footer);
@@ -936,10 +988,22 @@
936
988
  }
937
989
  };
938
990
  //#endregion
939
- //#region src/version.ts
940
- const VERSION = "0.1.0";
941
- //#endregion
942
991
  //#region src/core/KanbanBoard.ts
992
+ function isRecord(value) {
993
+ return typeof value === "object" && value !== null;
994
+ }
995
+ function isCard(value) {
996
+ return isRecord(value) && typeof value.id === "string" && typeof value.title === "string" && typeof value.order === "number" && typeof value.columnId === "string";
997
+ }
998
+ function isColumn(value) {
999
+ return isRecord(value) && typeof value.id === "string" && typeof value.title === "string" && Array.isArray(value.cards) && typeof value.order === "number";
1000
+ }
1001
+ function isCardDropPayload(value) {
1002
+ return isRecord(value) && typeof value.cardId === "string" && (value.fromColumnId === null || typeof value.fromColumnId === "string") && typeof value.toColumnId === "string" && typeof value.toIndex === "number";
1003
+ }
1004
+ function isColumnDropPayload(value) {
1005
+ return isRecord(value) && typeof value.fromColumnId === "string" && typeof value.toColumnId === "string" && typeof value.toIndex === "number";
1006
+ }
943
1007
  var KanbanBoard = class {
944
1008
  constructor(options) {
945
1009
  this.destroyed = false;
@@ -974,7 +1038,7 @@
974
1038
  this.render();
975
1039
  this.events.emit("ready", { instance: this });
976
1040
  this.options.onReady?.(this);
977
- if (this.options.debug) console.log("[KanbanBoard] Initialized", {
1041
+ if (this.options.debug) console.debug("[KanbanBoard] Initialized", {
978
1042
  columns: this.columnManager.getColumns().length,
979
1043
  cards: this.columnManager.getCardCount()
980
1044
  });
@@ -984,30 +1048,57 @@
984
1048
  */
985
1049
  bindInternalEvents() {
986
1050
  this.events.on("card:drop", (payload) => {
1051
+ if (!isCardDropPayload(payload)) return;
1052
+ const card = this.columnManager.getCard(payload.cardId);
987
1053
  this.columnManager.moveCard(payload.cardId, payload.toColumnId, payload.toIndex);
988
- this.options.onCardMove?.(this.columnManager.getCard(payload.cardId), payload.fromColumnId, payload.toColumnId, payload.toIndex);
1054
+ if (card && card.columnId === payload.toColumnId) this.options.onCardMove?.(card, payload.fromColumnId ?? "", payload.toColumnId, card.order);
989
1055
  this.scheduleRender();
990
1056
  });
991
1057
  this.events.on("column:drop", (payload) => {
992
- const fromIndex = this.columnManager.getColumns().findIndex((c) => c.id === payload.fromColumnId);
1058
+ if (!isColumnDropPayload(payload)) return;
1059
+ const columns = this.columnManager.getColumns();
1060
+ if (payload.fromColumnId === payload.toColumnId || !columns.some((column) => column.id === payload.fromColumnId) || !columns.some((column) => column.id === payload.toColumnId)) return;
1061
+ const fromIndex = columns.findIndex((c) => c.id === payload.fromColumnId);
993
1062
  this.columnManager.moveColumn(payload.fromColumnId, payload.toIndex);
994
1063
  const col = this.columnManager.getColumn(payload.fromColumnId);
995
- if (col) this.options.onColumnMove?.(col, fromIndex, payload.toIndex);
1064
+ if (col) this.options.onColumnMove?.(col, fromIndex, col.order);
996
1065
  this.scheduleRender();
997
1066
  });
998
1067
  this.events.on("ui:toggle-collapse", (payload) => {
1068
+ if (!isRecord(payload) || typeof payload.columnId !== "string") return;
999
1069
  this.columnManager.toggleCollapse(payload.columnId);
1000
1070
  const col = this.columnManager.getColumn(payload.columnId);
1001
1071
  if (col) this.options.onColumnCollapse?.(col, col.collapsed ?? false);
1002
1072
  this.render();
1003
1073
  });
1004
- this.events.on("card:add", (p) => this.options.onCardAdd?.(p.card, p.columnId));
1005
- this.events.on("card:remove", (p) => this.options.onCardRemove?.(p.card, p.columnId));
1006
- this.events.on("card:click", (p) => this.options.onCardClick?.(p.card));
1007
- this.events.on("card:update", (p) => this.options.onCardUpdate?.(p.card));
1008
- this.events.on("column:add", (p) => this.options.onColumnAdd?.(p.column));
1009
- this.events.on("column:remove", (p) => this.options.onColumnRemove?.(p.column));
1010
- this.events.on("error", (p) => this.options.onError?.(p.error));
1074
+ this.events.on("card:add", (p) => {
1075
+ if (!isRecord(p) || !isCard(p.card) || typeof p.columnId !== "string") return;
1076
+ this.options.onCardAdd?.(p.card, p.columnId);
1077
+ });
1078
+ this.events.on("card:remove", (p) => {
1079
+ if (!isRecord(p) || !isCard(p.card) || typeof p.columnId !== "string") return;
1080
+ this.options.onCardRemove?.(p.card, p.columnId);
1081
+ });
1082
+ this.events.on("card:click", (p) => {
1083
+ if (!isRecord(p) || !isCard(p.card)) return;
1084
+ this.options.onCardClick?.(p.card);
1085
+ });
1086
+ this.events.on("card:update", (p) => {
1087
+ if (!isRecord(p) || !isCard(p.card)) return;
1088
+ this.options.onCardUpdate?.(p.card);
1089
+ });
1090
+ this.events.on("column:add", (p) => {
1091
+ if (!isRecord(p) || !isColumn(p.column)) return;
1092
+ this.options.onColumnAdd?.(p.column);
1093
+ });
1094
+ this.events.on("column:remove", (p) => {
1095
+ if (!isRecord(p) || !isColumn(p.column)) return;
1096
+ this.options.onColumnRemove?.(p.column);
1097
+ });
1098
+ this.events.on("error", (p) => {
1099
+ if (!isRecord(p) || !(p.error instanceof Error)) return;
1100
+ this.options.onError?.(p.error);
1101
+ });
1011
1102
  }
1012
1103
  /**
1013
1104
  * 添加列
@@ -1148,8 +1239,12 @@
1148
1239
  * 导入看板数据
1149
1240
  */
1150
1241
  importData(data) {
1242
+ if (!data || !Array.isArray(data.columns)) {
1243
+ this.events.emit("error", { error: /* @__PURE__ */ new Error("[KanbanBoard] Invalid board data: columns must be an array") });
1244
+ return;
1245
+ }
1151
1246
  this.columnManager.setColumns(data.columns);
1152
- this.labels = data.labels || [];
1247
+ this.labels = Array.isArray(data.labels) ? [...data.labels] : [];
1153
1248
  this.filterEngine.clearFilter();
1154
1249
  this.events.emit("data:import", { board: data });
1155
1250
  this.render();
@@ -1239,7 +1334,7 @@
1239
1334
  this.events.emit("destroy", void 0);
1240
1335
  this.options.onDestroy?.();
1241
1336
  this.events.removeAll();
1242
- if (this.options.debug) console.log("[KanbanBoard] Destroyed");
1337
+ if (this.options.debug) console.debug("[KanbanBoard] Destroyed");
1243
1338
  }
1244
1339
  /**
1245
1340
  * 是否已销毁