@editora/ui-core 0.1.13 → 0.1.15

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 (79) hide show
  1. package/README.md +95 -5
  2. package/dist/color-picker.cjs.js +1 -1
  3. package/dist/color-picker.esm.js +2 -1
  4. package/dist/components/date-time-utils.d.ts.map +1 -1
  5. package/dist/components/ui-context-menu.d.ts +6 -0
  6. package/dist/components/ui-context-menu.d.ts.map +1 -1
  7. package/dist/components/ui-dialog.d.ts.map +1 -1
  8. package/dist/components/ui-floating-toolbar.d.ts +3 -0
  9. package/dist/components/ui-floating-toolbar.d.ts.map +1 -1
  10. package/dist/components/ui-gantt.d.ts +30 -1
  11. package/dist/components/ui-gantt.d.ts.map +1 -1
  12. package/dist/components/ui-hover-card.d.ts +3 -0
  13. package/dist/components/ui-hover-card.d.ts.map +1 -1
  14. package/dist/components/ui-positioner.d.ts.map +1 -1
  15. package/dist/components/ui-sortable.d.ts.map +1 -1
  16. package/dist/context-menu.cjs.js +12 -12
  17. package/dist/context-menu.esm.js +159 -132
  18. package/dist/date-field.cjs.js +1 -1
  19. package/dist/date-field.esm.js +2 -1
  20. package/dist/date-picker.cjs.js +1 -1
  21. package/dist/date-picker.esm.js +2 -1
  22. package/dist/date-range-picker.cjs.js +1 -1
  23. package/dist/date-range-picker.esm.js +2 -1
  24. package/dist/date-range-time-picker.cjs.js +2 -2
  25. package/dist/date-range-time-picker.esm.js +2 -1
  26. package/dist/date-time-picker.cjs.js +2 -2
  27. package/dist/date-time-picker.esm.js +2 -1
  28. package/dist/date-time-utils-66480de4.js +1 -0
  29. package/dist/date-time-utils-a2c7af25.mjs +307 -0
  30. package/dist/dialog.cjs.js +11 -11
  31. package/dist/dialog.esm.js +2 -8
  32. package/dist/floating-toolbar.cjs.js +4 -4
  33. package/dist/floating-toolbar.esm.js +52 -25
  34. package/dist/gantt.cjs.js +656 -74
  35. package/dist/gantt.esm.js +1050 -100
  36. package/dist/hover-card.cjs.js +4 -4
  37. package/dist/hover-card.esm.js +61 -38
  38. package/dist/index.cjs.js +1 -1
  39. package/dist/index.esm.js +158 -146
  40. package/dist/menu.cjs.js +1 -1
  41. package/dist/menu.esm.js +2 -2
  42. package/dist/menubar.cjs.js +1 -1
  43. package/dist/menubar.esm.js +2 -2
  44. package/dist/popover.cjs.js +1 -1
  45. package/dist/popover.esm.js +2 -2
  46. package/dist/portal-c69756c0.js +1 -0
  47. package/dist/portal-fe73e5cc.mjs +123 -0
  48. package/dist/portal.d.ts +29 -3
  49. package/dist/portal.d.ts.map +1 -1
  50. package/dist/positioner-33529bb8.js +1 -0
  51. package/dist/positioner-e9770c66.mjs +582 -0
  52. package/dist/positioner.cjs.js +2 -2
  53. package/dist/positioner.esm.js +36 -26
  54. package/dist/primitives/positioner.d.ts +199 -1
  55. package/dist/primitives/positioner.d.ts.map +1 -1
  56. package/dist/{runtime-864efe2f.mjs → runtime-161f68a2.mjs} +24 -23
  57. package/dist/runtime-3f13807b.js +1 -0
  58. package/dist/runtime.cjs.js +1 -1
  59. package/dist/runtime.d.ts +2 -0
  60. package/dist/runtime.d.ts.map +1 -1
  61. package/dist/runtime.esm.js +39 -24
  62. package/dist/sortable.cjs.js +1 -1
  63. package/dist/sortable.esm.js +49 -48
  64. package/dist/split-button.cjs.js +1 -1
  65. package/dist/split-button.esm.js +2 -2
  66. package/dist/time-field.cjs.js +1 -1
  67. package/dist/time-field.esm.js +14 -13
  68. package/dist/time-picker.cjs.js +1 -1
  69. package/dist/time-picker.esm.js +2 -1
  70. package/dist/tooltip.cjs.js +1 -1
  71. package/dist/tooltip.esm.js +2 -1
  72. package/package.json +1 -1
  73. package/dist/date-time-utils-378ecc21.js +0 -1
  74. package/dist/date-time-utils-a8b17ea7.mjs +0 -299
  75. package/dist/portal-9060f7a6.mjs +0 -114
  76. package/dist/portal-d9bae456.js +0 -1
  77. package/dist/positioner-16f3cca1.mjs +0 -132
  78. package/dist/positioner-43b1c75a.js +0 -1
  79. package/dist/runtime-668914fe.js +0 -1
package/dist/gantt.esm.js CHANGED
@@ -1,214 +1,1164 @@
1
- import { E as w } from "./ElementBase-909688f3.mjs";
2
- const d = `
1
+ import { E as At } from "./ElementBase-909688f3.mjs";
2
+ const I = 24 * 60 * 60 * 1e3, $ = 42, _ = 62, J = 376, N = 28, Lt = 15, wt = Lt * Math.SQRT2 / 2, ht = {
3
+ day: 36,
4
+ week: 13,
5
+ month: 4.4,
6
+ quarter: 2.2,
7
+ year: 1.15
8
+ }, mt = `
3
9
  :host {
4
- --ui-gantt-bg: color-mix(in srgb, var(--ui-color-surface, #ffffff) 95%, transparent);
5
- --ui-gantt-border: color-mix(in srgb, var(--ui-color-border, #cbd5e1) 74%, transparent);
10
+ --ui-gantt-bg: color-mix(in srgb, var(--ui-color-surface, #ffffff) 96%, transparent);
11
+ --ui-gantt-panel: var(--ui-color-surface, #ffffff);
12
+ --ui-gantt-border: color-mix(in srgb, var(--ui-color-border, #cbd5e1) 76%, transparent);
13
+ --ui-gantt-grid: color-mix(in srgb, var(--ui-color-border, #cbd5e1) 56%, transparent);
6
14
  --ui-gantt-text: var(--ui-color-text, #0f172a);
7
15
  --ui-gantt-muted: var(--ui-color-muted, #64748b);
8
16
  --ui-gantt-accent: var(--ui-color-primary, #2563eb);
9
-
17
+ --ui-gantt-selected: color-mix(in srgb, var(--ui-gantt-accent) 10%, transparent);
10
18
  display: block;
11
19
  min-inline-size: 0;
12
20
  color-scheme: light dark;
13
21
  font-family: "Inter", "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
14
22
  }
15
23
 
24
+ *,
25
+ *::before,
26
+ *::after {
27
+ box-sizing: border-box;
28
+ }
29
+
16
30
  .frame {
17
31
  border: 1px solid var(--ui-gantt-border);
18
- border-radius: 14px;
32
+ border-radius: 12px;
19
33
  background: var(--ui-gantt-bg);
20
- box-shadow: 0 12px 28px rgba(15, 23, 42, 0.08);
21
- padding: 12px;
22
- display: grid;
23
- gap: 10px;
34
+ box-shadow: 0 14px 34px rgba(15, 23, 42, 0.08);
35
+ overflow: hidden;
24
36
  }
25
37
 
26
- .header {
38
+ .toolbar {
27
39
  display: flex;
40
+ align-items: center;
28
41
  justify-content: space-between;
29
- gap: 8px;
30
- font-size: 12px;
42
+ gap: 12px;
43
+ padding: 10px 12px;
44
+ border-bottom: 1px solid var(--ui-gantt-border);
45
+ background: color-mix(in srgb, var(--ui-gantt-panel) 92%, transparent);
46
+ }
47
+
48
+ .title {
49
+ min-inline-size: 0;
50
+ font-size: 13px;
51
+ font-weight: 750;
52
+ color: var(--ui-gantt-text);
53
+ }
54
+
55
+ .meta {
31
56
  color: var(--ui-gantt-muted);
57
+ font-weight: 600;
58
+ margin-inline-start: 8px;
59
+ }
60
+
61
+ .controls {
62
+ display: flex;
63
+ align-items: center;
64
+ gap: 6px;
65
+ flex-wrap: wrap;
66
+ }
67
+
68
+ .control,
69
+ .select,
70
+ .zoom-button {
71
+ border: 1px solid var(--ui-gantt-border);
72
+ background: var(--ui-gantt-panel);
73
+ color: var(--ui-gantt-text);
74
+ border-radius: 7px;
75
+ min-block-size: 30px;
76
+ font: inherit;
77
+ font-size: 12px;
78
+ }
79
+
80
+ .control {
81
+ inline-size: min(220px, 34vw);
82
+ padding: 0 9px;
83
+ }
84
+
85
+ .select {
86
+ padding: 0 28px 0 9px;
87
+ min-inline-size: 118px;
88
+ }
89
+
90
+ .zoom-button {
91
+ padding: 0 9px;
92
+ cursor: pointer;
93
+ }
94
+
95
+ .zoom-button[aria-pressed="true"] {
96
+ border-color: color-mix(in srgb, var(--ui-gantt-accent) 46%, var(--ui-gantt-border));
97
+ background: color-mix(in srgb, var(--ui-gantt-accent) 10%, var(--ui-gantt-panel));
98
+ color: var(--ui-gantt-accent);
99
+ }
100
+
101
+ .viewport {
102
+ overflow: auto;
103
+ max-block-size: var(--ui-gantt-max-height, 620px);
32
104
  }
33
105
 
34
- .rows {
106
+ .surface {
35
107
  display: grid;
36
- gap: 8px;
108
+ grid-template-columns: ${J}px minmax(640px, 1fr);
109
+ min-inline-size: 100%;
110
+ }
111
+
112
+ .grid-head,
113
+ .timeline-head {
114
+ position: sticky;
115
+ top: 0;
116
+ z-index: 4;
117
+ min-block-size: 62px;
118
+ background: color-mix(in srgb, var(--ui-gantt-panel) 96%, transparent);
119
+ border-bottom: 1px solid var(--ui-gantt-border);
37
120
  }
38
121
 
39
- .row {
122
+ .grid-head {
123
+ left: 0;
124
+ z-index: 5;
40
125
  display: grid;
41
- grid-template-columns: minmax(110px, 0.4fr) minmax(0, 1fr);
42
- gap: 10px;
126
+ grid-template-columns: minmax(170px, 1fr) 82px 80px 44px;
127
+ border-right: 1px solid var(--ui-gantt-border);
128
+ }
129
+
130
+ .grid-head span,
131
+ .head-button,
132
+ .grid-cell {
133
+ display: flex;
43
134
  align-items: center;
135
+ min-inline-size: 0;
136
+ padding: 0 10px;
137
+ border-right: 1px solid color-mix(in srgb, var(--ui-gantt-border) 62%, transparent);
44
138
  }
45
139
 
46
- .label {
47
- font-size: 12px;
140
+ .grid-head span {
141
+ font-size: 11px;
142
+ font-weight: 750;
143
+ color: var(--ui-gantt-muted);
144
+ text-transform: uppercase;
145
+ letter-spacing: 0;
146
+ }
147
+
148
+ .head-button {
149
+ border: 0;
150
+ background: transparent;
151
+ color: var(--ui-gantt-muted);
152
+ cursor: pointer;
153
+ font: inherit;
154
+ font-size: 11px;
155
+ font-weight: 750;
156
+ text-align: left;
157
+ text-transform: uppercase;
158
+ letter-spacing: 0;
159
+ }
160
+
161
+ .head-button:hover,
162
+ .head-button[aria-sort]:not([aria-sort="none"]) {
163
+ color: var(--ui-gantt-accent);
164
+ }
165
+
166
+ .grid-head span:last-child,
167
+ .grid-cell:last-child {
168
+ border-right: 0;
169
+ }
170
+
171
+ .timeline-head {
172
+ position: sticky;
173
+ top: 0;
174
+ overflow: hidden;
175
+ }
176
+
177
+ .scale {
178
+ position: relative;
179
+ min-block-size: 62px;
180
+ }
181
+
182
+ .tick {
183
+ position: absolute;
184
+ inset-block: 0;
185
+ border-left: 1px solid var(--ui-gantt-grid);
186
+ padding: 9px 8px;
187
+ color: var(--ui-gantt-muted);
188
+ font-size: 11px;
189
+ font-weight: 650;
190
+ white-space: nowrap;
191
+ }
192
+
193
+ .grid-body {
194
+ position: sticky;
195
+ left: 0;
196
+ z-index: 3;
197
+ background: var(--ui-gantt-panel);
198
+ border-right: 1px solid var(--ui-gantt-border);
199
+ }
200
+
201
+ .timeline-body {
202
+ position: relative;
203
+ background:
204
+ linear-gradient(to right, var(--ui-gantt-grid) 1px, transparent 1px) 0 0 / var(--day-width) 100%,
205
+ color-mix(in srgb, var(--ui-gantt-panel) 98%, transparent);
206
+ }
207
+
208
+ .grid-row,
209
+ .timeline-row {
210
+ block-size: ${$}px;
211
+ border-bottom: 1px solid color-mix(in srgb, var(--ui-gantt-border) 56%, transparent);
212
+ }
213
+
214
+ .grid-row {
215
+ display: grid;
216
+ grid-template-columns: minmax(170px, 1fr) 82px 80px 44px;
48
217
  color: var(--ui-gantt-text);
49
- font-weight: 600;
218
+ font-size: 12px;
219
+ }
220
+
221
+ .grid-row.selected,
222
+ .timeline-row.selected {
223
+ background: var(--ui-gantt-selected);
224
+ }
225
+
226
+ .task-name {
227
+ gap: 7px;
228
+ font-weight: 650;
229
+ }
230
+
231
+ .expander {
232
+ flex: 0 0 22px;
233
+ inline-size: 22px;
234
+ block-size: 22px;
235
+ border: 0;
236
+ border-radius: 6px;
237
+ background: transparent;
238
+ color: var(--ui-gantt-muted);
239
+ cursor: pointer;
240
+ display: grid;
241
+ place-items: center;
242
+ font-size: 13px;
243
+ }
244
+
245
+ .expander:hover {
246
+ background: color-mix(in srgb, var(--ui-gantt-accent) 9%, transparent);
247
+ color: var(--ui-gantt-accent);
248
+ }
249
+
250
+ .name-text {
50
251
  overflow: hidden;
51
252
  text-overflow: ellipsis;
52
253
  white-space: nowrap;
53
254
  }
54
255
 
55
- .track {
256
+ .badge {
257
+ justify-content: center;
258
+ font-variant-numeric: tabular-nums;
259
+ color: var(--ui-gantt-muted);
260
+ font-size: 11px;
261
+ }
262
+
263
+ .timeline-row {
56
264
  position: relative;
57
- border: 1px solid color-mix(in srgb, var(--ui-gantt-border) 72%, transparent);
58
- border-radius: 999px;
59
- background: color-mix(in srgb, var(--ui-gantt-bg) 90%, transparent);
60
- min-block-size: 14px;
61
265
  }
62
266
 
63
267
  .bar {
64
268
  position: absolute;
65
- top: 1px;
66
- bottom: 1px;
67
- border-radius: 999px;
68
- background: var(--tone, var(--ui-gantt-accent));
69
- box-shadow: 0 6px 14px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 26%, transparent);
269
+ top: 12px;
270
+ block-size: 18px;
271
+ min-inline-size: 10px;
272
+ border: 0;
273
+ border-radius: 4px;
274
+ background:
275
+ linear-gradient(180deg, color-mix(in srgb, #ffffff 22%, transparent), transparent 42%),
276
+ var(--tone, var(--ui-gantt-accent));
277
+ box-shadow:
278
+ 0 4px 10px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 18%, transparent),
279
+ inset 0 0 0 1px color-mix(in srgb, #ffffff 20%, transparent);
280
+ cursor: grab;
70
281
  overflow: hidden;
282
+ outline: 0;
283
+ touch-action: none;
284
+ transition: box-shadow 140ms ease, filter 140ms ease, transform 140ms ease;
285
+ will-change: left, width;
286
+ }
287
+
288
+ .bar:hover,
289
+ .bar.dragging {
290
+ filter: saturate(1.08);
291
+ box-shadow:
292
+ 0 6px 14px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 26%, transparent),
293
+ inset 0 0 0 1px color-mix(in srgb, #ffffff 30%, transparent);
294
+ }
295
+
296
+ .bar.dragging {
297
+ cursor: grabbing;
298
+ z-index: 5;
299
+ transform: translateY(-1px);
300
+ }
301
+
302
+ .bar:focus-visible {
303
+ outline: 2px solid var(--ui-gantt-accent);
304
+ outline-offset: 2px;
305
+ }
306
+
307
+ .bar.critical {
308
+ --tone: var(--ui-color-danger, #dc2626);
309
+ box-shadow:
310
+ 0 5px 12px color-mix(in srgb, var(--ui-color-danger, #dc2626) 24%, transparent),
311
+ inset 0 0 0 1px color-mix(in srgb, #ffffff 28%, transparent);
312
+ }
313
+
314
+ .bar.split {
315
+ background: transparent;
316
+ box-shadow: none;
317
+ overflow: visible;
318
+ }
319
+
320
+ .bar.split .progress {
321
+ display: none;
322
+ }
323
+
324
+ .split-segment {
325
+ position: absolute;
326
+ inset-block: 0;
327
+ border-radius: 4px;
328
+ background:
329
+ linear-gradient(180deg, color-mix(in srgb, #ffffff 22%, transparent), transparent 42%),
330
+ var(--tone, var(--ui-gantt-accent));
331
+ box-shadow:
332
+ 0 4px 10px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 18%, transparent),
333
+ inset 0 0 0 1px color-mix(in srgb, #ffffff 24%, transparent);
334
+ }
335
+
336
+ .baseline {
337
+ position: absolute;
338
+ top: 33px;
339
+ block-size: 4px;
340
+ border-radius: 999px;
341
+ background: color-mix(in srgb, var(--ui-gantt-muted) 54%, transparent);
342
+ z-index: 1;
343
+ }
344
+
345
+ .bar.summary {
346
+ block-size: 9px;
347
+ top: 17px;
348
+ border-radius: 3px;
349
+ background: color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 82%, #0f172a);
350
+ }
351
+
352
+ .bar.milestone {
353
+ inline-size: 28px !important;
354
+ min-inline-size: 28px;
355
+ block-size: 28px;
356
+ top: 7px;
357
+ border-radius: 8px;
358
+ background: transparent;
359
+ box-shadow: none;
360
+ cursor: grab;
361
+ overflow: visible;
362
+ display: grid;
363
+ place-items: center;
364
+ }
365
+
366
+ .bar.milestone:hover,
367
+ .bar.milestone.dragging {
368
+ box-shadow: none;
369
+ }
370
+
371
+ .milestone-shape {
372
+ inline-size: 15px;
373
+ block-size: 15px;
374
+ border-radius: 4px;
375
+ background:
376
+ linear-gradient(135deg, color-mix(in srgb, #ffffff 24%, transparent), transparent 48%),
377
+ var(--tone, var(--ui-gantt-accent));
378
+ box-shadow:
379
+ 0 7px 18px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 28%, transparent),
380
+ inset 0 0 0 1px color-mix(in srgb, #ffffff 34%, transparent);
381
+ transform: rotate(45deg);
382
+ transition: transform 140ms ease, box-shadow 140ms ease, filter 140ms ease;
383
+ }
384
+
385
+ .bar.milestone:hover .milestone-shape,
386
+ .bar.milestone.dragging .milestone-shape {
387
+ filter: saturate(1.1);
388
+ transform: rotate(45deg) scale(1.08);
389
+ box-shadow:
390
+ 0 10px 24px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 36%, transparent),
391
+ inset 0 0 0 1px color-mix(in srgb, #ffffff 44%, transparent);
392
+ }
393
+
394
+ .bar.milestone .progress,
395
+ .bar.milestone .handle,
396
+ .bar.summary .handle {
397
+ display: none;
71
398
  }
72
399
 
73
400
  .progress {
401
+ position: absolute;
402
+ inset-block: 0;
403
+ inset-inline-start: 0;
404
+ background: linear-gradient(90deg, color-mix(in srgb, #000 22%, transparent), transparent);
405
+ opacity: 0.3;
406
+ inline-size: var(--progress, 0%);
407
+ }
408
+
409
+ .handle {
410
+ position: absolute;
411
+ inset-block: 0;
412
+ inline-size: 14px;
413
+ cursor: ew-resize;
414
+ z-index: 2;
415
+ opacity: 0;
416
+ transition: opacity 120ms ease;
417
+ }
418
+
419
+ .handle.start {
420
+ inset-inline-start: 0;
421
+ }
422
+
423
+ .handle.end {
424
+ inset-inline-end: 0;
425
+ }
426
+
427
+ .handle::after {
428
+ content: "";
429
+ position: absolute;
430
+ inset-block-start: 50%;
431
+ inline-size: 4px;
432
+ block-size: 11px;
433
+ border-radius: 999px;
434
+ background: color-mix(in srgb, #ffffff 82%, transparent);
435
+ transform: translateY(-50%);
436
+ }
437
+
438
+ .handle.start::after {
439
+ inset-inline-start: 4px;
440
+ }
441
+
442
+ .handle.end::after {
443
+ inset-inline-end: 4px;
444
+ }
445
+
446
+ .bar:hover .handle,
447
+ .bar.dragging .handle {
448
+ opacity: 1;
449
+ }
450
+
451
+ .drag-tip {
452
+ position: absolute;
453
+ z-index: 6;
454
+ inset-block-start: -28px;
455
+ inset-inline-start: 50%;
456
+ transform: translateX(-50%);
457
+ padding: 4px 7px;
458
+ border-radius: 6px;
459
+ background: #0f172a;
460
+ color: #f8fafc;
461
+ font-size: 11px;
462
+ font-weight: 700;
463
+ white-space: nowrap;
464
+ pointer-events: none;
465
+ opacity: 0;
466
+ }
467
+
468
+ .bar.dragging .drag-tip {
469
+ opacity: 1;
470
+ }
471
+
472
+ :host([bar-variant="soft"]) .bar.task {
473
+ background:
474
+ linear-gradient(180deg, color-mix(in srgb, #ffffff 38%, transparent), transparent 46%),
475
+ color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 22%, var(--ui-gantt-panel));
476
+ color: var(--ui-gantt-text);
477
+ box-shadow:
478
+ inset 0 0 0 1px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 34%, transparent),
479
+ 0 6px 15px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 14%, transparent);
480
+ }
481
+
482
+ :host([bar-variant="soft"]) .milestone-shape {
483
+ background:
484
+ linear-gradient(135deg, color-mix(in srgb, #ffffff 42%, transparent), transparent 52%),
485
+ color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 28%, var(--ui-gantt-panel));
486
+ box-shadow:
487
+ inset 0 0 0 1px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 42%, transparent),
488
+ 0 7px 16px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 16%, transparent);
489
+ }
490
+
491
+ :host([bar-variant="striped"]) .bar.task {
492
+ background:
493
+ repeating-linear-gradient(135deg, color-mix(in srgb, #ffffff 24%, transparent) 0 7px, transparent 7px 14px),
494
+ var(--tone, var(--ui-gantt-accent));
495
+ }
496
+
497
+ :host([bar-variant="striped"]) .milestone-shape {
498
+ background:
499
+ repeating-linear-gradient(135deg, color-mix(in srgb, #ffffff 28%, transparent) 0 5px, transparent 5px 10px),
500
+ var(--tone, var(--ui-gantt-accent));
501
+ }
502
+
503
+ :host([bar-variant="outline"]) .bar.task {
504
+ background: color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 8%, var(--ui-gantt-panel));
505
+ box-shadow:
506
+ inset 0 0 0 1.5px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 72%, transparent),
507
+ 0 6px 15px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 10%, transparent);
508
+ }
509
+
510
+ :host([bar-variant="outline"]) .bar.task .progress {
511
+ background: color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 28%, transparent);
512
+ opacity: 1;
513
+ }
514
+
515
+ :host([bar-variant="outline"]) .milestone-shape {
516
+ background: color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 10%, var(--ui-gantt-panel));
517
+ box-shadow:
518
+ inset 0 0 0 2px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 72%, transparent),
519
+ 0 7px 16px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 10%, transparent);
520
+ }
521
+
522
+ :host([bar-variant="glass"]) .bar.task {
523
+ background:
524
+ linear-gradient(180deg, color-mix(in srgb, #ffffff 50%, transparent), color-mix(in srgb, #ffffff 10%, transparent)),
525
+ color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 72%, transparent);
526
+ backdrop-filter: blur(8px);
527
+ box-shadow:
528
+ inset 0 1px 0 color-mix(in srgb, #ffffff 48%, transparent),
529
+ inset 0 0 0 1px color-mix(in srgb, #ffffff 26%, transparent),
530
+ 0 10px 24px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 20%, transparent);
531
+ }
532
+
533
+ :host([bar-variant="glass"]) .milestone-shape {
534
+ background:
535
+ linear-gradient(135deg, color-mix(in srgb, #ffffff 52%, transparent), color-mix(in srgb, #ffffff 8%, transparent)),
536
+ color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 74%, transparent);
537
+ backdrop-filter: blur(8px);
538
+ }
539
+
540
+ .today {
541
+ position: absolute;
542
+ inset-block: 0;
543
+ inline-size: 2px;
544
+ background: var(--ui-color-danger, #dc2626);
545
+ z-index: 2;
546
+ pointer-events: none;
547
+ }
548
+
549
+ .today::before {
550
+ content: "Today";
551
+ position: absolute;
552
+ inset-block-start: 4px;
553
+ inset-inline-start: 6px;
554
+ color: var(--ui-color-danger, #dc2626);
555
+ font-size: 11px;
556
+ font-weight: 750;
557
+ }
558
+
559
+ .links {
74
560
  position: absolute;
75
561
  inset: 0;
76
- background: linear-gradient(90deg, color-mix(in srgb, #000 18%, transparent), transparent);
77
- opacity: 0.28;
78
- width: var(--progress, 0%);
562
+ overflow: visible;
563
+ pointer-events: none;
564
+ z-index: 1;
565
+ }
566
+
567
+ .links path {
568
+ fill: none;
569
+ stroke: color-mix(in srgb, var(--ui-gantt-muted) 82%, transparent);
570
+ stroke-width: 1.35;
571
+ stroke-linecap: round;
572
+ stroke-linejoin: round;
573
+ vector-effect: non-scaling-stroke;
574
+ marker-end: url(#ui-gantt-arrow);
575
+ }
576
+
577
+ .links .link-hit {
578
+ stroke: transparent;
579
+ stroke-width: 10;
580
+ marker-end: none;
581
+ pointer-events: stroke;
582
+ cursor: pointer;
583
+ }
584
+
585
+ .links path:not(.link-hit) {
586
+ pointer-events: none;
587
+ }
588
+
589
+ .links marker path {
590
+ fill: color-mix(in srgb, var(--ui-gantt-muted) 82%, transparent);
591
+ stroke: none;
592
+ }
593
+
594
+ .empty {
595
+ padding: 28px;
596
+ color: var(--ui-gantt-muted);
597
+ font-size: 13px;
79
598
  }
80
599
 
81
600
  :host([variant="contrast"]) {
82
601
  --ui-gantt-bg: #0f172a;
602
+ --ui-gantt-panel: #111827;
83
603
  --ui-gantt-border: #334155;
604
+ --ui-gantt-grid: #263449;
84
605
  --ui-gantt-text: #e2e8f0;
85
606
  --ui-gantt-muted: #93a4bd;
86
- --ui-gantt-accent: #93c5fd;
607
+ --ui-gantt-accent: #60a5fa;
608
+ --ui-gantt-selected: rgba(96, 165, 250, 0.13);
609
+ }
610
+
611
+ :host([readonly]) .bar {
612
+ cursor: pointer;
87
613
  }
88
614
 
89
615
  :host([headless]) .frame {
90
616
  display: none;
91
617
  }
92
618
 
93
- @media (prefers-reduced-motion: reduce) {
94
- .bar,
95
- .progress {
96
- transition: none !important;
619
+ @media (max-width: 760px) {
620
+ .surface {
621
+ grid-template-columns: 270px minmax(560px, 1fr);
97
622
  }
98
- }
99
623
 
100
- @media (prefers-contrast: more) {
101
- .frame {
102
- box-shadow: none;
624
+ .grid-head,
625
+ .grid-row {
626
+ grid-template-columns: minmax(150px, 1fr) 64px 56px;
627
+ }
628
+
629
+ .grid-head span:nth-child(3),
630
+ .grid-cell:nth-child(3) {
631
+ display: none;
103
632
  }
633
+ }
104
634
 
105
- .track {
106
- border-width: 2px;
635
+ @media (prefers-reduced-motion: reduce) {
636
+ .bar,
637
+ .progress {
638
+ transition: none !important;
107
639
  }
108
640
  }
109
641
 
110
642
  @media (forced-colors: active) {
111
643
  :host {
112
644
  --ui-gantt-bg: Canvas;
645
+ --ui-gantt-panel: Canvas;
113
646
  --ui-gantt-border: CanvasText;
647
+ --ui-gantt-grid: CanvasText;
114
648
  --ui-gantt-text: CanvasText;
115
649
  --ui-gantt-muted: CanvasText;
116
650
  --ui-gantt-accent: Highlight;
651
+ --ui-gantt-selected: Highlight;
117
652
  }
118
653
 
119
654
  .frame,
120
- .track,
655
+ .grid-head,
656
+ .timeline-head,
657
+ .grid-body,
121
658
  .bar,
122
- .progress {
659
+ .select,
660
+ .control,
661
+ .zoom-button {
123
662
  forced-color-adjust: none;
124
663
  box-shadow: none;
125
- border-color: CanvasText;
126
- }
127
-
128
- .frame,
129
- .track {
130
- background: Canvas;
131
- color: CanvasText;
132
- }
133
-
134
- .bar {
135
- background: Highlight;
136
664
  }
137
665
  }
138
- `, u = {
666
+ `, ft = {
139
667
  default: "var(--ui-color-primary, #2563eb)",
140
668
  info: "var(--ui-color-info, #0891b2)",
141
669
  success: "var(--ui-color-success, #16a34a)",
142
670
  warning: "var(--ui-color-warning, #d97706)",
143
671
  danger: "var(--ui-color-danger, #dc2626)"
144
672
  };
145
- function s(o) {
146
- const e = Date.parse(o);
147
- return Number.isFinite(e) ? e : null;
673
+ function b(n) {
674
+ return String(n ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
675
+ }
676
+ function x(n) {
677
+ if (n instanceof Date) {
678
+ const a = n.getTime();
679
+ return Number.isFinite(a) ? D(a) : null;
680
+ }
681
+ const r = String(n || "").trim(), d = /^(\d{4})-(\d{2})-(\d{2})$/.exec(r);
682
+ if (d) {
683
+ const a = Number(d[1]), e = Number(d[2]), i = Number(d[3]), g = new Date(a, e - 1, i).getTime();
684
+ return Number.isFinite(g) ? D(g) : null;
685
+ }
686
+ const l = Date.parse(r);
687
+ return Number.isFinite(l) ? D(l) : null;
688
+ }
689
+ function D(n) {
690
+ const r = new Date(n);
691
+ return r.setHours(0, 0, 0, 0), r.getTime();
692
+ }
693
+ function w(n, r) {
694
+ return D(n + r * I);
695
+ }
696
+ function S(n) {
697
+ const r = new Date(n), d = r.getFullYear(), l = String(r.getMonth() + 1).padStart(2, "0"), a = String(r.getDate()).padStart(2, "0");
698
+ return `${d}-${l}-${a}`;
699
+ }
700
+ function O(n) {
701
+ return new Intl.DateTimeFormat(void 0, { month: "short", day: "numeric" }).format(new Date(n));
702
+ }
703
+ function bt(n) {
704
+ return n === "day" || n === "month" || n === "quarter" || n === "year" ? n : "week";
705
+ }
706
+ function j(n) {
707
+ return n === "start" || n === "end" || n === "label" || n === "progress" ? n : "none";
708
+ }
709
+ function Ft(n) {
710
+ return n === "soft" || n === "striped" || n === "outline" || n === "glass" ? n : "solid";
148
711
  }
149
- function y(o) {
150
- if (!o)
712
+ function Dt(n, r) {
713
+ if (!n)
151
714
  return [];
152
715
  try {
153
- const e = JSON.parse(o);
154
- return Array.isArray(e) ? e.map((t) => {
155
- if (!t || typeof t != "object")
716
+ const d = JSON.parse(n);
717
+ return Array.isArray(d) ? d.map((l, a) => {
718
+ if (!l || typeof l != "object")
156
719
  return null;
157
- const i = String(t.label || "").trim(), a = String(t.start || "").trim(), n = String(t.end || "").trim();
158
- return !i || !a || !n || s(a) == null || s(n) == null ? null : {
159
- id: t.id ? String(t.id) : void 0,
720
+ const e = l, i = String(e.label || e.text || "").trim(), s = x(e.start), g = Number(e.duration);
721
+ let c = x(e.end);
722
+ if (c == null && s != null && Number.isFinite(g) && (c = w(s, Math.max(0, g))), !i || s == null)
723
+ return null;
724
+ const o = e.type === "summary" || e.type === "milestone" ? e.type : "task", u = o === "milestone" ? s : Math.max(c ?? w(s, 1), w(s, 1)), p = String(e.id || `task-${a + 1}`), m = r.get(p) || {};
725
+ return {
726
+ id: p,
160
727
  label: i,
161
- start: a,
162
- end: n,
163
- progress: Number.isFinite(Number(t.progress)) ? Number(t.progress) : void 0,
164
- tone: t.tone
728
+ start: m.start || S(s),
729
+ end: m.end || S(u),
730
+ progress: Number.isFinite(Number(e.progress)) ? Math.max(0, Math.min(100, Number(e.progress))) : void 0,
731
+ tone: typeof e.tone == "string" ? e.tone : void 0,
732
+ type: o,
733
+ parent: e.parent != null ? String(e.parent) : void 0,
734
+ open: e.open !== !1,
735
+ assignee: e.assignee != null ? String(e.assignee) : e.owner != null ? String(e.owner) : void 0,
736
+ readonly: e.readonly === !0,
737
+ baselineStart: e.baselineStart != null ? String(e.baselineStart) : void 0,
738
+ baselineEnd: e.baselineEnd != null ? String(e.baselineEnd) : void 0,
739
+ critical: e.critical === !0,
740
+ segments: Array.isArray(e.segments) ? e.segments : void 0
165
741
  };
166
742
  }).filter(Boolean) : [];
167
743
  } catch {
168
744
  return [];
169
745
  }
170
746
  }
171
- class k extends w {
747
+ function Rt(n) {
748
+ if (!n)
749
+ return [];
750
+ try {
751
+ const r = JSON.parse(n);
752
+ return Array.isArray(r) ? r.map((d, l) => {
753
+ if (!d || typeof d != "object")
754
+ return null;
755
+ const a = d, e = String(a.source || "").trim(), i = String(a.target || "").trim();
756
+ if (!e || !i)
757
+ return null;
758
+ const s = a.type === "s2s" || a.type === "e2e" || a.type === "s2e" ? a.type : "e2s";
759
+ return { id: String(a.id || `link-${l + 1}`), source: e, target: i, type: s };
760
+ }).filter(Boolean) : [];
761
+ } catch {
762
+ return [];
763
+ }
764
+ }
765
+ function qt(n, r) {
766
+ return r === "none" ? n : [...n].sort((d, l) => {
767
+ if (r === "label")
768
+ return d.label.localeCompare(l.label);
769
+ if (r === "progress")
770
+ return Number(l.progress ?? 0) - Number(d.progress ?? 0);
771
+ const a = x(r === "start" ? d.start : d.end) ?? 0, e = x(r === "start" ? l.start : l.end) ?? 0;
772
+ return a - e;
773
+ });
774
+ }
775
+ function Nt(n, r, d, l) {
776
+ const a = /* @__PURE__ */ new Map(), e = new Map(n.map((o) => [o.id, o]));
777
+ n.forEach((o) => {
778
+ const u = o.parent && e.has(o.parent) ? o.parent : "__root__", p = a.get(u) || [];
779
+ p.push(o), a.set(u, p);
780
+ });
781
+ const i = d.trim().toLowerCase(), s = (o) => !i || o.label.toLowerCase().includes(i) || (o.assignee || "").toLowerCase().includes(i) ? !0 : (a.get(o.id) || []).some(s), g = [], c = (o, u) => {
782
+ qt(a.get(o) || [], l).forEach((p) => {
783
+ if (!s(p))
784
+ return;
785
+ const m = x(p.start), h = x(p.end);
786
+ if (m == null || h == null)
787
+ return;
788
+ const f = (a.get(p.id) || []).length > 0;
789
+ g.push({ ...p, depth: u, hasChildren: f, startMs: m, endMs: h }), f && !r.has(p.id) && c(p.id, u + 1);
790
+ });
791
+ };
792
+ return c("__root__", 0), g;
793
+ }
794
+ function Ht(n, r, d, l, a = 0, e = Number.POSITIVE_INFINITY) {
795
+ const i = Math.max(1, Math.ceil((r - n) / I)), s = l === "day" ? 1 : l === "week" ? 7 : l === "month" ? 30 : l === "quarter" ? 91 : 365, g = Math.max(0, Math.floor(Math.max(0, a - 480) / d / s) * s), c = Math.min(i, Math.ceil((a + e + 960) / d / s) * s), o = [];
796
+ for (let u = g; u <= c; u += s) {
797
+ const p = u * d, m = w(n, u), h = l === "day" ? O(m) : new Intl.DateTimeFormat(void 0, { month: "short", year: l === "week" || l === "month" ? void 0 : "numeric" }).format(new Date(m));
798
+ o.push(`<span class="tick" style="left:${p.toFixed(2)}px">${b(h)}</span>`);
799
+ }
800
+ return o.join("");
801
+ }
802
+ function H(n) {
803
+ return n.type === "summary" || n.hasChildren ? "summary" : n.type === "milestone" || n.startMs === n.endMs ? "milestone" : "task";
804
+ }
805
+ function xt(n) {
806
+ const r = H(n);
807
+ return r === "summary" ? 17 + 9 / 2 : r === "milestone" ? 7 + N / 2 : 12 + 18 / 2;
808
+ }
809
+ function Vt(n, r) {
810
+ return H(n) === "milestone" ? r(n.startMs) - wt : r(n.startMs);
811
+ }
812
+ function Wt(n, r) {
813
+ return H(n) === "milestone" ? r(n.startMs) + wt : r(n.endMs);
814
+ }
815
+ function vt(n, r, d) {
816
+ return r === "start" ? Vt(n, d) : Wt(n, d);
817
+ }
818
+ function _t(n = "e2s") {
819
+ return n === "s2s" ? { source: "start", target: "start" } : n === "e2e" ? { source: "end", target: "end" } : n === "s2e" ? { source: "start", target: "end" } : { source: "end", target: "start" };
820
+ }
821
+ function jt(n, r, d, l, a, e) {
822
+ const i = _t(a), s = i.source === "start" ? -1 : 1, g = i.target === "start" ? 1 : -1, c = vt(n, i.source, e), o = vt(d, i.target, e), u = r * $ + xt(n), p = l * $ + xt(d), m = 22, h = c + s * m, f = o - g * m, v = 26;
823
+ let y = (h + f) / 2;
824
+ if (s === g) {
825
+ const T = s === 1 ? Math.max(h, f, c, o) + v : Math.min(h, f, c, o) - v;
826
+ y = (s === 1 ? f > h + v : f < h - v) ? y : T;
827
+ } else
828
+ (s === 1 ? f < h : f > h) && (y = s === 1 ? Math.max(h, c, o) + v : Math.min(h, c, o) - v);
829
+ const A = Math.abs(p - u) < 1 ? i.target === "start" ? p - 16 : p + 16 : p > u ? p - $ / 2 + 3 : p + $ / 2 - 3;
830
+ return [[c, u], [h, u], [y, u], [y, A], [f, A], [f, p], [o, p]].map(([T, R], P) => `${P === 0 ? "M" : "L"} ${T.toFixed(1)} ${R.toFixed(1)}`).join(" ");
831
+ }
832
+ class Ot extends At {
833
+ constructor() {
834
+ super(...arguments), this.collapsed = /* @__PURE__ */ new Set(), this.overrides = /* @__PURE__ */ new Map(), this.drag = null, this.selectedId = null, this.internalFilter = "", this.viewportTop = 0, this.viewportLeft = 0, this.viewportWidth = 1800, this.viewportHeight = 620, this.renderedFirstRow = 0, this.renderedLastRow = 0, this.scrollFrame = null, this.overridesVersion = 0, this.taskCacheRaw = null, this.taskCacheVersion = -1, this.taskCache = [], this.linkCacheRaw = null, this.linkCache = [], this.visibleCacheKey = "", this.visibleCache = [];
835
+ }
172
836
  static get observedAttributes() {
173
- return ["tasks", "variant", "headless"];
837
+ return ["tasks", "links", "zoom", "filter", "sort", "bar-variant", "variant", "readonly", "show-toolbar", "show-today", "headless"];
174
838
  }
175
839
  render() {
176
- const e = y(this.getAttribute("tasks"));
177
- if (!e.length) {
840
+ const r = this.getTasks(), d = this.getLinks(), l = bt(this.getAttribute("zoom")), a = j(this.getAttribute("sort")), e = Ft(this.getAttribute("bar-variant")), i = this.getAttribute("filter") ?? this.internalFilter, s = this.getVisibleTasks(r, i, a), g = this.hasAttribute("readonly"), c = this.getAttribute("show-toolbar") !== "false", o = this.getAttribute("show-today") !== "false";
841
+ if (!r.length) {
178
842
  this.setContent(`
179
- <style>${d}</style>
843
+ <style>${mt}</style>
180
844
  <section class="frame" part="frame">
181
- <div class="header">No tasks in this range</div>
845
+ <div class="empty" part="empty">No tasks in this range</div>
182
846
  </section>
183
847
  `);
184
848
  return;
185
849
  }
186
- const t = e.map((r) => s(r.start) || 0), i = e.map((r) => s(r.end) || 0), a = Math.min(...t), n = Math.max(...i), c = Math.max(1, n - a), p = e.map((r) => {
187
- const l = s(r.start) || a, b = s(r.end) || l, f = (l - a) / c * 100, x = Math.max(2, (Math.max(b, l) - l) / c * 100), v = Math.max(0, Math.min(100, Number(r.progress ?? 0))), h = u[r.tone || "default"] || u.default;
850
+ const u = r.map((t) => x(t.start)).filter((t) => t != null), p = r.map((t) => x(t.end)).filter((t) => t != null), m = w(Math.min(...u), -2), h = w(Math.max(...p), 3), f = Math.max(7, Math.ceil((h - m) / I)), v = ht[l], y = Math.max(680, f * v), A = Math.max(s.length * $, $), z = this.getAttribute("virtualized") !== "false" && s.length > 240, T = Math.max(20, Math.ceil(Math.max(420, this.viewportHeight - _) / $)), R = 160, P = Math.max(0, this.viewportTop - _), yt = Math.max(0, Math.floor(P / $)), X = Math.max(180, T * 4), L = z ? Math.max(0, Math.floor(yt / X) * X - R) : 0, B = z ? Math.min(s.length, L + T + R * 2 + X) : s.length, K = s.slice(L, B), V = L * $, W = Math.max(0, (s.length - B) * $);
851
+ this.renderedFirstRow = L, this.renderedLastRow = B;
852
+ const Q = new Map(s.map((t, M) => [t.id, { task: t, index: M }])), tt = new Set(K.map((t) => t.id)), C = (t) => (t - m) / I * v, kt = K.map((t, M) => {
853
+ const E = L + M, k = this.selectedId === t.id ? " selected" : "", F = H(t), G = t.progress == null ? "-" : `${Math.round(t.progress)}%`, U = F === "milestone" ? O(t.startMs) : `${O(t.startMs)} - ${O(t.endMs)}`;
188
854
  return `
189
- <article class="row" part="row">
190
- <span class="label" title="${r.label}">${r.label}</span>
191
- <div class="track">
192
- <div class="bar" style="left:${f.toFixed(2)}%;width:${x.toFixed(2)}%;--tone:${h}">
193
- <span class="progress" style="--progress:${v.toFixed(2)}%"></span>
194
- </div>
195
- </div>
196
- </article>
197
- `;
198
- }).join(""), g = new Date(a).toLocaleDateString(), m = new Date(n).toLocaleDateString();
855
+ <article class="grid-row${k}" part="grid-row" data-task-id="${b(t.id)}" role="row" aria-rowindex="${E + 2}" aria-level="${t.depth + 1}" ${t.hasChildren ? `aria-expanded="${!this.collapsed.has(t.id)}"` : ""}>
856
+ <div class="grid-cell task-name" role="gridcell" style="padding-left:${10 + t.depth * 18}px">
857
+ ${t.hasChildren ? `<button class="expander" data-action="toggle" data-task-id="${b(t.id)}" aria-label="${this.collapsed.has(t.id) ? "Expand" : "Collapse"} ${b(t.label)}">${this.collapsed.has(t.id) ? "›" : "⌄"}</button>` : '<span style="inline-size:22px"></span>'}
858
+ <span class="name-text" title="${b(t.label)}">${b(t.label)}</span>
859
+ </div>
860
+ <div class="grid-cell badge" role="gridcell">${b(t.assignee || F)}</div>
861
+ <div class="grid-cell badge" role="gridcell">${b(U)}</div>
862
+ <div class="grid-cell badge" role="gridcell">${b(G)}</div>
863
+ </article>
864
+ `;
865
+ }).join(""), $t = K.map((t, M) => {
866
+ var st;
867
+ const E = L + M, k = H(t), F = this.selectedId === t.id ? " selected" : "", G = this.selectedId === t.id || !this.selectedId && E === 0, U = ft[t.tone || (k === "summary" ? "info" : "default")] || ft.default, rt = k === "milestone" ? C(t.startMs) - N / 2 : C(t.startMs), zt = C(k === "milestone" ? t.startMs : t.endMs), Ct = k === "milestone" ? N : Math.max(12, zt - rt), Tt = Math.max(0, Math.min(100, Number(t.progress ?? 0))), at = !g && !t.readonly && k !== "summary", nt = at && k === "task", it = k === "task" && ((st = t.segments) != null && st.length) ? t.segments.map((ot) => {
868
+ const lt = x(ot.start), dt = x(ot.end);
869
+ if (lt == null || dt == null)
870
+ return "";
871
+ const ct = Math.max(I, t.endMs - t.startMs), Z = Math.max(t.startMs, lt), pt = Math.min(t.endMs, dt);
872
+ if (pt <= Z)
873
+ return "";
874
+ const ut = Math.max(0, Math.min(100, (Z - t.startMs) / ct * 100)), It = (pt - Z) / ct * 100, gt = Math.min(100 - ut, Math.max(1, It));
875
+ return gt <= 0 ? "" : `<span class="split-segment" style="left:${ut.toFixed(2)}%;width:${gt.toFixed(2)}%"></span>`;
876
+ }).join("") : "";
877
+ return `
878
+ <article class="timeline-row${F}" part="timeline-row" data-task-id="${b(t.id)}" role="row" aria-rowindex="${E + 2}">
879
+ ${t.baselineStart && t.baselineEnd ? `<span class="baseline" style="left:${C(x(t.baselineStart) || t.startMs).toFixed(2)}px;width:${Math.max(4, C(x(t.baselineEnd) || t.endMs) - C(x(t.baselineStart) || t.startMs)).toFixed(2)}px"></span>` : ""}
880
+ <button
881
+ class="bar ${k}${t.critical ? " critical" : ""}${it ? " split" : ""}"
882
+ part="bar ${k}"
883
+ data-task-id="${b(t.id)}"
884
+ data-kind="${k}"
885
+ role="gridcell"
886
+ tabindex="${G ? "0" : "-1"}"
887
+ aria-selected="${this.selectedId === t.id}"
888
+ aria-colindex="5"
889
+ style="left:${rt.toFixed(2)}px;width:${Ct.toFixed(2)}px;--tone:${U};--progress:${Tt.toFixed(2)}%"
890
+ aria-label="${b(t.label)}"
891
+ ${at ? 'data-draggable="true"' : ""}
892
+ >
893
+ ${nt ? '<span class="handle start" data-mode="resize-start"></span>' : ""}
894
+ <span class="progress"></span>
895
+ ${it}
896
+ ${k === "milestone" ? '<span class="milestone-shape"></span>' : ""}
897
+ <span class="drag-tip"></span>
898
+ ${nt ? '<span class="handle end" data-mode="resize-end"></span>' : ""}
899
+ </button>
900
+ </article>
901
+ `;
902
+ }).join(""), et = d.map((t) => {
903
+ const M = Q.get(t.source), E = Q.get(t.target);
904
+ if (!M || !E || z && !tt.has(t.source) && !tt.has(t.target))
905
+ return "";
906
+ const k = jt(M.task, M.index, E.task, E.index, t.type, C), F = `${M.task.label} dependency to ${E.task.label}`;
907
+ return `<path class="link-hit" d="${k}" data-link-id="${b(t.id)}" data-source-id="${b(t.source)}" data-target-id="${b(t.target)}" data-link-type="${b(t.type || "e2s")}" aria-label="${b(F)}" /><path d="${k}" />`;
908
+ }).join(""), Mt = et ? '<defs><marker id="ui-gantt-arrow" markerWidth="9" markerHeight="9" refX="8.5" refY="4.5" orient="auto" markerUnits="userSpaceOnUse"><path d="M 0.5 0.75 L 8.5 4.5 L 0.5 8.25 z" /></marker></defs>' : "", Y = D(Date.now()), Et = C(Y), St = o && Y >= m && Y <= h ? `<div class="today" part="today" style="left:${Et.toFixed(2)}px"></div>` : "";
199
909
  this.setContent(`
200
- <style>${d}</style>
201
- <section class="frame" part="frame">
202
- <header class="header" part="header"><span>${g}</span><span>${m}</span></header>
203
- <div class="rows" part="rows">${p}</div>
910
+ <style>${mt}</style>
911
+ <section class="frame" part="frame" data-bar-variant="${e}">
912
+ ${c ? `
913
+ <header class="toolbar" part="toolbar">
914
+ <div class="title">Project schedule <span class="meta">${s.length}/${r.length} tasks</span></div>
915
+ <div class="controls">
916
+ <input class="control" value="${b(i)}" placeholder="Filter tasks" aria-label="Filter tasks" />
917
+ <select class="select" aria-label="Sort tasks">
918
+ ${[
919
+ ["none", "Manual"],
920
+ ["start", "Start date"],
921
+ ["end", "End date"],
922
+ ["label", "Name"],
923
+ ["progress", "Progress"]
924
+ ].map(([t, M]) => `<option value="${t}" ${a === t ? "selected" : ""}>${M}</option>`).join("")}
925
+ </select>
926
+ ${["day", "week", "month", "quarter", "year"].map((t) => `<button class="zoom-button" data-zoom="${t}" aria-pressed="${l === t}">${t}</button>`).join("")}
927
+ </div>
928
+ </header>
929
+ ` : ""}
930
+ <div class="viewport" part="viewport">
931
+ <div class="surface" part="surface" style="--day-width:${v}px;grid-template-columns:${J}px ${y.toFixed(2)}px;inline-size:max(100%, ${(J + y).toFixed(2)}px)">
932
+ <header class="grid-head" part="grid-head" role="row">
933
+ <button class="head-button" data-sort="label" role="columnheader" aria-sort="${a === "label" ? "ascending" : "none"}">Task</button>
934
+ <span role="columnheader">Owner</span>
935
+ <button class="head-button" data-sort="start" role="columnheader" aria-sort="${a === "start" ? "ascending" : "none"}">Dates</button>
936
+ <button class="head-button" data-sort="progress" role="columnheader" aria-sort="${a === "progress" ? "descending" : "none"}">%</button>
937
+ </header>
938
+ <header class="timeline-head" part="timeline-head">
939
+ <div class="scale" style="width:${y.toFixed(2)}px">${Ht(m, h, v, l, this.viewportLeft, this.viewportWidth)}</div>
940
+ </header>
941
+ <div class="grid-body" part="grid-body" role="treegrid" aria-rowcount="${s.length + 1}" aria-colcount="4">
942
+ ${z && V ? `<div style="height:${V}px"></div>` : ""}
943
+ ${kt || '<div class="empty">No matching tasks</div>'}
944
+ ${z && W ? `<div style="height:${W}px"></div>` : ""}
945
+ </div>
946
+ <div class="timeline-body" part="timeline-body" style="width:${y.toFixed(2)}px;min-height:${A}px">
947
+ <svg class="links" width="${y.toFixed(0)}" height="${A}" viewBox="0 0 ${y.toFixed(0)} ${A}" aria-hidden="true">${Mt}${et}</svg>
948
+ ${St}
949
+ ${z && V ? `<div style="height:${V}px"></div>` : ""}
950
+ ${$t}
951
+ ${z && W ? `<div style="height:${W}px"></div>` : ""}
952
+ </div>
953
+ </div>
954
+ </div>
204
955
  </section>
205
956
  `);
957
+ const q = this.root.querySelector(".viewport");
958
+ q && (q.scrollTop = this.viewportTop, q.scrollLeft = this.viewportLeft, this.viewportWidth = q.clientWidth || this.viewportWidth, this.viewportHeight = q.clientHeight || this.viewportHeight), this.bindEvents();
959
+ }
960
+ bindEvents() {
961
+ var r, d, l;
962
+ this.root.querySelectorAll("[data-task-id]").forEach((a) => {
963
+ a.addEventListener("click", (e) => {
964
+ var o;
965
+ const i = e.target, s = e.currentTarget;
966
+ if (!s.classList.contains("bar") && i.closest(".bar"))
967
+ return;
968
+ if ((o = this.drag) != null && o.moved) {
969
+ e.stopPropagation();
970
+ return;
971
+ }
972
+ const c = e.currentTarget.dataset.taskId;
973
+ c && (i.closest('[data-action="toggle"]') || (s.classList.contains("bar") && e.stopPropagation(), this.selectTask(c)));
974
+ });
975
+ }), this.root.querySelectorAll('[data-action="toggle"]').forEach((a) => {
976
+ a.addEventListener("click", (e) => {
977
+ e.stopPropagation();
978
+ const i = a.dataset.taskId;
979
+ i && (this.collapsed.has(i) ? this.collapsed.delete(i) : this.collapsed.add(i), this.requestRender());
980
+ });
981
+ }), this.root.querySelectorAll("[data-zoom]").forEach((a) => {
982
+ a.addEventListener("click", () => {
983
+ const e = a.dataset.zoom || "week";
984
+ this.setAttribute("zoom", e), this.dispatchEvent(new CustomEvent("zoomchange", { detail: { zoom: e }, bubbles: !0, composed: !0 }));
985
+ });
986
+ }), this.root.querySelectorAll(".head-button[data-sort]").forEach((a) => {
987
+ a.addEventListener("click", () => {
988
+ const e = j(a.dataset.sort || null);
989
+ e === "none" ? this.removeAttribute("sort") : this.setAttribute("sort", e), this.dispatchEvent(new CustomEvent("sortchange", { detail: { sort: e }, bubbles: !0, composed: !0 }));
990
+ });
991
+ }), (r = this.root.querySelector(".viewport")) == null || r.addEventListener("scroll", (a) => {
992
+ const e = a.currentTarget, i = e.scrollTop, s = e.scrollLeft, g = Math.max(20, Math.ceil(Math.max(420, this.viewportHeight - _) / $)), c = Math.max(0, Math.floor(Math.max(0, i - _) / $)), o = c < this.renderedFirstRow + 64 || c + g > this.renderedLastRow - 64, u = Math.abs(s - this.viewportLeft) >= Math.max(480, this.viewportWidth * 0.6);
993
+ this.viewportTop = i, this.viewportLeft = s, !(!o && !u) && this.scrollFrame == null && (this.scrollFrame = window.setTimeout(() => {
994
+ this.scrollFrame = null, this.requestRender();
995
+ }, 24));
996
+ }, { passive: !0 }), (d = this.root.querySelector(".control")) == null || d.addEventListener("input", (a) => {
997
+ this.internalFilter = a.currentTarget.value, this.dispatchEvent(new CustomEvent("filterchange", { detail: { filter: this.internalFilter }, bubbles: !0, composed: !0 })), this.requestRender();
998
+ }), (l = this.root.querySelector(".select")) == null || l.addEventListener("change", (a) => {
999
+ const e = j(a.currentTarget.value);
1000
+ e === "none" ? this.removeAttribute("sort") : this.setAttribute("sort", e), this.dispatchEvent(new CustomEvent("sortchange", { detail: { sort: e }, bubbles: !0, composed: !0 }));
1001
+ }), this.root.querySelectorAll(".link-hit").forEach((a) => {
1002
+ a.addEventListener("click", (e) => {
1003
+ e.stopPropagation(), this.dispatchEvent(new CustomEvent("linkselect", {
1004
+ detail: {
1005
+ id: a.dataset.linkId || "",
1006
+ source: a.dataset.sourceId || "",
1007
+ target: a.dataset.targetId || "",
1008
+ type: a.dataset.linkType || "e2s"
1009
+ },
1010
+ bubbles: !0,
1011
+ composed: !0
1012
+ }));
1013
+ });
1014
+ }), !this.hasAttribute("readonly") && this.root.querySelectorAll('.bar[data-draggable="true"]').forEach((a) => {
1015
+ a.addEventListener("pointerdown", (i) => {
1016
+ if (i.button !== 0)
1017
+ return;
1018
+ const s = a.dataset.taskId;
1019
+ if (!s)
1020
+ return;
1021
+ const c = this.getTasks().find((y) => y.id === s), o = x(c == null ? void 0 : c.start), u = x(c == null ? void 0 : c.end);
1022
+ if (!c || o == null || u == null)
1023
+ return;
1024
+ i.preventDefault();
1025
+ const p = a.dataset.kind || "task", m = i.target.dataset.mode || "move", h = p === "milestone" ? "move" : m, f = bt(this.getAttribute("zoom")), v = this.selectedId !== s;
1026
+ this.selectedId = s, this.dispatchEvent(new CustomEvent("taskselect", { detail: { id: s }, bubbles: !0, composed: !0 })), this.drag = {
1027
+ taskId: s,
1028
+ mode: h,
1029
+ startX: i.clientX,
1030
+ originalStart: o,
1031
+ originalEnd: u,
1032
+ kind: p,
1033
+ rangeMin: this.rangeMin(),
1034
+ dayWidth: ht[f],
1035
+ latestStart: o,
1036
+ latestEnd: u,
1037
+ frame: null,
1038
+ bar: a,
1039
+ moved: !1,
1040
+ selectedChanged: v
1041
+ }, a.classList.add("dragging"), a.setPointerCapture(i.pointerId);
1042
+ }), a.addEventListener("pointermove", (i) => {
1043
+ if (!this.drag || this.drag.taskId !== a.dataset.taskId)
1044
+ return;
1045
+ i.preventDefault();
1046
+ const s = i.clientX - this.drag.startX;
1047
+ Math.abs(s) > 2 && (this.drag.moved = !0);
1048
+ let g = this.drag.originalStart, c = this.drag.originalEnd;
1049
+ if (this.drag.mode === "move") {
1050
+ const o = Math.round(s / this.drag.dayWidth);
1051
+ g = w(this.drag.originalStart, o), c = this.drag.kind === "milestone" ? g : w(this.drag.originalEnd, o);
1052
+ } else if (this.drag.mode === "resize-start") {
1053
+ const o = Math.round(s / this.drag.dayWidth);
1054
+ g = Math.min(w(this.drag.originalStart, o), w(c, -1));
1055
+ } else {
1056
+ const o = Math.round(s / this.drag.dayWidth);
1057
+ c = Math.max(w(this.drag.originalEnd, o), w(g, 1));
1058
+ }
1059
+ this.drag.latestStart = g, this.drag.latestEnd = c, this.drag.frame == null && (this.drag.frame = requestAnimationFrame(() => {
1060
+ if (!this.drag)
1061
+ return;
1062
+ this.drag.frame = null;
1063
+ const o = (this.drag.latestStart - this.drag.rangeMin) / I * this.drag.dayWidth - (this.drag.kind === "milestone" ? N / 2 : 0), u = this.drag.kind === "milestone" ? N : Math.max(12, (this.drag.latestEnd - this.drag.latestStart) / I * this.drag.dayWidth);
1064
+ this.drag.bar.style.left = `${o.toFixed(2)}px`, this.drag.bar.style.width = `${u.toFixed(2)}px`;
1065
+ const p = this.drag.bar.querySelector(".drag-tip");
1066
+ p && (p.textContent = this.drag.kind === "milestone" ? S(this.drag.latestStart) : `${S(this.drag.latestStart)} - ${S(this.drag.latestEnd)}`);
1067
+ }));
1068
+ });
1069
+ const e = () => {
1070
+ if (!this.drag)
1071
+ return;
1072
+ this.drag.frame != null && cancelAnimationFrame(this.drag.frame);
1073
+ const i = this.drag;
1074
+ if (i.bar.classList.remove("dragging"), this.drag = null, !i.moved) {
1075
+ i.selectedChanged && this.requestRender();
1076
+ return;
1077
+ }
1078
+ const s = {
1079
+ id: i.taskId,
1080
+ start: S(i.latestStart),
1081
+ end: S(i.latestEnd)
1082
+ };
1083
+ this.overrides.set(i.taskId, { start: s.start, end: s.end }), this.bumpOverrides(), this.dispatchEvent(new CustomEvent("taskchange", {
1084
+ detail: s,
1085
+ bubbles: !0,
1086
+ composed: !0
1087
+ })), this.requestRender();
1088
+ };
1089
+ a.addEventListener("pointerup", e), a.addEventListener("pointercancel", e), a.addEventListener("keydown", (i) => {
1090
+ const s = a.dataset.taskId;
1091
+ if (!s)
1092
+ return;
1093
+ if (i.key === "Delete" || i.key === "Backspace") {
1094
+ i.preventDefault(), this.dispatchEvent(new CustomEvent("taskdelete", { detail: { id: s }, bubbles: !0, composed: !0 }));
1095
+ return;
1096
+ }
1097
+ if (i.key === "Enter" || i.key === " ") {
1098
+ i.preventDefault(), this.selectTask(s);
1099
+ return;
1100
+ }
1101
+ if (i.key === "ArrowUp" || i.key === "ArrowDown") {
1102
+ i.preventDefault(), this.selectAdjacentTask(s, i.key === "ArrowDown" ? 1 : -1);
1103
+ return;
1104
+ }
1105
+ if (i.key !== "ArrowLeft" && i.key !== "ArrowRight")
1106
+ return;
1107
+ const c = this.getTasks().find((y) => y.id === s), o = x(c == null ? void 0 : c.start), u = x(c == null ? void 0 : c.end);
1108
+ if (!c || o == null || u == null)
1109
+ return;
1110
+ i.preventDefault();
1111
+ const p = i.key === "ArrowLeft" ? -1 : 1, m = a.dataset.kind === "milestone", h = i.shiftKey && !m ? o : w(o, p), f = m ? h : i.shiftKey ? Math.max(w(u, p), w(o, 1)) : w(u, p), v = { id: s, start: S(h), end: S(f) };
1112
+ this.overrides.set(s, { start: v.start, end: v.end }), this.bumpOverrides(), this.dispatchEvent(new CustomEvent("taskchange", { detail: v, bubbles: !0, composed: !0 })), this.requestRender();
1113
+ });
1114
+ });
1115
+ }
1116
+ rangeMin() {
1117
+ const d = this.getTasks().map((l) => x(l.start)).filter((l) => l != null);
1118
+ return d.length ? w(Math.min(...d), -2) : D(Date.now());
1119
+ }
1120
+ selectTask(r) {
1121
+ if (this.captureViewport(), this.selectedId === r) {
1122
+ this.dispatchEvent(new CustomEvent("taskselect", { detail: { id: r }, bubbles: !0, composed: !0 }));
1123
+ return;
1124
+ }
1125
+ this.selectedId = r, this.dispatchEvent(new CustomEvent("taskselect", { detail: { id: r }, bubbles: !0, composed: !0 })), this.requestRender();
1126
+ }
1127
+ selectAdjacentTask(r, d) {
1128
+ const l = this.getVisibleTasks(
1129
+ this.getTasks(),
1130
+ this.getAttribute("filter") ?? this.internalFilter,
1131
+ j(this.getAttribute("sort"))
1132
+ ), a = l.findIndex((i) => i.id === r), e = l[Math.max(0, Math.min(l.length - 1, a + d))];
1133
+ !e || e.id === r || (this.selectedId = e.id, this.dispatchEvent(new CustomEvent("taskselect", { detail: { id: e.id }, bubbles: !0, composed: !0 })), this.requestRender(), window.requestAnimationFrame(() => {
1134
+ var i;
1135
+ (i = this.root.querySelector(`.bar[data-task-id="${CSS.escape(e.id)}"]`)) == null || i.focus();
1136
+ }));
1137
+ }
1138
+ captureViewport() {
1139
+ const r = this.root.querySelector(".viewport");
1140
+ r && (this.viewportTop = r.scrollTop, this.viewportLeft = r.scrollLeft, this.viewportWidth = r.clientWidth || this.viewportWidth, this.viewportHeight = r.clientHeight || this.viewportHeight);
1141
+ }
1142
+ getTasks() {
1143
+ const r = this.getAttribute("tasks");
1144
+ return r === this.taskCacheRaw && this.taskCacheVersion === this.overridesVersion ? this.taskCache : (this.taskCacheRaw = r, this.taskCacheVersion = this.overridesVersion, this.taskCache = Dt(r, this.overrides), this.visibleCacheKey = "", this.taskCache);
1145
+ }
1146
+ getLinks() {
1147
+ const r = this.getAttribute("links");
1148
+ return r === this.linkCacheRaw ? this.linkCache : (this.linkCacheRaw = r, this.linkCache = Rt(r), this.linkCache);
1149
+ }
1150
+ getVisibleTasks(r, d, l) {
1151
+ const a = Array.from(this.collapsed).sort().join(","), e = `${this.taskCacheRaw || ""}|${this.overridesVersion}|${d}|${l}|${a}`;
1152
+ return e === this.visibleCacheKey ? this.visibleCache : (this.visibleCacheKey = e, this.visibleCache = Nt(r, this.collapsed, d, l), this.visibleCache);
1153
+ }
1154
+ bumpOverrides() {
1155
+ this.overridesVersion += 1, this.taskCacheVersion = -1, this.visibleCacheKey = "";
206
1156
  }
207
- shouldRenderOnAttributeChange(e, t, i) {
208
- return !0;
1157
+ shouldRenderOnAttributeChange(r, d, l) {
1158
+ return r === "tasks" && (this.overrides.clear(), this.bumpOverrides(), this.viewportTop = 0, this.viewportLeft = 0), r === "links" && (this.linkCacheRaw = null), !0;
209
1159
  }
210
1160
  }
211
- typeof customElements < "u" && !customElements.get("ui-gantt") && customElements.define("ui-gantt", k);
1161
+ typeof customElements < "u" && !customElements.get("ui-gantt") && customElements.define("ui-gantt", Ot);
212
1162
  export {
213
- k as UIGantt
1163
+ Ot as UIGantt
214
1164
  };