@editora/ui-core 0.1.24 → 0.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/gantt.cjs.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const At=require("./ElementBase-DxbvhLJs.js"),I=1440*60*1e3,y=42,V=62,J=376,N=28,Lt=15,vt=Lt*Math.SQRT2/2,gt={day:36,week:13,month:4.4,quarter:2.2,year:1.15},ht=`
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const Ee=require("./ElementBase-DxbvhLJs.js");function H(n,t){const e=new Date(n);return e.setDate(e.getDate()+t),e.setHours(0,0,0,0),e.getTime()}function wt(n,t){return Math.round((t-n)/864e5)}function le(n){const t=new Set(n.map(r=>r.id)),e=new Map;for(const r of n){if(!r.parent||!t.has(r.parent)||r.parent===r.id)continue;const i=e.get(r.parent)||[];i.push(r),e.set(r.parent,i)}return e}function de(n,t){const e=le(n),r=[],i=new Set([t]),a=[t];for(;a.length;)for(const s of e.get(a.shift())||[])i.has(s.id)||(i.add(s.id),r.push(s),a.push(s.id));return r}function Te(n){const t=le(n),e=new Map,r=new Set,i=a=>{const s=e.get(a.id);if(s)return s;const o={start:a.start,end:a.end,progress:a.progress},c=t.get(a.id);if(!c?.length||r.has(a.id))return o;r.add(a.id);let h=1/0,d=-1/0,g=0,u=0;for(const m of c){const p=i(m);if(h=Math.min(h,p.start),d=Math.max(d,p.end),p.progress!=null){const k=Math.max(1,wt(p.start,p.end));g+=k,u+=p.progress*k}}r.delete(a.id);const b={start:h,end:d,progress:g?u/g:void 0};return e.set(a.id,b),b};for(const a of n)i(a);return e}function Bt(n,t){const e=n.type==="e2s"||n.type==="e2e"?t.end:t.start;return{side:n.type==="e2s"||n.type==="s2s"?"start":"end",min:H(e,n.lag)}}function Ae(n,t,e){const r=Bt(n,t);return(r.side==="start"?e.start:e.end)>=r.min}function Le(n,t){const e=new Map(n.map(i=>[i.id,i])),r=new Set;for(const i of t){const a=e.get(i.source),s=e.get(i.target);a&&s&&!Ae(i,a,s)&&r.add(i.id)}return r}function Ie(n,t,e,r=!1){const i=new Map(n.map(u=>[u.id,{start:u.start,end:u.end}]));for(const[u,b]of e)i.has(u)&&i.set(u,{...b});const a=new Map(n.map(u=>[u.id,u])),s=new Map;for(const u of t){const b=s.get(u.source)||[];b.push(u),s.set(u.source,b)}const o=new Set,c=new Map,h=[...e.keys()],d=(u,b)=>{const m=i.get(u);m&&(i.set(u,{start:H(m.start,b),end:H(m.end,b)}),o.add(u))};for(;h.length;){const u=h.shift(),b=i.get(u);if(b)for(const m of s.get(u)||[]){const p=a.get(m.target),k=i.get(m.target);if(!p||!k||p.readonly)continue;const y=Bt(m,b),w=y.side==="start"?k.start:k.end;if(w>=y.min)continue;const x=(c.get(m.target)||0)+1;if(x>n.length)continue;c.set(m.target,x);const M=wt(w,y.min);if(d(m.target,M),h.push(m.target),r)for(const T of de(n,m.target))T.readonly||(d(T.id,M),h.push(T.id))}}const g=[];for(const u of o){const b=i.get(u),m=e.get(u);(!m||m.start!==b.start||m.end!==b.end)&&g.push({id:u,...b})}return g}function ze(n,t){const e=new Set(n.map(p=>p.parent).filter(p=>!!p)),r=new Map(n.filter(p=>!e.has(p.id)).map(p=>[p.id,p])),i=t.filter(p=>r.has(p.source)&&r.has(p.target)&&p.source!==p.target);if(!i.length)return new Set;const a=p=>wt(p.start,p.end),s=Math.max(...[...r.values()].map(p=>p.end)),o=new Map,c=new Map;for(const p of i)o.set(p.source,[...o.get(p.source)||[],p]),c.set(p.target,[...c.get(p.target)||[],p]);const h=new Map([...r.keys()].map(p=>[p,s])),d=new Map([...r.keys()].map(p=>[p,(o.get(p)||[]).length])),g=[...r.keys()].filter(p=>d.get(p)===0),u=new Set;for(;g.length;){const p=g.shift();u.add(p);const k=r.get(p),y=h.get(p),w=H(y,-a(k));for(const x of c.get(p)||[]){const M=r.get(x.source),T=-x.lag,z=x.type==="e2s"?H(w,T):x.type==="s2s"?H(H(w,T),a(M)):x.type==="e2e"?H(y,T):H(H(y,T),a(M));h.set(x.source,Math.min(h.get(x.source),z));const U=d.get(x.source)-1;d.set(x.source,U),U===0&&g.push(x.source)}}const b=new Set;for(const p of i){const k=r.get(p.source),y=r.get(p.target),w=Bt(p,k);(w.side==="start"?y.start:y.end)===w.min&&b.add(p.target)}const m=new Set;for(const p of u){const k=r.get(p),y=(o.get(p)||[]).length+(c.get(p)||[]).length>0,w=wt(k.end,h.get(p));y&&w<=0&&(!(c.get(p)||[]).length||b.has(p))&&m.add(p)}return m}const D=1440*60*1e3,S=42,N=62,Tt=376,ce="minmax(170px, 1fr) 82px 80px 44px",Ce=640,De=148,Fe=2,vt=28,Re=15,he=Re*Math.SQRT2/2,Jt={day:36,week:13,month:4.4,quarter:2.2,year:1.15},Gt=`
2
2
  :host {
3
3
  --ui-gantt-bg: color-mix(in srgb, var(--ui-color-surface, #ffffff) 96%, transparent);
4
4
  --ui-gantt-panel: var(--ui-color-surface, #ffffff);
@@ -21,6 +21,7 @@
21
21
  }
22
22
 
23
23
  .frame {
24
+ container: ui-gantt / inline-size;
24
25
  border: 1px solid var(--ui-gantt-border);
25
26
  border-radius: 12px;
26
27
  background: var(--ui-gantt-bg);
@@ -60,7 +61,9 @@
60
61
 
61
62
  .control,
62
63
  .select,
63
- .zoom-button {
64
+ .zoom-button,
65
+ .today-button,
66
+ .add-button {
64
67
  border: 1px solid var(--ui-gantt-border);
65
68
  background: var(--ui-gantt-panel);
66
69
  color: var(--ui-gantt-text);
@@ -80,11 +83,18 @@
80
83
  min-inline-size: 118px;
81
84
  }
82
85
 
83
- .zoom-button {
86
+ .zoom-button,
87
+ .today-button,
88
+ .add-button {
84
89
  padding: 0 9px;
85
90
  cursor: pointer;
86
91
  }
87
92
 
93
+ .today-button:disabled {
94
+ opacity: 0.45;
95
+ cursor: default;
96
+ }
97
+
88
98
  .zoom-button[aria-pressed="true"] {
89
99
  border-color: color-mix(in srgb, var(--ui-gantt-accent) 46%, var(--ui-gantt-border));
90
100
  background: color-mix(in srgb, var(--ui-gantt-accent) 10%, var(--ui-gantt-panel));
@@ -96,19 +106,67 @@
96
106
  max-block-size: var(--ui-gantt-max-height, 620px);
97
107
  }
98
108
 
109
+ /* fill: the chart takes the height of its container (which needs one): the toolbar keeps its height and the
110
+ scroll area takes the rest, however tall. */
111
+ :host([fill]) {
112
+ block-size: 100%;
113
+ }
114
+
115
+ :host([fill]) .frame {
116
+ display: flex;
117
+ flex-direction: column;
118
+ block-size: 100%;
119
+ }
120
+
121
+ :host([fill]) .toolbar {
122
+ flex: none;
123
+ }
124
+
125
+ :host([fill]) .viewport {
126
+ flex: 1 1 0;
127
+ min-block-size: 0;
128
+ max-block-size: none;
129
+ }
130
+
99
131
  .surface {
132
+ --gantt-grid-w: ${Tt}px;
133
+ --gantt-cols: ${ce};
100
134
  display: grid;
101
- grid-template-columns: ${J}px minmax(640px, 1fr);
135
+ grid-template-columns: var(--gantt-grid-w) minmax(640px, 1fr);
102
136
  min-inline-size: 100%;
103
137
  }
104
138
 
139
+ /* The task grid is sticky, so at 376px it covers a phone-sized chart completely and no bar can be
140
+ reached. In a narrow host keep only the task name column and give the timeline the room. */
141
+ @container ui-gantt (max-width: 640px) {
142
+ .surface {
143
+ --gantt-grid-w: 148px;
144
+ --gantt-cols: minmax(0, 1fr);
145
+ }
146
+
147
+ .splitter-rail {
148
+ display: none;
149
+ }
150
+
151
+ /* !important: the sortable header wrappers carry an inline display: contents. */
152
+ .grid-head > :nth-child(n + 2),
153
+ .grid-cell:nth-child(n + 2) {
154
+ display: none !important;
155
+ }
156
+
157
+ .grid-cell.task-name {
158
+ padding-inline-end: 6px;
159
+ }
160
+ }
161
+
105
162
  .grid-head,
106
163
  .timeline-head {
107
164
  position: sticky;
108
165
  top: 0;
109
166
  z-index: 4;
110
167
  min-block-size: 62px;
111
- background: color-mix(in srgb, var(--ui-gantt-panel) 96%, transparent);
168
+ /* Opaque: the rows scroll underneath, and at 96% they showed through as a ghost of the text. */
169
+ background: var(--ui-gantt-panel);
112
170
  border-bottom: 1px solid var(--ui-gantt-border);
113
171
  }
114
172
 
@@ -127,7 +185,7 @@
127
185
  left: 0;
128
186
  z-index: 5;
129
187
  display: grid;
130
- grid-template-columns: minmax(170px, 1fr) 82px 80px 44px;
188
+ grid-template-columns: var(--gantt-cols);
131
189
  border-right: 1px solid var(--ui-gantt-border);
132
190
  }
133
191
 
@@ -183,15 +241,79 @@
183
241
  min-block-size: 62px;
184
242
  }
185
243
 
244
+ .band {
245
+ position: absolute;
246
+ inset-inline: 0;
247
+ }
248
+
249
+ .band.top {
250
+ inset-block-start: 0;
251
+ block-size: 26px;
252
+ border-bottom: 1px solid var(--ui-gantt-grid);
253
+ }
254
+
255
+ .band.bottom {
256
+ inset-block-start: 26px;
257
+ block-size: 36px;
258
+ }
259
+
260
+ .scale[data-zoom="year"] .band.bottom {
261
+ inset-block-start: 0;
262
+ block-size: 62px;
263
+ }
264
+
265
+ .band-cell,
186
266
  .tick {
187
267
  position: absolute;
188
268
  inset-block: 0;
269
+ display: flex;
270
+ align-items: center;
271
+ overflow: hidden;
189
272
  border-left: 1px solid var(--ui-gantt-grid);
190
- padding: 9px 8px;
273
+ padding: 0 8px;
191
274
  color: var(--ui-gantt-muted);
192
275
  font-size: 11px;
193
276
  font-weight: 650;
194
277
  white-space: nowrap;
278
+ text-overflow: ellipsis;
279
+ }
280
+
281
+ .band-cell {
282
+ color: var(--ui-gantt-text);
283
+ font-weight: 700;
284
+ }
285
+
286
+ /* Labels sit at the top of their row so the today pill has the bottom of it to itself. */
287
+ .band.bottom .tick {
288
+ align-items: flex-start;
289
+ padding-block-start: 6px;
290
+ }
291
+
292
+ .scale[data-zoom="year"] .tick {
293
+ padding-block-start: 9px;
294
+ }
295
+
296
+ .tick.day {
297
+ flex-direction: column;
298
+ justify-content: flex-start;
299
+ gap: 1px;
300
+ padding: 3px 0 0;
301
+ line-height: 1.15;
302
+ }
303
+
304
+ .tick .dow {
305
+ font-size: 9px;
306
+ font-weight: 600;
307
+ opacity: 0.8;
308
+ }
309
+
310
+ .tick .num {
311
+ font-variant-numeric: tabular-nums;
312
+ }
313
+
314
+ .tick.today-cell {
315
+ color: var(--ui-gantt-accent);
316
+ background: color-mix(in srgb, var(--ui-gantt-accent) 9%, transparent);
195
317
  }
196
318
 
197
319
  .grid-body {
@@ -204,24 +326,165 @@
204
326
 
205
327
  .timeline-body {
206
328
  position: relative;
329
+ /* grid lines can run a scrollbar's width past the track; clip them, and keep y visible for drag tips */
330
+ overflow-x: clip;
207
331
  background:
208
332
  linear-gradient(to right, var(--ui-gantt-grid) 1px, transparent 1px) 0 0 / var(--day-width) 100%,
209
333
  color-mix(in srgb, var(--ui-gantt-panel) 98%, transparent);
210
334
  }
211
335
 
336
+ /* The task list can be resized: a handle on its right edge, in the header and down the body. They are grid items
337
+ of their own, in the cells of the header and the body, so the sticky grid underneath stays a plain treegrid. */
338
+ .splitter-rail {
339
+ position: sticky;
340
+ left: 0;
341
+ grid-column: 1;
342
+ z-index: 4;
343
+ pointer-events: none;
344
+ }
345
+
346
+ .splitter-rail.head {
347
+ grid-row: 1;
348
+ top: 0;
349
+ z-index: 6;
350
+ }
351
+
352
+ .splitter-rail.body {
353
+ grid-row: 2;
354
+ }
355
+
356
+ .splitter {
357
+ position: absolute;
358
+ inset-block: 0;
359
+ inset-inline-end: -4px;
360
+ inline-size: 8px;
361
+ cursor: col-resize;
362
+ pointer-events: auto;
363
+ touch-action: none;
364
+ outline: 0;
365
+ }
366
+
367
+ .splitter::after {
368
+ content: "";
369
+ position: absolute;
370
+ inset-block: 0;
371
+ inset-inline-start: 3px;
372
+ inline-size: 2px;
373
+ background: transparent;
374
+ transition: background 120ms ease;
375
+ }
376
+
377
+ .splitter:hover::after,
378
+ .splitter.dragging::after,
379
+ .splitter:focus-visible::after {
380
+ background: var(--ui-gantt-accent);
381
+ }
382
+
383
+ .timeline-body[data-zoom="month"],
384
+ .timeline-body[data-zoom="quarter"],
385
+ .timeline-body[data-zoom="year"] {
386
+ background: color-mix(in srgb, var(--ui-gantt-panel) 98%, transparent);
387
+ }
388
+
389
+ /* Non-working days: a tint behind the rows, a warmer one for a holiday. */
390
+ .off-day {
391
+ position: absolute;
392
+ inset-block: 0;
393
+ background: color-mix(in srgb, var(--ui-gantt-muted) 11%, transparent);
394
+ pointer-events: none;
395
+ }
396
+
397
+ .off-day.holiday {
398
+ background: color-mix(in srgb, var(--ui-color-warning, #d97706) 15%, transparent);
399
+ }
400
+
401
+ .tick.off {
402
+ background: color-mix(in srgb, var(--ui-gantt-muted) 9%, transparent);
403
+ }
404
+
405
+ /* The dots beside a bar that start a link (the linkable attribute), shown with the row's hover or focus. */
406
+ .connector {
407
+ position: absolute;
408
+ top: 15px;
409
+ inline-size: 12px;
410
+ block-size: 12px;
411
+ border: 2px solid var(--ui-gantt-accent);
412
+ border-radius: 50%;
413
+ background: var(--ui-gantt-panel);
414
+ cursor: crosshair;
415
+ opacity: 0;
416
+ z-index: 3;
417
+ touch-action: none;
418
+ transition: opacity 120ms ease;
419
+ }
420
+
421
+ .timeline-row:hover .connector,
422
+ .timeline-row:focus-within .connector,
423
+ .frame[data-linking] .connector {
424
+ opacity: 1;
425
+ }
426
+
427
+ .link-draft {
428
+ fill: none;
429
+ stroke: var(--ui-gantt-accent);
430
+ stroke-width: 2;
431
+ stroke-dasharray: 5 3;
432
+ stroke-linecap: round;
433
+ pointer-events: none;
434
+ }
435
+
436
+ .bar.link-target {
437
+ outline: 2px solid var(--ui-gantt-accent);
438
+ outline-offset: 2px;
439
+ }
440
+
441
+ .bar.link-target[data-link-side="start"] {
442
+ box-shadow: inset 7px 0 0 var(--ui-gantt-accent);
443
+ }
444
+
445
+ .bar.link-target[data-link-side="end"] {
446
+ box-shadow: inset -7px 0 0 var(--ui-gantt-accent);
447
+ }
448
+
449
+ .links path.selected:not(.link-hit) {
450
+ stroke: var(--ui-gantt-accent);
451
+ stroke-width: 2.4;
452
+ }
453
+
454
+ .links path.link-hit {
455
+ outline: 0;
456
+ }
457
+
458
+ .unit-line {
459
+ position: absolute;
460
+ inset-block: 0;
461
+ inline-size: 1px;
462
+ background: color-mix(in srgb, var(--ui-gantt-border) 90%, transparent);
463
+ pointer-events: none;
464
+ }
465
+
212
466
  .grid-row,
213
467
  .timeline-row {
214
- block-size: ${y}px;
468
+ block-size: ${S}px;
215
469
  border-bottom: 1px solid color-mix(in srgb, var(--ui-gantt-border) 56%, transparent);
216
470
  }
217
471
 
218
472
  .grid-row {
219
473
  display: grid;
220
- grid-template-columns: minmax(170px, 1fr) 82px 80px 44px;
474
+ grid-template-columns: var(--gantt-cols);
221
475
  color: var(--ui-gantt-text);
222
476
  font-size: 12px;
223
477
  }
224
478
 
479
+ .grid-row {
480
+ outline: 0;
481
+ }
482
+
483
+ .grid-row:focus-visible {
484
+ outline: 2px solid var(--ui-gantt-accent);
485
+ outline-offset: -2px;
486
+ }
487
+
225
488
  .grid-row.selected,
226
489
  .timeline-row.selected {
227
490
  background: var(--ui-gantt-selected);
@@ -251,6 +514,20 @@
251
514
  color: var(--ui-gantt-accent);
252
515
  }
253
516
 
517
+ .rename {
518
+ flex: 1 1 auto;
519
+ min-inline-size: 0;
520
+ block-size: 26px;
521
+ padding: 0 6px;
522
+ border: 1px solid var(--ui-gantt-accent);
523
+ border-radius: 6px;
524
+ background: var(--ui-gantt-panel);
525
+ color: var(--ui-gantt-text);
526
+ font: inherit;
527
+ font-weight: 650;
528
+ outline: 0;
529
+ }
530
+
254
531
  .name-text {
255
532
  overflow: hidden;
256
533
  text-overflow: ellipsis;
@@ -321,13 +598,15 @@
321
598
  overflow: visible;
322
599
  }
323
600
 
324
- .bar.split .progress {
601
+ /* A split bar shows the progress of each part, inside the part, and not one fill across the gaps. */
602
+ .bar.split > .progress {
325
603
  display: none;
326
604
  }
327
605
 
328
606
  .split-segment {
329
607
  position: absolute;
330
608
  inset-block: 0;
609
+ overflow: hidden;
331
610
  border-radius: 4px;
332
611
  background:
333
612
  linear-gradient(180deg, color-mix(in srgb, #ffffff 22%, transparent), transparent 42%),
@@ -401,13 +680,33 @@
401
680
  display: none;
402
681
  }
403
682
 
683
+ /* The done part of a bar: a solid, darker shade of the bar's tone with a light edge where it stops, so
684
+ the amount is readable from the bar itself and not only from the % column. */
404
685
  .progress {
405
686
  position: absolute;
406
687
  inset-block: 0;
407
688
  inset-inline-start: 0;
408
- background: linear-gradient(90deg, color-mix(in srgb, #000 22%, transparent), transparent);
409
- opacity: 0.3;
410
689
  inline-size: var(--progress, 0%);
690
+ background:
691
+ linear-gradient(180deg, color-mix(in srgb, #ffffff 16%, transparent), transparent 46%),
692
+ color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 58%, #0b1220);
693
+ box-shadow: inset -2px 0 0 color-mix(in srgb, #ffffff 62%, transparent);
694
+ }
695
+
696
+ .bar.complete .progress,
697
+ .progress.full {
698
+ box-shadow: none;
699
+ }
700
+
701
+ .bar.summary .progress {
702
+ background: color-mix(in srgb, #0b1220 58%, transparent);
703
+ }
704
+
705
+ :host([bar-variant="soft"]) .bar.task .progress {
706
+ background:
707
+ linear-gradient(180deg, color-mix(in srgb, #ffffff 24%, transparent), transparent 46%),
708
+ color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 62%, var(--ui-gantt-panel));
709
+ box-shadow: inset -2px 0 0 color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 85%, transparent);
411
710
  }
412
711
 
413
712
  .handle {
@@ -452,28 +751,75 @@
452
751
  opacity: 1;
453
752
  }
454
753
 
455
- .drag-tip {
754
+ /* A finger needs the 24px minimum target size, and a touch screen has no hover to reveal the handles:
755
+ task bars grow from 18 to 24px tall (centred on the same line, so links and baselines still meet them),
756
+ the resize handles from 14 to 24px wide and always show their grip. A very short bar keeps a middle to
757
+ grab by giving each handle at most 35% of it. */
758
+ @media (pointer: coarse) {
759
+ .bar.task {
760
+ block-size: 24px;
761
+ top: 9px;
762
+ }
763
+
764
+ .handle {
765
+ inline-size: min(24px, 35%);
766
+ opacity: 1;
767
+ }
768
+
769
+ .handle.start::after {
770
+ inset-inline-start: 8px;
771
+ }
772
+
773
+ .handle.end::after {
774
+ inset-inline-end: 8px;
775
+ }
776
+
777
+ .connector {
778
+ opacity: 1;
779
+ inline-size: 20px;
780
+ block-size: 20px;
781
+ top: 11px;
782
+ }
783
+ }
784
+
785
+ /* One tooltip for the whole chart, outside the bars (which clip their children), placed by script. It
786
+ repeats the bar's accessible name, so it is hidden from assistive technology. */
787
+ .bar-tip {
456
788
  position: absolute;
457
- z-index: 6;
458
- inset-block-start: -28px;
459
- inset-inline-start: 50%;
460
- transform: translateX(-50%);
461
- padding: 4px 7px;
462
- border-radius: 6px;
789
+ inset-block-start: 0;
790
+ inset-inline-start: 0;
791
+ z-index: 7;
792
+ max-inline-size: 280px;
793
+ padding: 6px 9px;
794
+ border-radius: 8px;
463
795
  background: #0f172a;
464
796
  color: #f8fafc;
465
797
  font-size: 11px;
466
- font-weight: 700;
467
- white-space: nowrap;
798
+ line-height: 1.4;
799
+ box-shadow: 0 8px 24px rgba(15, 23, 42, 0.28);
468
800
  pointer-events: none;
469
801
  opacity: 0;
802
+ visibility: hidden;
803
+ transition: opacity 90ms ease;
470
804
  }
471
805
 
472
- .bar.dragging .drag-tip {
806
+ .bar-tip[data-open] {
473
807
  opacity: 1;
808
+ visibility: visible;
809
+ }
810
+
811
+ .bar-tip strong {
812
+ display: block;
813
+ font-weight: 750;
814
+ }
815
+
816
+ .bar-tip span {
817
+ display: block;
818
+ opacity: 0.88;
474
819
  }
475
820
 
476
- :host([bar-variant="soft"]) .bar.task {
821
+ :host([bar-variant="soft"]) .bar.task,
822
+ :host([bar-variant="soft"]) .split-segment {
477
823
  background:
478
824
  linear-gradient(180deg, color-mix(in srgb, #ffffff 38%, transparent), transparent 46%),
479
825
  color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 22%, var(--ui-gantt-panel));
@@ -492,7 +838,8 @@
492
838
  0 7px 16px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 16%, transparent);
493
839
  }
494
840
 
495
- :host([bar-variant="striped"]) .bar.task {
841
+ :host([bar-variant="striped"]) .bar.task,
842
+ :host([bar-variant="striped"]) .split-segment {
496
843
  background:
497
844
  repeating-linear-gradient(135deg, color-mix(in srgb, #ffffff 24%, transparent) 0 7px, transparent 7px 14px),
498
845
  var(--tone, var(--ui-gantt-accent));
@@ -504,7 +851,8 @@
504
851
  var(--tone, var(--ui-gantt-accent));
505
852
  }
506
853
 
507
- :host([bar-variant="outline"]) .bar.task {
854
+ :host([bar-variant="outline"]) .bar.task,
855
+ :host([bar-variant="outline"]) .split-segment {
508
856
  background: color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 8%, var(--ui-gantt-panel));
509
857
  box-shadow:
510
858
  inset 0 0 0 1.5px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 72%, transparent),
@@ -512,8 +860,8 @@
512
860
  }
513
861
 
514
862
  :host([bar-variant="outline"]) .bar.task .progress {
515
- background: color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 28%, transparent);
516
- opacity: 1;
863
+ background: color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 42%, transparent);
864
+ box-shadow: inset -2px 0 0 color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 85%, transparent);
517
865
  }
518
866
 
519
867
  :host([bar-variant="outline"]) .milestone-shape {
@@ -523,7 +871,8 @@
523
871
  0 7px 16px color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 10%, transparent);
524
872
  }
525
873
 
526
- :host([bar-variant="glass"]) .bar.task {
874
+ :host([bar-variant="glass"]) .bar.task,
875
+ :host([bar-variant="glass"]) .split-segment {
527
876
  background:
528
877
  linear-gradient(180deg, color-mix(in srgb, #ffffff 50%, transparent), color-mix(in srgb, #ffffff 10%, transparent)),
529
878
  color-mix(in srgb, var(--tone, var(--ui-gantt-accent)) 72%, transparent);
@@ -541,6 +890,19 @@
541
890
  backdrop-filter: blur(8px);
542
891
  }
543
892
 
893
+ /* The parts of a split bar carry the variant skin; the bar around them stays empty so the gaps are gaps. */
894
+ :host([bar-variant]) .bar.split {
895
+ background: transparent;
896
+ box-shadow: none;
897
+ backdrop-filter: none;
898
+ }
899
+
900
+ /* After the variant skins, which draw their own edge: a finished bar or part has no edge to show. */
901
+ :host([bar-variant]) .bar.complete .progress,
902
+ :host([bar-variant]) .bar .progress.full {
903
+ box-shadow: none;
904
+ }
905
+
544
906
  .today {
545
907
  position: absolute;
546
908
  inset-block: 0;
@@ -550,14 +912,22 @@
550
912
  pointer-events: none;
551
913
  }
552
914
 
553
- .today::before {
554
- content: "Today";
915
+ /* In the header, under the unit labels (which sit at the top of their row), centred on the today line
916
+ and kept inside the track at either end. */
917
+ .today-pill {
555
918
  position: absolute;
556
- inset-block-start: 4px;
557
- inset-inline-start: 6px;
558
- color: var(--ui-color-danger, #dc2626);
559
- font-size: 11px;
919
+ inset-block-end: 3px;
920
+ z-index: 3;
921
+ transform: translateX(-50%);
922
+ padding: 0 6px;
923
+ block-size: 14px;
924
+ border-radius: 7px;
925
+ background: var(--ui-color-danger, #dc2626);
926
+ color: #ffffff;
927
+ font-size: 10px;
560
928
  font-weight: 750;
929
+ line-height: 14px;
930
+ pointer-events: none;
561
931
  }
562
932
 
563
933
  .links {
@@ -590,6 +960,18 @@
590
960
  pointer-events: none;
591
961
  }
592
962
 
963
+ /* A dependency whose target starts or ends earlier than the link allows. */
964
+ .links path.violated:not(.link-hit) {
965
+ stroke: var(--ui-color-danger, #dc2626);
966
+ stroke-width: 1.8;
967
+ stroke-dasharray: 5 3;
968
+ marker-end: url(#ui-gantt-arrow-bad);
969
+ }
970
+
971
+ .links marker#ui-gantt-arrow-bad path {
972
+ fill: var(--ui-color-danger, #dc2626);
973
+ }
974
+
593
975
  .links marker path {
594
976
  fill: color-mix(in srgb, var(--ui-gantt-muted) 82%, transparent);
595
977
  stroke: none;
@@ -620,26 +1002,46 @@
620
1002
  display: none;
621
1003
  }
622
1004
 
623
- @media (max-width: 760px) {
624
- .surface {
625
- grid-template-columns: 270px minmax(560px, 1fr);
1005
+ @media (prefers-reduced-motion: reduce) {
1006
+ .bar,
1007
+ .progress {
1008
+ transition: none !important;
626
1009
  }
1010
+ }
627
1011
 
628
- .grid-head,
629
- .grid-row {
630
- grid-template-columns: minmax(150px, 1fr) 64px 56px;
1012
+ /* Print: the whole chart, not a scroll area. Controls and handles are for the screen; the sticky headers stay put on the
1013
+ page; colours print as they are. A wide timeline is cut at the page edge, so print with the fit zoom or use exportSvg(). */
1014
+ @media print {
1015
+ .frame {
1016
+ box-shadow: none;
1017
+ -webkit-print-color-adjust: exact;
1018
+ print-color-adjust: exact;
631
1019
  }
632
1020
 
633
- .grid-head span:nth-child(3),
634
- .grid-cell:nth-child(3) {
635
- display: none;
1021
+ .controls,
1022
+ .splitter-rail,
1023
+ .connector,
1024
+ .bar-tip,
1025
+ .handle {
1026
+ display: none !important;
636
1027
  }
637
- }
638
1028
 
639
- @media (prefers-reduced-motion: reduce) {
640
- .bar,
641
- .progress {
642
- transition: none !important;
1029
+ .viewport,
1030
+ :host([fill]) .viewport {
1031
+ overflow: visible;
1032
+ max-block-size: none;
1033
+ }
1034
+
1035
+ .grid-head,
1036
+ .timeline-head,
1037
+ .grid-body,
1038
+ .splitter-rail.head {
1039
+ position: static;
1040
+ }
1041
+
1042
+ .grid-row,
1043
+ .timeline-row {
1044
+ break-inside: avoid;
643
1045
  }
644
1046
  }
645
1047
 
@@ -662,89 +1064,102 @@
662
1064
  .bar,
663
1065
  .select,
664
1066
  .control,
665
- .zoom-button {
1067
+ .zoom-button,
1068
+ .today-button,
1069
+ .add-button {
1070
+ forced-color-adjust: none;
1071
+ box-shadow: none;
1072
+ }
1073
+
1074
+ .bar-tip {
666
1075
  forced-color-adjust: none;
1076
+ background: Canvas;
1077
+ color: CanvasText;
1078
+ border: 1px solid CanvasText;
667
1079
  box-shadow: none;
668
1080
  }
669
1081
  }
670
- `,mt={default:"var(--ui-color-primary, #2563eb)",info:"var(--ui-color-info, #0e7490)",success:"var(--ui-color-success, #15803d)",warning:"var(--ui-color-warning, #b45309)",danger:"var(--ui-color-danger, #dc2626)"};function b(a){return String(a??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function f(a){if(a instanceof Date){const n=a.getTime();return Number.isFinite(n)?F(n):null}const t=String(a||"").trim(),i=/^(\d{4})-(\d{2})-(\d{2})$/.exec(t);if(i){const n=Number(i[1]),s=Number(i[2]),l=Number(i[3]),p=new Date(n,s-1,l).getTime();return Number.isFinite(p)?F(p):null}const e=Date.parse(t);return Number.isFinite(e)?F(e):null}function F(a){const t=new Date(a);return t.setHours(0,0,0,0),t.getTime()}function x(a,t){return F(a+t*I)}function E(a){const t=new Date(a),i=t.getFullYear(),e=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0");return`${i}-${e}-${n}`}function O(a){return new Intl.DateTimeFormat(void 0,{month:"short",day:"numeric"}).format(new Date(a))}function bt(a){return a==="day"||a==="month"||a==="quarter"||a==="year"?a:"week"}function W(a){return a==="start"||a==="end"||a==="label"||a==="progress"?a:"none"}function Dt(a){return a==="soft"||a==="striped"||a==="outline"||a==="glass"?a:"solid"}function Ft(a,t){if(!a)return[];try{const i=JSON.parse(a);return Array.isArray(i)?i.map((e,n)=>{if(!e||typeof e!="object")return null;const s=e,l=String(s.label||s.text||"").trim(),o=f(s.start),p=Number(s.duration);let u=f(s.end);if(u==null&&o!=null&&Number.isFinite(p)&&(u=x(o,Math.max(0,p))),!l||o==null)return null;const d=s.type==="summary"||s.type==="milestone"?s.type:"task",g=d==="milestone"?o:Math.max(u??x(o,1),x(o,1)),c=String(s.id||`task-${n+1}`),m=t.get(c)||{};return{id:c,label:l,start:m.start||E(o),end:m.end||E(g),progress:Number.isFinite(Number(s.progress))?Math.max(0,Math.min(100,Number(s.progress))):void 0,tone:typeof s.tone=="string"?s.tone:void 0,type:d,parent:s.parent!=null?String(s.parent):void 0,open:s.open!==!1,assignee:s.assignee!=null?String(s.assignee):s.owner!=null?String(s.owner):void 0,readonly:s.readonly===!0,baselineStart:s.baselineStart!=null?String(s.baselineStart):void 0,baselineEnd:s.baselineEnd!=null?String(s.baselineEnd):void 0,critical:s.critical===!0,segments:Array.isArray(s.segments)?s.segments:void 0}}).filter(Boolean):[]}catch{return[]}}function Rt(a){if(!a)return[];try{const t=JSON.parse(a);return Array.isArray(t)?t.map((i,e)=>{if(!i||typeof i!="object")return null;const n=i,s=String(n.source||"").trim(),l=String(n.target||"").trim();if(!s||!l)return null;const o=n.type==="s2s"||n.type==="e2e"||n.type==="s2e"?n.type:"e2s";return{id:String(n.id||`link-${e+1}`),source:s,target:l,type:o}}).filter(Boolean):[]}catch{return[]}}function qt(a,t){return t==="none"?a:[...a].sort((i,e)=>{if(t==="label")return i.label.localeCompare(e.label);if(t==="progress")return Number(e.progress??0)-Number(i.progress??0);const n=f(t==="start"?i.start:i.end)??0,s=f(t==="start"?e.start:e.end)??0;return n-s})}function Nt(a,t,i,e){const n=new Map,s=new Map(a.map(d=>[d.id,d]));a.forEach(d=>{const g=d.parent&&s.has(d.parent)?d.parent:"__root__",c=n.get(g)||[];c.push(d),n.set(g,c)});const l=i.trim().toLowerCase(),o=d=>!l||d.label.toLowerCase().includes(l)||(d.assignee||"").toLowerCase().includes(l)?!0:(n.get(d.id)||[]).some(o),p=[],u=(d,g)=>{qt(n.get(d)||[],e).forEach(c=>{if(!o(c))return;const m=f(c.start),h=f(c.end);if(m==null||h==null)return;const w=(n.get(c.id)||[]).length>0;p.push({...c,depth:g,hasChildren:w,startMs:m,endMs:h}),w&&!t.has(c.id)&&u(c.id,g+1)})};return u("__root__",0),p}function Ht(a,t,i,e,n=0,s=Number.POSITIVE_INFINITY){const l=Math.max(1,Math.ceil((t-a)/I)),o=e==="day"?1:e==="week"?7:e==="month"?30:e==="quarter"?91:365,p=Math.max(0,Math.floor(Math.max(0,n-480)/i/o)*o),u=Math.min(l,Math.ceil((n+s+960)/i/o)*o),d=[];for(let g=p;g<=u;g+=o){const c=g*i,m=x(a,g),h=e==="day"?O(m):new Intl.DateTimeFormat(void 0,{month:"short",year:e==="week"||e==="month"?void 0:"numeric"}).format(new Date(m));d.push(`<span class="tick" style="left:${c.toFixed(2)}px">${b(h)}</span>`)}return d.join("")}function H(a){return a.type==="summary"||a.hasChildren?"summary":a.type==="milestone"||a.startMs===a.endMs?"milestone":"task"}function ft(a){const t=H(a);return t==="summary"?17+9/2:t==="milestone"?7+N/2:12+18/2}function _t(a,t){return H(a)==="milestone"?t(a.startMs)-vt:t(a.startMs)}function jt(a,t){return H(a)==="milestone"?t(a.startMs)+vt:t(a.endMs)}function xt(a,t,i){return t==="start"?_t(a,i):jt(a,i)}function Vt(a="e2s"){return a==="s2s"?{source:"start",target:"start"}:a==="e2e"?{source:"end",target:"end"}:a==="s2e"?{source:"start",target:"end"}:{source:"end",target:"start"}}function Wt(a,t,i,e,n,s){const l=Vt(n),o=l.source==="start"?-1:1,p=l.target==="start"?1:-1,u=xt(a,l.source,s),d=xt(i,l.target,s),g=t*y+ft(a),c=e*y+ft(i),m=22,h=u+o*m,w=d-p*m,M=26;let k=(h+w)/2;if(o===p){const T=o===1?Math.max(h,w,u,d)+M:Math.min(h,w,u,d)-M;k=(o===1?w>h+M:w<h-M)?k:T}else(o===1?w<h:w>h)&&(k=o===1?Math.max(h,u,d)+M:Math.min(h,u,d)-M);const A=Math.abs(c-g)<1?l.target==="start"?c-16:c+16:c>g?c-y/2+3:c+y/2-3;return[[u,g],[h,g],[k,g],[k,A],[w,A],[w,c],[d,c]].map(([T,R],P)=>`${P===0?"M":"L"} ${T.toFixed(1)} ${R.toFixed(1)}`).join(" ")}class wt extends At.ElementBase{constructor(){super(...arguments),this.collapsed=new Set,this.overrides=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=[]}static get observedAttributes(){return["tasks","links","zoom","filter","sort","bar-variant","variant","readonly","show-toolbar","show-today","headless"]}render(){const t=this.getTasks(),i=this.getLinks(),e=bt(this.getAttribute("zoom")),n=W(this.getAttribute("sort")),s=Dt(this.getAttribute("bar-variant")),l=this.getAttribute("filter")??this.internalFilter,o=this.getVisibleTasks(t,l,n),p=this.hasAttribute("readonly"),u=this.getAttribute("show-toolbar")!=="false",d=this.getAttribute("show-today")!=="false";if(!t.length){this.setContent(`
671
- <style>${ht}</style>
1082
+ `,mt={default:"var(--ui-color-primary, #2563eb)",info:"var(--ui-color-info, #0e7490)",success:"var(--ui-color-success, #15803d)",warning:"var(--ui-color-warning, #b45309)",danger:"var(--ui-color-danger, #dc2626)"};function v(n){return String(n??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function $(n){if(n instanceof Date){const i=n.getTime();return Number.isFinite(i)?Y(i):null}const t=String(n||"").trim(),e=/^(\d{4})-(\d{2})-(\d{2})(?:[T\s].*)?$/.exec(t);if(e){const i=Number(e[1]),a=Number(e[2]),s=Number(e[3]),c=new Date(i,a-1,s).getTime();return Number.isFinite(c)?Y(c):null}const r=Date.parse(t);return Number.isFinite(r)?Y(r):null}function Y(n){const t=new Date(n);return t.setHours(0,0,0,0),t.getTime()}function A(n,t){return Y(n+t*D)}function W(n){const t=new Date(n),e=t.getFullYear(),r=String(t.getMonth()+1).padStart(2,"0"),i=String(t.getDate()).padStart(2,"0");return`${e}-${r}-${i}`}function ht(n){return Z({month:"short",day:"numeric"}).format(new Date(n))}function qe(n){return n==="day"||n==="month"||n==="quarter"||n==="year"||n==="fit"?n:"week"}const Ne=.2,We=48,Be=680,Oe=160,He=7;function Pe(n){return n>=22?"day":n*7>=44?"week":n*30>=44?"month":n*91>=44?"quarter":"year"}function bt(n){return n==="start"||n==="end"||n==="label"||n==="progress"?n:"none"}function _e(n){return n==="soft"||n==="striped"||n==="outline"||n==="glass"?n:"solid"}const Qt=new Date(1970,0,1).getTime();function je(n,t,e=!1){if(!n)return[];try{const r=JSON.parse(n);return Array.isArray(r)?r.map((i,a)=>{if(!i||typeof i!="object")return null;const s=i,o=String(s.label||s.text||"").trim();let c=$(s.start);const h=Number(s.duration);let d=$(s.end);d==null&&c!=null&&Number.isFinite(h)&&(d=A(c,Math.max(0,h)));const g=e&&c==null&&s.type==="summary";if(g&&(c=Qt,d=A(Qt,1)),!o||c==null)return null;const u=s.type==="summary"||s.type==="milestone"?s.type:"task",b=u==="milestone"?c:Math.max(d??A(c,1),A(c,1)),m=String(s.id||`task-${a+1}`),p=t.get(m)||{};return{id:m,label:p.label||o,start:p.start||W(c),end:p.end||W(b),progress:Number.isFinite(Number(s.progress))?Math.max(0,Math.min(100,Number(s.progress))):void 0,tone:typeof s.tone=="string"?s.tone:void 0,type:u,parent:s.parent!=null?String(s.parent):void 0,open:s.open!==!1,assignee:s.assignee!=null?String(s.assignee):s.owner!=null?String(s.owner):void 0,readonly:s.readonly===!0,baselineStart:s.baselineStart!=null?String(s.baselineStart):void 0,baselineEnd:s.baselineEnd!=null?String(s.baselineEnd):void 0,critical:s.critical===!0,undated:g,segments:Array.isArray(s.segments)?s.segments:void 0}}).filter(Boolean):[]}catch{return[]}}function Zt(n){if(!n)return[];try{const t=JSON.parse(n);return Array.isArray(t)?t.map((e,r)=>{if(!e||typeof e!="object")return null;const i=e,a=String(i.source||"").trim(),s=String(i.target||"").trim();if(!a||!s)return null;const o=i.type==="s2s"||i.type==="e2e"||i.type==="s2e"?i.type:"e2s",c=Math.round(Number(i.lag));return{id:String(i.id||`link-${r+1}`),source:a,target:s,type:o,lag:Number.isFinite(c)?c:0}}).filter(Boolean):[]}catch{return[]}}function Ve(n,t){return t==="none"?n:[...n].sort((e,r)=>{if(t==="label")return e.label.localeCompare(r.label);if(t==="progress")return Number(r.progress??0)-Number(e.progress??0);const i=$(t==="start"?e.start:e.end)??0,a=$(t==="start"?r.start:r.end)??0;return i-a})}function Xe(n,t,e,r){const i=new Map,a=new Map(n.map(d=>[d.id,d]));n.forEach(d=>{const g=d.parent&&a.has(d.parent)?d.parent:"__root__",u=i.get(g)||[];u.push(d),i.set(g,u)});const s=e.trim().toLowerCase(),o=d=>!s||d.label.toLowerCase().includes(s)||(d.assignee||"").toLowerCase().includes(s)?!0:(i.get(d.id)||[]).some(o),c=[],h=(d,g)=>{Ve(i.get(d)||[],r).forEach(u=>{if(!o(u))return;const b=$(u.start),m=$(u.end);if(b==null||m==null)return;const p=(i.get(u.id)||[]).length>0;c.push({...u,depth:g,hasChildren:p,startMs:b,endMs:m}),p&&!t.has(u.id)&&h(u.id,g+1)})};return h("__root__",0),c}function Ye(){try{const n=Z({}).resolvedOptions().locale,t=new Intl.Locale(n),e=typeof t.getWeekInfo=="function"?t.getWeekInfo():t.weekInfo;if(e&&Number.isInteger(e.firstDay))return e.firstDay%7}catch{}return 1}function Ft(n){const t=n==null||n===""?NaN:Number(n);return Number.isInteger(t)&&t>=0&&t<=6?t:Ye()}function ue(n,t,e){const r=new Date(n);return r.setHours(0,0,0,0),t==="week"?r.setDate(r.getDate()-(r.getDay()-e+7)%7):t==="month"?r.setDate(1):t==="quarter"?r.setMonth(Math.floor(r.getMonth()/3)*3,1):t==="year"&&r.setMonth(0,1),r.getTime()}function pe(n,t){const e=new Date(n);return t==="day"?e.setDate(e.getDate()+1):t==="week"?e.setDate(e.getDate()+7):t==="month"?e.setMonth(e.getMonth()+1):t==="quarter"?e.setMonth(e.getMonth()+3):e.setFullYear(e.getFullYear()+1),e.getTime()}function kt(n,t,e,r,i,a,s){const o=[],c=e+Math.max(0,a-480)/i*D,h=a+s+960;let d=ue(Math.max(e,c),n,t);for(;d<r;){const g=pe(d,n),u=Math.max(d,e),b=Math.min(g,r),m=(u-e)/D*i;if(m>h)break;b>u&&o.push({left:m,width:(b-u)/D*i,start:d,label:""}),d=g}return o}const Ue={weekdays:new Set,holidays:new Map};function te(n,t){const e=new Set;if(n!=null){const i=n.trim();if(i==="")e.add(0),e.add(6);else{let a=i.split(",");try{const s=JSON.parse(i);Array.isArray(s)?a=s:typeof s=="number"&&(a=[s])}catch{}for(const s of a){const o=Number(typeof s=="string"?s.trim():s);Number.isInteger(o)&&o>=0&&o<=6&&e.add(o)}}}const r=new Map;if(t)try{const i=JSON.parse(t);if(Array.isArray(i))for(const a of i){const s=a&&typeof a=="object"?a:null,o=$(s?s.date:a);o!=null&&r.set(o,s&&typeof s.label=="string"?s.label:"")}}catch{}return{weekdays:e,holidays:r}}function ge(n,t){return t.holidays.has(n)||t.weekdays.has(new Date(n).getDay())}const fe=8;function Ke(n,t,e,r,i,a){if(e<fe||!r.weekdays.size&&!r.holidays.size)return"";const s=Math.max(0,Math.floor(Math.max(0,i-480)/e)),o=Math.min(Math.ceil((t-n)/D),Math.ceil((i+a+960)/e)),c=[];let h=-1,d="";const g=u=>{h>=0&&c.push(`<span class="off-day${d}" style="left:${(h*e).toFixed(2)}px;width:${((u-h)*e).toFixed(2)}px"></span>`),h=-1};for(let u=s;u<o;u+=1){const b=H(n,u),m=r.holidays.has(b)?" holiday":r.weekdays.has(new Date(b).getDay())?"":null;(m===null||h>=0&&m!==d)&&g(u),m!==null&&h<0&&(h=u,d=m)}return g(o),c.join("")}const It=36;function Je(n,t,e,r,i,a=0,s=Number.POSITIVE_INFINITY,o=Ue){const c=Y(Date.now()),h=Z({month:"long",year:"numeric"}),d=Z({month:"short"}),g=Z({weekday:"narrow"}),u=(y,w,x,M="")=>`<span class="${y}" style="left:${w.left.toFixed(2)}px;width:${w.width.toFixed(2)}px"${M?` title="${v(M)}"`:""}>${x}</span>`,m=kt(r,i,n,t,e,a,s).map(y=>{const w=new Date(y.start),x=y.width>=It;if(r==="day"){const M=y.start===c,T=o.holidays.get(y.start);return u(`tick day${M?" today-cell":""}${ge(y.start,o)?" off":""}`,y,`<span class="dow">${v(g.format(w))}</span><span class="num">${w.getDate()}</span>`,T||"")}return r==="week"?u("tick",y,x?`<span class="num">${w.getDate()}</span>`:"",ht(y.start)):r==="month"?u("tick",y,x?v(d.format(w)):"",h.format(w)):r==="quarter"?u("tick",y,x?`Q${Math.floor(w.getMonth()/3)+1}`:"",`Q${Math.floor(w.getMonth()/3)+1} ${w.getFullYear()}`):u("tick",y,x?String(w.getFullYear()):"")}).join("");if(r==="year")return`<div class="band bottom">${m}</div>`;const p=r==="day"||r==="week"?"month":"year";return`<div class="band top">${kt(p,i,n,t,e,a,s).map(y=>{const w=new Date(y.start),x=p==="month"?h.format(w):String(w.getFullYear()),M=y.width<It?"":p==="month"&&y.width<120?d.format(w):x;return u("band-cell",y,v(M),x)}).join("")}</div><div class="band bottom">${m}</div>`}function ee(n){const t=new Set;if(!n)return t;try{const e=JSON.parse(n);Array.isArray(e)&&e.forEach((r,i)=>{r&&typeof r=="object"&&t.add(String(r.id||`task-${i+1}`))})}catch{}return t}function Ge(n,t){for(const e of n)if(t.has(e))return!0;return!1}function Qe(n,t,e,r,i,a,s){return r==="day"?"":kt(r,i,n,t,e,a,s).filter(o=>o.start>=n).map(o=>`<span class="unit-line" style="left:${o.left.toFixed(2)}px"></span>`).join("")}function Ze(n,t){const e=i=>Math.max(0,Math.min(100,i));let r=n.reduce((i,a)=>a.progress==null?i+a.duration:i,0)*(e(t)/100);return n.map(i=>{if(i.progress!=null)return e(i.progress);const a=Math.min(i.duration,r);return r-=a,i.duration>0?a/i.duration*100:0})}function tr(n,t){const e=Math.max(D,n.endMs-n.startMs),r=[];for(const a of n.segments||[]){const s=$(a.start),o=$(a.end);if(s==null||o==null)continue;const c=Math.max(n.startMs,s),h=Math.min(n.endMs,o);if(h<=c)continue;const d=Math.max(0,Math.min(100,(c-n.startMs)/e*100)),g=Math.min(100-d,Math.max(1,(h-c)/e*100));if(g<=0)continue;const u=Number(a.progress);r.push({left:d,width:g,duration:h-c,progress:a.progress!=null&&Number.isFinite(u)?u:void 0})}const i=Ze(r,t);return r.map((a,s)=>`<span class="split-segment" style="left:${a.left.toFixed(2)}%;width:${a.width.toFixed(2)}%"><span class="progress${i[s]>=100?" full":""}" style="--progress:${i[s].toFixed(2)}%"></span></span>`).join("")}function ot(n){return typeof CSS<"u"&&typeof CSS.escape=="function"?CSS.escape(n):n.replace(/["\\]/g,"\\$&")}function Nt(n){return`.grid-row[data-task-id="${ot(n)}"]`}function At(n){return`.bar[data-task-id="${ot(n)}"]`}function er(n){return n.classList.contains("control")?".control":n.classList.contains("select")?".select":n.classList.contains("zoom-button")&&n.dataset.zoom?`.zoom-button[data-zoom="${ot(n.dataset.zoom)}"]`:n.classList.contains("today-button")?".today-button":n.classList.contains("add-button")?".add-button":n.classList.contains("rename")?".rename":n.classList.contains("head-button")&&n.dataset.sort?`.head-button[data-sort="${ot(n.dataset.sort)}"]`:n.classList.contains("splitter")&&n.getAttribute("role")==="separator"?'.splitter[role="separator"]':n.classList.contains("link-hit")&&n.dataset.linkId?`.link-hit[data-link-id="${ot(n.dataset.linkId)}"]`:n.classList.contains("bar")&&n.dataset.taskId?At(n.dataset.taskId):n.classList.contains("grid-row")&&n.dataset.taskId?Nt(n.dataset.taskId):n.classList.contains("expander")&&n.dataset.taskId?`.expander[data-task-id="${ot(n.dataset.taskId)}"]`:null}const Et={title:"Project schedule",tasksCount:"{shown}/{total} tasks",filterPlaceholder:"Filter tasks",filterLabel:"Filter tasks",sortLabel:"Sort tasks",sortManual:"Manual",sortStart:"Start date",sortEnd:"End date",sortName:"Name",sortProgress:"Progress",zoomDay:"day",zoomWeek:"week",zoomMonth:"month",zoomQuarter:"quarter",zoomYear:"year",zoomFit:"fit",zoomFitTitle:"Fit the whole project to the width",columnTask:"Task",columnOwner:"Owner",columnDates:"Dates",columnProgress:"%",columnStart:"Start",columnEnd:"End",columnDuration:"Days",splitter:"Resize the task list",scrollToToday:"Scroll to today",addTask:"Add task",rename:"Rename task",linkViolated:"dependency not met",empty:"No tasks in this range",noMatches:"No matching tasks",expand:"Expand",collapse:"Collapse",timeline:"Timeline",today:"Today",summary:"summary",milestone:"milestone",to:"to",complete:"complete",assigned:"assigned to",critical:"critical"},Rt={label:{title:"columnTask",sort:"label",width:170},assignee:{title:"columnOwner",width:82},dates:{title:"columnDates",sort:"start",width:80},start:{title:"columnStart",sort:"start",width:74},end:{title:"columnEnd",sort:"end",width:74},duration:{title:"columnDuration",width:56},progress:{title:"columnProgress",sort:"progress",width:44}},rr=["label","assignee","dates","progress"],re=96;function ir(n){let t=rr;if(n)try{const a=JSON.parse(n);Array.isArray(a)&&a.length&&(t=a)}catch{}const e=[],r=new Set;for(const a of t){const s=a&&typeof a=="object"?a:null;let o=typeof a=="string"?a:s?s.key:null;if(o==="owner"&&(o="assignee"),typeof o!="string"||!(o in Rt)||r.has(o))continue;r.add(o);const c=Rt[o],h=s?Number(s.width):NaN;e.push({key:o,title:c.title,custom:s&&typeof s.label=="string"&&s.label?s.label:void 0,sort:c.sort,width:Number.isFinite(h)&&h>=36?Math.min(Math.round(h),400):c.width})}const i=e.findIndex(a=>a.key==="label");return i>0&&e.unshift(e.splice(i,1)[0]),i<0&&e.unshift({key:"label",title:"columnTask",sort:"label",width:Rt.label.width}),e}function nr(n,t){const e=n.slice(1).reduce((a,s)=>a+s.width,0),r=t!=null?re:n[0].width,i=e+re;return{width:t!=null?Math.max(Math.round(t),i):e+n[0].width,min:i,template:n.map((a,s)=>s===0?`minmax(${r}px, 1fr)`:`${a.width}px`).join(" ")}}let Lt;function Z(n){try{return new Intl.DateTimeFormat(Lt,n)}catch{return new Intl.DateTimeFormat(void 0,n)}}function sr(n,t,e,r){const i=e.format(new Date(n.startMs)),a=t==="milestone"?i:`${i} ${r.to} ${e.format(new Date(n.endMs))}`,s=t==="summary"||t==="milestone"?r[t]:"",o=[];return n.progress!=null&&t!=="milestone"&&o.push(`${Math.round(n.progress)}% ${r.complete}`),n.assignee&&o.push(`${r.assigned} ${n.assignee}`),n.critical&&o.push(r.critical),{name:[n.label,s,a,...o].filter(Boolean).join(", "),title:n.label,kindWord:s,when:[s,a].filter(Boolean).join(" · "),more:o.join(" · ")}}function ar(n,t){return{day:t.zoomDay,week:t.zoomWeek,month:t.zoomMonth,quarter:t.zoomQuarter,year:t.zoomYear,fit:t.zoomFit}[n]}function or(n){const t=(n||"").trim();return t==="none"||/^\d*\.?\d+(px|em|rem|vh|dvh|svh|lvh|vw|%|ch)$/.test(t)?t:null}function Wt(n){return{id:n.id,source:n.source,target:n.target,type:n.type||"e2s",lag:n.lag??0}}function ie(n){return n==="auto"||n==="off"?n:"warn"}function yt(n){const t=$(n.start),e=$(n.end);return t==null||e==null?null:{id:n.id,start:t,end:e,parent:n.parent,type:n.type,readonly:n.readonly,progress:n.progress}}function lr(n){const t=Te(n.map(yt).filter(e=>e!=null));return n.map(e=>{const r=t.get(e.id);return r?{...e,undated:!1,start:W(r.start),end:W(r.end),progress:e.progress??r.progress}:e}).filter(e=>!e.undated)}function dr(n,t){const e=ze(n.map(yt).filter(r=>r!=null),t.map(Wt));return e.size?n.map(r=>e.has(r.id)&&!r.critical?{...r,critical:!0}:r):n}function qt(n){if(!n)return[];try{const t=JSON.parse(n);return Array.isArray(t)?t:[]}catch{return[]}}function xt(n){return n instanceof Date?Number.isFinite(n.getTime())?W(n.getTime()):void 0:n}function ne(n,t,e,r){if(!Array.isArray(e)){n.removeAttribute(t);return}const i=r?e.map(a=>{const s={...a};for(const o of["start","end","baselineStart","baselineEnd"])o in s&&(s[o]=xt(s[o]));return Array.isArray(s.segments)&&(s.segments=s.segments.map(o=>({...o,start:xt(o.start),end:xt(o.end)}))),s}):e;try{n.setAttribute(t,JSON.stringify(i))}catch{}}function cr(n){const t=n==null||n===""?NaN:Number(n);return Number.isFinite(t)&&t>0?t:null}function ut(n){return n.type==="summary"||n.hasChildren?"summary":n.type==="milestone"||n.startMs===n.endMs?"milestone":"task"}function se(n){const t=ut(n);return t==="summary"?17+9/2:t==="milestone"?7+vt/2:12+18/2}function hr(n,t){return ut(n)==="milestone"?t(n.startMs)-he:t(n.startMs)}function ur(n,t){return ut(n)==="milestone"?t(n.startMs)+he:t(n.endMs)}function ae(n,t,e){return t==="start"?hr(n,e):ur(n,e)}function pr(n="e2s"){return n==="s2s"?{source:"start",target:"start"}:n==="e2e"?{source:"end",target:"end"}:n==="s2e"?{source:"start",target:"end"}:{source:"end",target:"start"}}function oe(n,t,e,r,i,a){const s=pr(i),o=s.source==="start"?-1:1,c=s.target==="start"?1:-1,h=ae(n,s.source,a),d=ae(e,s.target,a),g=t*S+se(n),u=r*S+se(e),b=22,m=h+o*b,p=d-c*b,k=26;let y=(m+p)/2;if(o===c){const M=o===1?Math.max(m,p,h,d)+k:Math.min(m,p,h,d)-k;y=(o===1?p>m+k:p<m-k)?y:M}else(o===1?p<m:p>m)&&(y=o===1?Math.max(m,h,d)+k:Math.min(m,h,d)-k);const w=Math.abs(u-g)<1?s.target==="start"?u-16:u+16:u>g?u-S/2+3:u+S/2-3;return[[h,g],[m,g],[y,g],[y,w],[p,w],[p,u],[d,u]].map(([M,T],z)=>`${z===0?"M":"L"} ${M.toFixed(1)} ${T.toFixed(1)}`).join(" ")}class me extends Ee.ElementBase{constructor(){super(...arguments),this.focusRequest=null,this.collapsed=new Set,this.seededIds=new Set,this.overrides=new Map,this.drag=null,this.selectedId=null,this.internalFilter="",this.viewportTop=0,this.viewportLeft=0,this.viewportWidth=1800,this.hostWidth=0,this.renderedLeft=0,this.smoothTarget=null,this.selectedLinkId=null,this.linking=null,this.editingId=null,this.editingValue="",this.suppressBlur=!1,this.layoutInfo={min:0,dayWidth:13,gridWidth:Tt,rangeEnd:0},this.visibleIds=[],this.initialScrollPending=!0,this.pendingReveal=null,this.hostHeight=0,this.virtualizedNow=!1,this.gridBounds={min:0,max:0,width:Tt},this.descriptions=new Map,this.tipTimer=null,this.pointerFocus=!1,this.scrollbarWidth=0,this.dayWidthNow=Jt.week,this.resizeObserver=null,this.viewportHeight=620,this.renderedFirstRow=0,this.renderedLastRow=0,this.scrollFrame=null,this.overridesVersion=0,this.taskCacheRaw=null,this.taskCacheVersion=-1,this.taskCacheAuto=!1,this.taskCacheCritical=null,this.taskCache=[],this.linkCacheRaw=null,this.linkCache=[],this.visibleCacheKey="",this.visibleCache=[],this.labelsRaw=null,this.labelsCache=Et}static get observedAttributes(){return["tasks","links","zoom","filter","sort","bar-variant","variant","readonly","show-toolbar","show-today","headless","week-start","labels","locale","columns","grid-width","fill","max-height","snap","creatable","auto-summary","cascade","schedule","critical-path","non-working-days","holidays","linkable"]}connectedCallback(){for(const t of["tasks","links","holidays"])if(Object.prototype.hasOwnProperty.call(this,t)){const e=this[t];delete this[t],this[t]=e}super.connectedCallback(),!(typeof ResizeObserver>"u"||this.resizeObserver)&&(this.resizeObserver=new ResizeObserver(t=>{const e=t[t.length-1]?.contentRect,r=e?.width??0,i=e?.height??0,a=Math.abs(r-this.hostWidth)>=1,s=Math.abs(i-this.hostHeight)>=1&&(this.hasAttribute("fill")||this.virtualizedNow);this.hostHeight=i,!(!a&&!s)&&(a&&(this.hostWidth=Math.round(r)),this.captureViewport(),this.requestRender())}),this.resizeObserver.observe(this))}disconnectedCallback(){this.resizeObserver?.disconnect(),this.resizeObserver=null,this.tipTimer!=null&&window.clearTimeout(this.tipTimer),this.tipTimer=null,super.disconnectedCallback()}labelSet(){const t=this.getAttribute("labels");if(t===this.labelsRaw)return this.labelsCache;if(this.labelsRaw=t,this.labelsCache=Et,t)try{const e=JSON.parse(t);if(e&&typeof e=="object"){const r={...Et};for(const[i,a]of Object.entries(e))i in Et&&typeof a=="string"&&(r[i]=a);this.labelsCache=r}}catch{}return this.labelsCache}render(){this.suppressBlur=!0,queueMicrotask(()=>{this.suppressBlur=!1}),Lt=this.getAttribute("locale")||void 0;const t=this.labelSet(),e=this.getTasks(),r=this.getLinks(),i=qe(this.getAttribute("zoom")),a=bt(this.getAttribute("sort")),s=_e(this.getAttribute("bar-variant")),o=this.getAttribute("filter")??this.internalFilter,c=this.getVisibleTasks(e,o,a),h=this.hasAttribute("readonly"),d=this.hasAttribute("creatable")&&!h,g=this.hasAttribute("cascade"),u=this.hasAttribute("linkable")&&!h,b=this.getAttribute("show-toolbar")!=="false",m=this.getAttribute("show-today")!=="false";if(!e.length){this.setContent(`
1083
+ <style>${Gt}</style>
672
1084
  <section class="frame" part="frame">
673
- <div class="empty" part="empty">No tasks in this range</div>
1085
+ <div class="empty" part="empty">${v(t.empty)}</div>
674
1086
  </section>
675
- `);return}const g=t.map(r=>f(r.start)).filter(r=>r!=null),c=t.map(r=>f(r.end)).filter(r=>r!=null),m=x(Math.min(...g),-2),h=x(Math.max(...c),3),w=Math.max(7,Math.ceil((h-m)/I)),M=gt[e],k=Math.max(680,w*M),A=Math.max(o.length*y,y),S=this.getAttribute("virtualized")!=="false"&&o.length>240,T=Math.max(20,Math.ceil(Math.max(420,this.viewportHeight-V)/y)),R=160,P=Math.max(0,this.viewportTop-V),yt=Math.max(0,Math.floor(P/y)),X=Math.max(180,T*4),L=S?Math.max(0,Math.floor(yt/X)*X-R):0,B=S?Math.min(o.length,L+T+R*2+X):o.length,K=o.slice(L,B),_=L*y,j=Math.max(0,(o.length-B)*y);this.renderedFirstRow=L,this.renderedLastRow=B;const Q=new Map(o.map((r,$)=>[r.id,{task:r,index:$}])),tt=new Set(K.map(r=>r.id)),z=r=>(r-m)/I*M,kt=K.map((r,$)=>{const C=L+$,v=this.selectedId===r.id?" selected":"",D=H(r),G=r.progress==null?"-":`${Math.round(r.progress)}%`,U=D==="milestone"?O(r.startMs):`${O(r.startMs)} - ${O(r.endMs)}`;return`
676
- <div class="grid-row${v}" part="grid-row" data-task-id="${b(r.id)}" role="row" aria-rowindex="${C+2}" aria-level="${r.depth+1}" ${r.hasChildren?`aria-expanded="${!this.collapsed.has(r.id)}"`:""}>
677
- <div class="grid-cell task-name" role="gridcell" style="padding-left:${10+r.depth*18}px">
678
- ${r.hasChildren?`<button class="expander" data-action="toggle" data-task-id="${b(r.id)}" aria-label="${this.collapsed.has(r.id)?"Expand":"Collapse"} ${b(r.label)}">${this.collapsed.has(r.id)?"›":"⌄"}</button>`:'<span style="inline-size:22px"></span>'}
679
- <span class="name-text" title="${b(r.label)}">${b(r.label)}</span>
680
- </div>
681
- <div class="grid-cell badge" role="gridcell">${b(r.assignee||D)}</div>
682
- <div class="grid-cell badge" role="gridcell">${b(U)}</div>
683
- <div class="grid-cell badge" role="gridcell">${b(G)}</div>
1087
+ `);return}const p=e.map(l=>$(l.start)).filter(l=>l!=null),k=e.map(l=>$(l.end)).filter(l=>l!=null),y=A(Math.min(...p),-2),w=A(Math.max(...k),3),x=Math.max(7,Math.ceil((w-y)/D));this.hostWidth=this.clientWidth||this.hostWidth;const M=this.hostWidth-Fe;this.hostHeight=this.clientHeight||this.hostHeight;const T=or(this.getAttribute("max-height")),z=this.hostWidth>0&&M<=Ce,U=ir(this.getAttribute("columns")),P=nr(U,cr(this.getAttribute("grid-width"))),lt=z?De:P.width;this.gridBounds={min:P.min,max:Math.max(P.min,this.hostWidth>0?M-240:1200),width:P.width};const st=i==="fit",R=this.hostWidth>0?M-lt-this.scrollbarWidth:Be,q=st?Math.max(Ne,Math.min(We,R/x)):Jt[i],K=st?Pe(q):i;this.dayWidthNow=q;const J=Math.max(st?Oe:680,x*q),O=Math.max(c.length*S,S),V=this.getAttribute("virtualized")!=="false"&&c.length>240;this.virtualizedNow=V;const et=Math.max(20,Math.ceil(Math.max(420,this.viewportHeight-N)/S)),f=160;this.viewportTop=Math.min(this.viewportTop,Math.max(0,O+N-this.viewportHeight));const E=Math.max(0,this.viewportTop-N),L=Math.max(0,Math.floor(E/S)),_=Math.max(180,et*4),G=V?Math.max(0,Math.floor(L/_)*_-f):0,at=V?Math.min(c.length,G+et+f*2+_):c.length,dt=c.slice(G,at),rt=G*S,it=Math.max(0,(c.length-at)*S);this.renderedFirstRow=G,this.renderedLastRow=at;const $t=Z({dateStyle:"medium"}),pt=new Map,F=new Map(c.map((l,B)=>[l.id,{task:l,index:B}])),C=new Set(dt.map(l=>l.id)),tt=this.selectedId!=null&&C.has(this.selectedId)?this.selectedId:dt[0]?.id,nt=l=>(l-y)/D*q,be=dt.map((l,B)=>{const I=G+B,Q=this.selectedId===l.id?" selected":"",X=ut(l),ct=ft=>{switch(ft.key){case"label":return`<div class="grid-cell task-name" role="gridcell" style="padding-left:${10+l.depth*18}px">
1088
+ ${l.hasChildren?`<button class="expander" data-action="toggle" data-task-id="${v(l.id)}" aria-label="${v(this.collapsed.has(l.id)?t.expand:t.collapse)} ${v(l.label)}">${this.collapsed.has(l.id)?"›":"⌄"}</button>`:'<span style="inline-size:22px"></span>'}
1089
+ ${this.editingId===l.id?`<input class="rename" value="${v(this.editingValue)}" aria-label="${v(t.rename)}" />`:`<span class="name-text" title="${v(l.label)}">${v(l.label)}</span>`}
1090
+ </div>`;case"assignee":return`<div class="grid-cell badge" role="gridcell">${v(l.assignee||X)}</div>`;case"dates":return`<div class="grid-cell badge" role="gridcell">${v(X==="milestone"?ht(l.startMs):`${ht(l.startMs)} - ${ht(l.endMs)}`)}</div>`;case"start":return`<div class="grid-cell badge" role="gridcell">${v(ht(l.startMs))}</div>`;case"end":return`<div class="grid-cell badge" role="gridcell">${v(X==="milestone"?"-":ht(l.endMs))}</div>`;case"duration":return`<div class="grid-cell badge" role="gridcell">${X==="milestone"?"-":Math.max(0,Math.round((l.endMs-l.startMs)/D))}</div>`;default:return`<div class="grid-cell badge" role="gridcell">${l.progress==null?"-":`${Math.round(l.progress)}%`}</div>`}};return`
1091
+ <div class="grid-row${Q}" part="grid-row" data-task-id="${v(l.id)}" role="row" tabindex="${l.id===tt?0:-1}" aria-rowindex="${I+2}" aria-level="${l.depth+1}" ${Q?'aria-selected="true"':""} ${l.hasChildren?`aria-expanded="${!this.collapsed.has(l.id)}"`:""}>
1092
+ ${U.map(ct).join("")}
684
1093
  </div>
685
- `}).join(""),$t=K.map((r,$)=>{const C=L+$,v=H(r),D=this.selectedId===r.id?" selected":"",G=this.selectedId===r.id||!this.selectedId&&C===0,U=mt[r.tone||(v==="summary"?"info":"default")]||mt.default,rt=v==="milestone"?z(r.startMs)-N/2:z(r.startMs),zt=z(v==="milestone"?r.startMs:r.endMs),Ct=v==="milestone"?N:Math.max(12,zt-rt),Tt=Math.max(0,Math.min(100,Number(r.progress??0))),at=!p&&!r.readonly&&v!=="summary",nt=at&&v==="task",it=v==="task"&&r.segments?.length?r.segments.map(st=>{const ot=f(st.start),lt=f(st.end);if(ot==null||lt==null)return"";const dt=Math.max(I,r.endMs-r.startMs),Z=Math.max(r.startMs,ot),ct=Math.min(r.endMs,lt);if(ct<=Z)return"";const pt=Math.max(0,Math.min(100,(Z-r.startMs)/dt*100)),It=(ct-Z)/dt*100,ut=Math.min(100-pt,Math.max(1,It));return ut<=0?"":`<span class="split-segment" style="left:${pt.toFixed(2)}%;width:${ut.toFixed(2)}%"></span>`}).join(""):"";return`
686
- <div class="timeline-row${D}" part="timeline-row" data-task-id="${b(r.id)}">
687
- ${r.baselineStart&&r.baselineEnd?`<span class="baseline" style="left:${z(f(r.baselineStart)||r.startMs).toFixed(2)}px;width:${Math.max(4,z(f(r.baselineEnd)||r.endMs)-z(f(r.baselineStart)||r.startMs)).toFixed(2)}px"></span>`:""}
1094
+ `}).join(""),ve=dt.map((l,B)=>{const I=ut(l),Q=sr(l,I,$t,t);pt.set(l.id,Q);const X=this.selectedId===l.id?" selected":"",ct=l.id===tt,ft=l.critical?mt.danger:mt[l.tone||(I==="summary"?"info":"default")]||mt.default,Mt=I==="milestone"?nt(l.startMs)-vt/2:nt(l.startMs),Se=nt(I==="milestone"?l.startMs:l.endMs),Xt=I==="milestone"?vt:Math.max(12,Se-Mt),St=Math.max(0,Math.min(100,Number(l.progress??0))),Yt=!h&&!l.readonly&&(I!=="summary"||g),Ut=Yt&&I==="task",Kt=I==="task"&&l.segments?.length?tr(l,St):"";return`
1095
+ <div class="timeline-row${X}" part="timeline-row" data-task-id="${v(l.id)}">
1096
+ ${l.baselineStart&&l.baselineEnd?`<span class="baseline" style="left:${nt($(l.baselineStart)||l.startMs).toFixed(2)}px;width:${Math.max(4,nt($(l.baselineEnd)||l.endMs)-nt($(l.baselineStart)||l.startMs)).toFixed(2)}px"></span>`:""}
688
1097
  <button
689
- class="bar ${v}${r.critical?" critical":""}${it?" split":""}"
690
- part="bar ${v}"
691
- data-task-id="${b(r.id)}"
692
- data-kind="${v}"
693
- tabindex="${G?"0":"-1"}"
694
- ${this.selectedId===r.id?'aria-current="true"':""}
695
- style="left:${rt.toFixed(2)}px;width:${Ct.toFixed(2)}px;--tone:${U};--progress:${Tt.toFixed(2)}%"
696
- aria-label="${b(r.label)}"
697
- ${at?'data-draggable="true"':""}
1098
+ class="bar ${I}${l.critical?" critical":""}${Kt?" split":""}${St>=100?" complete":""}"
1099
+ part="bar ${I}"
1100
+ data-task-id="${v(l.id)}"
1101
+ data-kind="${I}"
1102
+ ${l.progress!=null?`data-progress="${Math.round(St)}"`:""}
1103
+ tabindex="${ct?"0":"-1"}"
1104
+ ${this.selectedId===l.id?'aria-current="true"':""}
1105
+ style="left:${Mt.toFixed(2)}px;width:${Xt.toFixed(2)}px;--tone:${ft};--progress:${St.toFixed(2)}%"
1106
+ aria-label="${v(Q.name)}"
1107
+ ${Yt?'data-draggable="true"':""}
698
1108
  >
699
- ${nt?'<span class="handle start" data-mode="resize-start"></span>':""}
1109
+ ${Ut?'<span class="handle start" data-mode="resize-start"></span>':""}
700
1110
  <span class="progress"></span>
701
- ${it}
702
- ${v==="milestone"?'<span class="milestone-shape"></span>':""}
703
- <span class="drag-tip"></span>
704
- ${nt?'<span class="handle end" data-mode="resize-end"></span>':""}
1111
+ ${Kt}
1112
+ ${I==="milestone"?'<span class="milestone-shape"></span>':""}
1113
+ ${Ut?'<span class="handle end" data-mode="resize-end"></span>':""}
705
1114
  </button>
1115
+ ${u?`<span class="connector" data-task-id="${v(l.id)}" data-side="start" style="left:${(Mt-18).toFixed(2)}px"></span><span class="connector" data-task-id="${v(l.id)}" data-side="end" style="left:${(Mt+Xt+6).toFixed(2)}px"></span>`:""}
706
1116
  </div>
707
- `}).join(""),et=i.map(r=>{const $=Q.get(r.source),C=Q.get(r.target);if(!$||!C||S&&!tt.has(r.source)&&!tt.has(r.target))return"";const v=Wt($.task,$.index,C.task,C.index,r.type,z),D=`${$.task.label} dependency to ${C.task.label}`;return`<path class="link-hit" d="${v}" data-link-id="${b(r.id)}" data-source-id="${b(r.source)}" data-target-id="${b(r.target)}" data-link-type="${b(r.type||"e2s")}" aria-label="${b(D)}" /><path d="${v}" />`}).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=F(Date.now()),Et=z(Y),St=d&&Y>=m&&Y<=h?`<div class="today" part="today" style="left:${Et.toFixed(2)}px"></div>`:"";this.setContent(`
708
- <style>${ht}</style>
709
- <section class="frame" part="frame" data-bar-variant="${s}">
710
- ${u?`
1117
+ `}).join("");this.descriptions=pt,this.selectedLinkId&&!r.some(l=>l.id===this.selectedLinkId)&&(this.selectedLinkId=null);const ye=ie(this.getAttribute("schedule"))==="off"?new Set:Le(e.map(yt).filter(l=>l!=null),r.map(Wt)),Ot=r.map(l=>{const B=F.get(l.source),I=F.get(l.target);if(!B||!I||V&&!C.has(l.source)&&!C.has(l.target))return"";const Q=oe(B.task,B.index,I.task,I.index,l.type,nt),X=ye.has(l.id),ct=this.selectedLinkId===l.id,ft=`${B.task.label} dependency to ${I.task.label}${X?`, ${t.linkViolated}`:""}`;return`<path class="link-hit${X?" violated":""}${ct?" selected":""}" tabindex="-1" d="${Q}" data-link-id="${v(l.id)}" data-source-id="${v(l.source)}" data-target-id="${v(l.target)}" data-link-type="${v(l.type||"e2s")}"${X?' data-violated="true"':""} aria-label="${v(ft)}" /><path class="${[X?"violated":"",ct?"selected":""].filter(Boolean).join(" ")}" d="${Q}" />`}).join(""),Ht=l=>`<marker id="${l}" 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>`,xe=Ot?`<defs>${Ht("ui-gantt-arrow")}${Ht("ui-gantt-arrow-bad")}</defs>`:"",Pt=Ft(this.getAttribute("week-start")),_t=te(this.getAttribute("non-working-days"),this.getAttribute("holidays")),we=Math.max(J,M-lt),gt=y+we/q*D;this.layoutInfo={min:y,dayWidth:q,gridWidth:lt,rangeEnd:gt},this.renderedLeft=this.viewportLeft,this.visibleIds=c.map(l=>l.id);const zt=Y(Date.now()),Ct=nt(zt),Dt=zt>=y&&zt<=gt,jt=m&&Dt,ke=jt?`<div class="today" part="today" style="left:${Ct.toFixed(2)}px"></div>`:"",$e=jt&&K!=="day"?`<span class="today-pill" part="today-pill" style="left:clamp(22px, ${Ct.toFixed(2)}px, calc(100% - 22px))">${v(t.today)}</span>`:"",Me=this.focusRequest??this.captureFocus();this.focusRequest=null;const Vt=this.root.querySelector(".frame");this.setContent(`
1118
+ <style>${Gt}</style>
1119
+ ${P.width!==Tt||P.template!==ce?`<style>@container ui-gantt (min-width: 641px) { .surface { --gantt-grid-w: ${P.width}px; --gantt-cols: ${P.template}; } }</style>`:""}
1120
+ <section class="frame" part="frame" data-bar-variant="${s}"${T?` style="--ui-gantt-max-height:${T}"`:""}>
1121
+ ${b?`
711
1122
  <header class="toolbar" part="toolbar">
712
- <div class="title">Project schedule <span class="meta">${o.length}/${t.length} tasks</span></div>
1123
+ <div class="title">${v(t.title)} <span class="meta">${v(t.tasksCount.replace("{shown}",String(c.length)).replace("{total}",String(e.length)))}</span></div>
713
1124
  <div class="controls">
714
- <input class="control" value="${b(l)}" placeholder="Filter tasks" aria-label="Filter tasks" />
715
- <select class="select" aria-label="Sort tasks">
716
- ${[["none","Manual"],["start","Start date"],["end","End date"],["label","Name"],["progress","Progress"]].map(([r,$])=>`<option value="${r}" ${n===r?"selected":""}>${$}</option>`).join("")}
1125
+ <input class="control" value="${v(o)}" placeholder="${v(t.filterPlaceholder)}" aria-label="${v(t.filterLabel)}" />
1126
+ <select class="select" aria-label="${v(t.sortLabel)}">
1127
+ ${[["none",t.sortManual],["start",t.sortStart],["end",t.sortEnd],["label",t.sortName],["progress",t.sortProgress]].map(([l,B])=>`<option value="${l}" ${a===l?"selected":""}>${v(B)}</option>`).join("")}
717
1128
  </select>
718
- ${["day","week","month","quarter","year"].map(r=>`<button class="zoom-button" data-zoom="${r}" aria-pressed="${e===r}">${r}</button>`).join("")}
1129
+ ${d?`<button class="add-button" data-action="add"><span aria-hidden="true">+</span> ${v(t.addTask)}</button>`:""}
1130
+ ${m?`<button class="today-button" data-action="today" title="${v(t.scrollToToday)}" aria-label="${v(t.scrollToToday)}"${Dt?"":" disabled"}>${v(t.today)}</button>`:""}
1131
+ ${["day","week","month","quarter","year","fit"].map(l=>`<button class="zoom-button" data-zoom="${l}" aria-pressed="${i===l}"${l==="fit"?` title="${v(t.zoomFitTitle)}"`:""}>${v(ar(l,t))}</button>`).join("")}
719
1132
  </div>
720
1133
  </header>
721
1134
  `:""}
722
1135
  <div class="viewport" part="viewport">
723
- <div class="surface" part="surface" style="--day-width:${M}px;grid-template-columns:${J}px ${k.toFixed(2)}px;inline-size:max(100%, ${(J+k).toFixed(2)}px)">
724
- <div class="grid-semantics" role="treegrid" aria-label="${b(this.getAttribute("aria-label")||"Project schedule")}" aria-rowcount="${o.length+1}" aria-colcount="4">
1136
+ <div class="surface" part="surface" style="--day-width:${q}px;grid-template-columns:var(--gantt-grid-w) minmax(${J.toFixed(2)}px, 1fr);inline-size:max(100%, calc(var(--gantt-grid-w) + ${J.toFixed(2)}px))">
1137
+ <div class="grid-semantics" role="treegrid" aria-label="${v(this.getAttribute("aria-label")||t.title)}" aria-rowcount="${c.length+1}" aria-colcount="${z?1:U.length}">
725
1138
  <div class="grid-head" part="grid-head" role="row">
726
- <div role="columnheader" aria-sort="${n==="label"?"ascending":"none"}" style="display:contents"><button class="head-button" data-sort="label">Task</button></div>
727
- <span role="columnheader">Owner</span>
728
- <div role="columnheader" aria-sort="${n==="start"?"ascending":"none"}" style="display:contents"><button class="head-button" data-sort="start">Dates</button></div>
729
- <div role="columnheader" aria-sort="${n==="progress"?"descending":"none"}" style="display:contents"><button class="head-button" data-sort="progress">%</button></div>
1139
+ ${U.map(l=>{const B=v(l.custom??t[l.title]);if(!l.sort)return`<span role="columnheader">${B}</span>`;const I=a===l.sort,Q=l.sort==="progress"?"descending":"ascending";return`<div role="columnheader" aria-sort="${I?Q:"none"}" style="display:contents"><button class="head-button" data-sort="${l.sort}">${B}</button></div>`}).join("")}
730
1140
  </div>
731
1141
  <div class="grid-body" part="grid-body" role="rowgroup">
732
- ${S&&_?`<div style="height:${_}px"></div>`:""}
733
- ${kt||'<div class="empty">No matching tasks</div>'}
734
- ${S&&j?`<div style="height:${j}px"></div>`:""}
1142
+ ${V&&rt?`<div style="height:${rt}px"></div>`:""}
1143
+ ${be||`<div class="empty">${v(t.noMatches)}</div>`}
1144
+ ${V&&it?`<div style="height:${it}px"></div>`:""}
735
1145
  </div>
736
1146
  </div>
1147
+ <div class="splitter-rail head"><div class="splitter" role="separator" aria-orientation="vertical" aria-label="${v(t.splitter)}" aria-valuemin="${P.min}" aria-valuemax="${this.gridBounds.max}" aria-valuenow="${P.width}" tabindex="0"></div></div>
1148
+ <div class="splitter-rail body"><div class="splitter" aria-hidden="true"></div></div>
737
1149
  <div class="timeline-head" part="timeline-head" aria-hidden="true">
738
- <div class="scale" style="width:${k.toFixed(2)}px">${Ht(m,h,M,e,this.viewportLeft,this.viewportWidth)}</div>
1150
+ <div class="scale" data-zoom="${K}" style="min-width:${J.toFixed(2)}px">${Je(y,gt,q,K,Pt,this.viewportLeft,this.viewportWidth,_t)}${$e}</div>
739
1151
  </div>
740
- <div class="timeline-body" part="timeline-body" role="group" aria-label="Timeline" style="width:${k.toFixed(2)}px;min-height:${A}px">
741
- <svg class="links" width="${k.toFixed(0)}" height="${A}" viewBox="0 0 ${k.toFixed(0)} ${A}" aria-hidden="true">${Mt}${et}</svg>
742
- ${St}
743
- ${S&&_?`<div style="height:${_}px"></div>`:""}
744
- ${$t}
745
- ${S&&j?`<div style="height:${j}px"></div>`:""}
1152
+ <div class="timeline-body" part="timeline-body" role="group" aria-label="${v(t.timeline)}" data-zoom="${K}" style="min-width:${J.toFixed(2)}px;min-height:${O}px">
1153
+ ${Ke(y,gt,q,_t,this.viewportLeft,this.viewportWidth)}
1154
+ ${Qe(y,gt,q,K,Pt,this.viewportLeft,this.viewportWidth)}
1155
+ <svg class="links" width="${J.toFixed(0)}" height="${O}" viewBox="0 0 ${J.toFixed(0)} ${O}" aria-hidden="true">${xe}${Ot}</svg>
1156
+ ${ke}
1157
+ ${V&&rt?`<div style="height:${rt}px"></div>`:""}
1158
+ ${ve}
1159
+ ${V&&it?`<div style="height:${it}px"></div>`:""}
1160
+ <div class="bar-tip" part="tip" aria-hidden="true"></div>
746
1161
  </div>
747
1162
  </div>
748
1163
  </div>
749
1164
  </section>
750
- `);const q=this.root.querySelector(".viewport");q&&(q.scrollTop=this.viewportTop,q.scrollLeft=this.viewportLeft,this.viewportWidth=q.clientWidth||this.viewportWidth,this.viewportHeight=q.clientHeight||this.viewportHeight),this.bindEvents()}bindEvents(){this.root.querySelectorAll("[data-task-id]").forEach(t=>{t.addEventListener("click",i=>{const e=i.target,n=i.currentTarget;if(!n.classList.contains("bar")&&e.closest(".bar"))return;if(this.drag?.moved){i.stopPropagation();return}const l=i.currentTarget.dataset.taskId;l&&(e.closest('[data-action="toggle"]')||(n.classList.contains("bar")&&i.stopPropagation(),this.selectTask(l)))})}),this.root.querySelectorAll('[data-action="toggle"]').forEach(t=>{t.addEventListener("click",i=>{i.stopPropagation();const e=t.dataset.taskId;e&&(this.collapsed.has(e)?this.collapsed.delete(e):this.collapsed.add(e),this.requestRender())})}),this.root.querySelectorAll("[data-zoom]").forEach(t=>{t.addEventListener("click",()=>{const i=t.dataset.zoom||"week";this.setAttribute("zoom",i),this.dispatchEvent(new CustomEvent("zoomchange",{detail:{zoom:i},bubbles:!0,composed:!0}))})}),this.root.querySelectorAll(".head-button[data-sort]").forEach(t=>{t.addEventListener("click",()=>{const i=W(t.dataset.sort||null);i==="none"?this.removeAttribute("sort"):this.setAttribute("sort",i),this.dispatchEvent(new CustomEvent("sortchange",{detail:{sort:i},bubbles:!0,composed:!0}))})}),this.root.querySelector(".viewport")?.addEventListener("scroll",t=>{const i=t.currentTarget,e=i.scrollTop,n=i.scrollLeft,s=Math.max(20,Math.ceil(Math.max(420,this.viewportHeight-V)/y)),l=Math.max(0,Math.floor(Math.max(0,e-V)/y)),o=l<this.renderedFirstRow+64||l+s>this.renderedLastRow-64,p=Math.abs(n-this.viewportLeft)>=Math.max(480,this.viewportWidth*.6);this.viewportTop=e,this.viewportLeft=n,!(!o&&!p)&&this.scrollFrame==null&&(this.scrollFrame=window.setTimeout(()=>{this.scrollFrame=null,this.requestRender()},24))},{passive:!0}),this.root.querySelector(".control")?.addEventListener("input",t=>{this.internalFilter=t.currentTarget.value,this.dispatchEvent(new CustomEvent("filterchange",{detail:{filter:this.internalFilter},bubbles:!0,composed:!0})),this.requestRender()}),this.root.querySelector(".select")?.addEventListener("change",t=>{const i=W(t.currentTarget.value);i==="none"?this.removeAttribute("sort"):this.setAttribute("sort",i),this.dispatchEvent(new CustomEvent("sortchange",{detail:{sort:i},bubbles:!0,composed:!0}))}),this.root.querySelectorAll(".link-hit").forEach(t=>{t.addEventListener("click",i=>{i.stopPropagation(),this.dispatchEvent(new CustomEvent("linkselect",{detail:{id:t.dataset.linkId||"",source:t.dataset.sourceId||"",target:t.dataset.targetId||"",type:t.dataset.linkType||"e2s"},bubbles:!0,composed:!0}))})}),!this.hasAttribute("readonly")&&this.root.querySelectorAll('.bar[data-draggable="true"]').forEach(t=>{t.addEventListener("pointerdown",e=>{if(e.button!==0)return;const n=t.dataset.taskId;if(!n)return;const l=this.getTasks().find(h=>h.id===n),o=f(l?.start),p=f(l?.end);if(!l||o==null||p==null)return;e.preventDefault();const u=t.dataset.kind||"task",d=e.target.dataset.mode||"move",g=u==="milestone"?"move":d,c=bt(this.getAttribute("zoom")),m=this.selectedId!==n;this.selectedId=n,this.dispatchEvent(new CustomEvent("taskselect",{detail:{id:n},bubbles:!0,composed:!0})),this.drag={taskId:n,mode:g,startX:e.clientX,originalStart:o,originalEnd:p,kind:u,rangeMin:this.rangeMin(),dayWidth:gt[c],latestStart:o,latestEnd:p,frame:null,bar:t,moved:!1,selectedChanged:m},t.classList.add("dragging"),t.setPointerCapture(e.pointerId)}),t.addEventListener("pointermove",e=>{if(!this.drag||this.drag.taskId!==t.dataset.taskId)return;e.preventDefault();const n=e.clientX-this.drag.startX;Math.abs(n)>2&&(this.drag.moved=!0);let s=this.drag.originalStart,l=this.drag.originalEnd;if(this.drag.mode==="move"){const o=Math.round(n/this.drag.dayWidth);s=x(this.drag.originalStart,o),l=this.drag.kind==="milestone"?s:x(this.drag.originalEnd,o)}else if(this.drag.mode==="resize-start"){const o=Math.round(n/this.drag.dayWidth);s=Math.min(x(this.drag.originalStart,o),x(l,-1))}else{const o=Math.round(n/this.drag.dayWidth);l=Math.max(x(this.drag.originalEnd,o),x(s,1))}this.drag.latestStart=s,this.drag.latestEnd=l,this.drag.frame==null&&(this.drag.frame=requestAnimationFrame(()=>{if(!this.drag)return;this.drag.frame=null;const o=(this.drag.latestStart-this.drag.rangeMin)/I*this.drag.dayWidth-(this.drag.kind==="milestone"?N/2:0),p=this.drag.kind==="milestone"?N:Math.max(12,(this.drag.latestEnd-this.drag.latestStart)/I*this.drag.dayWidth);this.drag.bar.style.left=`${o.toFixed(2)}px`,this.drag.bar.style.width=`${p.toFixed(2)}px`;const u=this.drag.bar.querySelector(".drag-tip");u&&(u.textContent=this.drag.kind==="milestone"?E(this.drag.latestStart):`${E(this.drag.latestStart)} - ${E(this.drag.latestEnd)}`)}))});const i=()=>{if(!this.drag)return;this.drag.frame!=null&&cancelAnimationFrame(this.drag.frame);const e=this.drag;if(e.bar.classList.remove("dragging"),this.drag=null,!e.moved){e.selectedChanged&&this.requestRender();return}const n={id:e.taskId,start:E(e.latestStart),end:E(e.latestEnd)};this.overrides.set(e.taskId,{start:n.start,end:n.end}),this.bumpOverrides(),this.dispatchEvent(new CustomEvent("taskchange",{detail:n,bubbles:!0,composed:!0})),this.requestRender()};t.addEventListener("pointerup",i),t.addEventListener("pointercancel",i),t.addEventListener("keydown",e=>{const n=t.dataset.taskId;if(!n)return;if(e.key==="Delete"||e.key==="Backspace"){e.preventDefault(),this.dispatchEvent(new CustomEvent("taskdelete",{detail:{id:n},bubbles:!0,composed:!0}));return}if(e.key==="Enter"||e.key===" "){e.preventDefault(),this.selectTask(n);return}if(e.key==="ArrowUp"||e.key==="ArrowDown"){e.preventDefault(),this.selectAdjacentTask(n,e.key==="ArrowDown"?1:-1);return}if(e.key!=="ArrowLeft"&&e.key!=="ArrowRight")return;const l=this.getTasks().find(h=>h.id===n),o=f(l?.start),p=f(l?.end);if(!l||o==null||p==null)return;e.preventDefault();const u=e.key==="ArrowLeft"?-1:1,d=t.dataset.kind==="milestone",g=e.shiftKey&&!d?o:x(o,u),c=d?g:e.shiftKey?Math.max(x(p,u),x(o,1)):x(p,u),m={id:n,start:E(g),end:E(c)};this.overrides.set(n,{start:m.start,end:m.end}),this.bumpOverrides(),this.dispatchEvent(new CustomEvent("taskchange",{detail:m,bubbles:!0,composed:!0})),this.requestRender()})})}rangeMin(){const i=this.getTasks().map(e=>f(e.start)).filter(e=>e!=null);return i.length?x(Math.min(...i),-2):F(Date.now())}selectTask(t){if(this.captureViewport(),this.selectedId===t){this.dispatchEvent(new CustomEvent("taskselect",{detail:{id:t},bubbles:!0,composed:!0}));return}this.selectedId=t,this.dispatchEvent(new CustomEvent("taskselect",{detail:{id:t},bubbles:!0,composed:!0})),this.requestRender()}selectAdjacentTask(t,i){const e=this.getVisibleTasks(this.getTasks(),this.getAttribute("filter")??this.internalFilter,W(this.getAttribute("sort"))),n=e.findIndex(l=>l.id===t),s=e[Math.max(0,Math.min(e.length-1,n+i))];!s||s.id===t||(this.selectedId=s.id,this.dispatchEvent(new CustomEvent("taskselect",{detail:{id:s.id},bubbles:!0,composed:!0})),this.requestRender(),window.requestAnimationFrame(()=>{this.root.querySelector(`.bar[data-task-id="${CSS.escape(s.id)}"]`)?.focus()}))}captureViewport(){const t=this.root.querySelector(".viewport");t&&(this.viewportTop=t.scrollTop,this.viewportLeft=t.scrollLeft,this.viewportWidth=t.clientWidth||this.viewportWidth,this.viewportHeight=t.clientHeight||this.viewportHeight)}getTasks(){const t=this.getAttribute("tasks");return t===this.taskCacheRaw&&this.taskCacheVersion===this.overridesVersion?this.taskCache:(this.taskCacheRaw=t,this.taskCacheVersion=this.overridesVersion,this.taskCache=Ft(t,this.overrides),this.visibleCacheKey="",this.taskCache)}getLinks(){const t=this.getAttribute("links");return t===this.linkCacheRaw?this.linkCache:(this.linkCacheRaw=t,this.linkCache=Rt(t),this.linkCache)}getVisibleTasks(t,i,e){const n=Array.from(this.collapsed).sort().join(","),s=`${this.taskCacheRaw||""}|${this.overridesVersion}|${i}|${e}|${n}`;return s===this.visibleCacheKey?this.visibleCache:(this.visibleCacheKey=s,this.visibleCache=Nt(t,this.collapsed,i,e),this.visibleCache)}bumpOverrides(){this.overridesVersion+=1,this.taskCacheVersion=-1,this.visibleCacheKey=""}shouldRenderOnAttributeChange(t,i,e){return t==="tasks"&&(this.overrides.clear(),this.bumpOverrides(),this.viewportTop=0,this.viewportLeft=0),t==="links"&&(this.linkCacheRaw=null),!0}}typeof customElements<"u"&&!customElements.get("ui-gantt")&&customElements.define("ui-gantt",wt);exports.UIGantt=wt;
1165
+ `);const j=this.root.querySelector(".viewport");if(j){this.root.querySelector(".frame")!==Vt&&(j.scrollTop=this.viewportTop,j.scrollLeft=this.viewportLeft,this.smoothTarget&&typeof j.scrollTo=="function"&&j.scrollTo({...this.smoothTarget,behavior:"smooth"})),this.viewportWidth=j.clientWidth||this.viewportWidth,this.viewportHeight=j.clientHeight||this.viewportHeight;const l=Math.max(0,j.offsetWidth-j.clientWidth);l!==this.scrollbarWidth&&(this.scrollbarWidth=l,st&&this.requestRender())}if(this.root.querySelector(".frame")!==Vt&&this.bindEvents(),this.restoreFocus(Me),j&&j.clientWidth>0&&this.initialScrollPending&&(this.initialScrollPending=!1,this.getAttribute("initial-scroll")!=="start"&&!st&&m&&Dt&&Ct>j.clientWidth-lt-48&&this.scrollToDate("today",{behavior:"auto"})),this.pendingReveal){const l=this.pendingReveal;this.pendingReveal=null,this.reveal(l.id,l.behavior)}}captureFocus(){const t=this.root.activeElement;if(!t)return null;const e=er(t);if(!e)return null;const r={selector:e};return t instanceof HTMLInputElement&&(r.selection=[t.selectionStart,t.selectionEnd,t.selectionDirection]),r}restoreFocus(t){if(!t)return;const e=this.root.querySelector(t.selector);if(!e&&t.reveal&&(t.retries??0)<5){this.focusRequest={...t,retries:(t.retries??0)+1};return}if(!(!e||this.root.activeElement===e)&&(e.focus({preventScroll:!t.reveal}),t.selectAll&&e instanceof HTMLInputElement&&e.select(),t.selection&&e instanceof HTMLInputElement))try{e.setSelectionRange(t.selection[0],t.selection[1],t.selection[2]??void 0)}catch{}}bindEvents(){this.root.querySelectorAll("[data-task-id]").forEach(t=>{t.addEventListener("click",e=>{const r=e.target,i=e.currentTarget;if(!i.classList.contains("bar")&&r.closest(".bar"))return;if(this.drag?.moved){e.stopPropagation();return}const s=e.currentTarget.dataset.taskId;s&&(r.closest('[data-action="toggle"]')||(i.classList.contains("bar")&&e.stopPropagation(),this.selectTask(s)))})}),this.root.querySelectorAll('[data-action="toggle"]').forEach(t=>{t.addEventListener("click",e=>{e.stopPropagation();const r=t.dataset.taskId;r&&this.toggleTask(r)})}),this.root.querySelector(".today-button")?.addEventListener("click",()=>{this.scrollToDate("today",{behavior:"smooth",align:"center"})}),this.root.querySelectorAll(".zoom-button[data-zoom]").forEach(t=>{t.addEventListener("click",()=>{const e=t.dataset.zoom||"week";this.setAttribute("zoom",e),this.dispatchEvent(new CustomEvent("zoomchange",{detail:{zoom:e},bubbles:!0,composed:!0}))})}),this.root.querySelectorAll(".head-button[data-sort]").forEach(t=>{t.addEventListener("click",()=>{const e=bt(t.dataset.sort||null);e==="none"?this.removeAttribute("sort"):this.setAttribute("sort",e),this.dispatchEvent(new CustomEvent("sortchange",{detail:{sort:e},bubbles:!0,composed:!0}))})});for(const t of["wheel","pointerdown","touchstart","keydown"])this.root.querySelector(".viewport")?.addEventListener(t,()=>{this.smoothTarget=null},{passive:!0});this.root.querySelector(".viewport")?.addEventListener("scroll",t=>{const e=t.currentTarget,r=e.scrollTop,i=e.scrollLeft,a=Math.max(20,Math.ceil(Math.max(420,this.viewportHeight-N)/S)),s=Math.max(0,Math.floor(Math.max(0,r-N)/S)),o=this.virtualizedNow&&(s<this.renderedFirstRow+64||s+a>this.renderedLastRow-64),c=Math.abs(i-this.renderedLeft)>=400;this.viewportTop=r,this.viewportLeft=i;const h=this.smoothTarget;h&&(h.left==null||Math.abs(i-h.left)<2)&&(h.top==null||Math.abs(r-h.top)<2)&&(this.smoothTarget=null),!(this.drag||this.linking||!o&&!c)&&this.scrollFrame==null&&(this.scrollFrame=window.setTimeout(()=>{this.scrollFrame=null,this.requestRender()},24))},{passive:!0}),this.root.querySelector(".control")?.addEventListener("input",t=>{this.internalFilter=t.currentTarget.value,this.dispatchEvent(new CustomEvent("filterchange",{detail:{filter:this.internalFilter},bubbles:!0,composed:!0})),this.requestRender()}),this.root.querySelector(".select")?.addEventListener("change",t=>{const e=bt(t.currentTarget.value);e==="none"?this.removeAttribute("sort"):this.setAttribute("sort",e),this.dispatchEvent(new CustomEvent("sortchange",{detail:{sort:e},bubbles:!0,composed:!0}))}),this.bindLinks(),this.root.querySelectorAll(".bar").forEach(t=>{t.addEventListener("keydown",e=>this.handleBarKeydown(t,e))}),this.bindTooltips(),this.bindSplitters(),this.bindRows(),this.bindEditing(),!this.hasAttribute("readonly")&&this.root.querySelectorAll('.bar[data-draggable="true"]').forEach(t=>{t.addEventListener("pointerdown",e=>this.startDrag(t,e)),t.addEventListener("pointermove",e=>{!this.drag||this.drag.bar!==t||(e.preventDefault(),this.drag.lastClientX=e.clientX,this.updateDrag())}),t.addEventListener("pointerup",()=>this.endDrag(!0)),t.addEventListener("pointercancel",()=>this.endDrag(!1))})}snapUnit(){const t=this.getAttribute("snap");return t==="week"||t==="month"?t:"day"}snapDate(t){const e=this.snapUnit();if(e==="day")return t;const r=Ft(this.getAttribute("week-start")),i=ue(t,e,r),a=pe(i,e);return t-i<a-t?i:a}startDrag(t,e){if(e.button!==0)return;const r=t.dataset.taskId;if(!r)return;const i=this.getTasks().find(u=>u.id===r),a=$(i?.start),s=$(i?.end);if(!i||a==null||s==null)return;e.preventDefault();const o=t.dataset.kind||"task",c=e.target.dataset.mode||"move",h=o==="milestone"?"move":c,d=this.selectedId!==r;this.selectedId=r,this.dispatchEvent(new CustomEvent("taskselect",{detail:{id:r},bubbles:!0,composed:!0}));const g=u=>{u.key==="Escape"&&(u.preventDefault(),u.stopPropagation(),this.endDrag(!1))};this.drag={taskId:r,mode:h,startX:e.clientX,startScroll:this.root.querySelector(".viewport")?.scrollLeft??0,lastClientX:e.clientX,originalStart:a,originalEnd:s,kind:o,rangeMin:this.rangeMin(),dayWidth:this.dayWidthNow,latestStart:a,latestEnd:s,frame:null,scrollFrame:null,bar:t,originalLeft:t.style.left,originalWidth:t.style.width,moved:!1,selectedChanged:d,onKey:g},window.addEventListener("keydown",g,!0),t.classList.add("dragging"),t.setPointerCapture(e.pointerId),this.autoScrollLoop()}updateDrag(){const t=this.drag;if(!t)return;const e=this.root.querySelector(".viewport"),r=t.lastClientX-t.startX+(e?e.scrollLeft-t.startScroll:0);Math.abs(r)>2&&(t.moved=!0);const i=Math.round(r/t.dayWidth);let a=t.originalStart,s=t.originalEnd;if(t.mode==="move"){const o=Math.round((this.snapDate(A(t.originalStart,i))-t.originalStart)/D);a=A(t.originalStart,o),s=t.kind==="milestone"?a:A(t.originalEnd,o)}else t.mode==="resize-start"?a=Math.min(this.snapDate(A(t.originalStart,i)),A(s,-1)):s=Math.max(this.snapDate(A(t.originalEnd,i)),A(a,1));t.latestStart=a,t.latestEnd=s,t.frame==null&&(t.frame=requestAnimationFrame(()=>{const o=this.drag;if(!o)return;o.frame=null;const c=(o.latestStart-o.rangeMin)/D*o.dayWidth-(o.kind==="milestone"?vt/2:0),h=o.kind==="milestone"?vt:Math.max(12,(o.latestEnd-o.latestStart)/D*o.dayWidth);o.bar.style.left=`${c.toFixed(2)}px`,o.bar.style.width=`${h.toFixed(2)}px`,Lt=this.getAttribute("locale")||void 0;const d=Z({dateStyle:"medium"}),g=this.labelSet();this.showTip(o.bar,o.kind==="milestone"?d.format(new Date(o.latestStart)):`${d.format(new Date(o.latestStart))} ${g.to} ${d.format(new Date(o.latestEnd))}`)}))}autoScrollLoop(){const t=this.drag;t&&(t.scrollFrame=requestAnimationFrame(()=>{const e=this.drag;if(!e)return;const r=this.root.querySelector(".viewport");if(r&&r.clientWidth>0&&e.moved){const i=r.getBoundingClientRect(),a=56,s=Math.max(i.left,this.root.querySelector(".grid-body")?.getBoundingClientRect().right??i.left);let o=0;e.lastClientX>i.right-a?o=(e.lastClientX-(i.right-a))/a:e.lastClientX<s+a&&r.scrollLeft>0&&(o=-(s+a-e.lastClientX)/a),o&&(r.scrollLeft+=Math.max(-1.5,Math.min(1.5,o))*16,this.updateDrag())}this.autoScrollLoop()}))}endDrag(t){const e=this.drag;if(!e)return;e.frame!=null&&cancelAnimationFrame(e.frame),e.scrollFrame!=null&&cancelAnimationFrame(e.scrollFrame),window.removeEventListener("keydown",e.onKey,!0),e.bar.classList.remove("dragging"),this.drag=null,this.hideTip();const r=()=>{e.bar.style.left=e.originalLeft,e.bar.style.width=e.originalWidth};if(!e.moved){e.selectedChanged&&this.requestRender();return}if(!t){r(),this.requestRender();return}if(e.latestStart===e.originalStart&&e.latestEnd===e.originalEnd){r();return}this.applyChanges(this.plan({id:e.taskId,start:e.latestStart,end:e.latestEnd,previousStart:e.originalStart,previousEnd:e.originalEnd}),e.mode==="move"?"drag":"resize")||r()}plan(t){const e=this.withCascade(t);if(ie(this.getAttribute("schedule"))!=="auto")return e;const r=this.getLinks();if(!r.length)return e;const i=this.getTasks().map(yt).filter(c=>c!=null),a=new Map(i.map(c=>[c.id,c])),s=new Map(e.map(c=>[c.id,{start:c.start,end:c.end}])),o=[...e];for(const c of Ie(i,r.map(Wt),s,this.hasAttribute("cascade"))){const h=o.findIndex(d=>d.id===c.id);if(h>=0)o[h]={...o[h],start:c.start,end:c.end};else{const d=a.get(c.id);d&&o.push({id:c.id,start:c.start,end:c.end,previousStart:d.start,previousEnd:d.end})}}return o}withCascade(t){if(!this.hasAttribute("cascade"))return[t];const e=wt(t.previousStart,t.start);if(e===0||t.end-t.start!==t.previousEnd-t.previousStart)return[t];const r=this.getTasks(),i=de(r.map(yt).filter(s=>s!=null),t.id);if(!i.length)return[t];const a=i.filter(s=>!s.readonly).map(s=>({id:s.id,start:H(s.start,e),end:H(s.end,e),previousStart:s.start,previousEnd:s.end}));return[t,...a]}applyChanges(t,e){const[r,...i]=t,a=o=>({id:o.id,start:W(o.start),end:W(o.end),previousStart:W(o.previousStart),previousEnd:W(o.previousEnd)});if(!this.dispatchEvent(new CustomEvent("beforetaskchange",{detail:{...a(r),source:e,affected:i.map(a)},bubbles:!0,composed:!0,cancelable:!0})))return this.requestRender(),!1;for(const o of t)this.overrides.set(o.id,{...this.overrides.get(o.id),start:W(o.start),end:W(o.end)});this.bumpOverrides();for(const o of t)this.dispatchEvent(new CustomEvent("taskchange",{detail:{id:o.id,start:W(o.start),end:W(o.end)},bubbles:!0,composed:!0}));return this.requestRender(),!0}handleBarKeydown(t,e){const r=t.dataset.taskId;if(!r)return;const i=t.dataset.draggable==="true"&&!this.hasAttribute("readonly");if(e.key==="Escape"){this.hideTip();return}if(e.key==="F2"){e.preventDefault(),this.startRename(r);return}if(e.key==="Enter"||e.key===" "){e.preventDefault(),this.selectTask(r);return}if(["ArrowUp","ArrowDown","Home","End","PageUp","PageDown"].includes(e.key)){e.preventDefault(),this.navigate(r,e.key,"bar");return}if(!i)return;if(e.key==="Delete"||e.key==="Backspace"){e.preventDefault(),this.dispatchEvent(new CustomEvent("taskdelete",{detail:{id:r},bubbles:!0,composed:!0}));return}if(e.key!=="ArrowLeft"&&e.key!=="ArrowRight")return;const a=this.getTasks().find(b=>b.id===r),s=$(a?.start),o=$(a?.end);if(!a||s==null||o==null)return;e.preventDefault();const c=e.key==="ArrowLeft"?-1:1,h=t.dataset.kind==="milestone",d=e.shiftKey&&!h&&t.dataset.kind!=="summary",g=d?s:A(s,c),u=h?g:d?Math.max(A(o,c),A(s,1)):A(o,c);this.applyChanges(this.plan({id:r,start:g,end:u,previousStart:s,previousEnd:o}),"keyboard")}showTip(t,e){const r=this.root.querySelector(".bar-tip"),i=this.root.querySelector(".timeline-body"),a=this.root.querySelector(".viewport"),s=this.root.querySelector(".grid-body"),o=this.descriptions.get(t.dataset.taskId||"");if(!r||!i||!a||!o)return;r.innerHTML=`<strong>${v(o.title)}</strong><span>${v(e?[o.kindWord,e].filter(Boolean).join(" · "):o.when)}</span>`+(o.more?`<span>${v(o.more)}</span>`:""),r.setAttribute("data-open","");const c=i.getBoundingClientRect(),h=t.getBoundingClientRect(),d=a.getBoundingClientRect(),g=Math.max(d.left,s?s.getBoundingClientRect().right:d.left)+4,u=d.right-4,b=r.offsetWidth,m=r.offsetHeight,p=(Math.max(h.left,g)+Math.min(h.right,u))/2,k=Math.max(g,Math.min(p-b/2,u-b)),w=h.top-(d.top+N)>=m+8?h.top-m-6:h.bottom+6;r.style.left=`${(k-c.left).toFixed(1)}px`,r.style.top=`${(w-c.top).toFixed(1)}px`}hideTip(){this.tipTimer!=null&&(window.clearTimeout(this.tipTimer),this.tipTimer=null),this.root.querySelector(".bar-tip")?.removeAttribute("data-open")}bindLinks(){const t=e=>({id:e.dataset.linkId||"",source:e.dataset.sourceId||"",target:e.dataset.targetId||"",type:e.dataset.linkType||"e2s"});this.root.querySelectorAll(".link-hit").forEach(e=>{e.addEventListener("click",r=>{r.stopPropagation(),this.selectedLinkId=e.dataset.linkId||null,this.focusRequest={selector:`.link-hit[data-link-id="${ot(e.dataset.linkId||"")}"]`},this.dispatchEvent(new CustomEvent("linkselect",{detail:t(e),bubbles:!0,composed:!0})),this.requestRender()}),e.addEventListener("keydown",r=>{if(r.key==="Delete"||r.key==="Backspace"){if(this.hasAttribute("readonly"))return;r.preventDefault(),this.dispatchEvent(new CustomEvent("linkdelete",{detail:t(e),bubbles:!0,composed:!0}))}else r.key==="Enter"||r.key===" "?(r.preventDefault(),this.dispatchEvent(new CustomEvent("linkselect",{detail:t(e),bubbles:!0,composed:!0}))):r.key==="Escape"&&(this.selectedLinkId=null,this.requestRender())})}),this.root.querySelectorAll(".connector").forEach(e=>{e.addEventListener("pointerdown",r=>this.startLink(e,r)),e.addEventListener("pointermove",r=>this.updateLink(r)),e.addEventListener("pointerup",()=>this.endLink(!0)),e.addEventListener("pointercancel",()=>this.endLink(!1))})}startLink(t,e){if(e.button!==0)return;const r=t.dataset.taskId,i=t.dataset.side==="start"?"start":"end",a=this.root.querySelector(".timeline-body"),s=this.root.querySelector("svg.links"),o=r?this.root.querySelector(At(r)):null;if(!r||!a||!s||!o)return;e.preventDefault();const c=a.getBoundingClientRect(),h=o.getBoundingClientRect(),d=document.createElementNS("http://www.w3.org/2000/svg","path");d.setAttribute("class","link-draft"),s.appendChild(d);const g=u=>{u.key==="Escape"&&(u.preventDefault(),u.stopPropagation(),this.endLink(!1))};this.linking={sourceId:r,side:i,fromX:(i==="start"?h.left:h.right)-c.left,fromY:h.top+h.height/2-c.top,draft:d,targetId:null,targetSide:null,onKey:g},window.addEventListener("keydown",g,!0),this.root.querySelector(".frame")?.setAttribute("data-linking",""),t.setPointerCapture(e.pointerId),this.updateLink(e)}updateLink(t){const e=this.linking,r=this.root.querySelector(".timeline-body");if(!e||!r)return;const i=r.getBoundingClientRect();e.draft.setAttribute("d",`M ${e.fromX.toFixed(1)} ${e.fromY.toFixed(1)} L ${(t.clientX-i.left).toFixed(1)} ${(t.clientY-i.top).toFixed(1)}`),this.root.querySelectorAll(".bar.link-target").forEach(d=>{d.classList.remove("link-target"),d.removeAttribute("data-link-side")}),e.targetId=null,e.targetSide=null;const a=this.root.elementFromPoint(t.clientX,t.clientY),s=a?.closest?.(".connector"),o=s?this.root.querySelector(At(s.dataset.taskId||"")):a?.closest?.(".bar"),c=o?.dataset.taskId;if(!o||!c||c===e.sourceId)return;const h=o.getBoundingClientRect();e.targetId=c,e.targetSide=s?s.dataset.side==="start"?"start":"end":t.clientX<h.left+h.width/2?"start":"end",o.classList.add("link-target"),o.setAttribute("data-link-side",e.targetSide)}endLink(t){const e=this.linking;if(!e||(window.removeEventListener("keydown",e.onKey,!0),this.linking=null,e.draft.remove(),this.root.querySelector(".frame")?.removeAttribute("data-linking"),this.root.querySelectorAll(".bar.link-target").forEach(i=>{i.classList.remove("link-target"),i.removeAttribute("data-link-side")}),!t||!e.targetId||!e.targetSide))return;const r=`${e.side==="start"?"s":"e"}2${e.targetSide==="start"?"s":"e"}`;this.getLinks().some(i=>i.source===e.sourceId&&i.target===e.targetId&&(i.type||"e2s")===r)||this.dispatchEvent(new CustomEvent("linkcreate",{detail:{source:e.sourceId,target:e.targetId,type:r},bubbles:!0,composed:!0}))}startRename(t){if(this.hasAttribute("readonly"))return;const e=this.getTasks().find(r=>r.id===t);!e||e.readonly||(this.editingId=t,this.editingValue=e.label,this.focusRequest={selector:".rename",reveal:!0,retries:0,selectAll:!0},this.reveal(t,"auto"),this.requestRender())}finishRename(t){const e=this.editingId;if(!e)return;const r=this.editingValue.trim(),i=this.getTasks().find(a=>a.id===e)?.label;this.editingId=null,t&&r&&i!=null&&r!==i&&this.dispatchEvent(new CustomEvent("taskrename",{detail:{id:e,label:r,previousLabel:i},bubbles:!0,composed:!0,cancelable:!0}))&&(this.overrides.set(e,{...this.overrides.get(e),label:r}),this.bumpOverrides()),this.focusRequest={selector:Nt(e)},this.requestRender()}requestCreate(t,e){const r=e?this.getTasks().find(i=>i.id===e):void 0;this.dispatchEvent(new CustomEvent("taskcreate",{detail:{start:W(t),end:W(A(t,He)),parent:r?.parent,after:r?.id},bubbles:!0,composed:!0}))}bindEditing(){this.root.querySelectorAll(".grid-row .task-name").forEach(e=>{e.addEventListener("dblclick",r=>{if(r.target.closest(".expander, .rename"))return;const i=e.closest(".grid-row")?.dataset.taskId;i&&this.startRename(i)})});const t=this.root.querySelector(".rename");t&&(t.addEventListener("input",()=>{this.editingValue=t.value}),t.addEventListener("keydown",e=>{e.stopPropagation(),e.key==="Enter"?(e.preventDefault(),this.finishRename(!0)):e.key==="Escape"&&(e.preventDefault(),this.finishRename(!1))}),t.addEventListener("blur",()=>{this.suppressBlur||this.finishRename(!0)})),this.root.querySelector(".add-button")?.addEventListener("click",()=>{const{min:e,rangeEnd:r}=this.layoutInfo,i=Y(Date.now());this.requestCreate(i>=e&&i<=r?i:e,this.selectedId??void 0)}),this.hasAttribute("creatable")&&!this.hasAttribute("readonly")&&this.root.querySelectorAll(".timeline-row").forEach(e=>{e.addEventListener("dblclick",r=>{if(r.target!==e)return;const i=this.root.querySelector(".timeline-body");if(!i)return;const{min:a,dayWidth:s}=this.layoutInfo,o=Math.floor((r.clientX-i.getBoundingClientRect().left)/s);this.requestCreate(A(a,o),e.dataset.taskId)})})}bindRows(){this.root.querySelectorAll(".grid-row").forEach(t=>{t.addEventListener("keydown",e=>{if(e.target!==t)return;const r=t.dataset.taskId;if(r){if(e.key==="Enter"||e.key===" "){e.preventDefault(),this.selectTask(r);return}if(e.key==="F2"){e.preventDefault(),this.startRename(r);return}this.navigate(r,e.key,"row")&&e.preventDefault()}})})}bindSplitters(){const t=this.root.querySelector(".surface"),e=this.root.querySelectorAll(".splitter"),r=i=>{const a=Math.round(Math.max(this.gridBounds.min,Math.min(this.gridBounds.max,i)));a!==this.gridBounds.width&&(this.setAttribute("grid-width",String(a)),this.dispatchEvent(new CustomEvent("gridresize",{detail:{width:a},bubbles:!0,composed:!0})))};e.forEach(i=>{i.addEventListener("pointerdown",a=>{if(a.button!==0||!t)return;a.preventDefault();const s=a.clientX,{min:o,max:c,width:h}=this.gridBounds;let d=h;i.setPointerCapture(a.pointerId),e.forEach(b=>b.classList.add("dragging"));const g=b=>{d=Math.max(o,Math.min(c,h+b.clientX-s)),t.style.setProperty("--gantt-grid-w",`${d}px`)},u=b=>{if(i.removeEventListener("pointermove",g),i.removeEventListener("pointerup",u),i.removeEventListener("pointercancel",u),e.forEach(m=>m.classList.remove("dragging")),b.type==="pointercancel"){t.style.removeProperty("--gantt-grid-w");return}r(d)};i.addEventListener("pointermove",g),i.addEventListener("pointerup",u),i.addEventListener("pointercancel",u)}),i.addEventListener("keydown",a=>{if(i.getAttribute("role")!=="separator")return;const s=a.shiftKey?64:16,{min:o,max:c,width:h}=this.gridBounds,d=a.key==="ArrowLeft"?h-s:a.key==="ArrowRight"?h+s:a.key==="Home"?o:a.key==="End"?c:null;d!=null&&(a.preventDefault(),r(d))})})}bindTooltips(){this.root.querySelectorAll(".bar").forEach(t=>{t.addEventListener("pointerenter",e=>{e.pointerType!=="touch"&&(this.hideTip(),this.tipTimer=window.setTimeout(()=>{this.tipTimer=null,this.showTip(t)},150))}),t.addEventListener("pointerleave",()=>this.hideTip()),t.addEventListener("pointerdown",()=>{this.pointerFocus=!0,window.setTimeout(()=>{this.pointerFocus=!1},0),this.hideTip()}),t.addEventListener("blur",()=>this.hideTip()),t.addEventListener("focus",()=>{this.pointerFocus||this.showTip(t)})})}get tasks(){return qt(this.getAttribute("tasks"))}set tasks(t){ne(this,"tasks",t,!0)}get holidays(){return qt(this.getAttribute("holidays"))}set holidays(t){if(!Array.isArray(t)){this.removeAttribute("holidays");return}try{this.setAttribute("holidays",JSON.stringify(t.map(e=>e&&typeof e=="object"&&!(e instanceof Date)?{...e,date:xt(e.date)}:xt(e))))}catch{}}get links(){return qt(this.getAttribute("links"))}set links(t){ne(this,"links",t,!1)}resolveBehavior(t){return t==="smooth"&&typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches?"auto":t??"auto"}scrollViewport(t,e){const r=this.root.querySelector(".viewport");if(!r)return;const i=this.resolveBehavior(e);if(this.smoothTarget=i==="smooth"?t:null,this.smoothTarget){const a=this.smoothTarget;window.setTimeout(()=>{this.smoothTarget===a&&(this.smoothTarget=null)},2e3)}if(i!=="smooth"&&(t.left!=null&&(this.viewportLeft=t.left),t.top!=null&&(this.viewportTop=t.top)),typeof r.scrollTo=="function"){r.scrollTo({...t,behavior:i});return}t.left!=null&&(r.scrollLeft=t.left),t.top!=null&&(r.scrollTop=t.top)}scrollToDate(t,e={}){const r=t==="today"?Y(Date.now()):typeof t=="number"?Number.isFinite(t)?Y(t):null:$(t),i=this.root.querySelector(".viewport");r==null||!i||this.scrollViewport({left:this.leftFor(i,r,e.align??"center")},e.behavior)}leftFor(t,e,r){const{min:i,dayWidth:a,gridWidth:s}=this.layoutInfo,o=(e-i)/D*a,c=r==="start"?o-24:o-Math.max(0,t.clientWidth-s)/2;return Math.max(0,Math.min(c,Math.max(0,t.scrollWidth-t.clientWidth)))}scrollToTask(t,e={}){const r=this.getTasks(),i=new Map(r.map(d=>[d.id,d])),a=i.get(t);if(!a)return!1;let s=!1,o=a.parent;for(let d=0;o&&d<1e3;d+=1)this.collapsed.has(o)&&(this.collapsed.delete(o),this.dispatchEvent(new CustomEvent("tasktoggle",{detail:{id:o,open:!0},bubbles:!0,composed:!0})),s=!0),o=i.get(o)?.parent;const c=this.getAttribute("filter")??this.internalFilter;return this.getVisibleTasks(r,c,bt(this.getAttribute("sort"))).some(d=>d.id===t)?s||!this.visibleIds.includes(t)?(this.pendingReveal={id:t,behavior:e.behavior??"auto"},this.requestRender(),!0):this.reveal(t,e.behavior??"auto"):!1}reveal(t,e){const r=this.visibleIds.indexOf(t),i=this.root.querySelector(".viewport"),a=this.getTasks().find(g=>g.id===t);if(r<0||!i||!a)return!1;const s={},o=N+r*S;if(!(o>=i.scrollTop+N&&o+S<=i.scrollTop+i.clientHeight)){const g=r*S+S/2-(i.clientHeight-N)/2;s.top=Math.max(0,Math.min(g,Math.max(0,i.scrollHeight-i.clientHeight)))}const h=$(a.start),d=$(a.end);if(h!=null){const{min:g,dayWidth:u,gridWidth:b}=this.layoutInfo,m=(h-g)/D*u,p=((d??h)-g)/D*u,k=i.scrollLeft;(m<k||p>k+i.clientWidth-b)&&(s.left=this.leftFor(i,h,"start"))}return(s.left!=null||s.top!=null)&&this.scrollViewport(s,e),!0}toggleTask(t,e){const r=!this.collapsed.has(t),i=e??!r;i!==r&&(i?this.collapsed.delete(t):this.collapsed.add(t),this.dispatchEvent(new CustomEvent("tasktoggle",{detail:{id:t,open:i},bubbles:!0,composed:!0})),this.requestRender())}expandAll(){for(const t of[...this.collapsed])this.toggleTask(t,!0)}collapseAll(){const t=new Set(this.getTasks().map(e=>e.parent).filter(e=>!!e));for(const e of t)this.toggleTask(e,!1)}exportSvg(){Lt=this.getAttribute("locale")||void 0;const t=this.labelSet(),e=this.getTasks(),{min:r,dayWidth:i}=this.layoutInfo;if(!e.length||r===0)return"";const a=this.getVisibleTasks(e,this.getAttribute("filter")??this.internalFilter,bt(this.getAttribute("sort")));if(!a.length)return"";const s=getComputedStyle(this),o=f=>{const E=/^var\((--[\w-]+),\s*([^)]+)\)$/.exec(f);return E?s.getPropertyValue(E[1]).trim()||E[2].trim():f},c=o("var(--ui-color-text, #0f172a)"),h=o("var(--ui-color-muted, #526175)"),d=o("var(--ui-color-border, #cbd5e1)"),g=o("var(--ui-color-surface, #ffffff)");o("var(--ui-color-primary, #2563eb)");const u=o("var(--ui-color-danger, #dc2626)"),b=this.root.querySelector(".scale")?.getAttribute("data-zoom")||"week",m=Ft(this.getAttribute("week-start")),p=te(this.getAttribute("non-working-days"),this.getAttribute("holidays")),k=A(Math.max(...e.map(f=>$(f.end)??0)),3),y=Math.max(320,(k-r)/D*i),w=r+y/i*D,x=260,M=x+y,T=N+a.length*S,z=f=>(f-r)/D*i,U=Z({dateStyle:"medium"}),P=Z({month:"long",year:"numeric"}),lt=Z({month:"short"}),st=(f,E)=>{const L=new Date(f);return E==="day"||E==="week"?String(L.getDate()):E==="month"?lt.format(L):E==="quarter"?`Q${Math.floor(L.getMonth()/3)+1}`:String(L.getFullYear())},R=[],q=(f,E,L,_,G)=>`<rect x="${f.toFixed(1)}" y="${E.toFixed(1)}" width="${Math.max(0,L).toFixed(1)}" height="${_}" ${G}/>`;R.push(q(0,0,M,T,`fill="${g}"`));const K=b==="year"?null:b==="day"||b==="week"?"month":"year";if(K)for(const f of kt(K,m,r,w,i,0,Number.POSITIVE_INFINITY)){const E=K==="month"?P.format(new Date(f.start)):String(new Date(f.start).getFullYear());R.push(`<line x1="${(x+f.left).toFixed(1)}" y1="0" x2="${(x+f.left).toFixed(1)}" y2="26" stroke="${d}"/>`),f.width>=It&&R.push(`<text x="${(x+f.left+8).toFixed(1)}" y="17" font-size="11" font-weight="700" fill="${c}">${v(E)}</text>`)}const J=K?26:0;for(const f of kt(b,m,r,w,i,0,Number.POSITIVE_INFINITY))R.push(`<line x1="${(x+f.left).toFixed(1)}" y1="${J}" x2="${(x+f.left).toFixed(1)}" y2="${T}" stroke="${d}" stroke-opacity=".45"/>`),(f.width>=It||b==="day")&&R.push(`<text x="${(x+f.left+8).toFixed(1)}" y="${J+18}" font-size="11" font-weight="650" fill="${h}">${v(st(f.start,b))}</text>`);if(R.push(`<line x1="0" y1="${N}" x2="${M}" y2="${N}" stroke="${d}"/>`),R.push(`<text x="10" y="${N/2+4}" font-size="11" font-weight="700" fill="${h}">${v(t.columnTask.toUpperCase())}</text>`),i>=fe&&(p.weekdays.size||p.holidays.size))for(let f=r;f<w;f=A(f,1))ge(f,p)&&R.push(q(x+z(f),N,i,a.length*S,`fill="${p.holidays.has(f)?"#d97706":h}" fill-opacity="${p.holidays.has(f)?".15":".1"}"`));a.forEach((f,E)=>{const L=N+E*S;R.push(`<line x1="0" y1="${L+S}" x2="${M}" y2="${L+S}" stroke="${d}" stroke-opacity=".5"/>`),R.push(`<text x="${10+f.depth*18}" y="${L+S/2+4}" font-size="12" font-weight="650" fill="${c}">${v(f.label.length>34-f.depth*2?`${f.label.slice(0,33-f.depth*2)}…`:f.label)}</text>`)}),R.push(`<line x1="${x}" y1="0" x2="${x}" y2="${T}" stroke="${d}"/>`);const O=[],V=new Map(a.map((f,E)=>[f.id,{task:f,index:E}]));for(const f of this.getLinks()){const E=V.get(f.source),L=V.get(f.target);!E||!L||O.push(`<path d="${oe(E.task,E.index,L.task,L.index,f.type,z)}" fill="none" stroke="${h}" stroke-width="1.35" stroke-linecap="round" stroke-linejoin="round" marker-end="url(#arrow)"/>`)}a.forEach((f,E)=>{const L=E*S,_=ut(f),G=f.critical?u:o(mt[f.tone||(_==="summary"?"info":"default")]||mt.default),at=z(f.startMs),dt=Math.max(12,z(f.endMs)-at);if(f.baselineStart&&f.baselineEnd){const F=$(f.baselineStart)??f.startMs,C=$(f.baselineEnd)??f.endMs;O.push(q(z(F),L+33,Math.max(4,z(C)-z(F)),4,`rx="2" fill="${h}" fill-opacity=".5"`))}if(_==="milestone"){const F=at,C=L+21;O.push(`<polygon points="${F},${C-10} ${F+10},${C} ${F},${C+10} ${F-10},${C}" fill="${G}"/>`);return}const rt=_==="summary"?L+17:L+12,it=_==="summary"?9:18,$t=Math.max(0,Math.min(100,Number(f.progress??0))),pt=(F,C,tt)=>{O.push(q(F,rt,C,it,`rx="4" fill="${G}"`)),_==="summary"&&O.push(q(F,rt,C,it,'rx="3" fill="#000" fill-opacity=".2"')),tt>0&&O.push(q(F,rt,C*tt/100,it,'rx="4" fill="#000" fill-opacity=".28"'))};if(_==="task"&&f.segments?.length)for(const F of f.segments){const C=$(F.start),tt=$(F.end);C==null||tt==null||tt<=C||pt(z(Math.max(f.startMs,C)),z(Math.min(f.endMs,tt))-z(Math.max(f.startMs,C)),F.progress??$t)}else pt(at,dt,$t)});const et=Y(Date.now());return this.getAttribute("show-today")!=="false"&&et>=r&&et<=w&&(O.push(`<line x1="${z(et).toFixed(1)}" y1="0" x2="${z(et).toFixed(1)}" y2="${a.length*S}" stroke="${u}" stroke-width="2"/>`),R.push(`<text x="${(x+z(et)).toFixed(1)}" y="${N-6}" font-size="10" font-weight="700" text-anchor="middle" fill="${u}">${v(t.today)}</text>`)),R.push(`<g transform="translate(${x} ${N})">${O.join("")}</g>`),R.push(`<title>${v(this.getAttribute("aria-label")||t.title)} (${v(U.format(new Date(r)))} – ${v(U.format(new Date(k)))})</title>`),`<svg xmlns="http://www.w3.org/2000/svg" width="${Math.round(M)}" height="${T}" viewBox="0 0 ${Math.round(M)} ${T}" font-family="system-ui, -apple-system, 'Segoe UI', sans-serif"><defs><marker id="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" fill="${h}"/></marker></defs>`+R.join("")+"</svg>"}rangeMin(){const e=this.getTasks().map(r=>$(r.start)).filter(r=>r!=null);return e.length?A(Math.min(...e),-2):Y(Date.now())}selectTask(t){if(this.captureViewport(),this.selectedLinkId=null,this.selectedId===t){this.dispatchEvent(new CustomEvent("taskselect",{detail:{id:t},bubbles:!0,composed:!0}));return}this.selectedId=t,this.dispatchEvent(new CustomEvent("taskselect",{detail:{id:t},bubbles:!0,composed:!0})),this.requestRender()}focusTask(t,e){this.selectedId!==t&&(this.selectedId=t,this.dispatchEvent(new CustomEvent("taskselect",{detail:{id:t},bubbles:!0,composed:!0}))),this.focusRequest={selector:e==="row"?Nt(t):At(t),reveal:!0,retries:0},this.reveal(t,"auto"),this.requestRender()}navigate(t,e,r){const i=this.visibleIds,a=i.indexOf(t);if(a<0)return!1;let s;switch(e){case"ArrowUp":s=i[a-1];break;case"ArrowDown":s=i[a+1];break;case"Home":s=i[0];break;case"End":s=i[i.length-1];break;case"PageUp":s=i[Math.max(0,a-10)];break;case"PageDown":s=i[Math.min(i.length-1,a+10)];break;case"ArrowRight":case"ArrowLeft":{if(r!=="row")return!1;const o=this.getTasks(),h=new Set(o.map(d=>d.parent).filter(d=>!!d)).has(t);if(e==="ArrowRight"){if(!h)return!0;this.collapsed.has(t)?this.toggleTask(t,!0):s=i[a+1]}else if(h&&!this.collapsed.has(t))this.toggleTask(t,!1);else{const d=o.find(g=>g.id===t)?.parent;d&&i.includes(d)&&(s=d)}break}default:return!1}return s&&s!==t&&this.focusTask(s,r),!0}captureViewport(){const t=this.root.querySelector(".viewport");t&&(this.viewportTop=t.scrollTop,this.viewportLeft=t.scrollLeft,this.viewportWidth=t.clientWidth||this.viewportWidth,this.viewportHeight=t.clientHeight||this.viewportHeight)}getTasks(){const t=this.getAttribute("tasks"),e=this.hasAttribute("auto-summary"),r=this.hasAttribute("critical-path")?this.getAttribute("links")??"":null;return t===this.taskCacheRaw&&this.taskCacheVersion===this.overridesVersion&&e===this.taskCacheAuto&&r===this.taskCacheCritical?this.taskCache:(this.taskCacheRaw=t,this.taskCacheVersion=this.overridesVersion,this.taskCacheAuto=e,this.taskCacheCritical=r,this.taskCache=je(t,this.overrides,e),e&&(this.taskCache=lr(this.taskCache)),r!==null&&(this.taskCache=dr(this.taskCache,Zt(r))),this.seedCollapsed(this.taskCache),this.visibleCacheKey="",this.taskCache)}seedCollapsed(t){for(const e of t)this.seededIds.has(e.id)||(this.seededIds.add(e.id),e.open===!1&&this.collapsed.add(e.id))}getLinks(){const t=this.getAttribute("links");return t===this.linkCacheRaw?this.linkCache:(this.linkCacheRaw=t,this.linkCache=Zt(t),this.linkCache)}getVisibleTasks(t,e,r){const i=Array.from(this.collapsed).sort().join(","),a=`${this.taskCacheRaw||""}|${this.overridesVersion}|${e}|${r}|${i}`;return a===this.visibleCacheKey?this.visibleCache:(this.visibleCacheKey=a,this.visibleCache=Xe(t,this.collapsed,e,r),this.visibleCache)}bumpOverrides(){this.overridesVersion+=1,this.taskCacheVersion=-1,this.visibleCacheKey=""}shouldRenderOnAttributeChange(t,e,r){if(t==="tasks"){this.overrides.clear(),this.bumpOverrides();const i=ee(r);Ge(ee(e),i)||(this.viewportTop=0,this.viewportLeft=0,this.initialScrollPending=!0,this.collapsed.clear(),this.seededIds.clear()),this.selectedId!=null&&!i.has(this.selectedId)&&(this.selectedId=null)}return t==="links"&&(this.linkCacheRaw=null),(t==="auto-summary"||t==="cascade")&&this.bumpOverrides(),!0}}typeof customElements<"u"&&!customElements.get("ui-gantt")&&customElements.define("ui-gantt",me);exports.UIGantt=me;