@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.min.js
CHANGED
|
@@ -1 +1,758 @@
|
|
|
1
|
-
(function(global,factory){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={}));})(this,function(exports){Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function createElement(tag,className,attributes){const el=document.createElement(tag);if(className)el.className=className;if(attributes)Object.entries(attributes).forEach(([key,value])=>{el.setAttribute(key,value);});return el;}function resolveElement(target){if(typeof target==="string")return document.querySelector(target);return target;}function clearElement(el){el.innerHTML="";}function removeElement(el){el?.parentNode?.removeChild(el);}function insertAt(parent,child,index){const ref=parent.children[index];if(ref)parent.insertBefore(child,ref);else parent.appendChild(child);}var EventEmitter=class{constructor(){this.listeners=new Map();}on(event,handler){if(!this.listeners.has(event))this.listeners.set(event,new Set());this.listeners.get(event).add(handler);return()=>this.off(event,handler);}once(event,handler){const wrapper=(...args)=>{this.off(event,wrapper);handler(...args);};return this.on(event,wrapper);}off(event,handler){this.listeners.get(event)?.delete(handler);}emit(event,payload){const handlers=this.listeners.get(event);if(handlers)handlers.forEach((handler)=>{try{handler(payload);}catch(err){console.error(`[KanbanBoard]Error in event handler for "${event}":`,err);}});}removeAll(){this.listeners.clear();}listenerCount(event){return this.listeners.get(event)?.size??0;}};let counter=0;function generateId(prefix="kb"){counter++;return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,6)}_${counter}`;}function resetIdCounter(){counter=0;}var ColumnManager=class{constructor(events){this.columns=[];this.events=events;}init(columns){this.columns=columns.map((col,index)=>this.normalizeColumn(col,index));}normalizeColumn(partial,index){return{id:partial.id||generateId("col"),title:partial.title||`Column ${index+1}`,cards:(partial.cards||[]).map((card,cardIndex)=>this.normalizeCard(card,partial.id||"",cardIndex)),wipLimit:partial.wipLimit,collapsed:partial.collapsed??false,color:partial.color,order:partial.order??index};}normalizeCard(partial,columnId,index){return{id:partial.id||generateId("card"),title:partial.title||"",description:partial.description,labels:partial.labels||[],assignees:partial.assignees||[],dueDate:partial.dueDate,priority:partial.priority,order:partial.order??index,columnId:partial.columnId||columnId,data:partial.data,createdAt:partial.createdAt||(new Date()).toISOString(),updatedAt:partial.updatedAt};}getColumns(){return[...this.columns].sort((a,b)=>a.order-b.order);}getColumn(id){return this.columns.find((c)=>c.id===id);}addColumn(partial){const column=this.normalizeColumn(partial,this.columns.length);column.cards=column.cards.map((card)=>({...card,columnId:column.id}));this.columns.push(column);this.events.emit("column:add",{column});return column;}removeColumn(id){const index=this.columns.findIndex((c)=>c.id===id);if(index===-1)return void 0;const[column]=this.columns.splice(index,1);this.reorderColumns();this.events.emit("column:remove",{column});return column;}updateColumn(id,changes){const column=this.columns.find((c)=>c.id===id);if(!column)return void 0;Object.assign(column,changes,{id});this.events.emit("column:update",{column,changes});return column;}moveColumn(id,toIndex){const fromIndex=this.columns.findIndex((c)=>c.id===id);if(fromIndex===-1)return;const[column]=this.columns.splice(fromIndex,1);this.columns.splice(toIndex,0,column);this.reorderColumns();this.events.emit("column:move",{column,fromIndex,toIndex});}toggleCollapse(id){const column=this.columns.find((c)=>c.id===id);if(!column)return;column.collapsed=!column.collapsed;this.events.emit("column:collapse",{column,collapsed:column.collapsed});}getCards(columnId){const column=this.columns.find((c)=>c.id===columnId);return column?[...column.cards].sort((a,b)=>a.order-b.order):[];}addCard(columnId,partial){const column=this.columns.find((c)=>c.id===columnId);if(!column)return void 0;if(column.wipLimit&&column.cards.length>=column.wipLimit){this.events.emit("error",{error:new Error(`Column "${column.title}" reached WIP limit(${column.wipLimit})`)});return;}const card=this.normalizeCard({...partial,columnId},columnId,column.cards.length);column.cards.push(card);this.events.emit("card:add",{card,columnId});return card;}removeCard(cardId){for(const column of this.columns){const index=column.cards.findIndex((c)=>c.id===cardId);if(index!==-1){const[card]=column.cards.splice(index,1);this.reorderCards(column);this.events.emit("card:remove",{card,columnId:column.id});return card;}}}updateCard(cardId,changes){for(const column of this.columns){const card=column.cards.find((c)=>c.id===cardId);if(card){Object.assign(card,changes,{id:cardId,columnId:column.id});card.updatedAt=(new Date()).toISOString();this.events.emit("card:update",{card,changes});return card;}}}getCard(cardId){for(const column of this.columns){const card=column.cards.find((c)=>c.id===cardId);if(card)return card;}}moveCard(cardId,toColumnId,toIndex){let fromColumn;let cardIndex=-1;for(const column of this.columns){const idx=column.cards.findIndex((c)=>c.id===cardId);if(idx!==-1){fromColumn=column;cardIndex=idx;break;}}if(!fromColumn||cardIndex===-1)return;const toColumn=this.columns.find((c)=>c.id===toColumnId);if(!toColumn)return;if(fromColumn.id!==toColumnId&&toColumn.wipLimit&&toColumn.cards.length>=toColumn.wipLimit){this.events.emit("error",{error:new Error(`Column "${toColumn.title}" reached WIP limit(${toColumn.wipLimit})`)});return;}const[card]=fromColumn.cards.splice(cardIndex,1);card.columnId=toColumnId;toColumn.cards.splice(toIndex,0,card);this.reorderCards(fromColumn);this.reorderCards(toColumn);this.events.emit("card:move",{card,fromColumnId:fromColumn.id,toColumnId,toIndex});}getAllCards(){const cards=[];for(const column of this.columns)cards.push(...column.cards);return cards;}getCardCount(){return this.columns.reduce((sum,col)=>sum+col.cards.length,0);}reorderColumns(){this.columns.forEach((col,index)=>{col.order=index;});}reorderCards(column){column.cards.forEach((card,index)=>{card.order=index;});}setColumns(columns){this.columns=columns;}clear(){this.columns=[];}};var DragController=class{constructor(events,prefix){this.state={dragging:false,cardId:null,sourceColumnId:null,targetColumnId:null,targetIndex:0,placeholder:null};this.cleanupFns=[];this.events=events;this.prefix=prefix;}enableCardDrag(cardEl,cardId,columnId){cardEl.setAttribute("draggable","true");cardEl.dataset.cardId=cardId;cardEl.dataset.columnId=columnId;const onDragStart=(e)=>{if(!e.dataTransfer)return;e.dataTransfer.effectAllowed="move";e.dataTransfer.setData("text/plain",cardId);this.state.dragging=true;this.state.cardId=cardId;this.state.sourceColumnId=columnId;cardEl.classList.add(`${this.prefix}-card--dragging`);this.events.emit("card:drag-start",{cardId,columnId});this.createPlaceholder(cardEl);};const onDragEnd=()=>{cardEl.classList.remove(`${this.prefix}-card--dragging`);this.removePlaceholder();this.state.dragging=false;this.state.cardId=null;this.state.sourceColumnId=null;this.events.emit("card:drag-end",{cardId});};cardEl.addEventListener("dragstart",onDragStart);cardEl.addEventListener("dragend",onDragEnd);this.cleanupFns.push(()=>{cardEl.removeEventListener("dragstart",onDragStart);cardEl.removeEventListener("dragend",onDragEnd);});}enableColumnDrop(columnBodyEl,columnId){const onDragOver=(e)=>{e.preventDefault();if(!e.dataTransfer)return;e.dataTransfer.dropEffect="move";this.state.targetColumnId=columnId;const afterElement=this.getDragAfterElement(columnBodyEl,e.clientY);const placeholder=this.state.placeholder;if(placeholder)if(afterElement)columnBodyEl.insertBefore(placeholder,afterElement);else{const addBtn=columnBodyEl.querySelector(`.${this.prefix}-add-card`);if(addBtn)columnBodyEl.insertBefore(placeholder,addBtn);else columnBodyEl.appendChild(placeholder);}};const onDragEnter=(e)=>{e.preventDefault();columnBodyEl.classList.add(`${this.prefix}-column-body--drag-over`);};const onDragLeave=(e)=>{const rect=columnBodyEl.getBoundingClientRect();if(e.clientX<rect.left||e.clientX>rect.right||e.clientY<rect.top||e.clientY>rect.bottom)columnBodyEl.classList.remove(`${this.prefix}-column-body--drag-over`);};const onDrop=(e)=>{e.preventDefault();columnBodyEl.classList.remove(`${this.prefix}-column-body--drag-over`);const cardId=this.state.cardId;if(!cardId)return;const placeholder=this.state.placeholder;let dropIndex=0;if(placeholder&&placeholder.parentNode===columnBodyEl){const children=[...columnBodyEl.children];for(const child of children){if(child===placeholder)break;if(child.hasAttribute("data-card-id")&&!child.classList.contains(`${this.prefix}-card--dragging`))dropIndex++;}}else dropIndex=columnBodyEl.querySelectorAll(`[data-card-id]:not(.${this.prefix}-card--dragging)`).length;this.events.emit("card:drop",{cardId,fromColumnId:this.state.sourceColumnId,toColumnId:columnId,toIndex:dropIndex});this.removePlaceholder();};columnBodyEl.addEventListener("dragover",onDragOver);columnBodyEl.addEventListener("dragenter",onDragEnter);columnBodyEl.addEventListener("dragleave",onDragLeave);columnBodyEl.addEventListener("drop",onDrop);this.cleanupFns.push(()=>{columnBodyEl.removeEventListener("dragover",onDragOver);columnBodyEl.removeEventListener("dragenter",onDragEnter);columnBodyEl.removeEventListener("dragleave",onDragLeave);columnBodyEl.removeEventListener("drop",onDrop);});}enableColumnDrag(columnEl,handleEl,columnId,boardEl){handleEl.setAttribute("draggable","true");const onDragStart=(e)=>{if(!e.dataTransfer)return;e.dataTransfer.effectAllowed="move";e.dataTransfer.setData("application/kanban-column",columnId);columnEl.classList.add(`${this.prefix}-column--dragging`);};const onDragEnd=()=>{columnEl.classList.remove(`${this.prefix}-column--dragging`);boardEl.querySelectorAll(`.${this.prefix}-column--drag-over`).forEach((el)=>{el.classList.remove(`${this.prefix}-column--drag-over`);});};const onDragOver=(e)=>{if(!e.dataTransfer?.types.includes("application/kanban-column"))return;e.preventDefault();e.dataTransfer.dropEffect="move";columnEl.classList.add(`${this.prefix}-column--drag-over`);};const onDragLeave=()=>{columnEl.classList.remove(`${this.prefix}-column--drag-over`);};const onDrop=(e)=>{e.preventDefault();columnEl.classList.remove(`${this.prefix}-column--drag-over`);const fromColumnId=e.dataTransfer?.getData("application/kanban-column");if(!fromColumnId||fromColumnId===columnId)return;const toIndex=[...boardEl.querySelectorAll(`[data-column-id]`)].indexOf(columnEl);this.events.emit("column:drop",{fromColumnId,toColumnId:columnId,toIndex});};handleEl.addEventListener("dragstart",onDragStart);handleEl.addEventListener("dragend",onDragEnd);columnEl.addEventListener("dragover",onDragOver);columnEl.addEventListener("dragleave",onDragLeave);columnEl.addEventListener("drop",onDrop);this.cleanupFns.push(()=>{handleEl.removeEventListener("dragstart",onDragStart);handleEl.removeEventListener("dragend",onDragEnd);columnEl.removeEventListener("dragover",onDragOver);columnEl.removeEventListener("dragleave",onDragLeave);columnEl.removeEventListener("drop",onDrop);});}getDragAfterElement(container,y){const draggableElements=[...container.querySelectorAll(`[data-card-id]:not(.${this.prefix}-card--dragging):not(.${this.prefix}-placeholder)`)];let closestOffset=Number.NEGATIVE_INFINITY;let closestElement=null;draggableElements.forEach((child)=>{const box=child.getBoundingClientRect();const offset=y-box.top-box.height/2;if(offset<0&&offset>closestOffset){closestOffset=offset;closestElement=child;}});return closestElement;}createPlaceholder(cardEl){this.removePlaceholder();const placeholder=document.createElement("div");placeholder.className=`${this.prefix}-placeholder`;placeholder.style.height=`${cardEl.offsetHeight}px`;this.state.placeholder=placeholder;}removePlaceholder(){if(this.state.placeholder){this.state.placeholder.parentNode?.removeChild(this.state.placeholder);this.state.placeholder=null;}}getState(){return{...this.state};}destroy(){this.cleanupFns.forEach((fn)=>fn());this.cleanupFns=[];this.removePlaceholder();}};var FilterEngine=class{constructor(events){this.currentCriteria=null;this.hiddenCardIds=new Set();this.events=events;}filter(criteria,columns){this.currentCriteria=criteria;this.hiddenCardIds.clear();const allCards=columns.flatMap((col)=>col.cards);for(const card of allCards)if(!this.matchCriteria(card,criteria))this.hiddenCardIds.add(card.id);this.events.emit("filter:change",{criteria});return new Set(this.hiddenCardIds);}search(keyword,columns){this.events.emit("search:change",{keyword});if(!keyword.trim()){this.clearFilter();return columns.flatMap((col)=>col.cards);}const lowerKeyword=keyword.toLowerCase();const allCards=columns.flatMap((col)=>col.cards);const matched=[];this.hiddenCardIds.clear();for(const card of allCards){const titleMatch=card.title.toLowerCase().includes(lowerKeyword);const descMatch=card.description?.toLowerCase().includes(lowerKeyword)??false;const labelMatch=card.labels?.some((l)=>l.name.toLowerCase().includes(lowerKeyword))??false;const assigneeMatch=card.assignees?.some((a)=>a.toLowerCase().includes(lowerKeyword))??false;if(titleMatch||descMatch||labelMatch||assigneeMatch)matched.push(card);else this.hiddenCardIds.add(card.id);}return matched;}clearFilter(){this.currentCriteria=null;this.hiddenCardIds.clear();this.events.emit("filter:clear",void 0);}isCardVisible(cardId){return!this.hiddenCardIds.has(cardId);}getHiddenCardIds(){return new Set(this.hiddenCardIds);}getCurrentCriteria(){return this.currentCriteria;}matchCriteria(card,criteria){if(criteria.keyword){const kw=criteria.keyword.toLowerCase();const titleMatch=card.title.toLowerCase().includes(kw);const descMatch=card.description?.toLowerCase().includes(kw)??false;if(!titleMatch&&!descMatch)return false;}if(criteria.labelIds?.length){const cardLabelIds=card.labels?.map((l)=>l.id)??[];if(!criteria.labelIds.some((id)=>cardLabelIds.includes(id)))return false;}if(criteria.assignees?.length){if(!criteria.assignees.some((a)=>card.assignees?.includes(a)))return false;}if(criteria.priorities?.length){if(!card.priority||!criteria.priorities.includes(card.priority))return false;}if(criteria.columnIds?.length){if(!criteria.columnIds.includes(card.columnId))return false;}if(criteria.dueDateRange){if(!card.dueDate)return false;const dueDate=new Date(card.dueDate).getTime();if(criteria.dueDateRange.start){if(dueDate<new Date(criteria.dueDateRange.start).getTime())return false;}if(criteria.dueDateRange.end){if(dueDate>new Date(criteria.dueDateRange.end).getTime())return false;}}if(criteria.custom){if(!criteria.custom(card))return false;}return true;}destroy(){this.hiddenCardIds.clear();this.currentCriteria=null;}};var Renderer=class{constructor(container,events,dragController,options){this.boardEl=null;this.columnElements=new Map();this.columnBodyElements=new Map();this.cardElements=new Map();this.container=container;this.events=events;this.dragController=dragController;this.options=options;this.prefix=options.classPrefix||"kanban";}render(columns,hiddenCardIds){this.cleanup();this.container.querySelectorAll("[data-kanban-board]").forEach((el)=>el.remove());const p=this.prefix;this.boardEl=createElement("div",`${p}-board`);this.boardEl.setAttribute("data-kanban-board","");const columnsContainer=createElement("div",`${p}-columns`);for(const column of columns){const colEl=this.renderColumn(column,hiddenCardIds);columnsContainer.appendChild(colEl);}if(this.options.showAddColumn!==false){const addColBtn=createElement("div",`${p}-add-column`);addColBtn.innerHTML=`<button class="${p}-add-column-btn">+\u6DFB\u52A0\u5217</button>`;addColBtn.querySelector("button").addEventListener("click",()=>{this.events.emit("ui:add-column");});columnsContainer.appendChild(addColBtn);}this.boardEl.appendChild(columnsContainer);this.container.appendChild(this.boardEl);}renderColumn(column,hiddenCardIds){const p=this.prefix;const colEl=createElement("div",`${p}-column`,{"data-column-id":column.id});if(column.collapsed)colEl.classList.add(`${p}-column--collapsed`);const header=this.renderColumnHeader(column);colEl.appendChild(header);const body=createElement("div",`${p}-column-body`);body.dataset.columnId=column.id;if(!column.collapsed){const visibleCards=column.cards.filter((card)=>!hiddenCardIds||!hiddenCardIds.has(card.id)).sort((a,b)=>a.order-b.order);for(const card of visibleCards){const cardEl=this.renderCard(card,column.id);body.appendChild(cardEl);}if(this.options.showAddCard!==false){const addBtn=createElement("div",`${p}-add-card`);addBtn.innerHTML=`<button class="${p}-add-card-btn">+\u6DFB\u52A0\u5361\u7247</button>`;addBtn.querySelector("button").addEventListener("click",()=>{this.events.emit("ui:add-card",{columnId:column.id});});body.appendChild(addBtn);}if(this.options.draggable!==false)this.dragController.enableColumnDrop(body,column.id);}colEl.appendChild(body);if(this.options.columnDraggable!==false&&this.boardEl){const dragHandle=header.querySelector(`.${p}-column-drag-handle`);if(dragHandle)this.dragController.enableColumnDrag(colEl,dragHandle,column.id,this.boardEl);}this.columnElements.set(column.id,colEl);this.columnBodyElements.set(column.id,body);return colEl;}renderColumnHeader(column){const p=this.prefix;const header=createElement("div",`${p}-column-header`);if(this.options.columnHeaderRenderer){this.options.columnHeaderRenderer(column,header);return header;}if(this.options.columnDraggable!==false){const handle=createElement("span",`${p}-column-drag-handle`);handle.innerHTML="⋮⋮";handle.title="拖拽排序";header.appendChild(handle);}if(column.color){const indicator=createElement("span",`${p}-column-color`);indicator.style.backgroundColor=column.color;header.appendChild(indicator);}const titleEl=createElement("span",`${p}-column-title`);titleEl.textContent=column.title;header.appendChild(titleEl);if(this.options.showCardCount!==false){const count=createElement("span",`${p}-column-count`);const wipText=column.wipLimit?`/${column.wipLimit}`:"";count.textContent=`${column.cards.length}${wipText}`;if(column.wipLimit&&column.cards.length>=column.wipLimit)count.classList.add(`${p}-column-count--limit`);header.appendChild(count);}const collapseBtn=createElement("button",`${p}-column-collapse-btn`);collapseBtn.innerHTML=column.collapsed?"▶":"▼";collapseBtn.title=column.collapsed?"展开":"折叠";collapseBtn.addEventListener("click",()=>{this.events.emit("ui:toggle-collapse",{columnId:column.id});});header.appendChild(collapseBtn);if(this.options.showColumnActions!==false){const actions=createElement("button",`${p}-column-actions-btn`);actions.innerHTML="⋯";actions.title="更多操作";actions.addEventListener("click",(e)=>{e.stopPropagation();this.events.emit("ui:column-actions",{columnId:column.id,event:e});});header.appendChild(actions);}return header;}renderCard(card,columnId){const p=this.prefix;const cardEl=createElement("div",`${p}-card`,{"data-card-id":card.id});if(this.options.cardRenderer)this.options.cardRenderer(card,cardEl);else this.renderDefaultCard(card,cardEl);if(this.options.draggable!==false)this.dragController.enableCardDrag(cardEl,card.id,columnId);cardEl.addEventListener("click",()=>{this.events.emit("card:click",{card});});this.cardElements.set(card.id,cardEl);return cardEl;}renderDefaultCard(card,cardEl){const p=this.prefix;if(card.priority){const priorityEl=createElement("div",`${p}-card-priority ${p}-card-priority--${card.priority}`);cardEl.appendChild(priorityEl);}if(card.labels?.length){const labelsEl=createElement("div",`${p}-card-labels`);for(const label of card.labels){const labelEl=createElement("span",`${p}-card-label`);labelEl.style.backgroundColor=label.color;labelEl.textContent=label.name;labelsEl.appendChild(labelEl);}cardEl.appendChild(labelsEl);}const titleEl=createElement("div",`${p}-card-title`);titleEl.textContent=card.title;cardEl.appendChild(titleEl);if(card.description){const descEl=createElement("div",`${p}-card-desc`);descEl.textContent=card.description;cardEl.appendChild(descEl);}if(card.assignees?.length||card.dueDate){const footer=createElement("div",`${p}-card-footer`);if(card.assignees?.length){const assigneesEl=createElement("div",`${p}-card-assignees`);for(const assignee of card.assignees.slice(0,3)){const avatar=createElement("span",`${p}-card-avatar`);avatar.textContent=assignee.charAt(0).toUpperCase();avatar.title=assignee;assigneesEl.appendChild(avatar);}if(card.assignees.length>3){const more=createElement("span",`${p}-card-avatar-more`);more.textContent=`+${card.assignees.length-3}`;assigneesEl.appendChild(more);}footer.appendChild(assigneesEl);}if(card.dueDate){const dueEl=createElement("span",`${p}-card-due`);const date=new Date(card.dueDate);dueEl.textContent=`${date.getMonth()+1}/${date.getDate()}`;if(date<new Date())dueEl.classList.add(`${p}-card-due--overdue`);footer.appendChild(dueEl);}cardEl.appendChild(footer);}}getColumnElement(columnId){return this.columnElements.get(columnId);}getCardElement(cardId){return this.cardElements.get(cardId);}getBoardElement(){return this.boardEl;}cleanup(){this.columnElements.clear();this.columnBodyElements.clear();this.cardElements.clear();if(this.boardEl){this.boardEl.remove();this.boardEl=null;}}destroy(){this.cleanup();}};const VERSION="0.1.0";var KanbanBoard=class{constructor(options){this.destroyed=false;this.labels=[];this.renderTimer=null;this.options={draggable:true,columnDraggable:true,crossColumnDrag:true,searchable:true,showAddCard:true,showAddColumn:true,showColumnActions:true,showCardCount:true,classPrefix:"kanban",...options};const el=resolveElement(options.container);if(!el)throw new Error("[KanbanBoard]Container element not found");this.container=el;this.events=new EventEmitter();this.columnManager=new ColumnManager(this.events);this.dragController=new DragController(this.events,this.options.classPrefix||"kanban");this.filterEngine=new FilterEngine(this.events);this.renderer=new Renderer(this.container,this.events,this.dragController,this.options);this.labels=options.labels||[];this.init();}init(){if(this.options.columns?.length)this.columnManager.init(this.options.columns);this.bindInternalEvents();this.render();this.events.emit("ready",{instance:this});this.options.onReady?.(this);if(this.options.debug)console.log("[KanbanBoard]Initialized",{columns:this.columnManager.getColumns().length,cards:this.columnManager.getCardCount()});}bindInternalEvents(){this.events.on("card:drop",(payload)=>{this.columnManager.moveCard(payload.cardId,payload.toColumnId,payload.toIndex);this.options.onCardMove?.(this.columnManager.getCard(payload.cardId),payload.fromColumnId,payload.toColumnId,payload.toIndex);this.scheduleRender();});this.events.on("column:drop",(payload)=>{const fromIndex=this.columnManager.getColumns().findIndex((c)=>c.id===payload.fromColumnId);this.columnManager.moveColumn(payload.fromColumnId,payload.toIndex);const col=this.columnManager.getColumn(payload.fromColumnId);if(col)this.options.onColumnMove?.(col,fromIndex,payload.toIndex);this.scheduleRender();});this.events.on("ui:toggle-collapse",(payload)=>{this.columnManager.toggleCollapse(payload.columnId);const col=this.columnManager.getColumn(payload.columnId);if(col)this.options.onColumnCollapse?.(col,col.collapsed??false);this.render();});this.events.on("card:add",(p)=>this.options.onCardAdd?.(p.card,p.columnId));this.events.on("card:remove",(p)=>this.options.onCardRemove?.(p.card,p.columnId));this.events.on("card:click",(p)=>this.options.onCardClick?.(p.card));this.events.on("card:update",(p)=>this.options.onCardUpdate?.(p.card));this.events.on("column:add",(p)=>this.options.onColumnAdd?.(p.column));this.events.on("column:remove",(p)=>this.options.onColumnRemove?.(p.column));this.events.on("error",(p)=>this.options.onError?.(p.error));}addColumn(partial){const column=this.columnManager.addColumn(partial);this.render();return column;}removeColumn(id){const column=this.columnManager.removeColumn(id);if(column)this.render();return column;}updateColumn(id,changes){const column=this.columnManager.updateColumn(id,changes);if(column)this.render();return column;}moveColumn(id,toIndex){this.columnManager.moveColumn(id,toIndex);this.render();}toggleColumnCollapse(id){this.columnManager.toggleCollapse(id);this.render();}getColumns(){return this.columnManager.getColumns();}getColumn(id){return this.columnManager.getColumn(id);}addCard(columnId,partial){const card=this.columnManager.addCard(columnId,partial);if(card)this.render();return card;}removeCard(cardId){const card=this.columnManager.removeCard(cardId);if(card)this.render();return card;}updateCard(cardId,changes){const card=this.columnManager.updateCard(cardId,changes);if(card)this.render();return card;}moveCard(cardId,toColumnId,toIndex){this.columnManager.moveCard(cardId,toColumnId,toIndex);this.render();}getCard(cardId){return this.columnManager.getCard(cardId);}getAllCards(){return this.columnManager.getAllCards();}getCardCount(){return this.columnManager.getCardCount();}filter(criteria){this.filterEngine.filter(criteria,this.columnManager.getColumns());this.render();}search(keyword){const result=this.filterEngine.search(keyword,this.columnManager.getColumns());this.render();return result;}clearFilter(){this.filterEngine.clearFilter();this.render();}exportData(){const data={version:VERSION,title:this.options.title||"Kanban Board",columns:this.columnManager.getColumns(),labels:this.labels,exportedAt:(new Date()).toISOString()};this.events.emit("data:export",{board:data});return data;}importData(data){this.columnManager.setColumns(data.columns);this.labels=data.labels||[];this.filterEngine.clearFilter();this.events.emit("data:import",{board:data});this.render();}on(event,handler){return this.events.on(event,handler);}once(event,handler){return this.events.once(event,handler);}off(event,handler){this.events.off(event,handler);}getLabels(){return[...this.labels];}addLabel(label){this.labels.push(label);}removeLabel(id){this.labels=this.labels.filter((l)=>l.id!==id);}scheduleRender(){if(this.renderTimer!==null)return;this.renderTimer=setTimeout(()=>{this.renderTimer=null;this.render();},0);}render(){if(this.destroyed)return;this.dragController.destroy();this.dragController=new DragController(this.events,this.options.classPrefix||"kanban");this.renderer=new Renderer(this.container,this.events,this.dragController,this.options);this.renderer.render(this.columnManager.getColumns(),this.filterEngine.getHiddenCardIds());}getBoardElement(){return this.renderer.getBoardElement();}getOptions(){return{...this.options};}destroy(){if(this.destroyed)return;this.destroyed=true;if(this.renderTimer!==null){clearTimeout(this.renderTimer);this.renderTimer=null;}this.dragController.destroy();this.filterEngine.destroy();this.renderer.destroy();this.columnManager.clear();this.events.emit("destroy",void 0);this.options.onDestroy?.();this.events.removeAll();if(this.options.debug)console.log("[KanbanBoard]Destroyed");}isDestroyed(){return this.destroyed;}};function createKanbanBoard(options){return new KanbanBoard(options);}function getElement(target){if(typeof target==="string")return document.querySelector(target);return target;}exports.ColumnManager=ColumnManager;exports.DragController=DragController;exports.EventEmitter=EventEmitter;exports.FilterEngine=FilterEngine;exports.KanbanBoard=KanbanBoard;exports.Renderer=Renderer;exports.VERSION=VERSION;exports.clearElement=clearElement;exports.createElement=createElement;exports.createKanbanBoard=createKanbanBoard;exports.generateId=generateId;exports.getElement=getElement;exports.insertAt=insertAt;exports.removeElement=removeElement;exports.resetIdCounter=resetIdCounter;exports.resolveElement=resolveElement;});
|
|
1
|
+
/*!
|
|
2
|
+
* ***********************************
|
|
3
|
+
* @composy/kanban-core v0.0.1 *
|
|
4
|
+
* Built with rollup *
|
|
5
|
+
* Build time: 2024-10-12 17:29:01 *
|
|
6
|
+
* Build mode: production *
|
|
7
|
+
* Minified: No *
|
|
8
|
+
* ***********************************
|
|
9
|
+
*/
|
|
10
|
+
(function(e, t) {
|
|
11
|
+
"object" == typeof exports && "undefined" != typeof module ? t(exports) : "function" == typeof define && define.amd ? define(["exports"], t) : t((e = "undefined" != typeof globalThis ? globalThis : e || self).LDesignKanbanCore = {});
|
|
12
|
+
})(this, function(exports) {
|
|
13
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
14
|
+
let e = 0;
|
|
15
|
+
function t(t = "kb") {
|
|
16
|
+
return e++, `${t}_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 6)}_${e}`;
|
|
17
|
+
}
|
|
18
|
+
var n = class {
|
|
19
|
+
constructor(e) {
|
|
20
|
+
this.columns = [], this.events = e;
|
|
21
|
+
}
|
|
22
|
+
init(e) {
|
|
23
|
+
this.columns = e.map((e, t) => this.normalizeColumn(e, t)), this.sortColumns();
|
|
24
|
+
}
|
|
25
|
+
normalizeColumn(e, n) {
|
|
26
|
+
const r = e.id || t("col"), s = (e.cards ?? []).map((e, t) => this.normalizeCard({
|
|
27
|
+
...e,
|
|
28
|
+
columnId: r
|
|
29
|
+
}, r, t));
|
|
30
|
+
return s.sort((e, t) => e.order - t.order), s.forEach((e, t) => {
|
|
31
|
+
e.order = t;
|
|
32
|
+
}), {
|
|
33
|
+
id: r,
|
|
34
|
+
title: e.title ?? `Column ${n + 1}`,
|
|
35
|
+
cards: s,
|
|
36
|
+
wipLimit: e.wipLimit,
|
|
37
|
+
collapsed: e.collapsed ?? !1,
|
|
38
|
+
color: e.color,
|
|
39
|
+
order: e.order ?? n
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
normalizeCard(e, n, r) {
|
|
43
|
+
return {
|
|
44
|
+
id: e.id || t("card"),
|
|
45
|
+
title: e.title ?? "",
|
|
46
|
+
description: e.description,
|
|
47
|
+
labels: e.labels?.map((e) => ({ ...e })) ?? [],
|
|
48
|
+
assignees: e.assignees ? [...e.assignees] : [],
|
|
49
|
+
dueDate: e.dueDate,
|
|
50
|
+
priority: e.priority,
|
|
51
|
+
order: e.order ?? r,
|
|
52
|
+
columnId: e.columnId ?? n,
|
|
53
|
+
data: e.data,
|
|
54
|
+
createdAt: e.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
55
|
+
updatedAt: e.updatedAt
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
getColumns() {
|
|
59
|
+
return [...this.columns].sort((e, t) => e.order - t.order);
|
|
60
|
+
}
|
|
61
|
+
getColumn(e) {
|
|
62
|
+
return this.columns.find((t) => t.id === e);
|
|
63
|
+
}
|
|
64
|
+
addColumn(e) {
|
|
65
|
+
const t = this.normalizeColumn(e, this.columns.length);
|
|
66
|
+
return this.columns.push(t), this.sortColumns(), this.events.emit("column:add", { column: t }), t;
|
|
67
|
+
}
|
|
68
|
+
removeColumn(e) {
|
|
69
|
+
const t = this.columns.findIndex((t) => t.id === e);
|
|
70
|
+
if (-1 === t) return;
|
|
71
|
+
const [n] = this.columns.splice(t, 1);
|
|
72
|
+
return this.reorderColumns(), this.events.emit("column:remove", { column: n }), n;
|
|
73
|
+
}
|
|
74
|
+
updateColumn(e, t) {
|
|
75
|
+
const n = this.columns.find((t) => t.id === e);
|
|
76
|
+
if (n) return Object.assign(n, t, { id: e }), t.cards && (n.cards = t.cards.map((t, n) => this.normalizeCard(t, e, n)), n.cards.sort((e, t) => e.order - t.order), this.reorderCards(n)), void 0 !== t.order && this.sortColumns(), this.events.emit("column:update", {
|
|
77
|
+
column: n,
|
|
78
|
+
changes: t
|
|
79
|
+
}), n;
|
|
80
|
+
}
|
|
81
|
+
moveColumn(e, t) {
|
|
82
|
+
this.sortColumns();
|
|
83
|
+
const n = this.columns.findIndex((t) => t.id === e);
|
|
84
|
+
if (-1 === n) return;
|
|
85
|
+
const [r] = this.columns.splice(n, 1), s = this.clampIndex(t, this.columns.length);
|
|
86
|
+
this.columns.splice(s, 0, r), this.reorderColumns(), this.events.emit("column:move", {
|
|
87
|
+
column: r,
|
|
88
|
+
fromIndex: n,
|
|
89
|
+
toIndex: s
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
toggleCollapse(e) {
|
|
93
|
+
const t = this.columns.find((t) => t.id === e);
|
|
94
|
+
t && (t.collapsed = !t.collapsed, this.events.emit("column:collapse", {
|
|
95
|
+
column: t,
|
|
96
|
+
collapsed: t.collapsed
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
getCards(e) {
|
|
100
|
+
const t = this.columns.find((t) => t.id === e);
|
|
101
|
+
return t ? [...t.cards].sort((e, t) => e.order - t.order) : [];
|
|
102
|
+
}
|
|
103
|
+
addCard(e, t) {
|
|
104
|
+
const n = this.columns.find((t) => t.id === e);
|
|
105
|
+
if (!n) return;
|
|
106
|
+
if (n.wipLimit && n.cards.length >= n.wipLimit) return void this.events.emit("error", { error: /* @__PURE__ */ new Error(`Column "${n.title}" reached WIP limit (${n.wipLimit})`) });
|
|
107
|
+
const r = this.normalizeCard({
|
|
108
|
+
...t,
|
|
109
|
+
columnId: e
|
|
110
|
+
}, e, n.cards.length);
|
|
111
|
+
return n.cards.push(r), this.events.emit("card:add", {
|
|
112
|
+
card: r,
|
|
113
|
+
columnId: e
|
|
114
|
+
}), r;
|
|
115
|
+
}
|
|
116
|
+
removeCard(e) {
|
|
117
|
+
for (const t of this.columns) {
|
|
118
|
+
const n = t.cards.findIndex((t) => t.id === e);
|
|
119
|
+
if (-1 !== n) {
|
|
120
|
+
const [e] = t.cards.splice(n, 1);
|
|
121
|
+
return this.reorderCards(t), this.events.emit("card:remove", {
|
|
122
|
+
card: e,
|
|
123
|
+
columnId: t.id
|
|
124
|
+
}), e;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
updateCard(e, t) {
|
|
129
|
+
for (const n of this.columns) {
|
|
130
|
+
const r = n.cards.find((t) => t.id === e);
|
|
131
|
+
if (r) return Object.assign(r, t, {
|
|
132
|
+
id: e,
|
|
133
|
+
columnId: n.id
|
|
134
|
+
}), r.updatedAt = (/* @__PURE__ */ new Date()).toISOString(), this.events.emit("card:update", {
|
|
135
|
+
card: r,
|
|
136
|
+
changes: t
|
|
137
|
+
}), r;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
getCard(e) {
|
|
141
|
+
for (const t of this.columns) {
|
|
142
|
+
const n = t.cards.find((t) => t.id === e);
|
|
143
|
+
if (n) return n;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
moveCard(e, t, n) {
|
|
147
|
+
let r, s = -1;
|
|
148
|
+
for (const t of this.columns) {
|
|
149
|
+
const n = t.cards.findIndex((t) => t.id === e);
|
|
150
|
+
if (-1 !== n) {
|
|
151
|
+
r = t, s = n;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (!r || -1 === s) return;
|
|
156
|
+
const o = this.columns.find((e) => e.id === t);
|
|
157
|
+
if (!o) return;
|
|
158
|
+
if (r.id !== t && o.wipLimit && o.cards.length >= o.wipLimit) return void this.events.emit("error", { error: /* @__PURE__ */ new Error(`Column "${o.title}" reached WIP limit (${o.wipLimit})`) });
|
|
159
|
+
const [i] = r.cards.splice(s, 1);
|
|
160
|
+
i.columnId = t;
|
|
161
|
+
const a = this.clampIndex(n, o.cards.length);
|
|
162
|
+
o.cards.splice(a, 0, i), this.reorderCards(r), this.reorderCards(o), this.events.emit("card:move", {
|
|
163
|
+
card: i,
|
|
164
|
+
fromColumnId: r.id,
|
|
165
|
+
toColumnId: t,
|
|
166
|
+
toIndex: a
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
getAllCards() {
|
|
170
|
+
const e = [];
|
|
171
|
+
for (const t of this.columns) e.push(...t.cards);
|
|
172
|
+
return e;
|
|
173
|
+
}
|
|
174
|
+
getCardCount() {
|
|
175
|
+
return this.columns.reduce((e, t) => e + t.cards.length, 0);
|
|
176
|
+
}
|
|
177
|
+
reorderColumns() {
|
|
178
|
+
this.columns.forEach((e, t) => {
|
|
179
|
+
e.order = t;
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
reorderCards(e) {
|
|
183
|
+
e.cards.forEach((e, t) => {
|
|
184
|
+
e.order = t;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
setColumns(e) {
|
|
188
|
+
this.columns = e.map((e, t) => this.normalizeColumn(e, t)), this.sortColumns();
|
|
189
|
+
}
|
|
190
|
+
clear() {
|
|
191
|
+
this.columns = [];
|
|
192
|
+
}
|
|
193
|
+
clampIndex(e, t) {
|
|
194
|
+
return Number.isFinite(e) ? Math.min(Math.max(Math.trunc(e), 0), t) : t;
|
|
195
|
+
}
|
|
196
|
+
sortColumns() {
|
|
197
|
+
this.columns.sort((e, t) => e.order - t.order), this.reorderColumns();
|
|
198
|
+
}
|
|
199
|
+
}, r = class {
|
|
200
|
+
constructor(e, t) {
|
|
201
|
+
this.state = {
|
|
202
|
+
dragging: !1,
|
|
203
|
+
cardId: null,
|
|
204
|
+
sourceColumnId: null,
|
|
205
|
+
targetColumnId: null,
|
|
206
|
+
targetIndex: 0,
|
|
207
|
+
placeholder: null
|
|
208
|
+
}, this.cleanupFns = [], this.events = e, this.prefix = t;
|
|
209
|
+
}
|
|
210
|
+
enableCardDrag(e, t, n) {
|
|
211
|
+
e.setAttribute("draggable", "true"), e.dataset.cardId = t, e.dataset.columnId = n;
|
|
212
|
+
const r = (r) => {
|
|
213
|
+
r.dataTransfer && (r.dataTransfer.effectAllowed = "move", r.dataTransfer.setData("text/plain", t), this.state.dragging = !0, this.state.cardId = t, this.state.sourceColumnId = n, this.state.targetColumnId = n, this.state.targetIndex = 0, e.classList.add(`${this.prefix}-card--dragging`), this.events.emit("card:drag-start", {
|
|
214
|
+
cardId: t,
|
|
215
|
+
columnId: n
|
|
216
|
+
}), this.createPlaceholder(e));
|
|
217
|
+
}, s = () => {
|
|
218
|
+
e.classList.remove(`${this.prefix}-card--dragging`), this.removePlaceholder(), this.state.dragging = !1, this.state.cardId = null, this.state.sourceColumnId = null, this.state.targetColumnId = null, this.state.targetIndex = 0, this.events.emit("card:drag-end", { cardId: t });
|
|
219
|
+
};
|
|
220
|
+
e.addEventListener("dragstart", r), e.addEventListener("dragend", s), this.cleanupFns.push(() => {
|
|
221
|
+
e.removeEventListener("dragstart", r), e.removeEventListener("dragend", s);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
enableColumnDrop(e, t) {
|
|
225
|
+
const n = (n) => {
|
|
226
|
+
if (n.preventDefault(), !n.dataTransfer) return;
|
|
227
|
+
n.dataTransfer.dropEffect = "move", this.state.targetColumnId = t;
|
|
228
|
+
const r = this.getDragAfterElement(e, n.clientY), s = this.state.placeholder;
|
|
229
|
+
if (s) if (r) e.insertBefore(s, r);
|
|
230
|
+
else {
|
|
231
|
+
const t = e.querySelector(`.${this.prefix}-add-card`);
|
|
232
|
+
t ? e.insertBefore(s, t) : e.appendChild(s);
|
|
233
|
+
}
|
|
234
|
+
}, r = (t) => {
|
|
235
|
+
t.preventDefault(), e.classList.add(`${this.prefix}-column-body--drag-over`);
|
|
236
|
+
}, s = (t) => {
|
|
237
|
+
const n = e.getBoundingClientRect();
|
|
238
|
+
(t.clientX < n.left || t.clientX > n.right || t.clientY < n.top || t.clientY > n.bottom) && e.classList.remove(`${this.prefix}-column-body--drag-over`);
|
|
239
|
+
}, o = (n) => {
|
|
240
|
+
n.preventDefault(), e.classList.remove(`${this.prefix}-column-body--drag-over`);
|
|
241
|
+
const r = this.state.cardId;
|
|
242
|
+
if (!r) return;
|
|
243
|
+
const s = this.state.placeholder;
|
|
244
|
+
let o = 0;
|
|
245
|
+
if (s && s.parentNode === e) {
|
|
246
|
+
const t = [...e.children];
|
|
247
|
+
for (const e of t) {
|
|
248
|
+
if (e === s) break;
|
|
249
|
+
e.hasAttribute("data-card-id") && !e.classList.contains(`${this.prefix}-card--dragging`) && o++;
|
|
250
|
+
}
|
|
251
|
+
} else o = e.querySelectorAll(`[data-card-id]:not(.${this.prefix}-card--dragging)`).length;
|
|
252
|
+
this.state.targetIndex = o, this.events.emit("card:drop", {
|
|
253
|
+
cardId: r,
|
|
254
|
+
fromColumnId: this.state.sourceColumnId,
|
|
255
|
+
toColumnId: t,
|
|
256
|
+
toIndex: o
|
|
257
|
+
}), this.removePlaceholder();
|
|
258
|
+
};
|
|
259
|
+
e.addEventListener("dragover", n), e.addEventListener("dragenter", r), e.addEventListener("dragleave", s), e.addEventListener("drop", o), this.cleanupFns.push(() => {
|
|
260
|
+
e.removeEventListener("dragover", n), e.removeEventListener("dragenter", r), e.removeEventListener("dragleave", s), e.removeEventListener("drop", o);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
enableColumnDrag(e, t, n, r) {
|
|
264
|
+
t.setAttribute("draggable", "true");
|
|
265
|
+
const s = (t) => {
|
|
266
|
+
t.dataTransfer && (t.dataTransfer.effectAllowed = "move", t.dataTransfer.setData("application/kanban-column", n), e.classList.add(`${this.prefix}-column--dragging`));
|
|
267
|
+
}, o = () => {
|
|
268
|
+
e.classList.remove(`${this.prefix}-column--dragging`), r.querySelectorAll(`.${this.prefix}-column--drag-over`).forEach((e) => {
|
|
269
|
+
e.classList.remove(`${this.prefix}-column--drag-over`);
|
|
270
|
+
});
|
|
271
|
+
}, i = (t) => {
|
|
272
|
+
t.dataTransfer?.types.includes("application/kanban-column") && (t.preventDefault(), t.dataTransfer.dropEffect = "move", e.classList.add(`${this.prefix}-column--drag-over`));
|
|
273
|
+
}, a = () => {
|
|
274
|
+
e.classList.remove(`${this.prefix}-column--drag-over`);
|
|
275
|
+
}, d = (t) => {
|
|
276
|
+
t.preventDefault(), e.classList.remove(`${this.prefix}-column--drag-over`);
|
|
277
|
+
const s = t.dataTransfer?.getData("application/kanban-column");
|
|
278
|
+
if (!s || s === n) return;
|
|
279
|
+
const o = [...r.querySelectorAll("[data-column-id]")].indexOf(e);
|
|
280
|
+
this.events.emit("column:drop", {
|
|
281
|
+
fromColumnId: s,
|
|
282
|
+
toColumnId: n,
|
|
283
|
+
toIndex: o
|
|
284
|
+
});
|
|
285
|
+
};
|
|
286
|
+
t.addEventListener("dragstart", s), t.addEventListener("dragend", o), e.addEventListener("dragover", i), e.addEventListener("dragleave", a), e.addEventListener("drop", d), this.cleanupFns.push(() => {
|
|
287
|
+
t.removeEventListener("dragstart", s), t.removeEventListener("dragend", o), e.removeEventListener("dragover", i), e.removeEventListener("dragleave", a), e.removeEventListener("drop", d);
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
getDragAfterElement(e, t) {
|
|
291
|
+
const n = [...e.querySelectorAll(`[data-card-id]:not(.${this.prefix}-card--dragging):not(.${this.prefix}-placeholder)`)];
|
|
292
|
+
let r = Number.NEGATIVE_INFINITY, s = null;
|
|
293
|
+
return n.forEach((e) => {
|
|
294
|
+
const n = e.getBoundingClientRect(), o = t - n.top - n.height / 2;
|
|
295
|
+
o < 0 && o > r && (r = o, s = e);
|
|
296
|
+
}), s;
|
|
297
|
+
}
|
|
298
|
+
createPlaceholder(e) {
|
|
299
|
+
this.removePlaceholder();
|
|
300
|
+
const t = document.createElement("div");
|
|
301
|
+
t.className = `${this.prefix}-placeholder`, t.style.height = `${e.offsetHeight}px`, this.state.placeholder = t;
|
|
302
|
+
}
|
|
303
|
+
removePlaceholder() {
|
|
304
|
+
this.state.placeholder && (this.state.placeholder.parentNode?.removeChild(this.state.placeholder), this.state.placeholder = null);
|
|
305
|
+
}
|
|
306
|
+
getState() {
|
|
307
|
+
return { ...this.state };
|
|
308
|
+
}
|
|
309
|
+
destroy() {
|
|
310
|
+
this.cleanupFns.forEach((e) => e()), this.cleanupFns = [], this.removePlaceholder(), this.state.dragging = !1, this.state.cardId = null, this.state.sourceColumnId = null, this.state.targetColumnId = null, this.state.targetIndex = 0;
|
|
311
|
+
}
|
|
312
|
+
}, s = class {
|
|
313
|
+
constructor() {
|
|
314
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
315
|
+
}
|
|
316
|
+
on(e, t) {
|
|
317
|
+
this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set());
|
|
318
|
+
const n = this.listeners.get(e);
|
|
319
|
+
return n ? (n.add(t), () => this.off(e, t)) : () => this.off(e, t);
|
|
320
|
+
}
|
|
321
|
+
once(e, t) {
|
|
322
|
+
const n = (...r) => {
|
|
323
|
+
this.off(e, n), t(...r);
|
|
324
|
+
};
|
|
325
|
+
return this.on(e, n);
|
|
326
|
+
}
|
|
327
|
+
off(e, t) {
|
|
328
|
+
const n = this.listeners.get(e);
|
|
329
|
+
n && (n.delete(t), 0 === n.size && this.listeners.delete(e));
|
|
330
|
+
}
|
|
331
|
+
emit(e, t) {
|
|
332
|
+
const n = this.listeners.get(e);
|
|
333
|
+
n && n.forEach((n) => {
|
|
334
|
+
try {
|
|
335
|
+
n(t);
|
|
336
|
+
} catch (t) {
|
|
337
|
+
console.error(`[KanbanBoard] Error in event handler for "${e}":`, t);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
removeAll() {
|
|
342
|
+
this.listeners.clear();
|
|
343
|
+
}
|
|
344
|
+
listenerCount(e) {
|
|
345
|
+
return this.listeners.get(e)?.size ?? 0;
|
|
346
|
+
}
|
|
347
|
+
}, o = class {
|
|
348
|
+
constructor(e) {
|
|
349
|
+
this.currentCriteria = null, this.hiddenCardIds = /* @__PURE__ */ new Set(), this.events = e;
|
|
350
|
+
}
|
|
351
|
+
filter(e, t) {
|
|
352
|
+
this.currentCriteria = e, this.hiddenCardIds.clear();
|
|
353
|
+
const n = t.flatMap((e) => e.cards);
|
|
354
|
+
for (const t of n) this.matchCriteria(t, e) || this.hiddenCardIds.add(t.id);
|
|
355
|
+
return this.events.emit("filter:change", { criteria: e }), new Set(this.hiddenCardIds);
|
|
356
|
+
}
|
|
357
|
+
search(e, t) {
|
|
358
|
+
this.events.emit("search:change", { keyword: e });
|
|
359
|
+
const n = e.trim();
|
|
360
|
+
if (this.currentCriteria = n ? { keyword: n } : null, !n) return this.clearFilter(), t.flatMap((e) => e.cards);
|
|
361
|
+
const r = n.toLowerCase(), s = t.flatMap((e) => e.cards), o = [];
|
|
362
|
+
this.hiddenCardIds.clear();
|
|
363
|
+
for (const e of s) {
|
|
364
|
+
const t = e.title.toLowerCase().includes(r), n = e.description?.toLowerCase().includes(r) ?? !1, s = e.labels?.some((e) => e.name.toLowerCase().includes(r)) ?? !1, i = e.assignees?.some((e) => e.toLowerCase().includes(r)) ?? !1;
|
|
365
|
+
t || n || s || i ? o.push(e) : this.hiddenCardIds.add(e.id);
|
|
366
|
+
}
|
|
367
|
+
return o;
|
|
368
|
+
}
|
|
369
|
+
clearFilter() {
|
|
370
|
+
this.currentCriteria = null, this.hiddenCardIds.clear(), this.events.emit("filter:clear", void 0);
|
|
371
|
+
}
|
|
372
|
+
isCardVisible(e) {
|
|
373
|
+
return !this.hiddenCardIds.has(e);
|
|
374
|
+
}
|
|
375
|
+
getHiddenCardIds() {
|
|
376
|
+
return new Set(this.hiddenCardIds);
|
|
377
|
+
}
|
|
378
|
+
getCurrentCriteria() {
|
|
379
|
+
return this.currentCriteria;
|
|
380
|
+
}
|
|
381
|
+
matchCriteria(e, t) {
|
|
382
|
+
if (t.keyword) {
|
|
383
|
+
const n = t.keyword.toLowerCase(), r = e.title.toLowerCase().includes(n), s = e.description?.toLowerCase().includes(n) ?? !1;
|
|
384
|
+
if (!r && !s) return !1;
|
|
385
|
+
}
|
|
386
|
+
if (t.labelIds?.length) {
|
|
387
|
+
const n = e.labels?.map((e) => e.id) ?? [];
|
|
388
|
+
if (!t.labelIds.some((e) => n.includes(e))) return !1;
|
|
389
|
+
}
|
|
390
|
+
if (t.assignees?.length && !t.assignees.some((t) => e.assignees?.includes(t))) return !1;
|
|
391
|
+
if (t.priorities?.length && (!e.priority || !t.priorities.includes(e.priority))) return !1;
|
|
392
|
+
if (t.columnIds?.length && !t.columnIds.includes(e.columnId)) return !1;
|
|
393
|
+
if (t.dueDateRange) {
|
|
394
|
+
if (!e.dueDate) return !1;
|
|
395
|
+
const n = new Date(e.dueDate).getTime();
|
|
396
|
+
if (Number.isNaN(n)) return !1;
|
|
397
|
+
if (t.dueDateRange.start) {
|
|
398
|
+
const e = new Date(t.dueDateRange.start).getTime();
|
|
399
|
+
if (Number.isNaN(e) || n < e) return !1;
|
|
400
|
+
}
|
|
401
|
+
if (t.dueDateRange.end) {
|
|
402
|
+
const e = new Date(t.dueDateRange.end).getTime();
|
|
403
|
+
if (Number.isNaN(e) || n > e) return !1;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return !(t.custom && !t.custom(e));
|
|
407
|
+
}
|
|
408
|
+
destroy() {
|
|
409
|
+
this.hiddenCardIds.clear(), this.currentCriteria = null;
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
function i(e, t, n) {
|
|
413
|
+
const r = document.createElement(e);
|
|
414
|
+
return t && (r.className = t), n && Object.entries(n).forEach(([e, t]) => {
|
|
415
|
+
r.setAttribute(e, t);
|
|
416
|
+
}), r;
|
|
417
|
+
}
|
|
418
|
+
function a(e) {
|
|
419
|
+
return "string" == typeof e ? document.querySelector(e) : e;
|
|
420
|
+
}
|
|
421
|
+
const d = "0.1.0";
|
|
422
|
+
var l = class {
|
|
423
|
+
constructor(e, t, n, r) {
|
|
424
|
+
this.boardEl = null, this.columnElements = /* @__PURE__ */ new Map(), this.columnBodyElements = /* @__PURE__ */ new Map(), this.cardElements = /* @__PURE__ */ new Map(), this.container = e, this.events = t, this.dragController = n, this.options = r, this.prefix = r.classPrefix || "kanban";
|
|
425
|
+
}
|
|
426
|
+
render(e, t) {
|
|
427
|
+
this.cleanup(), this.container.querySelectorAll("[data-kanban-board]").forEach((e) => e.remove());
|
|
428
|
+
const n = this.prefix;
|
|
429
|
+
this.boardEl = i("div", `${n}-board`), this.boardEl.setAttribute("data-kanban-board", "");
|
|
430
|
+
const r = i("div", `${n}-columns`);
|
|
431
|
+
for (const n of e) {
|
|
432
|
+
const e = this.renderColumn(n, t);
|
|
433
|
+
r.appendChild(e);
|
|
434
|
+
}
|
|
435
|
+
if (!1 !== this.options.showAddColumn) {
|
|
436
|
+
const e = i("div", `${n}-add-column`);
|
|
437
|
+
e.innerHTML = `<button class="${n}-add-column-btn">+ 添加列</button>`, e.querySelector("button")?.addEventListener("click", () => {
|
|
438
|
+
this.events.emit("ui:add-column");
|
|
439
|
+
}), r.appendChild(e);
|
|
440
|
+
}
|
|
441
|
+
this.boardEl.appendChild(r), this.container.appendChild(this.boardEl);
|
|
442
|
+
}
|
|
443
|
+
renderColumn(e, t) {
|
|
444
|
+
const n = this.prefix, r = i("div", `${n}-column`, { "data-column-id": e.id });
|
|
445
|
+
e.collapsed && r.classList.add(`${n}-column--collapsed`);
|
|
446
|
+
const s = this.renderColumnHeader(e);
|
|
447
|
+
r.appendChild(s);
|
|
448
|
+
const o = i("div", `${n}-column-body`);
|
|
449
|
+
if (o.dataset.columnId = e.id, !e.collapsed) {
|
|
450
|
+
const r = e.cards.filter((e) => !t || !t.has(e.id)).sort((e, t) => e.order - t.order);
|
|
451
|
+
for (const t of r) {
|
|
452
|
+
const n = this.renderCard(t, e.id);
|
|
453
|
+
o.appendChild(n);
|
|
454
|
+
}
|
|
455
|
+
if (!1 !== this.options.showAddCard) {
|
|
456
|
+
const t = i("div", `${n}-add-card`);
|
|
457
|
+
t.innerHTML = `<button class="${n}-add-card-btn">+ 添加卡片</button>`, t.querySelector("button")?.addEventListener("click", () => {
|
|
458
|
+
this.events.emit("ui:add-card", { columnId: e.id });
|
|
459
|
+
}), o.appendChild(t);
|
|
460
|
+
}
|
|
461
|
+
!1 !== this.options.draggable && this.dragController.enableColumnDrop(o, e.id);
|
|
462
|
+
}
|
|
463
|
+
r.appendChild(o);
|
|
464
|
+
const a = this.boardEl;
|
|
465
|
+
if (!1 !== this.options.columnDraggable && a) {
|
|
466
|
+
const t = s.querySelector(`.${n}-column-drag-handle`);
|
|
467
|
+
t && this.dragController.enableColumnDrag(r, t, e.id, a);
|
|
468
|
+
}
|
|
469
|
+
return this.columnElements.set(e.id, r), this.columnBodyElements.set(e.id, o), r;
|
|
470
|
+
}
|
|
471
|
+
renderColumnHeader(e) {
|
|
472
|
+
const t = this.prefix, n = i("div", `${t}-column-header`);
|
|
473
|
+
if (this.options.columnHeaderRenderer) return this.options.columnHeaderRenderer(e, n), n;
|
|
474
|
+
if (!1 !== this.options.columnDraggable) {
|
|
475
|
+
const e = i("span", `${t}-column-drag-handle`);
|
|
476
|
+
e.innerHTML = "⋮⋮", e.title = "拖拽排序", n.appendChild(e);
|
|
477
|
+
}
|
|
478
|
+
if (e.color) {
|
|
479
|
+
const r = i("span", `${t}-column-color`);
|
|
480
|
+
r.style.backgroundColor = e.color, n.appendChild(r);
|
|
481
|
+
}
|
|
482
|
+
const r = i("span", `${t}-column-title`);
|
|
483
|
+
if (r.textContent = e.title, n.appendChild(r), !1 !== this.options.showCardCount) {
|
|
484
|
+
const r = i("span", `${t}-column-count`), s = e.wipLimit ? `/${e.wipLimit}` : "";
|
|
485
|
+
r.textContent = `${e.cards.length}${s}`, e.wipLimit && e.cards.length >= e.wipLimit && r.classList.add(`${t}-column-count--limit`), n.appendChild(r);
|
|
486
|
+
}
|
|
487
|
+
const s = i("button", `${t}-column-collapse-btn`);
|
|
488
|
+
if (s.innerHTML = e.collapsed ? "▶" : "▼", s.title = e.collapsed ? "展开" : "折叠", s.addEventListener("click", () => {
|
|
489
|
+
this.events.emit("ui:toggle-collapse", { columnId: e.id });
|
|
490
|
+
}), n.appendChild(s), !1 !== this.options.showColumnActions) {
|
|
491
|
+
const r = i("button", `${t}-column-actions-btn`);
|
|
492
|
+
r.innerHTML = "⋯", r.title = "更多操作", r.addEventListener("click", (t) => {
|
|
493
|
+
t.stopPropagation(), this.events.emit("ui:column-actions", {
|
|
494
|
+
columnId: e.id,
|
|
495
|
+
event: t
|
|
496
|
+
});
|
|
497
|
+
}), n.appendChild(r);
|
|
498
|
+
}
|
|
499
|
+
return n;
|
|
500
|
+
}
|
|
501
|
+
renderCard(e, t) {
|
|
502
|
+
const n = i("div", `${this.prefix}-card`, { "data-card-id": e.id });
|
|
503
|
+
return this.options.cardRenderer ? this.options.cardRenderer(e, n) : this.renderDefaultCard(e, n), !1 !== this.options.draggable && this.dragController.enableCardDrag(n, e.id, t), n.addEventListener("click", () => {
|
|
504
|
+
this.events.emit("card:click", { card: e });
|
|
505
|
+
}), this.cardElements.set(e.id, n), n;
|
|
506
|
+
}
|
|
507
|
+
renderDefaultCard(e, t) {
|
|
508
|
+
const n = this.prefix;
|
|
509
|
+
if (e.priority) {
|
|
510
|
+
const r = i("div", `${n}-card-priority ${n}-card-priority--${e.priority}`);
|
|
511
|
+
t.appendChild(r);
|
|
512
|
+
}
|
|
513
|
+
if (e.labels?.length) {
|
|
514
|
+
const r = i("div", `${n}-card-labels`);
|
|
515
|
+
for (const t of e.labels) {
|
|
516
|
+
const e = i("span", `${n}-card-label`);
|
|
517
|
+
e.style.backgroundColor = t.color, e.textContent = t.name, r.appendChild(e);
|
|
518
|
+
}
|
|
519
|
+
t.appendChild(r);
|
|
520
|
+
}
|
|
521
|
+
const r = i("div", `${n}-card-title`);
|
|
522
|
+
if (r.textContent = e.title, t.appendChild(r), e.description) {
|
|
523
|
+
const r = i("div", `${n}-card-desc`);
|
|
524
|
+
r.textContent = e.description, t.appendChild(r);
|
|
525
|
+
}
|
|
526
|
+
if (e.assignees?.length || e.dueDate) {
|
|
527
|
+
const r = i("div", `${n}-card-footer`);
|
|
528
|
+
if (e.assignees?.length) {
|
|
529
|
+
const t = i("div", `${n}-card-assignees`);
|
|
530
|
+
for (const r of e.assignees.slice(0, 3)) {
|
|
531
|
+
const e = i("span", `${n}-card-avatar`);
|
|
532
|
+
e.textContent = r.charAt(0).toUpperCase(), e.title = r, t.appendChild(e);
|
|
533
|
+
}
|
|
534
|
+
if (e.assignees.length > 3) {
|
|
535
|
+
const r = i("span", `${n}-card-avatar-more`);
|
|
536
|
+
r.textContent = "+" + (e.assignees.length - 3), t.appendChild(r);
|
|
537
|
+
}
|
|
538
|
+
r.appendChild(t);
|
|
539
|
+
}
|
|
540
|
+
if (e.dueDate) {
|
|
541
|
+
const t = i("span", `${n}-card-due`), s = new Date(e.dueDate);
|
|
542
|
+
Number.isNaN(s.getTime()) ? t.textContent = "Invalid date" : (t.textContent = `${s.getMonth() + 1}/${s.getDate()}`, s < /* @__PURE__ */ new Date() && t.classList.add(`${n}-card-due--overdue`)), r.appendChild(t);
|
|
543
|
+
}
|
|
544
|
+
t.appendChild(r);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
getColumnElement(e) {
|
|
548
|
+
return this.columnElements.get(e);
|
|
549
|
+
}
|
|
550
|
+
getCardElement(e) {
|
|
551
|
+
return this.cardElements.get(e);
|
|
552
|
+
}
|
|
553
|
+
getBoardElement() {
|
|
554
|
+
return this.boardEl;
|
|
555
|
+
}
|
|
556
|
+
cleanup() {
|
|
557
|
+
this.columnElements.clear(), this.columnBodyElements.clear(), this.cardElements.clear(), this.boardEl && (this.boardEl.remove(), this.boardEl = null);
|
|
558
|
+
}
|
|
559
|
+
destroy() {
|
|
560
|
+
this.cleanup();
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
function c(e) {
|
|
564
|
+
return "object" == typeof e && null !== e;
|
|
565
|
+
}
|
|
566
|
+
function u(e) {
|
|
567
|
+
return c(e) && "string" == typeof e.id && "string" == typeof e.title && "number" == typeof e.order && "string" == typeof e.columnId;
|
|
568
|
+
}
|
|
569
|
+
function h(e) {
|
|
570
|
+
return c(e) && "string" == typeof e.id && "string" == typeof e.title && Array.isArray(e.cards) && "number" == typeof e.order;
|
|
571
|
+
}
|
|
572
|
+
var m = class {
|
|
573
|
+
constructor(e) {
|
|
574
|
+
this.destroyed = !1, this.labels = [], this.renderTimer = null, this.options = {
|
|
575
|
+
draggable: !0,
|
|
576
|
+
columnDraggable: !0,
|
|
577
|
+
crossColumnDrag: !0,
|
|
578
|
+
searchable: !0,
|
|
579
|
+
showAddCard: !0,
|
|
580
|
+
showAddColumn: !0,
|
|
581
|
+
showColumnActions: !0,
|
|
582
|
+
showCardCount: !0,
|
|
583
|
+
classPrefix: "kanban",
|
|
584
|
+
...e
|
|
585
|
+
};
|
|
586
|
+
const t = a(e.container);
|
|
587
|
+
if (!t) throw new Error("[KanbanBoard] Container element not found");
|
|
588
|
+
this.container = t, this.events = new s(), this.columnManager = new n(this.events), this.dragController = new r(this.events, this.options.classPrefix || "kanban"), this.filterEngine = new o(this.events), this.renderer = new l(this.container, this.events, this.dragController, this.options), this.labels = e.labels || [], this.init();
|
|
589
|
+
}
|
|
590
|
+
init() {
|
|
591
|
+
this.options.columns?.length && this.columnManager.init(this.options.columns), this.bindInternalEvents(), this.render(), this.events.emit("ready", { instance: this }), this.options.onReady?.(this), this.options.debug && console.debug("[KanbanBoard] Initialized", {
|
|
592
|
+
columns: this.columnManager.getColumns().length,
|
|
593
|
+
cards: this.columnManager.getCardCount()
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
bindInternalEvents() {
|
|
597
|
+
this.events.on("card:drop", (e) => {
|
|
598
|
+
if (!c(t = e) || "string" != typeof t.cardId || null !== t.fromColumnId && "string" != typeof t.fromColumnId || "string" != typeof t.toColumnId || "number" != typeof t.toIndex) return;
|
|
599
|
+
var t;
|
|
600
|
+
const n = this.columnManager.getCard(e.cardId);
|
|
601
|
+
this.columnManager.moveCard(e.cardId, e.toColumnId, e.toIndex), n && n.columnId === e.toColumnId && this.options.onCardMove?.(n, e.fromColumnId ?? "", e.toColumnId, n.order), this.scheduleRender();
|
|
602
|
+
}), this.events.on("column:drop", (e) => {
|
|
603
|
+
if (!c(t = e) || "string" != typeof t.fromColumnId || "string" != typeof t.toColumnId || "number" != typeof t.toIndex) return;
|
|
604
|
+
var t;
|
|
605
|
+
const n = this.columnManager.getColumns();
|
|
606
|
+
if (e.fromColumnId === e.toColumnId || !n.some((t) => t.id === e.fromColumnId) || !n.some((t) => t.id === e.toColumnId)) return;
|
|
607
|
+
const r = n.findIndex((t) => t.id === e.fromColumnId);
|
|
608
|
+
this.columnManager.moveColumn(e.fromColumnId, e.toIndex);
|
|
609
|
+
const s = this.columnManager.getColumn(e.fromColumnId);
|
|
610
|
+
s && this.options.onColumnMove?.(s, r, s.order), this.scheduleRender();
|
|
611
|
+
}), this.events.on("ui:toggle-collapse", (e) => {
|
|
612
|
+
if (!c(e) || "string" != typeof e.columnId) return;
|
|
613
|
+
this.columnManager.toggleCollapse(e.columnId);
|
|
614
|
+
const t = this.columnManager.getColumn(e.columnId);
|
|
615
|
+
t && this.options.onColumnCollapse?.(t, t.collapsed ?? !1), this.render();
|
|
616
|
+
}), this.events.on("card:add", (e) => {
|
|
617
|
+
c(e) && u(e.card) && "string" == typeof e.columnId && this.options.onCardAdd?.(e.card, e.columnId);
|
|
618
|
+
}), this.events.on("card:remove", (e) => {
|
|
619
|
+
c(e) && u(e.card) && "string" == typeof e.columnId && this.options.onCardRemove?.(e.card, e.columnId);
|
|
620
|
+
}), this.events.on("card:click", (e) => {
|
|
621
|
+
c(e) && u(e.card) && this.options.onCardClick?.(e.card);
|
|
622
|
+
}), this.events.on("card:update", (e) => {
|
|
623
|
+
c(e) && u(e.card) && this.options.onCardUpdate?.(e.card);
|
|
624
|
+
}), this.events.on("column:add", (e) => {
|
|
625
|
+
c(e) && h(e.column) && this.options.onColumnAdd?.(e.column);
|
|
626
|
+
}), this.events.on("column:remove", (e) => {
|
|
627
|
+
c(e) && h(e.column) && this.options.onColumnRemove?.(e.column);
|
|
628
|
+
}), this.events.on("error", (e) => {
|
|
629
|
+
c(e) && e.error instanceof Error && this.options.onError?.(e.error);
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
addColumn(e) {
|
|
633
|
+
const t = this.columnManager.addColumn(e);
|
|
634
|
+
return this.render(), t;
|
|
635
|
+
}
|
|
636
|
+
removeColumn(e) {
|
|
637
|
+
const t = this.columnManager.removeColumn(e);
|
|
638
|
+
return t && this.render(), t;
|
|
639
|
+
}
|
|
640
|
+
updateColumn(e, t) {
|
|
641
|
+
const n = this.columnManager.updateColumn(e, t);
|
|
642
|
+
return n && this.render(), n;
|
|
643
|
+
}
|
|
644
|
+
moveColumn(e, t) {
|
|
645
|
+
this.columnManager.moveColumn(e, t), this.render();
|
|
646
|
+
}
|
|
647
|
+
toggleColumnCollapse(e) {
|
|
648
|
+
this.columnManager.toggleCollapse(e), this.render();
|
|
649
|
+
}
|
|
650
|
+
getColumns() {
|
|
651
|
+
return this.columnManager.getColumns();
|
|
652
|
+
}
|
|
653
|
+
getColumn(e) {
|
|
654
|
+
return this.columnManager.getColumn(e);
|
|
655
|
+
}
|
|
656
|
+
addCard(e, t) {
|
|
657
|
+
const n = this.columnManager.addCard(e, t);
|
|
658
|
+
return n && this.render(), n;
|
|
659
|
+
}
|
|
660
|
+
removeCard(e) {
|
|
661
|
+
const t = this.columnManager.removeCard(e);
|
|
662
|
+
return t && this.render(), t;
|
|
663
|
+
}
|
|
664
|
+
updateCard(e, t) {
|
|
665
|
+
const n = this.columnManager.updateCard(e, t);
|
|
666
|
+
return n && this.render(), n;
|
|
667
|
+
}
|
|
668
|
+
moveCard(e, t, n) {
|
|
669
|
+
this.columnManager.moveCard(e, t, n), this.render();
|
|
670
|
+
}
|
|
671
|
+
getCard(e) {
|
|
672
|
+
return this.columnManager.getCard(e);
|
|
673
|
+
}
|
|
674
|
+
getAllCards() {
|
|
675
|
+
return this.columnManager.getAllCards();
|
|
676
|
+
}
|
|
677
|
+
getCardCount() {
|
|
678
|
+
return this.columnManager.getCardCount();
|
|
679
|
+
}
|
|
680
|
+
filter(e) {
|
|
681
|
+
this.filterEngine.filter(e, this.columnManager.getColumns()), this.render();
|
|
682
|
+
}
|
|
683
|
+
search(e) {
|
|
684
|
+
const t = this.filterEngine.search(e, this.columnManager.getColumns());
|
|
685
|
+
return this.render(), t;
|
|
686
|
+
}
|
|
687
|
+
clearFilter() {
|
|
688
|
+
this.filterEngine.clearFilter(), this.render();
|
|
689
|
+
}
|
|
690
|
+
exportData() {
|
|
691
|
+
const e = {
|
|
692
|
+
version: d,
|
|
693
|
+
title: this.options.title || "Kanban Board",
|
|
694
|
+
columns: this.columnManager.getColumns(),
|
|
695
|
+
labels: this.labels,
|
|
696
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
697
|
+
};
|
|
698
|
+
return this.events.emit("data:export", { board: e }), e;
|
|
699
|
+
}
|
|
700
|
+
importData(e) {
|
|
701
|
+
e && Array.isArray(e.columns) ? (this.columnManager.setColumns(e.columns), this.labels = Array.isArray(e.labels) ? [...e.labels] : [], this.filterEngine.clearFilter(), this.events.emit("data:import", { board: e }), this.render()) : this.events.emit("error", { error: /* @__PURE__ */ new Error("[KanbanBoard] Invalid board data: columns must be an array") });
|
|
702
|
+
}
|
|
703
|
+
on(e, t) {
|
|
704
|
+
return this.events.on(e, t);
|
|
705
|
+
}
|
|
706
|
+
once(e, t) {
|
|
707
|
+
return this.events.once(e, t);
|
|
708
|
+
}
|
|
709
|
+
off(e, t) {
|
|
710
|
+
this.events.off(e, t);
|
|
711
|
+
}
|
|
712
|
+
getLabels() {
|
|
713
|
+
return [...this.labels];
|
|
714
|
+
}
|
|
715
|
+
addLabel(e) {
|
|
716
|
+
this.labels.push(e);
|
|
717
|
+
}
|
|
718
|
+
removeLabel(e) {
|
|
719
|
+
this.labels = this.labels.filter((t) => t.id !== e);
|
|
720
|
+
}
|
|
721
|
+
scheduleRender() {
|
|
722
|
+
null === this.renderTimer && (this.renderTimer = setTimeout(() => {
|
|
723
|
+
this.renderTimer = null, this.render();
|
|
724
|
+
}, 0));
|
|
725
|
+
}
|
|
726
|
+
render() {
|
|
727
|
+
this.destroyed || (this.dragController.destroy(), this.dragController = new r(this.events, this.options.classPrefix || "kanban"), this.renderer = new l(this.container, this.events, this.dragController, this.options), this.renderer.render(this.columnManager.getColumns(), this.filterEngine.getHiddenCardIds()));
|
|
728
|
+
}
|
|
729
|
+
getBoardElement() {
|
|
730
|
+
return this.renderer.getBoardElement();
|
|
731
|
+
}
|
|
732
|
+
getOptions() {
|
|
733
|
+
return { ...this.options };
|
|
734
|
+
}
|
|
735
|
+
destroy() {
|
|
736
|
+
this.destroyed || (this.destroyed = !0, null !== this.renderTimer && (clearTimeout(this.renderTimer), this.renderTimer = null), this.dragController.destroy(), this.filterEngine.destroy(), this.renderer.destroy(), this.columnManager.clear(), this.events.emit("destroy", void 0), this.options.onDestroy?.(), this.events.removeAll(), this.options.debug && console.debug("[KanbanBoard] Destroyed"));
|
|
737
|
+
}
|
|
738
|
+
isDestroyed() {
|
|
739
|
+
return this.destroyed;
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
exports.ColumnManager = n, exports.DragController = r, exports.EventEmitter = s, exports.FilterEngine = o, exports.KanbanBoard = m, exports.Renderer = l, exports.VERSION = d, exports.clearElement = function(e) {
|
|
743
|
+
e.innerHTML = "";
|
|
744
|
+
}, exports.createElement = i, exports.createKanbanBoard = function(e) {
|
|
745
|
+
return new m(e);
|
|
746
|
+
}, exports.generateId = t, exports.getElement = function(e) {
|
|
747
|
+
return "string" == typeof e ? document.querySelector(e) : e;
|
|
748
|
+
}, exports.insertAt = function(e, t, n) {
|
|
749
|
+
const r = e.children[n];
|
|
750
|
+
r ? e.insertBefore(t, r) : e.appendChild(t);
|
|
751
|
+
}, exports.removeElement = function(e) {
|
|
752
|
+
e?.parentNode?.removeChild(e);
|
|
753
|
+
}, exports.resetIdCounter = function() {
|
|
754
|
+
e = 0;
|
|
755
|
+
}, exports.resolveElement = a;
|
|
756
|
+
});
|
|
757
|
+
/*! End of @composy/kanban-core | Powered by @composy/builder */
|
|
758
|
+
//# sourceMappingURL=index.min.js.map
|