@designbasekorea/ui-wc 0.5.5 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/dist/chunks/lottie-e273b936.js +17032 -0
  2. package/dist/esm/components/animation-background/canvas-controller.js +121 -1
  3. package/dist/esm/components/animation-background/controls.js +180 -1
  4. package/dist/esm/components/animation-background/graphite-controller.js +196 -1
  5. package/dist/esm/components/animation-background/graphite-presets.js +42 -1
  6. package/dist/esm/components/animation-background/graphite-shader.js +111 -1
  7. package/dist/esm/components/animation-background/layers.js +369 -1
  8. package/dist/esm/components/animation-background/lumina-presets.js +53 -1
  9. package/dist/esm/components/animation-background/lumina-shaders.js +181 -1
  10. package/dist/esm/components/animation-background/lumina-utils.js +60 -1
  11. package/dist/esm/components/animation-background/particle-defaults.js +36 -1
  12. package/dist/esm/components/animation-background/renderers.js +167 -1
  13. package/dist/esm/components/animation-background/shader-controller.js +154 -1
  14. package/dist/esm/components/color-picker-utils.js +134 -1
  15. package/dist/esm/components/date-picker-utils.js +140 -1
  16. package/dist/esm/components/db-accordion.js +304 -1
  17. package/dist/esm/components/db-ad-banner.js +455 -1
  18. package/dist/esm/components/db-alert.js +149 -1
  19. package/dist/esm/components/db-animation-background.js +486 -1
  20. package/dist/esm/components/db-animation-text.js +601 -1
  21. package/dist/esm/components/db-app-badge.js +88 -1
  22. package/dist/esm/components/db-audio-player.js +752 -1
  23. package/dist/esm/components/db-avatar.js +188 -1
  24. package/dist/esm/components/db-backdrop.js +139 -1
  25. package/dist/esm/components/db-badge.js +91 -1
  26. package/dist/esm/components/db-banner.js +188 -1
  27. package/dist/esm/components/db-bottom-navigation.js +146 -1
  28. package/dist/esm/components/db-bottom-sheet.js +481 -1
  29. package/dist/esm/components/db-breadcrumbs.js +329 -1
  30. package/dist/esm/components/db-button.js +316 -1
  31. package/dist/esm/components/db-calendar.js +675 -1
  32. package/dist/esm/components/db-card.js +265 -1
  33. package/dist/esm/components/db-carousel.js +264 -1
  34. package/dist/esm/components/db-checkbox.js +202 -1
  35. package/dist/esm/components/db-chip.js +104 -1
  36. package/dist/esm/components/db-code-block.js +304 -1
  37. package/dist/esm/components/db-color-picker.js +746 -1
  38. package/dist/esm/components/db-confirm.js +191 -1
  39. package/dist/esm/components/db-container.js +39 -1
  40. package/dist/esm/components/db-context-menu.js +265 -1
  41. package/dist/esm/components/db-countdown.js +146 -1
  42. package/dist/esm/components/db-cursor-follower.js +173 -1
  43. package/dist/esm/components/db-date-picker.js +837 -1
  44. package/dist/esm/components/db-divider.js +66 -1
  45. package/dist/esm/components/db-drawer.js +245 -1
  46. package/dist/esm/components/db-dropdown.js +493 -1
  47. package/dist/esm/components/db-dropzone.js +266 -1
  48. package/dist/esm/components/db-empty-state.js +97 -1
  49. package/dist/esm/components/db-file-uploader.js +256 -1
  50. package/dist/esm/components/db-floating-action-button.js +139 -1
  51. package/dist/esm/components/db-footer.js +385 -1
  52. package/dist/esm/components/db-form.js +566 -1
  53. package/dist/esm/components/db-gantt.js +882 -1
  54. package/dist/esm/components/db-gradient.js +133 -1
  55. package/dist/esm/components/db-grid-background.js +909 -1
  56. package/dist/esm/components/db-grid.js +73 -1
  57. package/dist/esm/components/db-icons.js +82 -1
  58. package/dist/esm/components/db-image-list.js +214 -1
  59. package/dist/esm/components/db-image-placeholder.js +96 -1
  60. package/dist/esm/components/db-image.js +289 -1
  61. package/dist/esm/components/db-indicator.js +517 -1
  62. package/dist/esm/components/db-input.js +420 -1
  63. package/dist/esm/components/db-kanban.js +538 -1
  64. package/dist/esm/components/db-label.js +66 -1
  65. package/dist/esm/components/db-lightbox.js +372 -1
  66. package/dist/esm/components/db-list.js +340 -1
  67. package/dist/esm/components/db-logo-svg.js +72 -1
  68. package/dist/esm/components/db-logo.js +106 -1
  69. package/dist/esm/components/db-lottie.js +109 -1
  70. package/dist/esm/components/db-markdown-editor.js +458 -1
  71. package/dist/esm/components/db-marquee.js +402 -1
  72. package/dist/esm/components/db-masonry.js +94 -1
  73. package/dist/esm/components/db-menu-item.js +211 -1
  74. package/dist/esm/components/db-modal.js +522 -1
  75. package/dist/esm/components/db-navbar.js +680 -1
  76. package/dist/esm/components/db-onboarding-modal.js +322 -1
  77. package/dist/esm/components/db-page-header.js +312 -1
  78. package/dist/esm/components/db-pagination.js +271 -1
  79. package/dist/esm/components/db-popover.js +580 -1
  80. package/dist/esm/components/db-progress-step.js +166 -1
  81. package/dist/esm/components/db-progress.js +171 -1
  82. package/dist/esm/components/db-progressbar.js +72 -1
  83. package/dist/esm/components/db-radio.js +172 -1
  84. package/dist/esm/components/db-random-gradient.js +121 -1
  85. package/dist/esm/components/db-range-slider.js +811 -1
  86. package/dist/esm/components/db-rating.js +325 -1
  87. package/dist/esm/components/db-reorder.js +286 -1
  88. package/dist/esm/components/db-resizable-panels.js +282 -1
  89. package/dist/esm/components/db-scroll-area.js +126 -1
  90. package/dist/esm/components/db-search-bar.js +668 -1
  91. package/dist/esm/components/db-section-app-download.js +101 -1
  92. package/dist/esm/components/db-section-contact.js +102 -1
  93. package/dist/esm/components/db-section-cta.js +150 -1
  94. package/dist/esm/components/db-section-faq.js +120 -1
  95. package/dist/esm/components/db-section-feature-grid.js +137 -1
  96. package/dist/esm/components/db-section-feature.js +179 -1
  97. package/dist/esm/components/db-section-hero.js +243 -1
  98. package/dist/esm/components/db-section-logo-cloud.js +138 -1
  99. package/dist/esm/components/db-section-pricing.js +166 -1
  100. package/dist/esm/components/db-section-stats.js +129 -1
  101. package/dist/esm/components/db-section-testimonials.js +252 -1
  102. package/dist/esm/components/db-section.js +385 -1
  103. package/dist/esm/components/db-segment-control.js +231 -1
  104. package/dist/esm/components/db-select.js +571 -1
  105. package/dist/esm/components/db-share-icons.js +40 -1
  106. package/dist/esm/components/db-share.js +489 -1
  107. package/dist/esm/components/db-sidebar.js +1068 -1
  108. package/dist/esm/components/db-skeleton.js +94 -1
  109. package/dist/esm/components/db-spinner.js +102 -1
  110. package/dist/esm/components/db-split-view.js +297 -1
  111. package/dist/esm/components/db-stack.js +46 -1
  112. package/dist/esm/components/db-stat.js +218 -1
  113. package/dist/esm/components/db-stepper.js +398 -1
  114. package/dist/esm/components/db-table.js +412 -1
  115. package/dist/esm/components/db-tabs.js +335 -1
  116. package/dist/esm/components/db-testimonial.js +162 -1
  117. package/dist/esm/components/db-textarea.js +233 -1
  118. package/dist/esm/components/db-time-picker.js +523 -1
  119. package/dist/esm/components/db-timeline.js +174 -1
  120. package/dist/esm/components/db-toast.js +158 -1
  121. package/dist/esm/components/db-toggle.js +162 -1
  122. package/dist/esm/components/db-toolbar.js +94 -1
  123. package/dist/esm/components/db-tooltip.js +440 -1
  124. package/dist/esm/components/db-top-banner.js +121 -1
  125. package/dist/esm/components/db-tutorial.js +324 -1
  126. package/dist/esm/components/db-video-player.js +503 -1
  127. package/dist/esm/components/db-youtube-player.js +194 -1
  128. package/dist/esm/components/highlight-code.js +65 -1
  129. package/dist/esm/components/input-validation.js +50 -1
  130. package/dist/esm/components/marketing-block-host-utils.js +9 -1
  131. package/dist/esm/components/number-scrub.js +117 -1
  132. package/dist/esm/components/random-gradient-palettes.js +67 -1
  133. package/dist/esm/components/section-block-header-utils.js +48 -1
  134. package/dist/esm/helpers/confirm.js +52 -1
  135. package/dist/esm/helpers/toast.js +73 -1
  136. package/dist/esm/index.js +111 -1
  137. package/dist/esm/node_modules/prismjs/components/prism-bash.js +235 -1
  138. package/dist/esm/node_modules/prismjs/components/prism-clike.js +31 -1
  139. package/dist/esm/node_modules/prismjs/components/prism-css.js +64 -1
  140. package/dist/esm/node_modules/prismjs/components/prism-javascript.js +172 -1
  141. package/dist/esm/node_modules/prismjs/components/prism-json.js +27 -1
  142. package/dist/esm/node_modules/prismjs/components/prism-jsx.js +143 -1
  143. package/dist/esm/node_modules/prismjs/components/prism-markup.js +186 -1
  144. package/dist/esm/node_modules/prismjs/components/prism-scss.js +81 -1
  145. package/dist/esm/node_modules/prismjs/components/prism-tsx.js +15 -1
  146. package/dist/esm/node_modules/prismjs/components/prism-typescript.js +60 -1
  147. package/dist/index.cjs +55414 -1
  148. package/dist/index.css +1 -1
  149. package/dist/index.esm.js +38284 -1
  150. package/dist/react/chunks/lottie-72a4c6d4.js +17032 -0
  151. package/dist/react/index.cjs +56503 -1
  152. package/dist/react/index.esm.js +39373 -1
  153. package/package.json +1 -1
  154. package/dist/chunks/lottie-2637aa0f.js +0 -1
  155. package/dist/react/chunks/lottie-14e7addb.js +0 -1
@@ -1 +1,882 @@
1
- import{createDbIcon as e}from"./db-icons.js";const t=["day","week"],a=["#86efac","#fde047","#93c5fd","#c4b5fd","#f9a8d4","#5eead4","#fdba74","#a5b4fc"],s=["일","월","화","수","목","금","토"],r=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];function parseDate(e){const[t,a,s]=e.split("-").map(Number);return new Date(t,a-1,s)}function toDateKey(e){return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`}function addDays(e,t){const a=new Date(e);return a.setDate(a.getDate()+t),a}function daysBetween(e,t){const a=Date.UTC(e.getFullYear(),e.getMonth(),e.getDate()),s=Date.UTC(t.getFullYear(),t.getMonth(),t.getDate());return Math.round((s-a)/864e5)}function clampDate(e,t,a){return e<t?new Date(t):e>a?new Date(a):e}class DbGantt extends HTMLElement{constructor(){super(...arguments),this.collapsedGroups=new Set,this.dragState=null,this.reorderState=null,this.reorderDropIndex=null,this.suppressRender=!1,this.didDrag=!1,this.handlePointerMove=e=>{if(this.reorderState&&e.pointerId===this.reorderState.pointerId)return void this.updateReorderDropTarget(e.clientY);if(!this.dragState||e.pointerId!==this.dragState.pointerId)return;const a=this.getOption("scale",t,"day"),s=this.getNumber("cell-width","week"===a?72:44),{start:r,end:n}=this.getViewRange(),i=Math.round((e.clientX-this.dragState.startX)/s);0!==i&&(this.didDrag=!0);let d=new Date(this.dragState.originalStart),o=new Date(this.dragState.originalEnd);if("move"===this.dragState.mode?(d=addDays(this.dragState.originalStart,i),o=addDays(this.dragState.originalEnd,i)):"resize-start"===this.dragState.mode?(d=addDays(this.dragState.originalStart,i),d>o&&(d=new Date(o))):(o=addDays(this.dragState.originalEnd,i),o<d&&(o=new Date(d))),d=clampDate(d,r,n),o=clampDate(o,r,n),d>o)return;const l=this.items.find(e=>e.id===this.dragState.itemId);l&&this.applyBarGeometry(this.dragState.barEl,{...l,startDate:toDateKey(d),endDate:toDateKey(o)},r,s)},this.handlePointerUp=e=>{if(this.reorderState&&e.pointerId===this.reorderState.pointerId)return void this.finishReorderDrag(e);if(!this.dragState||e.pointerId!==this.dragState.pointerId)return;const a=this.getOption("scale",t,"day"),s=this.getNumber("cell-width","week"===a?72:44),{start:r,end:n}=this.getViewRange(),i=Math.round((e.clientX-this.dragState.startX)/s);let d=new Date(this.dragState.originalStart),o=new Date(this.dragState.originalEnd);const l=this.dragState.mode,c=this.dragState.itemId;"move"===l?(d=addDays(this.dragState.originalStart,i),o=addDays(this.dragState.originalEnd,i)):"resize-start"===l?(d=addDays(this.dragState.originalStart,i),d>o&&(d=new Date(o))):(o=addDays(this.dragState.originalEnd,i),o<d&&(o=new Date(d))),d=clampDate(d,r,n),o=clampDate(o,r,n),this.dragState.barEl.releasePointerCapture(e.pointerId),this.dragState.barEl.classList.remove("designbase-wc-gantt__bar--dragging"),this.dragState=null;const g=this.items.find(e=>e.id===c);if(!g)return;const p=toDateKey(d),h=toDateKey(o);p===g.startDate&&h===g.endDate||(this.updateItem(c,{startDate:p,endDate:h},"move"===l?"move":"resize"),this.render())}}static get observedAttributes(){return["groups","rows","items","start-date","end-date","today","scale","cell-width","show-weekends","show-today","sidebar-label","full-width","disabled","draggable","reorderable","locale"]}get groups(){return this.parseJson("groups",[])}set groups(e){this.setAttribute("groups","string"==typeof e?e:JSON.stringify(e))}get rows(){return this.parseJson("rows",[])}set rows(e){this.setAttribute("rows","string"==typeof e?e:JSON.stringify(e))}get items(){return this.parseJson("items",[])}set items(e){this.setAttribute("items","string"==typeof e?e:JSON.stringify(e))}connectedCallback(){this.setAttribute("role","region"),this.setAttribute("aria-label","프로젝트 타임라인"),this.addEventListener("pointermove",this.handlePointerMove),this.addEventListener("pointerup",this.handlePointerUp),this.addEventListener("pointercancel",this.handlePointerUp),this.render()}disconnectedCallback(){this.removeEventListener("pointermove",this.handlePointerMove),this.removeEventListener("pointerup",this.handlePointerUp),this.removeEventListener("pointercancel",this.handlePointerUp)}attributeChangedCallback(){!this.isConnected||this.suppressRender||this.dragState||this.reorderState||this.render()}parseJson(e,t){try{return JSON.parse(this.getAttribute(e)||"null")??t}catch{return t}}getBool(e,t=!1){if(!this.hasAttribute(e))return t;const a=this.getAttribute(e);return""===a||"true"===a}getNumber(e,t){const a=Number(this.getAttribute(e));return Number.isFinite(a)&&a>0?a:t}getOption(e,t,a){const s=this.getAttribute(e);return s&&t.includes(s)?s:a}getLocale(){return this.getAttribute("locale")||"ko-KR"}getWeekdays(){return this.getLocale().startsWith("ko")?s:r}getViewRange(){const e=this.getAttribute("start-date"),t=this.getAttribute("end-date");if(e&&t)return{start:parseDate(e),end:parseDate(t)};const a=this.items.flatMap(e=>[parseDate(e.startDate),parseDate(e.endDate)]);if(0===a.length){const e=new Date,t=new Date(e.getFullYear(),e.getMonth(),1);return{start:t,end:addDays(t,29)}}const s=new Date(Math.min(...a.map(e=>e.getTime()))),r=new Date(Math.max(...a.map(e=>e.getTime())));return{start:addDays(s,-2),end:addDays(r,2)}}getTodayDate(){const e=this.getAttribute("today");return e?parseDate(e):new Date}getDateColumns(e,t){const a=daysBetween(e,t)+1;return Array.from({length:a},(t,a)=>addDays(e,a))}getVisibleRows(){const e=this.groups,t=this.rows;if(0===e.length)return t.map(e=>({type:"row",row:e}));const a=[];for(const s of e)a.push({type:"group",group:s}),this.collapsedGroups.has(s.id)||t.filter(e=>e.groupId===s.id).forEach(e=>a.push({type:"row",row:e}));return t.filter(t=>!t.groupId||!e.some(e=>e.id===t.groupId)).forEach(e=>a.push({type:"row",row:e})),a}getMonthSegments(e){const t=[];let a="",s=0;for(const r of e){const e=`${r.getFullYear()}-${r.getMonth()}`;e!==a?(s>0&&t.push({label:a,span:s}),a=e,s=1):s+=1}return s>0&&t.push({label:a,span:s}),t.map(e=>{const[t,a]=e.label.split("-").map(Number);return{label:new Date(t,a,1).toLocaleDateString(this.getLocale(),{year:"numeric",month:"long"}),span:e.span}})}getInitials(e){return e.initials?e.initials:e.name.split(/\s+/).map(e=>e[0]).join("").slice(0,2).toUpperCase()}updateItem(e,t,a){const s=this.items.map(a=>a.id===e?{...a,...t}:a);this.emitItemsChange(s,a,e)}emitItemsChange(e,t,a){this.suppressRender=!0,this.items=e,this.suppressRender=!1;const s=e.find(e=>e.id===a);this.dispatchEvent(new CustomEvent("db-item-change",{detail:{item:s,items:e,action:t},bubbles:!0,composed:!0})),this.dispatchEvent(new CustomEvent("db-change",{detail:{groups:this.groups,rows:this.rows,items:e},bubbles:!0,composed:!0}))}applyBarGeometry(e,t,a,s){const r=parseDate(t.startDate),n=parseDate(t.endDate),i=daysBetween(a,r),d=daysBetween(r,n)+1;e.style.left=i*s+2+"px",e.style.width=`${Math.max(d*s-4,s-4)}px`}startBarDrag(e,t,a,s){!this.getBool("draggable",!0)||this.getBool("disabled")||t.disabled||(e.preventDefault(),e.stopPropagation(),this.didDrag=!1,s.setPointerCapture(e.pointerId),s.classList.add("designbase-wc-gantt__bar--dragging"),this.dragState={itemId:t.id,mode:a,pointerId:e.pointerId,startX:e.clientX,originalStart:parseDate(t.startDate),originalEnd:parseDate(t.endDate),barEl:s})}canReorder(){return this.getBool("reorderable",!0)&&!this.getBool("disabled")}createDragHandle(t,a,s){const r=document.createElement("span");return r.className="designbase-wc-gantt__drag-handle",r.setAttribute("role","button"),r.setAttribute("aria-label","group"===t?"그룹 순서 변경":"행 순서 변경"),r.append(e("grip-vertical",12)),this.canReorder()?(r.addEventListener("pointerdown",e=>{e.preventDefault(),e.stopPropagation(),this.startReorderDrag(e,t,a,s,r)}),r):r}getGroupIndex(e){return this.groups.findIndex(t=>t.id===e)}getRowScope(e){return this.rows.filter(t=>(t.groupId||"")===(e||""))}getRowIndexInScope(e,t){return this.getRowScope(t).findIndex(t=>t.id===e)}getReorderTargets(e,t){return Array.from(this.querySelectorAll("group"===e?".designbase-wc-gantt__label-cell--group":`.designbase-wc-gantt__label-cell--row[data-group-id="${t||""}"]`))}startReorderDrag(e,t,a,s,r){const n="group"===t?this.getGroupIndex(a):this.getRowIndexInScope(a,s);n<0||(r.setPointerCapture(e.pointerId),this.reorderState={kind:t,id:a,groupId:s,pointerId:e.pointerId,startY:e.clientY,fromIndex:n,captureEl:r},this.classList.add("designbase-wc-gantt--reordering"),this.updateReorderDropTarget(e.clientY))}updateReorderDropTarget(e){if(!this.reorderState)return;const t=this.getReorderTargets(this.reorderState.kind,this.reorderState.groupId);let a=null;t.forEach((t,s)=>{const r=t.getBoundingClientRect(),n=r.top+r.height/2,i=e>=r.top&&e<=r.bottom,d=e>n;t.classList.toggle("designbase-wc-gantt__label-cell--drag-over",i),t.classList.toggle("designbase-wc-gantt__label-cell--drag-over-before",i&&!d),t.classList.toggle("designbase-wc-gantt__label-cell--drag-over-after",i&&d),i&&(a=d?s+1:s)}),this.reorderDropIndex=a}clearReorderIndicators(){this.querySelectorAll(".designbase-wc-gantt__label-cell--drag-over").forEach(e=>{e.classList.remove("designbase-wc-gantt__label-cell--drag-over","designbase-wc-gantt__label-cell--drag-over-before","designbase-wc-gantt__label-cell--drag-over-after")})}finishReorderDrag(e){if(!this.reorderState)return;const{kind:t,id:a,groupId:s,fromIndex:r,captureEl:n}=this.reorderState;try{n.releasePointerCapture(this.reorderState.pointerId)}catch{}const i=this.reorderDropIndex;if(this.reorderState=null,this.reorderDropIndex=null,this.classList.remove("designbase-wc-gantt--reordering"),this.clearReorderIndicators(),null===i)return;const d=i>r?i-1:i;d<0||d===r||("group"===t?this.reorderGroups(r,d,a):this.reorderRows(r,d,a,s))}emitStructureChange(e,t,a,s){this.suppressRender=!0,this.groups=e,this.rows=t,this.suppressRender=!1,this.dispatchEvent(new CustomEvent(s,{detail:{...a,groups:e,rows:t},bubbles:!0,composed:!0})),this.dispatchEvent(new CustomEvent("db-change",{detail:{groups:e,rows:t,items:this.items},bubbles:!0,composed:!0})),this.render()}reorderGroups(e,t,a){const s=[...this.groups],[r]=s.splice(e,1);r&&(s.splice(t,0,r),this.emitStructureChange(s,this.rows,{groupId:a,fromIndex:e,toIndex:t},"db-group-reorder"))}reorderRows(e,t,a,s){const r=this.getRowScope(s),n=[...r],[i]=n.splice(e,1);if(!i)return;n.splice(t,0,i);const d=new Set(r.map(e=>e.id)),o=[];let l=!1;for(const e of this.rows)d.has(e.id)?l||(o.push(...n),l=!0):o.push(e);l||o.push(...n),this.emitStructureChange(this.groups,o,{rowId:a,groupId:s,fromIndex:e,toIndex:t},"db-row-reorder")}createGroupBadge(e){const t=document.createElement("db-badge");return t.className="designbase-wc-gantt__group-badge",t.setAttribute("badge-style","dot"),t.setAttribute("size","s"),t.setAttribute("variant","primary"),t.setAttribute("aria-hidden","true"),e&&t.style.setProperty("--db-gantt-badge-color",e),t}createGroupChevron(t){const a=document.createElement("span");return a.className=["designbase-wc-gantt__group-chevron",t?"designbase-wc-gantt__group-chevron--collapsed":""].filter(Boolean).join(" "),a.append(e("chevron-down",12)),a}createAssignee(e){const t=document.createElement("span");if(t.className="designbase-wc-gantt__assignee",t.title=e.name,e.avatar){const a=document.createElement("img");a.src=e.avatar,a.alt=e.name,t.appendChild(a)}else t.textContent=this.getInitials(e);return t}createStatusIcon(e){const t=document.createElement("span");return t.className=`designbase-wc-gantt__status designbase-wc-gantt__status--${e}`,t.setAttribute("aria-hidden","true"),t.textContent="completed"===e?"✓":"!",t}deriveSummaryColor(e,t){if(0===e.length)return t||a[0];const s=e.filter(e=>e.color);if(0===s.length)return t||a[0];return s.reduce((e,t)=>{const a=daysBetween(parseDate(e.startDate),parseDate(e.endDate));return daysBetween(parseDate(t.startDate),parseDate(t.endDate))>a?t:e}).color||t||a[0]}getGroupSummary(e,t){const a=this.rows.filter(t=>t.groupId===e),s=this.items.filter(e=>a.some(t=>t.id===e.rowId));if(0===s.length)return null;const r=s.map(e=>parseDate(e.startDate).getTime()),n=s.map(e=>parseDate(e.endDate).getTime()),i=s.map(e=>e.progress).filter(e=>"number"==typeof e);return{startDate:toDateKey(new Date(Math.min(...r))),endDate:toDateKey(new Date(Math.max(...n))),color:this.deriveSummaryColor(s,t),progress:i.length>0?i.reduce((e,t)=>e+t,0)/i.length:0,count:s.length}}applyBarTone(e,t){const a=function deriveBarTone(e){return{base:`color-mix(in srgb, ${e} 42%, white)`,progress:`color-mix(in srgb, ${e} 58%, white)`}}(t);e.style.setProperty("--db-gantt-bar-color",t),e.style.setProperty("--db-gantt-bar-bg",a.base),e.style.setProperty("--db-gantt-bar-progress-bg",a.progress)}createGroupSummaryBar(e,t,a,s){const r=clampDate(parseDate(e.startDate),t,a),n=clampDate(parseDate(e.endDate),t,a);if(n<t||r>a)return null;const i=daysBetween(t,r),d=daysBetween(r,n)+1,o=Math.min(100,Math.max(0,e.progress)),l=document.createElement("div");if(l.className="designbase-wc-gantt__group-summary",l.setAttribute("aria-hidden","true"),l.title=`${e.count}개 태스크`,this.applyBarTone(l,e.color),l.style.left=i*s+2+"px",l.style.width=`${Math.max(d*s-4,s-4)}px`,o>0){const e=document.createElement("span");e.className="designbase-wc-gantt__group-summary-progress",e.style.width=`${o}%`,l.appendChild(e)}return l}createBar(e,t,s,r){const n=e.color||a[r%a.length],i=document.createElement("div");i.className="designbase-wc-gantt__bar",i.setAttribute("role","button"),i.setAttribute("tabindex",e.disabled?"-1":"0"),i.setAttribute("aria-label",`${e.title}, ${e.startDate} – ${e.endDate}`),this.applyBarTone(i,n),e.disabled&&i.classList.add("designbase-wc-gantt__bar--disabled"),this.applyBarGeometry(i,e,t,s);const d="number"==typeof e.progress?Math.min(100,Math.max(0,e.progress)):0;if(d>0){const e=document.createElement("span");e.className="designbase-wc-gantt__bar-progress",e.style.width=`${d}%`,i.appendChild(e)}const o=document.createElement("div");o.className="designbase-wc-gantt__bar-body","delayed"!==e.status&&"at-risk"!==e.status||o.appendChild(this.createStatusIcon(e.status));const l=document.createElement("span");l.className="designbase-wc-gantt__bar-label",l.textContent=e.title,o.appendChild(l),e.assignee&&o.appendChild(this.createAssignee(e.assignee));const c=document.createElement("span");c.className="designbase-wc-gantt__bar-handle designbase-wc-gantt__bar-handle--start",c.setAttribute("aria-hidden","true");const g=document.createElement("span");return g.className="designbase-wc-gantt__bar-handle designbase-wc-gantt__bar-handle--end",g.setAttribute("aria-hidden","true"),i.append(c,o,g),e.disabled||!this.getBool("draggable",!0)||this.getBool("disabled")||(c.addEventListener("pointerdown",t=>this.startBarDrag(t,e,"resize-start",i)),g.addEventListener("pointerdown",t=>this.startBarDrag(t,e,"resize-end",i)),o.addEventListener("pointerdown",t=>{t.target.closest(".designbase-wc-gantt__bar-handle")||this.startBarDrag(t,e,"move",i)})),i.addEventListener("click",t=>{this.dragState||this.didDrag?this.didDrag=!1:(t.stopPropagation(),this.dispatchEvent(new CustomEvent("db-item-click",{detail:{item:e},bubbles:!0,composed:!0})))}),i}toggleGroup(e){this.collapsedGroups.has(e)?this.collapsedGroups.delete(e):this.collapsedGroups.add(e),this.dispatchEvent(new CustomEvent("db-group-toggle",{detail:{groupId:e,collapsed:this.collapsedGroups.has(e)},bubbles:!0,composed:!0})),this.render()}render(){const e=this.items,s=this.getOption("scale",t,"day"),r=this.getNumber("cell-width","week"===s?72:44),n=this.getBool("show-weekends",!0),i=this.getBool("show-today",!0),d=this.getAttribute("sidebar-label")||"할 일",o=this.getBool("disabled"),l=this.getBool("full-width",!0),c=this.getWeekdays(),{start:g,end:p}=this.getViewRange(),h=this.getDateColumns(g,p),u=toDateKey(this.getTodayDate()),m=h.findIndex(e=>toDateKey(e)===u),b=this.getVisibleRows(),w=h.length*r,D=document.createElement("div");D.className=["designbase-wc-gantt",l?"designbase-wc-gantt--full-width":"",o?"designbase-wc-gantt--disabled":"",this.getBool("draggable",!0)?"designbase-wc-gantt--draggable":""].filter(Boolean).join(" ");const y=document.createElement("div");y.className="designbase-wc-gantt__viewport";const _=document.createElement("div");_.className="designbase-wc-gantt__grid",_.style.setProperty("--db-gantt-chart-width",`${w}px`),_.style.setProperty("--db-gantt-cell-width",`${r}px`);const f=document.createElement("div");f.className="designbase-wc-gantt__corner",f.textContent=d,_.appendChild(f);const v=document.createElement("div");v.className="designbase-wc-gantt__date-header";const E=document.createElement("div");E.className="designbase-wc-gantt__month-row";for(const e of this.getMonthSegments(h)){const t=document.createElement("div");t.className="designbase-wc-gantt__month-cell",t.style.width=e.span*r+"px",t.textContent=e.label,E.appendChild(t)}v.appendChild(E);const S=document.createElement("div");S.className="designbase-wc-gantt__day-row";for(const e of h){const t=toDateKey(e)===u,a=0===e.getDay()||6===e.getDay(),s=document.createElement("div");s.className=["designbase-wc-gantt__day-cell",t&&i?"designbase-wc-gantt__day-cell--today":"",a&&n?"designbase-wc-gantt__day-cell--weekend":""].filter(Boolean).join(" "),s.style.width=`${r}px`;const d=document.createElement("span");d.className="designbase-wc-gantt__day-num",d.textContent=String(e.getDate());const o=document.createElement("span");o.className="designbase-wc-gantt__day-name",o.textContent=c[e.getDay()],s.append(d,o),S.appendChild(s)}v.appendChild(S),_.appendChild(v);const C=new Map;e.forEach((e,t)=>{const s=C.get(e.rowId)||[];s.push({...e,color:e.color||a[t%a.length]}),C.set(e.rowId,s)});let I=0;for(const e of b){if("group"===e.type){const{group:t}=e,a=this.collapsedGroups.has(t.id),s=document.createElement("button");s.type="button",s.className="designbase-wc-gantt__label-cell designbase-wc-gantt__label-cell--group",s.setAttribute("aria-expanded",String(!a)),s.dataset.groupId=t.id;const i=document.createElement("span");i.className="designbase-wc-gantt__group-title",i.textContent=t.title,s.append(this.createDragHandle("group",t.id),this.createGroupChevron(a),this.createGroupBadge(t.color),i),s.addEventListener("click",e=>{e.target.closest(".designbase-wc-gantt__drag-handle")||this.toggleGroup(t.id)}),_.appendChild(s);const d=document.createElement("div");if(d.className="designbase-wc-gantt__track-cell designbase-wc-gantt__track-cell--group",a){const e=this.getGroupSummary(t.id,t.color);if(e){const t=document.createElement("div");t.className="designbase-wc-gantt__grid-bg",h.forEach(e=>{const a=document.createElement("div"),s=0===e.getDay()||6===e.getDay();a.className=["designbase-wc-gantt__grid-cell",s&&n?"designbase-wc-gantt__grid-cell--weekend":""].filter(Boolean).join(" "),a.style.width=`${r}px`,t.appendChild(a)}),d.appendChild(t);const a=document.createElement("div");a.className="designbase-wc-gantt__group-summary-wrap";const s=this.createGroupSummaryBar(e,g,p,r);s&&a.appendChild(s),d.appendChild(a)}}_.appendChild(d);continue}const{row:t}=e,a=document.createElement("button");a.type="button",a.className="designbase-wc-gantt__label-cell designbase-wc-gantt__label-cell--row",a.dataset.groupId=t.groupId||"",a.dataset.rowId=t.id;const s=document.createElement("span");s.className="designbase-wc-gantt__row-title",s.textContent=t.label,a.append(this.createDragHandle("row",t.id,t.groupId),s),a.addEventListener("click",e=>{e.target.closest(".designbase-wc-gantt__drag-handle")||this.dispatchEvent(new CustomEvent("db-row-click",{detail:{row:t},bubbles:!0,composed:!0}))}),_.appendChild(a);const i=document.createElement("div");i.className="designbase-wc-gantt__track-cell";const d=document.createElement("div");d.className="designbase-wc-gantt__grid-bg",h.forEach(e=>{const t=document.createElement("div"),a=0===e.getDay()||6===e.getDay();t.className=["designbase-wc-gantt__grid-cell",a&&n?"designbase-wc-gantt__grid-cell--weekend":""].filter(Boolean).join(" "),t.style.width=`${r}px`,d.appendChild(t)}),i.appendChild(d);const o=document.createElement("div");o.className="designbase-wc-gantt__bars",(C.get(t.id)||[]).forEach(e=>{const t=clampDate(parseDate(e.startDate),g,p),a=clampDate(parseDate(e.endDate),g,p);a<g||t>p||o.appendChild(this.createBar({...e,startDate:toDateKey(t),endDate:toDateKey(a)},g,r,I++))}),i.appendChild(o),_.appendChild(i)}if(y.appendChild(_),i&&m>=0){const e=document.createElement("div");e.className="designbase-wc-gantt__today-line",e.style.left=`calc(var(--db-gantt-label-width) + ${m*r+r/2}px)`,_.appendChild(e)}D.appendChild(y),this.replaceChildren(D)}}customElements.get("db-gantt")||customElements.define("db-gantt",DbGantt);export{DbGantt};
1
+ import { createDbIcon } from './db-icons.js';
2
+
3
+ const SCALES = ['day', 'week'];
4
+ const BAR_COLORS = ['#86efac', '#fde047', '#93c5fd', '#c4b5fd', '#f9a8d4', '#5eead4', '#fdba74', '#a5b4fc'];
5
+ const WEEKDAY_KO = ['일', '월', '화', '수', '목', '금', '토'];
6
+ const WEEKDAY_EN = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
7
+ function parseDate(value) {
8
+ const [year, month, day] = value.split('-').map(Number);
9
+ return new Date(year, month - 1, day);
10
+ }
11
+ function toDateKey(date) {
12
+ const year = date.getFullYear();
13
+ const month = String(date.getMonth() + 1).padStart(2, '0');
14
+ const day = String(date.getDate()).padStart(2, '0');
15
+ return `${year}-${month}-${day}`;
16
+ }
17
+ function addDays(date, days) {
18
+ const next = new Date(date);
19
+ next.setDate(next.getDate() + days);
20
+ return next;
21
+ }
22
+ function daysBetween(start, end) {
23
+ const startUtc = Date.UTC(start.getFullYear(), start.getMonth(), start.getDate());
24
+ const endUtc = Date.UTC(end.getFullYear(), end.getMonth(), end.getDate());
25
+ return Math.round((endUtc - startUtc) / (1000 * 60 * 60 * 24));
26
+ }
27
+ function clampDate(date, min, max) {
28
+ if (date < min)
29
+ return new Date(min);
30
+ if (date > max)
31
+ return new Date(max);
32
+ return date;
33
+ }
34
+ function deriveBarTone(color) {
35
+ return {
36
+ /** 바 전체 배경 — 숫자를 올리면 더 진해짐 */
37
+ base: `color-mix(in srgb, ${color} 42%, white)`,
38
+ /** 진행 구간 — base보다 살짝만 진하게 (black 대신 white 믹스로 연하게) */
39
+ progress: `color-mix(in srgb, ${color} 58%, white)`,
40
+ };
41
+ }
42
+ class DbGantt extends HTMLElement {
43
+ constructor() {
44
+ super(...arguments);
45
+ this.collapsedGroups = new Set();
46
+ this.dragState = null;
47
+ this.reorderState = null;
48
+ this.reorderDropIndex = null;
49
+ this.suppressRender = false;
50
+ this.didDrag = false;
51
+ this.handlePointerMove = (event) => {
52
+ if (this.reorderState && event.pointerId === this.reorderState.pointerId) {
53
+ this.updateReorderDropTarget(event.clientY);
54
+ return;
55
+ }
56
+ if (!this.dragState || event.pointerId !== this.dragState.pointerId)
57
+ return;
58
+ const scale = this.getOption('scale', SCALES, 'day');
59
+ const cellWidth = this.getNumber('cell-width', scale === 'week' ? 72 : 44);
60
+ const { start: viewStart, end: viewEnd } = this.getViewRange();
61
+ const dayDelta = Math.round((event.clientX - this.dragState.startX) / cellWidth);
62
+ if (dayDelta !== 0)
63
+ this.didDrag = true;
64
+ let nextStart = new Date(this.dragState.originalStart);
65
+ let nextEnd = new Date(this.dragState.originalEnd);
66
+ if (this.dragState.mode === 'move') {
67
+ nextStart = addDays(this.dragState.originalStart, dayDelta);
68
+ nextEnd = addDays(this.dragState.originalEnd, dayDelta);
69
+ }
70
+ else if (this.dragState.mode === 'resize-start') {
71
+ nextStart = addDays(this.dragState.originalStart, dayDelta);
72
+ if (nextStart > nextEnd)
73
+ nextStart = new Date(nextEnd);
74
+ }
75
+ else {
76
+ nextEnd = addDays(this.dragState.originalEnd, dayDelta);
77
+ if (nextEnd < nextStart)
78
+ nextEnd = new Date(nextStart);
79
+ }
80
+ nextStart = clampDate(nextStart, viewStart, viewEnd);
81
+ nextEnd = clampDate(nextEnd, viewStart, viewEnd);
82
+ if (nextStart > nextEnd)
83
+ return;
84
+ const item = this.items.find((entry) => entry.id === this.dragState.itemId);
85
+ if (!item)
86
+ return;
87
+ this.applyBarGeometry(this.dragState.barEl, { ...item, startDate: toDateKey(nextStart), endDate: toDateKey(nextEnd) }, viewStart, cellWidth);
88
+ };
89
+ this.handlePointerUp = (event) => {
90
+ if (this.reorderState && event.pointerId === this.reorderState.pointerId) {
91
+ this.finishReorderDrag(event);
92
+ return;
93
+ }
94
+ if (!this.dragState || event.pointerId !== this.dragState.pointerId)
95
+ return;
96
+ const scale = this.getOption('scale', SCALES, 'day');
97
+ const cellWidth = this.getNumber('cell-width', scale === 'week' ? 72 : 44);
98
+ const { start: viewStart, end: viewEnd } = this.getViewRange();
99
+ const dayDelta = Math.round((event.clientX - this.dragState.startX) / cellWidth);
100
+ let nextStart = new Date(this.dragState.originalStart);
101
+ let nextEnd = new Date(this.dragState.originalEnd);
102
+ const mode = this.dragState.mode;
103
+ const itemId = this.dragState.itemId;
104
+ if (mode === 'move') {
105
+ nextStart = addDays(this.dragState.originalStart, dayDelta);
106
+ nextEnd = addDays(this.dragState.originalEnd, dayDelta);
107
+ }
108
+ else if (mode === 'resize-start') {
109
+ nextStart = addDays(this.dragState.originalStart, dayDelta);
110
+ if (nextStart > nextEnd)
111
+ nextStart = new Date(nextEnd);
112
+ }
113
+ else {
114
+ nextEnd = addDays(this.dragState.originalEnd, dayDelta);
115
+ if (nextEnd < nextStart)
116
+ nextEnd = new Date(nextStart);
117
+ }
118
+ nextStart = clampDate(nextStart, viewStart, viewEnd);
119
+ nextEnd = clampDate(nextEnd, viewStart, viewEnd);
120
+ this.dragState.barEl.releasePointerCapture(event.pointerId);
121
+ this.dragState.barEl.classList.remove('designbase-wc-gantt__bar--dragging');
122
+ this.dragState = null;
123
+ const originalItem = this.items.find((entry) => entry.id === itemId);
124
+ if (!originalItem)
125
+ return;
126
+ const startKey = toDateKey(nextStart);
127
+ const endKey = toDateKey(nextEnd);
128
+ if (startKey === originalItem.startDate && endKey === originalItem.endDate)
129
+ return;
130
+ this.updateItem(itemId, { startDate: startKey, endDate: endKey }, mode === 'move' ? 'move' : 'resize');
131
+ this.render();
132
+ };
133
+ }
134
+ static get observedAttributes() {
135
+ return [
136
+ 'groups',
137
+ 'rows',
138
+ 'items',
139
+ 'start-date',
140
+ 'end-date',
141
+ 'today',
142
+ 'scale',
143
+ 'cell-width',
144
+ 'show-weekends',
145
+ 'show-today',
146
+ 'sidebar-label',
147
+ 'full-width',
148
+ 'disabled',
149
+ 'draggable',
150
+ 'reorderable',
151
+ 'locale',
152
+ ];
153
+ }
154
+ get groups() {
155
+ return this.parseJson('groups', []);
156
+ }
157
+ set groups(value) {
158
+ this.setAttribute('groups', typeof value === 'string' ? value : JSON.stringify(value));
159
+ }
160
+ get rows() {
161
+ return this.parseJson('rows', []);
162
+ }
163
+ set rows(value) {
164
+ this.setAttribute('rows', typeof value === 'string' ? value : JSON.stringify(value));
165
+ }
166
+ get items() {
167
+ return this.parseJson('items', []);
168
+ }
169
+ set items(value) {
170
+ this.setAttribute('items', typeof value === 'string' ? value : JSON.stringify(value));
171
+ }
172
+ connectedCallback() {
173
+ this.setAttribute('role', 'region');
174
+ this.setAttribute('aria-label', '프로젝트 타임라인');
175
+ this.addEventListener('pointermove', this.handlePointerMove);
176
+ this.addEventListener('pointerup', this.handlePointerUp);
177
+ this.addEventListener('pointercancel', this.handlePointerUp);
178
+ this.render();
179
+ }
180
+ disconnectedCallback() {
181
+ this.removeEventListener('pointermove', this.handlePointerMove);
182
+ this.removeEventListener('pointerup', this.handlePointerUp);
183
+ this.removeEventListener('pointercancel', this.handlePointerUp);
184
+ }
185
+ attributeChangedCallback() {
186
+ if (this.isConnected && !this.suppressRender && !this.dragState && !this.reorderState) {
187
+ this.render();
188
+ }
189
+ }
190
+ parseJson(name, fallback) {
191
+ try {
192
+ const parsed = JSON.parse(this.getAttribute(name) || 'null');
193
+ return parsed ?? fallback;
194
+ }
195
+ catch {
196
+ return fallback;
197
+ }
198
+ }
199
+ getBool(name, fallback = false) {
200
+ if (!this.hasAttribute(name))
201
+ return fallback;
202
+ const value = this.getAttribute(name);
203
+ return value === '' || value === 'true';
204
+ }
205
+ getNumber(name, fallback) {
206
+ const value = Number(this.getAttribute(name));
207
+ return Number.isFinite(value) && value > 0 ? value : fallback;
208
+ }
209
+ getOption(name, values, fallback) {
210
+ const value = this.getAttribute(name);
211
+ return value && values.includes(value) ? value : fallback;
212
+ }
213
+ getLocale() {
214
+ return this.getAttribute('locale') || 'ko-KR';
215
+ }
216
+ getWeekdays() {
217
+ return this.getLocale().startsWith('ko') ? WEEKDAY_KO : WEEKDAY_EN;
218
+ }
219
+ getViewRange() {
220
+ const explicitStart = this.getAttribute('start-date');
221
+ const explicitEnd = this.getAttribute('end-date');
222
+ if (explicitStart && explicitEnd) {
223
+ return { start: parseDate(explicitStart), end: parseDate(explicitEnd) };
224
+ }
225
+ const dates = this.items.flatMap((item) => [parseDate(item.startDate), parseDate(item.endDate)]);
226
+ if (dates.length === 0) {
227
+ const today = new Date();
228
+ const start = new Date(today.getFullYear(), today.getMonth(), 1);
229
+ const end = addDays(start, 29);
230
+ return { start, end };
231
+ }
232
+ const min = new Date(Math.min(...dates.map((d) => d.getTime())));
233
+ const max = new Date(Math.max(...dates.map((d) => d.getTime())));
234
+ return { start: addDays(min, -2), end: addDays(max, 2) };
235
+ }
236
+ getTodayDate() {
237
+ const attr = this.getAttribute('today');
238
+ return attr ? parseDate(attr) : new Date();
239
+ }
240
+ getDateColumns(start, end) {
241
+ const total = daysBetween(start, end) + 1;
242
+ return Array.from({ length: total }, (_, index) => addDays(start, index));
243
+ }
244
+ getVisibleRows() {
245
+ const groups = this.groups;
246
+ const rows = this.rows;
247
+ if (groups.length === 0) {
248
+ return rows.map((row) => ({ type: 'row', row }));
249
+ }
250
+ const result = [];
251
+ for (const group of groups) {
252
+ result.push({ type: 'group', group });
253
+ if (this.collapsedGroups.has(group.id))
254
+ continue;
255
+ rows
256
+ .filter((row) => row.groupId === group.id)
257
+ .forEach((row) => result.push({ type: 'row', row }));
258
+ }
259
+ rows
260
+ .filter((row) => !row.groupId || !groups.some((group) => group.id === row.groupId))
261
+ .forEach((row) => result.push({ type: 'row', row }));
262
+ return result;
263
+ }
264
+ getMonthSegments(dates) {
265
+ const segments = [];
266
+ let currentKey = '';
267
+ let currentSpan = 0;
268
+ for (const date of dates) {
269
+ const key = `${date.getFullYear()}-${date.getMonth()}`;
270
+ if (key !== currentKey) {
271
+ if (currentSpan > 0) {
272
+ segments.push({ label: currentKey, span: currentSpan });
273
+ }
274
+ currentKey = key;
275
+ currentSpan = 1;
276
+ }
277
+ else {
278
+ currentSpan += 1;
279
+ }
280
+ }
281
+ if (currentSpan > 0) {
282
+ segments.push({ label: currentKey, span: currentSpan });
283
+ }
284
+ return segments.map((segment) => {
285
+ const [year, month] = segment.label.split('-').map(Number);
286
+ const label = new Date(year, month, 1).toLocaleDateString(this.getLocale(), {
287
+ year: 'numeric',
288
+ month: 'long',
289
+ });
290
+ return { label, span: segment.span };
291
+ });
292
+ }
293
+ getInitials(assignee) {
294
+ if (assignee.initials)
295
+ return assignee.initials;
296
+ return assignee.name
297
+ .split(/\s+/)
298
+ .map((part) => part[0])
299
+ .join('')
300
+ .slice(0, 2)
301
+ .toUpperCase();
302
+ }
303
+ updateItem(itemId, patch, action) {
304
+ const next = this.items.map((item) => item.id === itemId ? { ...item, ...patch } : item);
305
+ this.emitItemsChange(next, action, itemId);
306
+ }
307
+ emitItemsChange(items, action, itemId) {
308
+ this.suppressRender = true;
309
+ this.items = items;
310
+ this.suppressRender = false;
311
+ const item = items.find((entry) => entry.id === itemId);
312
+ this.dispatchEvent(new CustomEvent('db-item-change', {
313
+ detail: { item, items, action },
314
+ bubbles: true,
315
+ composed: true,
316
+ }));
317
+ this.dispatchEvent(new CustomEvent('db-change', {
318
+ detail: { groups: this.groups, rows: this.rows, items },
319
+ bubbles: true,
320
+ composed: true,
321
+ }));
322
+ }
323
+ applyBarGeometry(barEl, item, viewStart, cellWidth) {
324
+ const itemStart = parseDate(item.startDate);
325
+ const itemEnd = parseDate(item.endDate);
326
+ const offset = daysBetween(viewStart, itemStart);
327
+ const duration = daysBetween(itemStart, itemEnd) + 1;
328
+ const gap = 2;
329
+ barEl.style.left = `${offset * cellWidth + gap}px`;
330
+ barEl.style.width = `${Math.max(duration * cellWidth - gap * 2, cellWidth - gap * 2)}px`;
331
+ }
332
+ startBarDrag(event, item, mode, barEl) {
333
+ if (!this.getBool('draggable', true) || this.getBool('disabled') || item.disabled)
334
+ return;
335
+ event.preventDefault();
336
+ event.stopPropagation();
337
+ this.didDrag = false;
338
+ barEl.setPointerCapture(event.pointerId);
339
+ barEl.classList.add('designbase-wc-gantt__bar--dragging');
340
+ this.dragState = {
341
+ itemId: item.id,
342
+ mode,
343
+ pointerId: event.pointerId,
344
+ startX: event.clientX,
345
+ originalStart: parseDate(item.startDate),
346
+ originalEnd: parseDate(item.endDate),
347
+ barEl,
348
+ };
349
+ }
350
+ canReorder() {
351
+ return this.getBool('reorderable', true) && !this.getBool('disabled');
352
+ }
353
+ createDragHandle(kind, id, groupId) {
354
+ const handle = document.createElement('span');
355
+ handle.className = 'designbase-wc-gantt__drag-handle';
356
+ handle.setAttribute('role', 'button');
357
+ handle.setAttribute('aria-label', kind === 'group' ? '그룹 순서 변경' : '행 순서 변경');
358
+ handle.append(createDbIcon('grip-vertical', 12));
359
+ if (!this.canReorder())
360
+ return handle;
361
+ handle.addEventListener('pointerdown', (event) => {
362
+ event.preventDefault();
363
+ event.stopPropagation();
364
+ this.startReorderDrag(event, kind, id, groupId, handle);
365
+ });
366
+ return handle;
367
+ }
368
+ getGroupIndex(groupId) {
369
+ return this.groups.findIndex((group) => group.id === groupId);
370
+ }
371
+ getRowScope(groupId) {
372
+ return this.rows.filter((row) => (row.groupId || '') === (groupId || ''));
373
+ }
374
+ getRowIndexInScope(rowId, groupId) {
375
+ return this.getRowScope(groupId).findIndex((row) => row.id === rowId);
376
+ }
377
+ getReorderTargets(kind, groupId) {
378
+ return Array.from(this.querySelectorAll(kind === 'group'
379
+ ? '.designbase-wc-gantt__label-cell--group'
380
+ : `.designbase-wc-gantt__label-cell--row[data-group-id="${groupId || ''}"]`));
381
+ }
382
+ startReorderDrag(event, kind, id, groupId, handleEl) {
383
+ const fromIndex = kind === 'group' ? this.getGroupIndex(id) : this.getRowIndexInScope(id, groupId);
384
+ if (fromIndex < 0)
385
+ return;
386
+ handleEl.setPointerCapture(event.pointerId);
387
+ this.reorderState = {
388
+ kind,
389
+ id,
390
+ groupId,
391
+ pointerId: event.pointerId,
392
+ startY: event.clientY,
393
+ fromIndex,
394
+ captureEl: handleEl,
395
+ };
396
+ this.classList.add('designbase-wc-gantt--reordering');
397
+ this.updateReorderDropTarget(event.clientY);
398
+ }
399
+ updateReorderDropTarget(clientY) {
400
+ if (!this.reorderState)
401
+ return;
402
+ const targets = this.getReorderTargets(this.reorderState.kind, this.reorderState.groupId);
403
+ let nextDrop = null;
404
+ targets.forEach((el, index) => {
405
+ const rect = el.getBoundingClientRect();
406
+ const mid = rect.top + rect.height / 2;
407
+ const isOver = clientY >= rect.top && clientY <= rect.bottom;
408
+ const placeAfter = clientY > mid;
409
+ el.classList.toggle('designbase-wc-gantt__label-cell--drag-over', isOver);
410
+ el.classList.toggle('designbase-wc-gantt__label-cell--drag-over-before', isOver && !placeAfter);
411
+ el.classList.toggle('designbase-wc-gantt__label-cell--drag-over-after', isOver && placeAfter);
412
+ if (isOver) {
413
+ nextDrop = placeAfter ? index + 1 : index;
414
+ }
415
+ });
416
+ this.reorderDropIndex = nextDrop;
417
+ }
418
+ clearReorderIndicators() {
419
+ this.querySelectorAll('.designbase-wc-gantt__label-cell--drag-over').forEach((el) => {
420
+ el.classList.remove('designbase-wc-gantt__label-cell--drag-over', 'designbase-wc-gantt__label-cell--drag-over-before', 'designbase-wc-gantt__label-cell--drag-over-after');
421
+ });
422
+ }
423
+ finishReorderDrag(event) {
424
+ if (!this.reorderState)
425
+ return;
426
+ const { kind, id, groupId, fromIndex, captureEl } = this.reorderState;
427
+ try {
428
+ captureEl.releasePointerCapture(this.reorderState.pointerId);
429
+ }
430
+ catch {
431
+ /* ignore */
432
+ }
433
+ const dropIndex = this.reorderDropIndex;
434
+ this.reorderState = null;
435
+ this.reorderDropIndex = null;
436
+ this.classList.remove('designbase-wc-gantt--reordering');
437
+ this.clearReorderIndicators();
438
+ if (dropIndex === null)
439
+ return;
440
+ const adjustedDrop = dropIndex > fromIndex ? dropIndex - 1 : dropIndex;
441
+ if (adjustedDrop < 0 || adjustedDrop === fromIndex)
442
+ return;
443
+ if (kind === 'group') {
444
+ this.reorderGroups(fromIndex, adjustedDrop, id);
445
+ }
446
+ else {
447
+ this.reorderRows(fromIndex, adjustedDrop, id, groupId);
448
+ }
449
+ }
450
+ emitStructureChange(groups, rows, detail, eventName) {
451
+ this.suppressRender = true;
452
+ this.groups = groups;
453
+ this.rows = rows;
454
+ this.suppressRender = false;
455
+ this.dispatchEvent(new CustomEvent(eventName, {
456
+ detail: { ...detail, groups, rows },
457
+ bubbles: true,
458
+ composed: true,
459
+ }));
460
+ this.dispatchEvent(new CustomEvent('db-change', {
461
+ detail: { groups, rows, items: this.items },
462
+ bubbles: true,
463
+ composed: true,
464
+ }));
465
+ this.render();
466
+ }
467
+ reorderGroups(fromIndex, toIndex, groupId) {
468
+ const next = [...this.groups];
469
+ const [moved] = next.splice(fromIndex, 1);
470
+ if (!moved)
471
+ return;
472
+ next.splice(toIndex, 0, moved);
473
+ this.emitStructureChange(next, this.rows, { groupId, fromIndex, toIndex }, 'db-group-reorder');
474
+ }
475
+ reorderRows(fromIndex, toIndex, rowId, groupId) {
476
+ const scope = this.getRowScope(groupId);
477
+ const nextScope = [...scope];
478
+ const [moved] = nextScope.splice(fromIndex, 1);
479
+ if (!moved)
480
+ return;
481
+ nextScope.splice(toIndex, 0, moved);
482
+ const scopeIds = new Set(scope.map((row) => row.id));
483
+ const nextRows = [];
484
+ let scopeInserted = false;
485
+ for (const row of this.rows) {
486
+ if (!scopeIds.has(row.id)) {
487
+ nextRows.push(row);
488
+ continue;
489
+ }
490
+ if (!scopeInserted) {
491
+ nextRows.push(...nextScope);
492
+ scopeInserted = true;
493
+ }
494
+ }
495
+ if (!scopeInserted)
496
+ nextRows.push(...nextScope);
497
+ this.emitStructureChange(this.groups, nextRows, { rowId, groupId, fromIndex, toIndex }, 'db-row-reorder');
498
+ }
499
+ createGroupBadge(color) {
500
+ const badge = document.createElement('db-badge');
501
+ badge.className = 'designbase-wc-gantt__group-badge';
502
+ badge.setAttribute('badge-style', 'dot');
503
+ badge.setAttribute('size', 's');
504
+ badge.setAttribute('variant', 'primary');
505
+ badge.setAttribute('aria-hidden', 'true');
506
+ if (color)
507
+ badge.style.setProperty('--db-gantt-badge-color', color);
508
+ return badge;
509
+ }
510
+ createGroupChevron(collapsed) {
511
+ const chevron = document.createElement('span');
512
+ chevron.className = [
513
+ 'designbase-wc-gantt__group-chevron',
514
+ collapsed ? 'designbase-wc-gantt__group-chevron--collapsed' : '',
515
+ ]
516
+ .filter(Boolean)
517
+ .join(' ');
518
+ chevron.append(createDbIcon('chevron-down', 12));
519
+ return chevron;
520
+ }
521
+ createAssignee(assignee) {
522
+ const el = document.createElement('span');
523
+ el.className = 'designbase-wc-gantt__assignee';
524
+ el.title = assignee.name;
525
+ if (assignee.avatar) {
526
+ const img = document.createElement('img');
527
+ img.src = assignee.avatar;
528
+ img.alt = assignee.name;
529
+ el.appendChild(img);
530
+ }
531
+ else {
532
+ el.textContent = this.getInitials(assignee);
533
+ }
534
+ return el;
535
+ }
536
+ createStatusIcon(status) {
537
+ const el = document.createElement('span');
538
+ el.className = `designbase-wc-gantt__status designbase-wc-gantt__status--${status}`;
539
+ el.setAttribute('aria-hidden', 'true');
540
+ el.textContent = status === 'completed' ? '✓' : '!';
541
+ return el;
542
+ }
543
+ deriveSummaryColor(items, fallback) {
544
+ if (items.length === 0)
545
+ return fallback || BAR_COLORS[0];
546
+ const withColor = items.filter((item) => item.color);
547
+ if (withColor.length === 0)
548
+ return fallback || BAR_COLORS[0];
549
+ const longest = withColor.reduce((current, item) => {
550
+ const currentDays = daysBetween(parseDate(current.startDate), parseDate(current.endDate));
551
+ const itemDays = daysBetween(parseDate(item.startDate), parseDate(item.endDate));
552
+ return itemDays > currentDays ? item : current;
553
+ });
554
+ return longest.color || fallback || BAR_COLORS[0];
555
+ }
556
+ getGroupSummary(groupId, groupColor) {
557
+ const groupRows = this.rows.filter((row) => row.groupId === groupId);
558
+ const groupItems = this.items.filter((item) => groupRows.some((row) => row.id === item.rowId));
559
+ if (groupItems.length === 0)
560
+ return null;
561
+ const starts = groupItems.map((item) => parseDate(item.startDate).getTime());
562
+ const ends = groupItems.map((item) => parseDate(item.endDate).getTime());
563
+ const progressValues = groupItems
564
+ .map((item) => item.progress)
565
+ .filter((value) => typeof value === 'number');
566
+ return {
567
+ startDate: toDateKey(new Date(Math.min(...starts))),
568
+ endDate: toDateKey(new Date(Math.max(...ends))),
569
+ color: this.deriveSummaryColor(groupItems, groupColor),
570
+ progress: progressValues.length > 0
571
+ ? progressValues.reduce((sum, value) => sum + value, 0) / progressValues.length
572
+ : 0,
573
+ count: groupItems.length,
574
+ };
575
+ }
576
+ applyBarTone(el, color) {
577
+ const tone = deriveBarTone(color);
578
+ el.style.setProperty('--db-gantt-bar-color', color);
579
+ el.style.setProperty('--db-gantt-bar-bg', tone.base);
580
+ el.style.setProperty('--db-gantt-bar-progress-bg', tone.progress);
581
+ }
582
+ createGroupSummaryBar(summary, viewStart, viewEnd, cellWidth) {
583
+ const clampedStart = clampDate(parseDate(summary.startDate), viewStart, viewEnd);
584
+ const clampedEnd = clampDate(parseDate(summary.endDate), viewStart, viewEnd);
585
+ if (clampedEnd < viewStart || clampedStart > viewEnd)
586
+ return null;
587
+ const offset = daysBetween(viewStart, clampedStart);
588
+ const duration = daysBetween(clampedStart, clampedEnd) + 1;
589
+ const gap = 2;
590
+ const progress = Math.min(100, Math.max(0, summary.progress));
591
+ const bar = document.createElement('div');
592
+ bar.className = 'designbase-wc-gantt__group-summary';
593
+ bar.setAttribute('aria-hidden', 'true');
594
+ bar.title = `${summary.count}개 태스크`;
595
+ this.applyBarTone(bar, summary.color);
596
+ bar.style.left = `${offset * cellWidth + gap}px`;
597
+ bar.style.width = `${Math.max(duration * cellWidth - gap * 2, cellWidth - gap * 2)}px`;
598
+ if (progress > 0) {
599
+ const progressEl = document.createElement('span');
600
+ progressEl.className = 'designbase-wc-gantt__group-summary-progress';
601
+ progressEl.style.width = `${progress}%`;
602
+ bar.appendChild(progressEl);
603
+ }
604
+ return bar;
605
+ }
606
+ createBar(item, viewStart, cellWidth, colorIndex) {
607
+ const barColor = item.color || BAR_COLORS[colorIndex % BAR_COLORS.length];
608
+ const bar = document.createElement('div');
609
+ bar.className = 'designbase-wc-gantt__bar';
610
+ bar.setAttribute('role', 'button');
611
+ bar.setAttribute('tabindex', item.disabled ? '-1' : '0');
612
+ bar.setAttribute('aria-label', `${item.title}, ${item.startDate} – ${item.endDate}`);
613
+ this.applyBarTone(bar, barColor);
614
+ if (item.disabled) {
615
+ bar.classList.add('designbase-wc-gantt__bar--disabled');
616
+ }
617
+ this.applyBarGeometry(bar, item, viewStart, cellWidth);
618
+ const progress = typeof item.progress === 'number' ? Math.min(100, Math.max(0, item.progress)) : 0;
619
+ if (progress > 0) {
620
+ const progressEl = document.createElement('span');
621
+ progressEl.className = 'designbase-wc-gantt__bar-progress';
622
+ progressEl.style.width = `${progress}%`;
623
+ bar.appendChild(progressEl);
624
+ }
625
+ const body = document.createElement('div');
626
+ body.className = 'designbase-wc-gantt__bar-body';
627
+ if (item.status === 'delayed' || item.status === 'at-risk') {
628
+ body.appendChild(this.createStatusIcon(item.status));
629
+ }
630
+ const label = document.createElement('span');
631
+ label.className = 'designbase-wc-gantt__bar-label';
632
+ label.textContent = item.title;
633
+ body.appendChild(label);
634
+ if (item.assignee) {
635
+ body.appendChild(this.createAssignee(item.assignee));
636
+ }
637
+ const resizeStart = document.createElement('span');
638
+ resizeStart.className = 'designbase-wc-gantt__bar-handle designbase-wc-gantt__bar-handle--start';
639
+ resizeStart.setAttribute('aria-hidden', 'true');
640
+ const resizeEnd = document.createElement('span');
641
+ resizeEnd.className = 'designbase-wc-gantt__bar-handle designbase-wc-gantt__bar-handle--end';
642
+ resizeEnd.setAttribute('aria-hidden', 'true');
643
+ bar.append(resizeStart, body, resizeEnd);
644
+ if (!item.disabled && this.getBool('draggable', true) && !this.getBool('disabled')) {
645
+ resizeStart.addEventListener('pointerdown', (event) => this.startBarDrag(event, item, 'resize-start', bar));
646
+ resizeEnd.addEventListener('pointerdown', (event) => this.startBarDrag(event, item, 'resize-end', bar));
647
+ body.addEventListener('pointerdown', (event) => {
648
+ if (event.target.closest('.designbase-wc-gantt__bar-handle'))
649
+ return;
650
+ this.startBarDrag(event, item, 'move', bar);
651
+ });
652
+ }
653
+ bar.addEventListener('click', (event) => {
654
+ if (this.dragState || this.didDrag) {
655
+ this.didDrag = false;
656
+ return;
657
+ }
658
+ event.stopPropagation();
659
+ this.dispatchEvent(new CustomEvent('db-item-click', {
660
+ detail: { item },
661
+ bubbles: true,
662
+ composed: true,
663
+ }));
664
+ });
665
+ return bar;
666
+ }
667
+ toggleGroup(groupId) {
668
+ if (this.collapsedGroups.has(groupId)) {
669
+ this.collapsedGroups.delete(groupId);
670
+ }
671
+ else {
672
+ this.collapsedGroups.add(groupId);
673
+ }
674
+ this.dispatchEvent(new CustomEvent('db-group-toggle', {
675
+ detail: { groupId, collapsed: this.collapsedGroups.has(groupId) },
676
+ bubbles: true,
677
+ composed: true,
678
+ }));
679
+ this.render();
680
+ }
681
+ render() {
682
+ const items = this.items;
683
+ const scale = this.getOption('scale', SCALES, 'day');
684
+ const cellWidth = this.getNumber('cell-width', scale === 'week' ? 72 : 44);
685
+ const showWeekends = this.getBool('show-weekends', true);
686
+ const showToday = this.getBool('show-today', true);
687
+ const sidebarLabel = this.getAttribute('sidebar-label') || '할 일';
688
+ const disabled = this.getBool('disabled');
689
+ const fullWidth = this.getBool('full-width', true);
690
+ const weekdays = this.getWeekdays();
691
+ const { start: viewStart, end: viewEnd } = this.getViewRange();
692
+ const dates = this.getDateColumns(viewStart, viewEnd);
693
+ const today = this.getTodayDate();
694
+ const todayKey = toDateKey(today);
695
+ const todayOffset = dates.findIndex((date) => toDateKey(date) === todayKey);
696
+ const visibleRows = this.getVisibleRows();
697
+ const chartWidth = dates.length * cellWidth;
698
+ const root = document.createElement('div');
699
+ root.className = [
700
+ 'designbase-wc-gantt',
701
+ fullWidth ? 'designbase-wc-gantt--full-width' : '',
702
+ disabled ? 'designbase-wc-gantt--disabled' : '',
703
+ this.getBool('draggable', true) ? 'designbase-wc-gantt--draggable' : '',
704
+ ]
705
+ .filter(Boolean)
706
+ .join(' ');
707
+ const viewport = document.createElement('div');
708
+ viewport.className = 'designbase-wc-gantt__viewport';
709
+ const grid = document.createElement('div');
710
+ grid.className = 'designbase-wc-gantt__grid';
711
+ grid.style.setProperty('--db-gantt-chart-width', `${chartWidth}px`);
712
+ grid.style.setProperty('--db-gantt-cell-width', `${cellWidth}px`);
713
+ const corner = document.createElement('div');
714
+ corner.className = 'designbase-wc-gantt__corner';
715
+ corner.textContent = sidebarLabel;
716
+ grid.appendChild(corner);
717
+ const dateHeader = document.createElement('div');
718
+ dateHeader.className = 'designbase-wc-gantt__date-header';
719
+ const monthRow = document.createElement('div');
720
+ monthRow.className = 'designbase-wc-gantt__month-row';
721
+ for (const segment of this.getMonthSegments(dates)) {
722
+ const cell = document.createElement('div');
723
+ cell.className = 'designbase-wc-gantt__month-cell';
724
+ cell.style.width = `${segment.span * cellWidth}px`;
725
+ cell.textContent = segment.label;
726
+ monthRow.appendChild(cell);
727
+ }
728
+ dateHeader.appendChild(monthRow);
729
+ const dayRow = document.createElement('div');
730
+ dayRow.className = 'designbase-wc-gantt__day-row';
731
+ for (const date of dates) {
732
+ const key = toDateKey(date);
733
+ const isToday = key === todayKey;
734
+ const isWeekend = date.getDay() === 0 || date.getDay() === 6;
735
+ const cell = document.createElement('div');
736
+ cell.className = [
737
+ 'designbase-wc-gantt__day-cell',
738
+ isToday && showToday ? 'designbase-wc-gantt__day-cell--today' : '',
739
+ isWeekend && showWeekends ? 'designbase-wc-gantt__day-cell--weekend' : '',
740
+ ]
741
+ .filter(Boolean)
742
+ .join(' ');
743
+ cell.style.width = `${cellWidth}px`;
744
+ const dayNum = document.createElement('span');
745
+ dayNum.className = 'designbase-wc-gantt__day-num';
746
+ dayNum.textContent = String(date.getDate());
747
+ const dayName = document.createElement('span');
748
+ dayName.className = 'designbase-wc-gantt__day-name';
749
+ dayName.textContent = weekdays[date.getDay()];
750
+ cell.append(dayNum, dayName);
751
+ dayRow.appendChild(cell);
752
+ }
753
+ dateHeader.appendChild(dayRow);
754
+ grid.appendChild(dateHeader);
755
+ const itemsByRow = new Map();
756
+ items.forEach((item, index) => {
757
+ const list = itemsByRow.get(item.rowId) || [];
758
+ list.push({ ...item, color: item.color || BAR_COLORS[index % BAR_COLORS.length] });
759
+ itemsByRow.set(item.rowId, list);
760
+ });
761
+ let colorIndex = 0;
762
+ for (const entry of visibleRows) {
763
+ if (entry.type === 'group') {
764
+ const { group } = entry;
765
+ const collapsed = this.collapsedGroups.has(group.id);
766
+ const labelCell = document.createElement('button');
767
+ labelCell.type = 'button';
768
+ labelCell.className = 'designbase-wc-gantt__label-cell designbase-wc-gantt__label-cell--group';
769
+ labelCell.setAttribute('aria-expanded', String(!collapsed));
770
+ labelCell.dataset.groupId = group.id;
771
+ const title = document.createElement('span');
772
+ title.className = 'designbase-wc-gantt__group-title';
773
+ title.textContent = group.title;
774
+ labelCell.append(this.createDragHandle('group', group.id), this.createGroupChevron(collapsed), this.createGroupBadge(group.color), title);
775
+ labelCell.addEventListener('click', (event) => {
776
+ if (event.target.closest('.designbase-wc-gantt__drag-handle'))
777
+ return;
778
+ this.toggleGroup(group.id);
779
+ });
780
+ grid.appendChild(labelCell);
781
+ const trackCell = document.createElement('div');
782
+ trackCell.className =
783
+ 'designbase-wc-gantt__track-cell designbase-wc-gantt__track-cell--group';
784
+ if (collapsed) {
785
+ const summary = this.getGroupSummary(group.id, group.color);
786
+ if (summary) {
787
+ const gridBg = document.createElement('div');
788
+ gridBg.className = 'designbase-wc-gantt__grid-bg';
789
+ dates.forEach((date) => {
790
+ const cell = document.createElement('div');
791
+ const isWeekend = date.getDay() === 0 || date.getDay() === 6;
792
+ cell.className = [
793
+ 'designbase-wc-gantt__grid-cell',
794
+ isWeekend && showWeekends
795
+ ? 'designbase-wc-gantt__grid-cell--weekend'
796
+ : '',
797
+ ]
798
+ .filter(Boolean)
799
+ .join(' ');
800
+ cell.style.width = `${cellWidth}px`;
801
+ gridBg.appendChild(cell);
802
+ });
803
+ trackCell.appendChild(gridBg);
804
+ const summaryWrap = document.createElement('div');
805
+ summaryWrap.className = 'designbase-wc-gantt__group-summary-wrap';
806
+ const summaryBar = this.createGroupSummaryBar(summary, viewStart, viewEnd, cellWidth);
807
+ if (summaryBar)
808
+ summaryWrap.appendChild(summaryBar);
809
+ trackCell.appendChild(summaryWrap);
810
+ }
811
+ }
812
+ grid.appendChild(trackCell);
813
+ continue;
814
+ }
815
+ const { row } = entry;
816
+ const labelCell = document.createElement('button');
817
+ labelCell.type = 'button';
818
+ labelCell.className = 'designbase-wc-gantt__label-cell designbase-wc-gantt__label-cell--row';
819
+ labelCell.dataset.groupId = row.groupId || '';
820
+ labelCell.dataset.rowId = row.id;
821
+ const rowTitle = document.createElement('span');
822
+ rowTitle.className = 'designbase-wc-gantt__row-title';
823
+ rowTitle.textContent = row.label;
824
+ labelCell.append(this.createDragHandle('row', row.id, row.groupId), rowTitle);
825
+ labelCell.addEventListener('click', (event) => {
826
+ if (event.target.closest('.designbase-wc-gantt__drag-handle'))
827
+ return;
828
+ this.dispatchEvent(new CustomEvent('db-row-click', {
829
+ detail: { row },
830
+ bubbles: true,
831
+ composed: true,
832
+ }));
833
+ });
834
+ grid.appendChild(labelCell);
835
+ const trackCell = document.createElement('div');
836
+ trackCell.className = 'designbase-wc-gantt__track-cell';
837
+ const gridBg = document.createElement('div');
838
+ gridBg.className = 'designbase-wc-gantt__grid-bg';
839
+ dates.forEach((date) => {
840
+ const cell = document.createElement('div');
841
+ const isWeekend = date.getDay() === 0 || date.getDay() === 6;
842
+ cell.className = [
843
+ 'designbase-wc-gantt__grid-cell',
844
+ isWeekend && showWeekends ? 'designbase-wc-gantt__grid-cell--weekend' : '',
845
+ ]
846
+ .filter(Boolean)
847
+ .join(' ');
848
+ cell.style.width = `${cellWidth}px`;
849
+ gridBg.appendChild(cell);
850
+ });
851
+ trackCell.appendChild(gridBg);
852
+ const bars = document.createElement('div');
853
+ bars.className = 'designbase-wc-gantt__bars';
854
+ (itemsByRow.get(row.id) || []).forEach((item) => {
855
+ const clampedStart = clampDate(parseDate(item.startDate), viewStart, viewEnd);
856
+ const clampedEnd = clampDate(parseDate(item.endDate), viewStart, viewEnd);
857
+ if (clampedEnd < viewStart || clampedStart > viewEnd)
858
+ return;
859
+ bars.appendChild(this.createBar({
860
+ ...item,
861
+ startDate: toDateKey(clampedStart),
862
+ endDate: toDateKey(clampedEnd),
863
+ }, viewStart, cellWidth, colorIndex++));
864
+ });
865
+ trackCell.appendChild(bars);
866
+ grid.appendChild(trackCell);
867
+ }
868
+ viewport.appendChild(grid);
869
+ if (showToday && todayOffset >= 0) {
870
+ const todayLine = document.createElement('div');
871
+ todayLine.className = 'designbase-wc-gantt__today-line';
872
+ todayLine.style.left = `calc(var(--db-gantt-label-width) + ${todayOffset * cellWidth + cellWidth / 2}px)`;
873
+ grid.appendChild(todayLine);
874
+ }
875
+ root.appendChild(viewport);
876
+ this.replaceChildren(root);
877
+ }
878
+ }
879
+ if (!customElements.get('db-gantt'))
880
+ customElements.define('db-gantt', DbGantt);
881
+
882
+ export { DbGantt };