@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.
- package/README.md +171 -37
- package/dist/index.css +114 -114
- package/dist/index.js +221 -126
- package/dist/index.js.map +1 -1
- package/dist/index.min.css +1 -1
- package/dist/index.min.js +758 -1
- package/dist/index.min.js.map +1 -0
- package/es/.dts-output-manifest.json +1 -0
- package/es/.dts.tsbuildinfo +1 -0
- package/es/core/ColumnManager.d.ts +4 -3
- package/es/core/ColumnManager.js +43 -18
- package/es/core/ColumnManager.js.map +1 -1
- package/es/core/DragController.d.ts +1 -2
- package/es/core/DragController.js +11 -1
- package/es/core/DragController.js.map +1 -1
- package/es/core/EventEmitter.d.ts +0 -1
- package/es/core/EventEmitter.js +8 -3
- package/es/core/EventEmitter.js.map +1 -1
- package/es/core/FilterEngine.d.ts +1 -2
- package/es/core/FilterEngine.js +10 -5
- package/es/core/FilterEngine.js.map +1 -1
- package/es/core/KanbanBoard.d.ts +3 -3
- package/es/core/KanbanBoard.js +61 -15
- package/es/core/KanbanBoard.js.map +1 -1
- package/es/core/Renderer.d.ts +2 -3
- package/es/core/Renderer.js +11 -7
- package/es/core/Renderer.js.map +1 -1
- package/es/core/index.d.ts +2 -3
- package/es/core/index.js +1 -1
- package/es/index.d.ts +3 -4
- package/es/index.js +2 -2
- package/es/styles/kanban.css +114 -114
- package/es/types/index.d.ts +3 -4
- package/es/types/index.js +1 -1
- package/es/utils/dom.d.ts +0 -1
- package/es/utils/dom.js +1 -1
- package/es/utils/dom.js.map +1 -1
- package/es/utils/id.d.ts +0 -1
- package/es/utils/id.js +1 -1
- package/es/utils/id.js.map +1 -1
- package/es/utils/index.d.ts +1 -2
- package/es/utils/index.js +1 -1
- package/es/utils/index.js.map +1 -1
- package/es/version.d.ts +0 -1
- package/es/version.js +1 -1
- package/es/version.js.map +1 -1
- package/esm/core/ColumnManager.js +43 -18
- package/esm/core/ColumnManager.js.map +1 -1
- package/esm/core/DragController.js +11 -1
- package/esm/core/DragController.js.map +1 -1
- package/esm/core/EventEmitter.js +8 -3
- package/esm/core/EventEmitter.js.map +1 -1
- package/esm/core/FilterEngine.js +10 -5
- package/esm/core/FilterEngine.js.map +1 -1
- package/esm/core/KanbanBoard.js +63 -17
- package/esm/core/KanbanBoard.js.map +1 -1
- package/esm/core/Renderer.js +11 -7
- package/esm/core/Renderer.js.map +1 -1
- package/esm/index.js +4 -4
- package/esm/styles/kanban.less +357 -339
- package/esm/utils/dom.js +1 -1
- package/esm/utils/dom.js.map +1 -1
- package/esm/utils/id.js +1 -1
- package/esm/utils/id.js.map +1 -1
- package/esm/utils/index.js +2 -2
- package/esm/utils/index.js.map +1 -1
- package/esm/version.js +1 -1
- package/esm/version.js.map +1 -1
- package/lib/core/ColumnManager.cjs +43 -18
- package/lib/core/ColumnManager.cjs.map +1 -1
- package/{esm/core/ColumnManager.d.ts → lib/core/ColumnManager.d.cts} +4 -3
- package/lib/core/DragController.cjs +11 -1
- package/lib/core/DragController.cjs.map +1 -1
- package/{esm/core/DragController.d.ts → lib/core/DragController.d.cts} +1 -2
- package/lib/core/EventEmitter.cjs +8 -3
- package/lib/core/EventEmitter.cjs.map +1 -1
- package/lib/core/{EventEmitter.d.ts → EventEmitter.d.cts} +0 -1
- package/lib/core/FilterEngine.cjs +10 -5
- package/lib/core/FilterEngine.cjs.map +1 -1
- package/{esm/core/FilterEngine.d.ts → lib/core/FilterEngine.d.cts} +1 -2
- package/lib/core/KanbanBoard.cjs +61 -15
- package/lib/core/KanbanBoard.cjs.map +1 -1
- package/{esm/core/KanbanBoard.d.ts → lib/core/KanbanBoard.d.cts} +3 -3
- package/lib/core/Renderer.cjs +11 -7
- package/lib/core/Renderer.cjs.map +1 -1
- package/{esm/core/Renderer.d.ts → lib/core/Renderer.d.cts} +2 -3
- package/lib/core/index.cjs +1 -1
- package/{esm/core/index.d.ts → lib/core/index.d.cts} +2 -3
- package/lib/index.cjs +2 -2
- package/lib/{index.d.ts → index.d.cts} +3 -4
- package/lib/styles/kanban.css +114 -114
- package/lib/types/index.cjs +1 -1
- package/{esm/types/index.d.ts → lib/types/index.d.cts} +3 -4
- package/lib/utils/dom.cjs +1 -1
- package/lib/utils/dom.cjs.map +1 -1
- package/lib/utils/{dom.d.ts → dom.d.cts} +0 -1
- package/lib/utils/id.cjs +1 -1
- package/lib/utils/id.cjs.map +1 -1
- package/{esm/utils/id.d.ts → lib/utils/id.d.cts} +0 -1
- package/lib/utils/index.cjs +1 -1
- package/lib/utils/index.cjs.map +1 -1
- package/lib/utils/{index.d.ts → index.d.cts} +1 -2
- package/lib/version.cjs +1 -1
- package/lib/version.cjs.map +1 -1
- package/{esm/version.d.ts → lib/version.d.cts} +0 -1
- package/package.json +23 -21
- package/es/core/ColumnManager.d.ts.map +0 -1
- package/es/core/DragController.d.ts.map +0 -1
- package/es/core/EventEmitter.d.ts.map +0 -1
- package/es/core/FilterEngine.d.ts.map +0 -1
- package/es/core/KanbanBoard.d.ts.map +0 -1
- package/es/core/Renderer.d.ts.map +0 -1
- package/es/core/index.d.ts.map +0 -1
- package/es/index.d.ts.map +0 -1
- package/es/types/index.d.ts.map +0 -1
- package/es/utils/dom.d.ts.map +0 -1
- package/es/utils/id.d.ts.map +0 -1
- package/es/utils/index.d.ts.map +0 -1
- package/es/version.d.ts.map +0 -1
- package/esm/core/ColumnManager.d.ts.map +0 -1
- package/esm/core/DragController.d.ts.map +0 -1
- package/esm/core/EventEmitter.d.ts +0 -35
- package/esm/core/EventEmitter.d.ts.map +0 -1
- package/esm/core/FilterEngine.d.ts.map +0 -1
- package/esm/core/KanbanBoard.d.ts.map +0 -1
- package/esm/core/Renderer.d.ts.map +0 -1
- package/esm/core/index.d.ts.map +0 -1
- package/esm/core/index.js +0 -17
- package/esm/index.d.ts +0 -15
- package/esm/index.d.ts.map +0 -1
- package/esm/types/index.d.ts.map +0 -1
- package/esm/types/index.js +0 -10
- package/esm/utils/dom.d.ts +0 -24
- package/esm/utils/dom.d.ts.map +0 -1
- package/esm/utils/id.d.ts.map +0 -1
- package/esm/utils/index.d.ts +0 -25
- package/esm/utils/index.d.ts.map +0 -1
- package/esm/version.d.ts.map +0 -1
- package/lib/core/ColumnManager.d.ts +0 -99
- package/lib/core/DragController.d.ts +0 -55
- package/lib/core/FilterEngine.d.ts +0 -45
- package/lib/core/KanbanBoard.d.ts +0 -152
- package/lib/core/Renderer.d.ts +0 -59
- package/lib/core/index.d.ts +0 -11
- package/lib/types/index.d.ts +0 -209
- package/lib/utils/id.d.ts +0 -9
- 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-
|
|
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.
|
|
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
|
|
123
|
-
title: partial.title
|
|
124
|
-
cards
|
|
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
|
|
73
|
+
columnId: partial.columnId ?? columnId,
|
|
145
74
|
data: partial.data,
|
|
146
|
-
createdAt: partial.createdAt
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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")
|
|
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")
|
|
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
|
-
|
|
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,
|
|
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 =
|
|
895
|
-
|
|
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.
|
|
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?.(
|
|
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
|
-
|
|
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,
|
|
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) =>
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
this.events.on("
|
|
1009
|
-
|
|
1010
|
-
|
|
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.
|
|
1337
|
+
if (this.options.debug) console.debug("[KanbanBoard] Destroyed");
|
|
1243
1338
|
}
|
|
1244
1339
|
/**
|
|
1245
1340
|
* 是否已销毁
|