@designbasekorea/ui-wc 0.5.6 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/dist/chunks/lottie-0e23d630.js +1 -0
  2. package/dist/esm/components/animation-background/canvas-controller.js +1 -121
  3. package/dist/esm/components/animation-background/controls.js +1 -180
  4. package/dist/esm/components/animation-background/graphite-controller.js +1 -196
  5. package/dist/esm/components/animation-background/graphite-presets.js +1 -42
  6. package/dist/esm/components/animation-background/graphite-shader.js +1 -111
  7. package/dist/esm/components/animation-background/layers.js +1 -369
  8. package/dist/esm/components/animation-background/lumina-presets.js +1 -53
  9. package/dist/esm/components/animation-background/lumina-shaders.js +1 -181
  10. package/dist/esm/components/animation-background/lumina-utils.js +1 -60
  11. package/dist/esm/components/animation-background/particle-defaults.js +1 -36
  12. package/dist/esm/components/animation-background/renderers.js +1 -167
  13. package/dist/esm/components/animation-background/shader-controller.js +1 -154
  14. package/dist/esm/components/color-picker-utils.js +1 -134
  15. package/dist/esm/components/date-picker-utils.js +1 -140
  16. package/dist/esm/components/db-accordion.js +1 -304
  17. package/dist/esm/components/db-ad-banner.js +1 -455
  18. package/dist/esm/components/db-ai-chat-list.d.ts +43 -0
  19. package/dist/esm/components/db-ai-chat-list.js +1 -0
  20. package/dist/esm/components/db-ai-chat.d.ts +159 -0
  21. package/dist/esm/components/db-ai-chat.js +1 -0
  22. package/dist/esm/components/db-alert.js +1 -149
  23. package/dist/esm/components/db-animation-background.js +1 -486
  24. package/dist/esm/components/db-animation-text.js +1 -601
  25. package/dist/esm/components/db-app-badge.js +1 -88
  26. package/dist/esm/components/db-audio-player.js +1 -752
  27. package/dist/esm/components/db-avatar.js +1 -188
  28. package/dist/esm/components/db-backdrop.js +1 -139
  29. package/dist/esm/components/db-badge.js +1 -91
  30. package/dist/esm/components/db-banner.js +1 -188
  31. package/dist/esm/components/db-bottom-navigation.js +1 -146
  32. package/dist/esm/components/db-bottom-sheet.js +1 -481
  33. package/dist/esm/components/db-breadcrumbs.js +1 -329
  34. package/dist/esm/components/db-button.js +1 -316
  35. package/dist/esm/components/db-calendar.js +1 -675
  36. package/dist/esm/components/db-card.js +1 -265
  37. package/dist/esm/components/db-carousel.js +1 -264
  38. package/dist/esm/components/db-chat-composer.d.ts +134 -0
  39. package/dist/esm/components/db-chat-composer.js +1 -0
  40. package/dist/esm/components/db-chat-list.d.ts +47 -0
  41. package/dist/esm/components/db-chat-list.js +1 -0
  42. package/dist/esm/components/db-chat-message.d.ts +39 -0
  43. package/dist/esm/components/db-chat-message.js +1 -0
  44. package/dist/esm/components/db-chat-orb.d.ts +99 -0
  45. package/dist/esm/components/db-chat-orb.js +1 -0
  46. package/dist/esm/components/db-chat-room.d.ts +71 -0
  47. package/dist/esm/components/db-chat-room.js +1 -0
  48. package/dist/esm/components/db-chat-suggestions.d.ts +44 -0
  49. package/dist/esm/components/db-chat-suggestions.js +1 -0
  50. package/dist/esm/components/db-chat-typing.d.ts +22 -0
  51. package/dist/esm/components/db-chat-typing.js +1 -0
  52. package/dist/esm/components/db-chat-welcome.d.ts +32 -0
  53. package/dist/esm/components/db-chat-welcome.js +1 -0
  54. package/dist/esm/components/db-chat-widget.d.ts +146 -0
  55. package/dist/esm/components/db-chat-widget.js +1 -0
  56. package/dist/esm/components/db-checkbox.js +1 -202
  57. package/dist/esm/components/db-chip.js +1 -104
  58. package/dist/esm/components/db-code-block.js +1 -304
  59. package/dist/esm/components/db-color-picker.js +1 -746
  60. package/dist/esm/components/db-confirm.js +1 -191
  61. package/dist/esm/components/db-container.js +1 -39
  62. package/dist/esm/components/db-context-menu.js +1 -265
  63. package/dist/esm/components/db-countdown.js +1 -146
  64. package/dist/esm/components/db-cursor-follower.js +1 -173
  65. package/dist/esm/components/db-date-picker.js +1 -837
  66. package/dist/esm/components/db-divider.js +1 -66
  67. package/dist/esm/components/db-download-progress.d.ts +89 -0
  68. package/dist/esm/components/db-download-progress.js +1 -0
  69. package/dist/esm/components/db-drawer.js +1 -245
  70. package/dist/esm/components/db-dropdown.js +1 -493
  71. package/dist/esm/components/db-dropzone.js +1 -266
  72. package/dist/esm/components/db-email-verify.d.ts +62 -0
  73. package/dist/esm/components/db-email-verify.js +1 -0
  74. package/dist/esm/components/db-empty-state.js +1 -97
  75. package/dist/esm/components/db-file-uploader.js +1 -256
  76. package/dist/esm/components/db-floating-action-button.js +1 -139
  77. package/dist/esm/components/db-footer.js +1 -385
  78. package/dist/esm/components/db-form.js +1 -566
  79. package/dist/esm/components/db-gantt.js +1 -882
  80. package/dist/esm/components/db-gradient.js +1 -133
  81. package/dist/esm/components/db-grid-background.js +1 -909
  82. package/dist/esm/components/db-grid.js +1 -73
  83. package/dist/esm/components/db-icons.d.ts +3 -0
  84. package/dist/esm/components/db-icons.js +1 -82
  85. package/dist/esm/components/db-image-list.js +1 -214
  86. package/dist/esm/components/db-image-placeholder.js +1 -96
  87. package/dist/esm/components/db-image.js +1 -289
  88. package/dist/esm/components/db-indicator.js +1 -517
  89. package/dist/esm/components/db-input.js +1 -420
  90. package/dist/esm/components/db-kanban.js +1 -538
  91. package/dist/esm/components/db-label.js +1 -66
  92. package/dist/esm/components/db-lightbox.js +1 -372
  93. package/dist/esm/components/db-list.js +1 -340
  94. package/dist/esm/components/db-logo-svg.js +1 -72
  95. package/dist/esm/components/db-logo.js +1 -106
  96. package/dist/esm/components/db-lottie.js +1 -109
  97. package/dist/esm/components/db-markdown-editor.js +1 -458
  98. package/dist/esm/components/db-marquee.js +1 -402
  99. package/dist/esm/components/db-masonry.js +1 -94
  100. package/dist/esm/components/db-menu-item.js +1 -211
  101. package/dist/esm/components/db-modal.js +1 -522
  102. package/dist/esm/components/db-navbar.js +1 -680
  103. package/dist/esm/components/db-onboarding-modal.js +1 -322
  104. package/dist/esm/components/db-otp-input.d.ts +53 -0
  105. package/dist/esm/components/db-otp-input.js +1 -0
  106. package/dist/esm/components/db-page-header.js +1 -312
  107. package/dist/esm/components/db-pagination.js +1 -271
  108. package/dist/esm/components/db-phone-verify.d.ts +61 -0
  109. package/dist/esm/components/db-phone-verify.js +1 -0
  110. package/dist/esm/components/db-popover.js +1 -580
  111. package/dist/esm/components/db-progress-step.js +1 -166
  112. package/dist/esm/components/db-progress.js +1 -171
  113. package/dist/esm/components/db-progressbar.js +1 -72
  114. package/dist/esm/components/db-radio.js +1 -172
  115. package/dist/esm/components/db-random-gradient.js +1 -121
  116. package/dist/esm/components/db-range-slider.js +1 -811
  117. package/dist/esm/components/db-rating.js +1 -325
  118. package/dist/esm/components/db-reorder.js +1 -286
  119. package/dist/esm/components/db-resizable-panels.js +1 -282
  120. package/dist/esm/components/db-scroll-area.js +1 -126
  121. package/dist/esm/components/db-search-bar.js +1 -668
  122. package/dist/esm/components/db-section-app-download.js +1 -101
  123. package/dist/esm/components/db-section-contact.js +1 -102
  124. package/dist/esm/components/db-section-cta.js +1 -150
  125. package/dist/esm/components/db-section-faq.js +1 -120
  126. package/dist/esm/components/db-section-feature-grid.js +1 -137
  127. package/dist/esm/components/db-section-feature.js +1 -179
  128. package/dist/esm/components/db-section-hero.js +1 -243
  129. package/dist/esm/components/db-section-logo-cloud.js +1 -138
  130. package/dist/esm/components/db-section-pricing.js +1 -166
  131. package/dist/esm/components/db-section-stats.js +1 -129
  132. package/dist/esm/components/db-section-testimonials.js +1 -252
  133. package/dist/esm/components/db-section.js +1 -385
  134. package/dist/esm/components/db-segment-control.js +1 -231
  135. package/dist/esm/components/db-select.js +1 -571
  136. package/dist/esm/components/db-share-icons.js +1 -40
  137. package/dist/esm/components/db-share.js +1 -489
  138. package/dist/esm/components/db-sidebar.js +1 -1068
  139. package/dist/esm/components/db-skeleton.js +1 -94
  140. package/dist/esm/components/db-spinner.js +1 -102
  141. package/dist/esm/components/db-split-view.js +1 -297
  142. package/dist/esm/components/db-stack.js +1 -46
  143. package/dist/esm/components/db-stat.js +1 -218
  144. package/dist/esm/components/db-stepper.js +1 -398
  145. package/dist/esm/components/db-table.js +1 -412
  146. package/dist/esm/components/db-tabs.js +1 -335
  147. package/dist/esm/components/db-testimonial.d.ts +2 -0
  148. package/dist/esm/components/db-testimonial.js +1 -162
  149. package/dist/esm/components/db-textarea.js +1 -233
  150. package/dist/esm/components/db-time-picker.js +1 -523
  151. package/dist/esm/components/db-timeline.js +1 -174
  152. package/dist/esm/components/db-toast.js +1 -158
  153. package/dist/esm/components/db-toggle.js +1 -162
  154. package/dist/esm/components/db-toolbar.js +1 -94
  155. package/dist/esm/components/db-tooltip.js +1 -440
  156. package/dist/esm/components/db-top-banner.js +1 -121
  157. package/dist/esm/components/db-tutorial.js +1 -324
  158. package/dist/esm/components/db-two-factor.d.ts +60 -0
  159. package/dist/esm/components/db-two-factor.js +1 -0
  160. package/dist/esm/components/db-video-player.js +1 -503
  161. package/dist/esm/components/db-youtube-player.js +1 -194
  162. package/dist/esm/components/highlight-code.js +1 -65
  163. package/dist/esm/components/input-validation.js +1 -50
  164. package/dist/esm/components/marketing-block-host-utils.js +1 -9
  165. package/dist/esm/components/number-scrub.js +1 -117
  166. package/dist/esm/components/random-gradient-palettes.js +1 -67
  167. package/dist/esm/components/section-block-header-utils.js +1 -48
  168. package/dist/esm/helpers/confirm.js +1 -52
  169. package/dist/esm/helpers/toast.js +1 -73
  170. package/dist/esm/index.d.ts +16 -0
  171. package/dist/esm/index.js +1 -111
  172. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-bash.js +1 -0
  173. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-clike.js +1 -0
  174. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-css.js +1 -0
  175. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-javascript.js +1 -0
  176. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-json.js +1 -0
  177. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-jsx.js +1 -0
  178. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-markup.js +1 -0
  179. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-scss.js +1 -0
  180. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-tsx.js +1 -0
  181. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-typescript.js +1 -0
  182. package/dist/index.cjs +1 -55414
  183. package/dist/index.css +1 -1
  184. package/dist/index.d.ts +1091 -2
  185. package/dist/index.esm.js +1 -38284
  186. package/dist/react/chunks/lottie-14e7addb.js +1 -0
  187. package/dist/react/index.cjs +1 -56503
  188. package/dist/react/index.d.ts +1620 -54
  189. package/dist/react/index.esm.js +1 -39373
  190. package/package.json +1 -1
  191. package/dist/chunks/lottie-e273b936.js +0 -17032
  192. package/dist/esm/node_modules/prismjs/components/prism-bash.js +0 -235
  193. package/dist/esm/node_modules/prismjs/components/prism-clike.js +0 -31
  194. package/dist/esm/node_modules/prismjs/components/prism-css.js +0 -64
  195. package/dist/esm/node_modules/prismjs/components/prism-javascript.js +0 -172
  196. package/dist/esm/node_modules/prismjs/components/prism-json.js +0 -27
  197. package/dist/esm/node_modules/prismjs/components/prism-jsx.js +0 -143
  198. package/dist/esm/node_modules/prismjs/components/prism-markup.js +0 -186
  199. package/dist/esm/node_modules/prismjs/components/prism-scss.js +0 -81
  200. package/dist/esm/node_modules/prismjs/components/prism-tsx.js +0 -15
  201. package/dist/esm/node_modules/prismjs/components/prism-typescript.js +0 -60
  202. package/dist/react/chunks/lottie-72a4c6d4.js +0 -17032
@@ -1,882 +1 @@
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 };
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};