construction-gantt 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # construction-gantt
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b59d1fe: Multi-baseline UI: render multiple captured baselines simultaneously for variation-claim delay analysis under NZS 3910 / AS 4000.
8
+
9
+ - New `baselineIndices: ReadonlyArray<BaselineIndex>` prop on `<Gantt>` overlays one phantom ghost row per selected baseline.
10
+ - Existing `baselineIndex` prop continues to work; deprecated, removed at v1.0.
11
+ - Each phantom row in multi-baseline mode carries its own variance pill; single-baseline mode keeps the pill on the live bar (existing behaviour).
12
+ - Per-baseline CSS class `bode-baseline-${N}` for indices 0–10 ships with a default palette; consumers override via higher-specificity rules.
13
+ - Phantom row label includes `baseline.name` (or `Baseline N` fallback) and formatted `capturedAt`.
14
+
15
+ No engine changes (ADR-005 preserved). Render-layer only. Test infrastructure additions: canvas 2D context stub at `vitest.setup.ts` so SVAR mounts under happy-dom.
16
+
3
17
  ## 0.1.0
4
18
 
5
19
  ### Minor Changes
package/LICENSE.md ADDED
@@ -0,0 +1,27 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rips Cassels
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ This software wraps and depends on free SVAR React Gantt (also MIT-licensed). Consumers
26
+ must also accept that license. See https://github.com/svar-widgets/gantt for SVAR's
27
+ terms.
package/dist/index.cjs CHANGED
@@ -1907,7 +1907,7 @@ function reducer(state, action) {
1907
1907
  };
1908
1908
  }
1909
1909
 
1910
- __insertCSS(".construction-gantt-non-working{background:rgba(241,245,249,.65)}.construction-gantt-marker-today{background:#ef4444;color:#fff;font-weight:600}.construction-gantt-marker-milestone{background:#2563eb;color:#fff;font-weight:500}");
1910
+ __insertCSS(".construction-gantt-non-working{background:rgba(241,245,249,.65)}.construction-gantt-marker-today{background:#ef4444;color:#fff;font-weight:600}.construction-gantt-marker-milestone{background:#2563eb;color:#fff;font-weight:500}.bode-baseline-ghost{display:flex;align-items:center;gap:6px;height:60%;margin-top:15%;padding:0 8px;border-radius:3px;font-size:9px;font-style:italic;color:#fff;opacity:.85;white-space:nowrap}.bode-baseline-0{background:#6b7280}.bode-baseline-1{background:#3b82f6}.bode-baseline-2{background:#8b5cf6}.bode-baseline-3{background:#14b8a6;color:#0f172a}.bode-baseline-4{background:#f59e0b;color:#0f172a}.bode-baseline-5{background:#ec4899}.bode-baseline-6{background:#06b6d4;color:#0f172a}.bode-baseline-7{background:#84cc16;color:#0f172a}.bode-baseline-8{background:#f97316;color:#0f172a}.bode-baseline-9{background:#a855f7}.bode-baseline-10{background:#dc2626}");
1911
1911
 
1912
1912
  // Render-only visibility filter for <Gantt> tasks.
1913
1913
  //
@@ -1938,8 +1938,12 @@ __insertCSS(".construction-gantt-non-working{background:rgba(241,245,249,.65)}.c
1938
1938
  return tasks.filter((t)=>visibleTaskIds.has(t.id));
1939
1939
  }
1940
1940
 
1941
- const Gantt = /*#__PURE__*/ react.forwardRef(function Gantt({ project, height = 500, cellWidth = 48, cellHeight = 42, preScheduled = false, markers, baselineIndex, showBaselineBars, columns, visibleTaskIds }, ref) {
1941
+ const Gantt = /*#__PURE__*/ react.forwardRef(function Gantt({ project, height = 500, cellWidth = 48, cellHeight = 42, preScheduled = false, markers, baselineIndex, baselineIndices, showBaselineBars, columns, visibleTaskIds }, ref) {
1942
1942
  const containerRef = react.useRef(null);
1943
+ const effectiveBaselineIndices = react.useMemo(()=>resolveEffectiveBaselineIndices(baselineIndices, baselineIndex), [
1944
+ baselineIndices,
1945
+ baselineIndex
1946
+ ]);
1943
1947
  const scheduled = react.useMemo(()=>preScheduled ? project : schedule(project), [
1944
1948
  project,
1945
1949
  preScheduled
@@ -1954,32 +1958,17 @@ const Gantt = /*#__PURE__*/ react.forwardRef(function Gantt({ project, height =
1954
1958
  scheduled.calendars,
1955
1959
  scheduled.defaultCalendarId
1956
1960
  ]);
1957
- const baseline = react.useMemo(()=>{
1958
- if (baselineIndex === undefined) return undefined;
1959
- return scheduled.baselines.find((b)=>b.index === baselineIndex);
1960
- }, [
1961
+ // Resolve effective indices → actual Baseline records, dropping any that
1962
+ // don't exist on the project. Preserves caller order so phantom rows
1963
+ // render in the array order the consumer passed.
1964
+ const resolvedBaselines = react.useMemo(()=>resolveBaselines(scheduled.baselines, effectiveBaselineIndices), [
1961
1965
  scheduled.baselines,
1962
- baselineIndex
1966
+ effectiveBaselineIndices
1963
1967
  ]);
1964
- const ghostBarsEnabled = baseline !== undefined && (showBaselineBars ?? true);
1965
- const svarTasks = react.useMemo(()=>{
1966
- if (!ghostBarsEnabled || !baseline) {
1967
- return renderableTasks.map((t)=>toSvarTask(t, baseline, calendar));
1968
- }
1969
- // Interleave each real task with its baseline-snapshot ghost row.
1970
- // Phantoms share the real task's `parent` so they stay grouped under
1971
- // the same summary in hierarchy views.
1972
- const out = [];
1973
- for (const t of renderableTasks){
1974
- out.push(toSvarTask(t, baseline, calendar));
1975
- if (t.type === 'summary') continue;
1976
- const phantom = makeBaselinePhantom(t, baseline);
1977
- if (phantom) out.push(phantom);
1978
- }
1979
- return out;
1980
- }, [
1968
+ const ghostBarsEnabled = resolvedBaselines.length > 0 && (showBaselineBars ?? true);
1969
+ const svarTasks = react.useMemo(()=>buildSvarTasks(renderableTasks, resolvedBaselines, calendar, ghostBarsEnabled), [
1981
1970
  renderableTasks,
1982
- baseline,
1971
+ resolvedBaselines,
1983
1972
  calendar,
1984
1973
  ghostBarsEnabled
1985
1974
  ]);
@@ -2010,7 +1999,7 @@ const Gantt = /*#__PURE__*/ react.forwardRef(function Gantt({ project, height =
2010
1999
  ]);
2011
2000
  react.useImperativeHandle(ref, ()=>({
2012
2001
  async exportPNG (options) {
2013
- const { exportPNG } = await Promise.resolve().then(function () { return require('./png-C8t74695.cjs'); });
2002
+ const { exportPNG } = await Promise.resolve().then(function () { return require('./png-BloW1DDl.cjs'); });
2014
2003
  return exportPNG({
2015
2004
  scheduled,
2016
2005
  ganttProps: {
@@ -2018,6 +2007,7 @@ const Gantt = /*#__PURE__*/ react.forwardRef(function Gantt({ project, height =
2018
2007
  cellHeight,
2019
2008
  markers,
2020
2009
  baselineIndex,
2010
+ baselineIndices,
2021
2011
  showBaselineBars,
2022
2012
  columns,
2023
2013
  height,
@@ -2027,7 +2017,7 @@ const Gantt = /*#__PURE__*/ react.forwardRef(function Gantt({ project, height =
2027
2017
  });
2028
2018
  },
2029
2019
  async exportPDF (options) {
2030
- const { exportPDF } = await Promise.resolve().then(function () { return require('./pdf-CAQDrX0w.cjs'); });
2020
+ const { exportPDF } = await Promise.resolve().then(function () { return require('./pdf-N2LwD-_F.cjs'); });
2031
2021
  return exportPDF({
2032
2022
  scheduled,
2033
2023
  ganttProps: {
@@ -2035,6 +2025,7 @@ const Gantt = /*#__PURE__*/ react.forwardRef(function Gantt({ project, height =
2035
2025
  cellHeight,
2036
2026
  markers,
2037
2027
  baselineIndex,
2028
+ baselineIndices,
2038
2029
  showBaselineBars,
2039
2030
  columns,
2040
2031
  height,
@@ -2056,6 +2047,7 @@ const Gantt = /*#__PURE__*/ react.forwardRef(function Gantt({ project, height =
2056
2047
  cellHeight,
2057
2048
  markers,
2058
2049
  baselineIndex,
2050
+ baselineIndices,
2059
2051
  showBaselineBars,
2060
2052
  columns,
2061
2053
  height,
@@ -2085,23 +2077,56 @@ const Gantt = /*#__PURE__*/ react.forwardRef(function Gantt({ project, height =
2085
2077
  const ConstructionBar = ({ data })=>{
2086
2078
  // Phantom baseline row — render a slim outlined ghost bar.
2087
2079
  if (data.is_baseline_ghost) {
2088
- return /*#__PURE__*/ jsxRuntime.jsx("div", {
2089
- style: {
2090
- height: '60%',
2091
- marginTop: '15%',
2092
- border: '1.5px dashed #94a3b8',
2093
- background: 'transparent',
2094
- borderRadius: 3,
2095
- fontSize: 9,
2096
- color: '#64748b',
2097
- display: 'flex',
2098
- alignItems: 'center',
2099
- padding: '0 6px',
2100
- fontStyle: 'italic',
2101
- whiteSpace: 'nowrap'
2102
- },
2080
+ const baselineIdx = data.baseline_index ?? 0;
2081
+ const phantomSlipped = data.is_slipped ?? false;
2082
+ const phantomAhead = data.is_ahead ?? false;
2083
+ return /*#__PURE__*/ jsxRuntime.jsxs("div", {
2084
+ className: `bode-baseline-ghost bode-baseline-${baselineIdx}`,
2103
2085
  title: "Baseline position — where this task was when the baseline was captured",
2104
- children: "baseline"
2086
+ children: [
2087
+ /*#__PURE__*/ jsxRuntime.jsx("span", {
2088
+ style: {
2089
+ flex: 1,
2090
+ overflow: 'hidden',
2091
+ textOverflow: 'ellipsis'
2092
+ },
2093
+ children: data.text
2094
+ }),
2095
+ phantomSlipped && /*#__PURE__*/ jsxRuntime.jsxs("span", {
2096
+ style: {
2097
+ padding: '0 6px',
2098
+ background: '#fed7aa',
2099
+ color: '#7c2d12',
2100
+ borderRadius: 3,
2101
+ fontSize: 10,
2102
+ fontWeight: 700,
2103
+ lineHeight: '16px',
2104
+ whiteSpace: 'nowrap'
2105
+ },
2106
+ title: "Drifted later than the baseline",
2107
+ children: [
2108
+ "+",
2109
+ workingMinutesToShortLabel(data.start_variance ?? 0)
2110
+ ]
2111
+ }),
2112
+ phantomAhead && /*#__PURE__*/ jsxRuntime.jsxs("span", {
2113
+ style: {
2114
+ padding: '0 6px',
2115
+ background: '#bbf7d0',
2116
+ color: '#14532d',
2117
+ borderRadius: 3,
2118
+ fontSize: 10,
2119
+ fontWeight: 700,
2120
+ lineHeight: '16px',
2121
+ whiteSpace: 'nowrap'
2122
+ },
2123
+ title: "Ahead of the baseline",
2124
+ children: [
2125
+ "−",
2126
+ workingMinutesToShortLabel(data.start_variance ?? 0)
2127
+ ]
2128
+ })
2129
+ ]
2105
2130
  });
2106
2131
  }
2107
2132
  const isCritical = data.is_critical ?? false;
@@ -2252,19 +2277,25 @@ function toSvarTask(t, baseline, calendar) {
2252
2277
  if (t.type === 'summary') base.open = t.open ?? true;
2253
2278
  return base;
2254
2279
  }
2255
- function makeBaselinePhantom(t, baseline) {
2280
+ function makeBaselinePhantom(t, baseline, calendar) {
2256
2281
  const snap = baseline.tasks.get(t.id);
2257
2282
  if (!snap) return null;
2283
+ const variance = calendar ? getTaskBaselineVariance(t, baseline, calendar) : undefined;
2284
+ const startVariance = variance?.startVariance ?? 0;
2258
2285
  return {
2259
- id: `${t.id}-baseline-${baseline.index}`,
2260
- text: '(baseline)',
2286
+ id: `${t.id}__baseline_${baseline.index}`,
2287
+ text: formatBaselineLabel(baseline),
2261
2288
  start: snap.start,
2262
2289
  end: snap.end,
2263
2290
  duration: snap.duration,
2264
2291
  progress: 0,
2265
2292
  type: 'task',
2266
2293
  parent: t.parent,
2267
- is_baseline_ghost: true
2294
+ is_baseline_ghost: true,
2295
+ baseline_index: baseline.index,
2296
+ start_variance: startVariance,
2297
+ is_slipped: startVariance >= 30,
2298
+ is_ahead: startVariance <= -30
2268
2299
  };
2269
2300
  }
2270
2301
  function toSvarLink(l) {
@@ -2288,6 +2319,26 @@ function dependencyTypeToSvar(t) {
2288
2319
  return 's2e';
2289
2320
  }
2290
2321
  }
2322
+ /**
2323
+ * Format a baseline's metadata for display in the phantom row's label.
2324
+ * Returns "${name ?? `Baseline ${index}`} — captured ${formatShortDate(capturedAt)}".
2325
+ *
2326
+ * Exported for testing. Not part of the public surface.
2327
+ */ function formatBaselineLabel(baseline) {
2328
+ const name = baseline.name ?? `Baseline ${baseline.index}`;
2329
+ return `${name} — captured ${formatShortDate(baseline.capturedAt)}`;
2330
+ }
2331
+ /**
2332
+ * Format a Date as YYYY-MM-DD using local-time components. Deterministic
2333
+ * across locales (no Intl.DateTimeFormat — those vary by host locale).
2334
+ *
2335
+ * Exported for testing. Not part of the public surface.
2336
+ */ function formatShortDate(d) {
2337
+ const year = d.getFullYear();
2338
+ const month = String(d.getMonth() + 1).padStart(2, '0');
2339
+ const day = String(d.getDate()).padStart(2, '0');
2340
+ return `${year}-${month}-${day}`;
2341
+ }
2291
2342
  function getProjectEnd(p) {
2292
2343
  if (p.end) return p.end;
2293
2344
  let latestMs = Number.NEGATIVE_INFINITY;
@@ -2298,6 +2349,72 @@ function getProjectEnd(p) {
2298
2349
  const cushion = 24 * 60 * 60 * 1000; // 1 day
2299
2350
  return Number.isFinite(latestMs) ? new Date(latestMs + cushion) : new Date(p.start);
2300
2351
  }
2352
+ /**
2353
+ * Resolve the effective baseline indices from the two GanttProps inputs.
2354
+ *
2355
+ * - `baselineIndices` takes precedence when set (including when empty —
2356
+ * passing `[]` is an explicit opt-out signal).
2357
+ * - When `baselineIndices` is undefined, fall back to wrapping `baselineIndex`
2358
+ * in a single-element array.
2359
+ * - When both are undefined, return an empty array.
2360
+ *
2361
+ * Exported for testing. Not part of the public surface; consumers don't
2362
+ * call this directly.
2363
+ */ function resolveEffectiveBaselineIndices(baselineIndices, baselineIndex) {
2364
+ return baselineIndices ?? (baselineIndex !== undefined ? [
2365
+ baselineIndex
2366
+ ] : []);
2367
+ }
2368
+ /**
2369
+ * Map effective baseline indices to actual Baseline records on a project,
2370
+ * preserving caller order. Indices not present on the project are silently
2371
+ * dropped (per spec — consumers can pass a fixed shape across projects
2372
+ * with varying baseline counts).
2373
+ *
2374
+ * Exported for testing. Not part of the public surface.
2375
+ */ function resolveBaselines(allBaselines, effectiveIndices) {
2376
+ if (effectiveIndices.length === 0) return [];
2377
+ const byIndex = new Map(allBaselines.map((b)=>[
2378
+ b.index,
2379
+ b
2380
+ ]));
2381
+ const out = [];
2382
+ for (const idx of effectiveIndices){
2383
+ const b = byIndex.get(idx);
2384
+ if (b) out.push(b);
2385
+ }
2386
+ return out;
2387
+ }
2388
+ /**
2389
+ * Convert the rendered tasks + resolved baselines into the SVAR ITask[]
2390
+ * shape, including phantom ghost rows for each (task × baseline) pair
2391
+ * when ghost bars are enabled.
2392
+ *
2393
+ * In single-baseline mode (resolvedBaselines.length === 1), the live row
2394
+ * carries the variance pill (`start_variance` / `is_slipped` / `is_ahead`
2395
+ * are populated relative to that one baseline). In multi-baseline mode
2396
+ * (length > 1), the live row gets no variance fields (would require a
2397
+ * "primary" baseline concept that the spec rejects); each phantom row
2398
+ * gets its own.
2399
+ *
2400
+ * Exported for testing. Not part of the public surface.
2401
+ */ function buildSvarTasks(renderableTasks, resolvedBaselines, calendar, ghostBarsEnabled) {
2402
+ if (!ghostBarsEnabled || resolvedBaselines.length === 0) {
2403
+ const primary = resolvedBaselines[0];
2404
+ return renderableTasks.map((t)=>toSvarTask(t, primary, calendar));
2405
+ }
2406
+ const out = [];
2407
+ for (const t of renderableTasks){
2408
+ const liveBarBaseline = resolvedBaselines.length === 1 ? resolvedBaselines[0] : undefined;
2409
+ out.push(toSvarTask(t, liveBarBaseline, calendar));
2410
+ if (t.type === 'summary') continue;
2411
+ for (const b of resolvedBaselines){
2412
+ const phantom = makeBaselinePhantom(t, b, calendar);
2413
+ if (phantom) out.push(phantom);
2414
+ }
2415
+ }
2416
+ return out;
2417
+ }
2301
2418
  function resolveMarkers(userMarkers, projectStart, projectEnd) {
2302
2419
  if (userMarkers) return userMarkers.map(toSvarMarker);
2303
2420
  // Default: today line, only if today falls within the project window.
package/dist/index.d.cts CHANGED
@@ -476,6 +476,26 @@ interface GanttProps {
476
476
  */
477
477
  markers?: GanttMarker[];
478
478
  /**
479
+ * Baselines to overlay as phantom ghost rows beneath each live task.
480
+ *
481
+ * - Single-baseline mode (length 1): the live bar carries the variance pill,
482
+ * matching the existing single-baseline behaviour.
483
+ * - Multi-baseline mode (length > 1): each phantom row carries its own
484
+ * variance pill against the live task; the live bar has no pill.
485
+ * - Indices not present on `project.baselines` are silently skipped (no
486
+ * throw) so consumers can pass a fixed shape regardless of how many
487
+ * baselines a particular project has captured.
488
+ *
489
+ * Phantom rows render in array order — consumers wanting chronological
490
+ * order should sort by `baseline.capturedAt` before passing.
491
+ */
492
+ baselineIndices?: ReadonlyArray<BaselineIndex>;
493
+ /**
494
+ * @deprecated Use `baselineIndices: [N]`. Single-index convenience prop
495
+ * kept as a no-friction alias for v0.x consumers. Removed at v1.0.
496
+ * If both `baselineIndex` and `baselineIndices` are set, `baselineIndices`
497
+ * takes precedence.
498
+ *
479
499
  * Show variance against this baseline index. If unset (or no matching
480
500
  * baseline exists on `project.baselines`), bars render without variance
481
501
  * pills. Construction-vertical use case (ADR-003): comparing the live
@@ -486,8 +506,9 @@ interface GanttProps {
486
506
  /**
487
507
  * Render the baseline as a separate "ghost" bar beneath each live task.
488
508
  * Matches the MS Project baseline-view idiom construction PMs expect
489
- * when reviewing variation claims. Default true when `baselineIndex`
490
- * is set; pass `false` to keep variance shown only as in-bar pills.
509
+ * when reviewing variation claims. Default true when either
510
+ * `baselineIndex` or `baselineIndices` is set; pass `false` to keep
511
+ * variance shown only as in-bar pills.
491
512
  */
492
513
  showBaselineBars?: boolean;
493
514
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sources":["../src/types.ts","../src/analysis.ts","../src/baseline.ts","../src/calendars/nz-holidays.ts","../src/editing/commands.ts","../src/editing/errors.ts","../src/editing/factories.ts","../src/editing/use-editable-project.ts","../src/export/types.ts","../src/Gantt.tsx","../src/mspdi/types.ts","../src/mspdi/parse.ts","../src/mspdi/serialize.ts","../src/SpikeGantt.tsx","../src/schedule.ts","../src/topological-sort.ts","../src/working-time.ts"],"mappings":";;;AAAA,KAAK,MAAM;AACX,KAAK,MAAM;AACX,KAAK,UAAU;AACf,KAAK,UAAU;AACf,KAAK,YAAY;AACjB,KAAK,QAAQ;AACb,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB,UAAU,UAAU;AACpB,UAAU,cAAc;AACxB;AACA,WAAW,IAAI;AACf;AACA,UAAU,IAAI;AACd,QAAQ,MAAM;AACd;AACA;AACA,aAAa,MAAM;AACnB,UAAU,QAAQ;AAClB,kBAAkB,YAAY;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,IAAI;AACf,SAAS,IAAI;AACb;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA,kBAAkB,UAAU;AAC5B;AACA;AACA;AACA;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA,eAAe,YAAY;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,YAAY;AACtB,gBAAgB,IAAI;AACpB,iBAAiB,IAAI;AACrB,eAAe,IAAI;AACnB,gBAAgB,IAAI;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB,UAAU,IAAI;AACd,QAAQ,MAAM;AACd,YAAY,MAAM;AAClB,YAAY,MAAM;AAClB,UAAU,cAAc;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,YAAY;AACtB;AACA;AACA;AACA,KAAK,SAAS;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,QAAQ;AAClB,QAAQ,UAAU;AAClB;AACA,cAAc,YAAY;AAC1B,gBAAgB,iBAAiB;AACjC,qBAAqB,UAAU;AAC/B;AACA,UAAU,iBAAiB;AAC3B,UAAU,IAAI;AACd;AACA;AACA,gBAAgB,YAAY;AAC5B;AACA;AACA;AACA,UAAU,QAAQ;AAClB,QAAQ,UAAU;AAClB;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,UAAU;AACpB,QAAQ,YAAY;AACpB,YAAY,MAAM;AAClB,gBAAgB,UAAU;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB,UAAU,QAAQ;AAClB,WAAW,aAAa;AACxB;AACA;AACA,gBAAgB,IAAI;AACpB;AACA,WAAW,GAAG,CAAC,MAAM,EAAE,oBAAoB;AAC3C;AACA,UAAU,oBAAoB;AAC9B,WAAW,IAAI;AACf,SAAS,IAAI;AACb;AACA;AACA,UAAU,OAAO;AACjB;AACA,WAAW,IAAI;AACf;AACA,UAAU,IAAI;AACd,uBAAuB,UAAU;AACjC,WAAW,IAAI;AACf,WAAW,IAAI;AACf,eAAe,QAAQ;AACvB,eAAe,QAAQ;AACvB;AACA,eAAe,QAAQ;AACvB;AACA,iBAAiB,UAAU;AAC3B;;AC5LA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,UAAU,OAAO,GAAG,IAAI;AACxD,UAAU,YAAY;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,IAAI;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,UAAU,OAAO,GAAG,YAAY;;AC3BhE;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,UAAU,OAAO,SAAS,aAAa;AACvE;AACA,iBAAiB,IAAI;AACrB,IAAI,OAAO;AACX,UAAU,oBAAoB;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B,UAAU,OAAO,iBAAiB,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,oBAAoB;AAC7H;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,IAAI,YAAY,QAAQ,YAAY,QAAQ,GAAG,oBAAoB;;ACtClH;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,QAAQ;AACb,UAAU,wBAAwB;AAClC;AACA;AACA;AACA,aAAa,QAAQ;AACrB;AACA;AACA;AACA;AACA,eAAe,YAAY;AAC3B;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA,iBAAiB,gBAAgB,oCAAoC,QAAQ,GAAG,iBAAiB;AACjG,iBAAiB,iBAAiB,UAAU,wBAAwB,GAAG,QAAQ;;ACvB/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,WAAW;AACrB;AACA;AACA;AACA;AACA;AACA,mBAAmB,OAAO,GAAG,OAAO;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,OAAO,GAAG,WAAW;AAC1C;;AC9BA;AACA;AACA;AACA;AACA;AACA,cAAc,SAAS,SAAS,KAAK;AACrC;AACA;AACA;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,UAAU,KAAK,MAAM,iBAAiB,WAAW;AAClE,iBAAiB,YAAY,KAAK,MAAM,SAAS,IAAI,GAAG,WAAW;AACnE,iBAAiB,eAAe,KAAK,MAAM,oBAAoB,WAAW;AAC1E,iBAAiB,eAAe,KAAK,MAAM,oBAAoB,WAAW;AAC1E,iBAAiB,UAAU,KAAK,MAAM,SAAS,OAAO,CAAC,IAAI,IAAI,WAAW;AAC1E,iBAAiB,UAAU,OAAO,IAAI,YAAY,MAAM,iBAAiB,MAAM,GAAG,WAAW;AAC7F,iBAAiB,UAAU,KAAK,MAAM,GAAG,WAAW;AACpD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,SAAS,SAAS,MAAM,UAAU,MAAM,SAAS,cAAc,iBAAiB,WAAW;AAC5G,iBAAiB,UAAU,KAAK,MAAM,SAAS,OAAO,CAAC,IAAI,IAAI,WAAW;AAC1E,iBAAiB,UAAU,KAAK,MAAM,GAAG,WAAW;;AC3BpD;AACA;AACA;AACA;AACA,UAAU,eAAe;AACzB,sBAAsB,OAAO;AAC7B;AACA;AACA;AACA,qBAAqB,WAAW;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,kBAAkB,UAAU,OAAO,GAAG,eAAe;;ACxBtE,UAAU,gBAAgB;AAC1B;AACA;AACA;AACA;AACA;AACA,UAAU,gBAAgB;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,UAAU;AACpB;AACA;AACA,iBAAiB,IAAI,WAAW,IAAI,uBAAuB,IAAI;AAC/D;AACA,UAAU,iBAAiB;AAC3B;AACA;AACA;AACA,cAAc,UAAU;AACxB;AACA,UAAU,WAAW;AACrB,wBAAwB,gBAAgB,GAAG,OAAO,CAAC,IAAI;AACvD,wBAAwB,gBAAgB,GAAG,OAAO,CAAC,IAAI;AACvD,yBAAyB,iBAAiB,GAAG,OAAO,CAAC,IAAI;AACzD;;AC1BA,UAAU,WAAW;AACrB,WAAW,IAAI;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,WAAW,SAAS,IAAI;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,EAAE;AACf,cAAc,IAAI;AAClB;AACA;AACA,UAAU,UAAU;AACpB,aAAa,OAAO;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,WAAW;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,aAAa;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,WAAW;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,WAAW,CAAC,MAAM;AACvC;AACA,cAAc,KAAK,EAAE,KAAK,CAAC,yBAAyB,CAAC,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,WAAW;;AC/GjG;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,gBAAgB;AAC1B,aAAa,OAAO;AACpB,mBAAmB,YAAY;AAC/B;AACA,UAAU,YAAY;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,qBAAqB;AAC/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrCA,iBAAiB,UAAU,eAAe,gBAAgB;;ACA1D,iBAAiB,cAAc,UAAU,OAAO,YAAY,qBAAqB;;ACAjF,iBAAiB,UAAU,IAAI,YAAY;;ACD3C,iBAAiB,QAAQ,UAAU,OAAO,GAAG,OAAO;;ACApD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,QAAQ,IAAI,WAAW,IAAI,KAAK,IAAI;;ACVpE,iBAAiB,YAAY,OAAO,IAAI,YAAY,QAAQ;AAC5D,iBAAiB,oBAAoB,OAAO,IAAI,YAAY,QAAQ;AACpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,iBAAiB,QAAQ,IAAI,6BAA6B,QAAQ,GAAG,IAAI;AAC1F;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,sBAAsB,MAAM,IAAI,6BAA6B,QAAQ,GAAG,IAAI;AAC7F;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,IAAI,YAAY,QAAQ,GAAG,IAAI;AAC9E;AACA;AACA;AACA;AACA,iBAAiB,2BAA2B,OAAO,IAAI,YAAY,QAAQ,GAAG,IAAI;AAClF;AACA;AACA;AACA;AACA;AACA,iBAAiB,qBAAqB,IAAI,IAAI,KAAK,IAAI,YAAY,QAAQ;;;;","names":[]}
1
+ {"version":3,"file":"index.d.cts","sources":["../src/types.ts","../src/analysis.ts","../src/baseline.ts","../src/calendars/nz-holidays.ts","../src/editing/commands.ts","../src/editing/errors.ts","../src/editing/factories.ts","../src/editing/use-editable-project.ts","../src/export/types.ts","../src/Gantt.tsx","../src/mspdi/types.ts","../src/mspdi/parse.ts","../src/mspdi/serialize.ts","../src/SpikeGantt.tsx","../src/schedule.ts","../src/topological-sort.ts","../src/working-time.ts"],"mappings":";;;AAAA,KAAK,MAAM;AACX,KAAK,MAAM;AACX,KAAK,UAAU;AACf,KAAK,UAAU;AACf,KAAK,YAAY;AACjB,KAAK,QAAQ;AACb,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB,UAAU,UAAU;AACpB,UAAU,cAAc;AACxB;AACA,WAAW,IAAI;AACf;AACA,UAAU,IAAI;AACd,QAAQ,MAAM;AACd;AACA;AACA,aAAa,MAAM;AACnB,UAAU,QAAQ;AAClB,kBAAkB,YAAY;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,IAAI;AACf,SAAS,IAAI;AACb;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA,kBAAkB,UAAU;AAC5B;AACA;AACA;AACA;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA,eAAe,YAAY;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,YAAY;AACtB,gBAAgB,IAAI;AACpB,iBAAiB,IAAI;AACrB,eAAe,IAAI;AACnB,gBAAgB,IAAI;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB,UAAU,IAAI;AACd,QAAQ,MAAM;AACd,YAAY,MAAM;AAClB,YAAY,MAAM;AAClB,UAAU,cAAc;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,YAAY;AACtB;AACA;AACA;AACA,KAAK,SAAS;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,QAAQ;AAClB,QAAQ,UAAU;AAClB;AACA,cAAc,YAAY;AAC1B,gBAAgB,iBAAiB;AACjC,qBAAqB,UAAU;AAC/B;AACA,UAAU,iBAAiB;AAC3B,UAAU,IAAI;AACd;AACA;AACA,gBAAgB,YAAY;AAC5B;AACA;AACA;AACA,UAAU,QAAQ;AAClB,QAAQ,UAAU;AAClB;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,UAAU;AACpB,QAAQ,YAAY;AACpB,YAAY,MAAM;AAClB,gBAAgB,UAAU;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB,UAAU,QAAQ;AAClB,WAAW,aAAa;AACxB;AACA;AACA,gBAAgB,IAAI;AACpB;AACA,WAAW,GAAG,CAAC,MAAM,EAAE,oBAAoB;AAC3C;AACA,UAAU,oBAAoB;AAC9B,WAAW,IAAI;AACf,SAAS,IAAI;AACb;AACA;AACA,UAAU,OAAO;AACjB;AACA,WAAW,IAAI;AACf;AACA,UAAU,IAAI;AACd,uBAAuB,UAAU;AACjC,WAAW,IAAI;AACf,WAAW,IAAI;AACf,eAAe,QAAQ;AACvB,eAAe,QAAQ;AACvB;AACA,eAAe,QAAQ;AACvB;AACA,iBAAiB,UAAU;AAC3B;;AC5LA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,UAAU,OAAO,GAAG,IAAI;AACxD,UAAU,YAAY;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,IAAI;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,UAAU,OAAO,GAAG,YAAY;;AC3BhE;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,UAAU,OAAO,SAAS,aAAa;AACvE;AACA,iBAAiB,IAAI;AACrB,IAAI,OAAO;AACX,UAAU,oBAAoB;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B,UAAU,OAAO,iBAAiB,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,oBAAoB;AAC7H;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,IAAI,YAAY,QAAQ,YAAY,QAAQ,GAAG,oBAAoB;;ACtClH;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,QAAQ;AACb,UAAU,wBAAwB;AAClC;AACA;AACA;AACA,aAAa,QAAQ;AACrB;AACA;AACA;AACA;AACA,eAAe,YAAY;AAC3B;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA,iBAAiB,gBAAgB,oCAAoC,QAAQ,GAAG,iBAAiB;AACjG,iBAAiB,iBAAiB,UAAU,wBAAwB,GAAG,QAAQ;;ACvB/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,WAAW;AACrB;AACA;AACA;AACA;AACA;AACA,mBAAmB,OAAO,GAAG,OAAO;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,OAAO,GAAG,WAAW;AAC1C;;AC9BA;AACA;AACA;AACA;AACA;AACA,cAAc,SAAS,SAAS,KAAK;AACrC;AACA;AACA;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,UAAU,KAAK,MAAM,iBAAiB,WAAW;AAClE,iBAAiB,YAAY,KAAK,MAAM,SAAS,IAAI,GAAG,WAAW;AACnE,iBAAiB,eAAe,KAAK,MAAM,oBAAoB,WAAW;AAC1E,iBAAiB,eAAe,KAAK,MAAM,oBAAoB,WAAW;AAC1E,iBAAiB,UAAU,KAAK,MAAM,SAAS,OAAO,CAAC,IAAI,IAAI,WAAW;AAC1E,iBAAiB,UAAU,OAAO,IAAI,YAAY,MAAM,iBAAiB,MAAM,GAAG,WAAW;AAC7F,iBAAiB,UAAU,KAAK,MAAM,GAAG,WAAW;AACpD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,SAAS,SAAS,MAAM,UAAU,MAAM,SAAS,cAAc,iBAAiB,WAAW;AAC5G,iBAAiB,UAAU,KAAK,MAAM,SAAS,OAAO,CAAC,IAAI,IAAI,WAAW;AAC1E,iBAAiB,UAAU,KAAK,MAAM,GAAG,WAAW;;AC3BpD;AACA;AACA;AACA;AACA,UAAU,eAAe;AACzB,sBAAsB,OAAO;AAC7B;AACA;AACA;AACA,qBAAqB,WAAW;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,kBAAkB,UAAU,OAAO,GAAG,eAAe;;ACxBtE,UAAU,gBAAgB;AAC1B;AACA;AACA;AACA;AACA;AACA,UAAU,gBAAgB;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,UAAU;AACpB;AACA;AACA,iBAAiB,IAAI,WAAW,IAAI,uBAAuB,IAAI;AAC/D;AACA,UAAU,iBAAiB;AAC3B;AACA;AACA;AACA,cAAc,UAAU;AACxB;AACA,UAAU,WAAW;AACrB,wBAAwB,gBAAgB,GAAG,OAAO,CAAC,IAAI;AACvD,wBAAwB,gBAAgB,GAAG,OAAO,CAAC,IAAI;AACvD,yBAAyB,iBAAiB,GAAG,OAAO,CAAC,IAAI;AACzD;;ACzBA,UAAU,WAAW;AACrB,WAAW,IAAI;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,WAAW,SAAS,IAAI;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,EAAE;AACf,cAAc,IAAI;AAClB;AACA;AACA,UAAU,UAAU;AACpB,aAAa,OAAO;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,WAAW;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,aAAa,CAAC,aAAa;AACjD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,aAAa;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,WAAW;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,WAAW,CAAC,MAAM;AACvC;AAgBA,cAAc,KAAK,EAAE,KAAK,CAAC,yBAAyB,CAAC,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,WAAW;;ACpJjG;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,gBAAgB;AAC1B,aAAa,OAAO;AACpB,mBAAmB,YAAY;AAC/B;AACA,UAAU,YAAY;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,qBAAqB;AAC/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrCA,iBAAiB,UAAU,eAAe,gBAAgB;;ACA1D,iBAAiB,cAAc,UAAU,OAAO,YAAY,qBAAqB;;ACAjF,iBAAiB,UAAU,IAAI,YAAY;;ACD3C,iBAAiB,QAAQ,UAAU,OAAO,GAAG,OAAO;;ACApD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,QAAQ,IAAI,WAAW,IAAI,KAAK,IAAI;;ACVpE,iBAAiB,YAAY,OAAO,IAAI,YAAY,QAAQ;AAC5D,iBAAiB,oBAAoB,OAAO,IAAI,YAAY,QAAQ;AACpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,iBAAiB,QAAQ,IAAI,6BAA6B,QAAQ,GAAG,IAAI;AAC1F;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,sBAAsB,MAAM,IAAI,6BAA6B,QAAQ,GAAG,IAAI;AAC7F;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,IAAI,YAAY,QAAQ,GAAG,IAAI;AAC9E;AACA;AACA;AACA;AACA,iBAAiB,2BAA2B,OAAO,IAAI,YAAY,QAAQ,GAAG,IAAI;AAClF;AACA;AACA;AACA;AACA;AACA,iBAAiB,qBAAqB,IAAI,IAAI,KAAK,IAAI,YAAY,QAAQ;;;;","names":[]}
package/dist/index.d.ts CHANGED
@@ -476,6 +476,26 @@ interface GanttProps {
476
476
  */
477
477
  markers?: GanttMarker[];
478
478
  /**
479
+ * Baselines to overlay as phantom ghost rows beneath each live task.
480
+ *
481
+ * - Single-baseline mode (length 1): the live bar carries the variance pill,
482
+ * matching the existing single-baseline behaviour.
483
+ * - Multi-baseline mode (length > 1): each phantom row carries its own
484
+ * variance pill against the live task; the live bar has no pill.
485
+ * - Indices not present on `project.baselines` are silently skipped (no
486
+ * throw) so consumers can pass a fixed shape regardless of how many
487
+ * baselines a particular project has captured.
488
+ *
489
+ * Phantom rows render in array order — consumers wanting chronological
490
+ * order should sort by `baseline.capturedAt` before passing.
491
+ */
492
+ baselineIndices?: ReadonlyArray<BaselineIndex>;
493
+ /**
494
+ * @deprecated Use `baselineIndices: [N]`. Single-index convenience prop
495
+ * kept as a no-friction alias for v0.x consumers. Removed at v1.0.
496
+ * If both `baselineIndex` and `baselineIndices` are set, `baselineIndices`
497
+ * takes precedence.
498
+ *
479
499
  * Show variance against this baseline index. If unset (or no matching
480
500
  * baseline exists on `project.baselines`), bars render without variance
481
501
  * pills. Construction-vertical use case (ADR-003): comparing the live
@@ -486,8 +506,9 @@ interface GanttProps {
486
506
  /**
487
507
  * Render the baseline as a separate "ghost" bar beneath each live task.
488
508
  * Matches the MS Project baseline-view idiom construction PMs expect
489
- * when reviewing variation claims. Default true when `baselineIndex`
490
- * is set; pass `false` to keep variance shown only as in-bar pills.
509
+ * when reviewing variation claims. Default true when either
510
+ * `baselineIndex` or `baselineIndices` is set; pass `false` to keep
511
+ * variance shown only as in-bar pills.
491
512
  */
492
513
  showBaselineBars?: boolean;
493
514
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sources":["../src/types.ts","../src/analysis.ts","../src/baseline.ts","../src/calendars/nz-holidays.ts","../src/editing/commands.ts","../src/editing/errors.ts","../src/editing/factories.ts","../src/editing/use-editable-project.ts","../src/export/types.ts","../src/Gantt.tsx","../src/mspdi/types.ts","../src/mspdi/parse.ts","../src/mspdi/serialize.ts","../src/SpikeGantt.tsx","../src/schedule.ts","../src/topological-sort.ts","../src/working-time.ts"],"mappings":";;;AAAA,KAAK,MAAM;AACX,KAAK,MAAM;AACX,KAAK,UAAU;AACf,KAAK,UAAU;AACf,KAAK,YAAY;AACjB,KAAK,QAAQ;AACb,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB,UAAU,UAAU;AACpB,UAAU,cAAc;AACxB;AACA,WAAW,IAAI;AACf;AACA,UAAU,IAAI;AACd,QAAQ,MAAM;AACd;AACA;AACA,aAAa,MAAM;AACnB,UAAU,QAAQ;AAClB,kBAAkB,YAAY;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,IAAI;AACf,SAAS,IAAI;AACb;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA,kBAAkB,UAAU;AAC5B;AACA;AACA;AACA;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA,eAAe,YAAY;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,YAAY;AACtB,gBAAgB,IAAI;AACpB,iBAAiB,IAAI;AACrB,eAAe,IAAI;AACnB,gBAAgB,IAAI;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB,UAAU,IAAI;AACd,QAAQ,MAAM;AACd,YAAY,MAAM;AAClB,YAAY,MAAM;AAClB,UAAU,cAAc;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,YAAY;AACtB;AACA;AACA;AACA,KAAK,SAAS;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,QAAQ;AAClB,QAAQ,UAAU;AAClB;AACA,cAAc,YAAY;AAC1B,gBAAgB,iBAAiB;AACjC,qBAAqB,UAAU;AAC/B;AACA,UAAU,iBAAiB;AAC3B,UAAU,IAAI;AACd;AACA;AACA,gBAAgB,YAAY;AAC5B;AACA;AACA;AACA,UAAU,QAAQ;AAClB,QAAQ,UAAU;AAClB;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,UAAU;AACpB,QAAQ,YAAY;AACpB,YAAY,MAAM;AAClB,gBAAgB,UAAU;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB,UAAU,QAAQ;AAClB,WAAW,aAAa;AACxB;AACA;AACA,gBAAgB,IAAI;AACpB;AACA,WAAW,GAAG,CAAC,MAAM,EAAE,oBAAoB;AAC3C;AACA,UAAU,oBAAoB;AAC9B,WAAW,IAAI;AACf,SAAS,IAAI;AACb;AACA;AACA,UAAU,OAAO;AACjB;AACA,WAAW,IAAI;AACf;AACA,UAAU,IAAI;AACd,uBAAuB,UAAU;AACjC,WAAW,IAAI;AACf,WAAW,IAAI;AACf,eAAe,QAAQ;AACvB,eAAe,QAAQ;AACvB;AACA,eAAe,QAAQ;AACvB;AACA,iBAAiB,UAAU;AAC3B;;AC5LA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,UAAU,OAAO,GAAG,IAAI;AACxD,UAAU,YAAY;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,IAAI;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,UAAU,OAAO,GAAG,YAAY;;AC3BhE;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,UAAU,OAAO,SAAS,aAAa;AACvE;AACA,iBAAiB,IAAI;AACrB,IAAI,OAAO;AACX,UAAU,oBAAoB;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B,UAAU,OAAO,iBAAiB,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,oBAAoB;AAC7H;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,IAAI,YAAY,QAAQ,YAAY,QAAQ,GAAG,oBAAoB;;ACtClH;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,QAAQ;AACb,UAAU,wBAAwB;AAClC;AACA;AACA;AACA,aAAa,QAAQ;AACrB;AACA;AACA;AACA;AACA,eAAe,YAAY;AAC3B;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA,iBAAiB,gBAAgB,oCAAoC,QAAQ,GAAG,iBAAiB;AACjG,iBAAiB,iBAAiB,UAAU,wBAAwB,GAAG,QAAQ;;ACvB/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,WAAW;AACrB;AACA;AACA;AACA;AACA;AACA,mBAAmB,OAAO,GAAG,OAAO;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,OAAO,GAAG,WAAW;AAC1C;;AC9BA;AACA;AACA;AACA;AACA;AACA,cAAc,SAAS,SAAS,KAAK;AACrC;AACA;AACA;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,UAAU,KAAK,MAAM,iBAAiB,WAAW;AAClE,iBAAiB,YAAY,KAAK,MAAM,SAAS,IAAI,GAAG,WAAW;AACnE,iBAAiB,eAAe,KAAK,MAAM,oBAAoB,WAAW;AAC1E,iBAAiB,eAAe,KAAK,MAAM,oBAAoB,WAAW;AAC1E,iBAAiB,UAAU,KAAK,MAAM,SAAS,OAAO,CAAC,IAAI,IAAI,WAAW;AAC1E,iBAAiB,UAAU,OAAO,IAAI,YAAY,MAAM,iBAAiB,MAAM,GAAG,WAAW;AAC7F,iBAAiB,UAAU,KAAK,MAAM,GAAG,WAAW;AACpD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,SAAS,SAAS,MAAM,UAAU,MAAM,SAAS,cAAc,iBAAiB,WAAW;AAC5G,iBAAiB,UAAU,KAAK,MAAM,SAAS,OAAO,CAAC,IAAI,IAAI,WAAW;AAC1E,iBAAiB,UAAU,KAAK,MAAM,GAAG,WAAW;;AC3BpD;AACA;AACA;AACA;AACA,UAAU,eAAe;AACzB,sBAAsB,OAAO;AAC7B;AACA;AACA;AACA,qBAAqB,WAAW;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,kBAAkB,UAAU,OAAO,GAAG,eAAe;;ACxBtE,UAAU,gBAAgB;AAC1B;AACA;AACA;AACA;AACA;AACA,UAAU,gBAAgB;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,UAAU;AACpB;AACA;AACA,iBAAiB,IAAI,WAAW,IAAI,uBAAuB,IAAI;AAC/D;AACA,UAAU,iBAAiB;AAC3B;AACA;AACA;AACA,cAAc,UAAU;AACxB;AACA,UAAU,WAAW;AACrB,wBAAwB,gBAAgB,GAAG,OAAO,CAAC,IAAI;AACvD,wBAAwB,gBAAgB,GAAG,OAAO,CAAC,IAAI;AACvD,yBAAyB,iBAAiB,GAAG,OAAO,CAAC,IAAI;AACzD;;AC1BA,UAAU,WAAW;AACrB,WAAW,IAAI;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,WAAW,SAAS,IAAI;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,EAAE;AACf,cAAc,IAAI;AAClB;AACA;AACA,UAAU,UAAU;AACpB,aAAa,OAAO;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,WAAW;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,aAAa;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,WAAW;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,WAAW,CAAC,MAAM;AACvC;AACA,cAAc,KAAK,EAAE,KAAK,CAAC,yBAAyB,CAAC,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,WAAW;;AC/GjG;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,gBAAgB;AAC1B,aAAa,OAAO;AACpB,mBAAmB,YAAY;AAC/B;AACA,UAAU,YAAY;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,qBAAqB;AAC/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrCA,iBAAiB,UAAU,eAAe,gBAAgB;;ACA1D,iBAAiB,cAAc,UAAU,OAAO,YAAY,qBAAqB;;ACAjF,iBAAiB,UAAU,IAAI,YAAY;;ACD3C,iBAAiB,QAAQ,UAAU,OAAO,GAAG,OAAO;;ACApD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,QAAQ,IAAI,WAAW,IAAI,KAAK,IAAI;;ACVpE,iBAAiB,YAAY,OAAO,IAAI,YAAY,QAAQ;AAC5D,iBAAiB,oBAAoB,OAAO,IAAI,YAAY,QAAQ;AACpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,iBAAiB,QAAQ,IAAI,6BAA6B,QAAQ,GAAG,IAAI;AAC1F;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,sBAAsB,MAAM,IAAI,6BAA6B,QAAQ,GAAG,IAAI;AAC7F;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,IAAI,YAAY,QAAQ,GAAG,IAAI;AAC9E;AACA;AACA;AACA;AACA,iBAAiB,2BAA2B,OAAO,IAAI,YAAY,QAAQ,GAAG,IAAI;AAClF;AACA;AACA;AACA;AACA;AACA,iBAAiB,qBAAqB,IAAI,IAAI,KAAK,IAAI,YAAY,QAAQ;;;;","names":[]}
1
+ {"version":3,"file":"index.d.ts","sources":["../src/types.ts","../src/analysis.ts","../src/baseline.ts","../src/calendars/nz-holidays.ts","../src/editing/commands.ts","../src/editing/errors.ts","../src/editing/factories.ts","../src/editing/use-editable-project.ts","../src/export/types.ts","../src/Gantt.tsx","../src/mspdi/types.ts","../src/mspdi/parse.ts","../src/mspdi/serialize.ts","../src/SpikeGantt.tsx","../src/schedule.ts","../src/topological-sort.ts","../src/working-time.ts"],"mappings":";;;AAAA,KAAK,MAAM;AACX,KAAK,MAAM;AACX,KAAK,UAAU;AACf,KAAK,UAAU;AACf,KAAK,YAAY;AACjB,KAAK,QAAQ;AACb,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB,UAAU,UAAU;AACpB,UAAU,cAAc;AACxB;AACA,WAAW,IAAI;AACf;AACA,UAAU,IAAI;AACd,QAAQ,MAAM;AACd;AACA;AACA,aAAa,MAAM;AACnB,UAAU,QAAQ;AAClB,kBAAkB,YAAY;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,IAAI;AACf,SAAS,IAAI;AACb;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA,kBAAkB,UAAU;AAC5B;AACA;AACA;AACA;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA,eAAe,YAAY;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,YAAY;AACtB,gBAAgB,IAAI;AACpB,iBAAiB,IAAI;AACrB,eAAe,IAAI;AACnB,gBAAgB,IAAI;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB,UAAU,IAAI;AACd,QAAQ,MAAM;AACd,YAAY,MAAM;AAClB,YAAY,MAAM;AAClB,UAAU,cAAc;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,YAAY;AACtB;AACA;AACA;AACA,KAAK,SAAS;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,QAAQ;AAClB,QAAQ,UAAU;AAClB;AACA,cAAc,YAAY;AAC1B,gBAAgB,iBAAiB;AACjC,qBAAqB,UAAU;AAC/B;AACA,UAAU,iBAAiB;AAC3B,UAAU,IAAI;AACd;AACA;AACA,gBAAgB,YAAY;AAC5B;AACA;AACA;AACA,UAAU,QAAQ;AAClB,QAAQ,UAAU;AAClB;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,UAAU;AACpB,QAAQ,YAAY;AACpB,YAAY,MAAM;AAClB,gBAAgB,UAAU;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB,UAAU,QAAQ;AAClB,WAAW,aAAa;AACxB;AACA;AACA,gBAAgB,IAAI;AACpB;AACA,WAAW,GAAG,CAAC,MAAM,EAAE,oBAAoB;AAC3C;AACA,UAAU,oBAAoB;AAC9B,WAAW,IAAI;AACf,SAAS,IAAI;AACb;AACA;AACA,UAAU,OAAO;AACjB;AACA,WAAW,IAAI;AACf;AACA,UAAU,IAAI;AACd,uBAAuB,UAAU;AACjC,WAAW,IAAI;AACf,WAAW,IAAI;AACf,eAAe,QAAQ;AACvB,eAAe,QAAQ;AACvB;AACA,eAAe,QAAQ;AACvB;AACA,iBAAiB,UAAU;AAC3B;;AC5LA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,UAAU,OAAO,GAAG,IAAI;AACxD,UAAU,YAAY;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,IAAI;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,UAAU,OAAO,GAAG,YAAY;;AC3BhE;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,UAAU,OAAO,SAAS,aAAa;AACvE;AACA,iBAAiB,IAAI;AACrB,IAAI,OAAO;AACX,UAAU,oBAAoB;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B,UAAU,OAAO,iBAAiB,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,oBAAoB;AAC7H;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,IAAI,YAAY,QAAQ,YAAY,QAAQ,GAAG,oBAAoB;;ACtClH;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,QAAQ;AACb,UAAU,wBAAwB;AAClC;AACA;AACA;AACA,aAAa,QAAQ;AACrB;AACA;AACA;AACA;AACA,eAAe,YAAY;AAC3B;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA,iBAAiB,gBAAgB,oCAAoC,QAAQ,GAAG,iBAAiB;AACjG,iBAAiB,iBAAiB,UAAU,wBAAwB,GAAG,QAAQ;;ACvB/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,WAAW;AACrB;AACA;AACA;AACA;AACA;AACA,mBAAmB,OAAO,GAAG,OAAO;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,OAAO,GAAG,WAAW;AAC1C;;AC9BA;AACA;AACA;AACA;AACA;AACA,cAAc,SAAS,SAAS,KAAK;AACrC;AACA;AACA;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,UAAU,KAAK,MAAM,iBAAiB,WAAW;AAClE,iBAAiB,YAAY,KAAK,MAAM,SAAS,IAAI,GAAG,WAAW;AACnE,iBAAiB,eAAe,KAAK,MAAM,oBAAoB,WAAW;AAC1E,iBAAiB,eAAe,KAAK,MAAM,oBAAoB,WAAW;AAC1E,iBAAiB,UAAU,KAAK,MAAM,SAAS,OAAO,CAAC,IAAI,IAAI,WAAW;AAC1E,iBAAiB,UAAU,OAAO,IAAI,YAAY,MAAM,iBAAiB,MAAM,GAAG,WAAW;AAC7F,iBAAiB,UAAU,KAAK,MAAM,GAAG,WAAW;AACpD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,SAAS,SAAS,MAAM,UAAU,MAAM,SAAS,cAAc,iBAAiB,WAAW;AAC5G,iBAAiB,UAAU,KAAK,MAAM,SAAS,OAAO,CAAC,IAAI,IAAI,WAAW;AAC1E,iBAAiB,UAAU,KAAK,MAAM,GAAG,WAAW;;AC3BpD;AACA;AACA;AACA;AACA,UAAU,eAAe;AACzB,sBAAsB,OAAO;AAC7B;AACA;AACA;AACA,qBAAqB,WAAW;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,kBAAkB,UAAU,OAAO,GAAG,eAAe;;ACxBtE,UAAU,gBAAgB;AAC1B;AACA;AACA;AACA;AACA;AACA,UAAU,gBAAgB;AAC1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,UAAU;AACpB;AACA;AACA,iBAAiB,IAAI,WAAW,IAAI,uBAAuB,IAAI;AAC/D;AACA,UAAU,iBAAiB;AAC3B;AACA;AACA;AACA,cAAc,UAAU;AACxB;AACA,UAAU,WAAW;AACrB,wBAAwB,gBAAgB,GAAG,OAAO,CAAC,IAAI;AACvD,wBAAwB,gBAAgB,GAAG,OAAO,CAAC,IAAI;AACvD,yBAAyB,iBAAiB,GAAG,OAAO,CAAC,IAAI;AACzD;;ACzBA,UAAU,WAAW;AACrB,WAAW,IAAI;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,WAAW,SAAS,IAAI;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,EAAE;AACf,cAAc,IAAI;AAClB;AACA;AACA,UAAU,UAAU;AACpB,aAAa,OAAO;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,WAAW;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,aAAa,CAAC,aAAa;AACjD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,aAAa;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,WAAW;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,WAAW,CAAC,MAAM;AACvC;AAgBA,cAAc,KAAK,EAAE,KAAK,CAAC,yBAAyB,CAAC,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,WAAW;;ACpJjG;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,gBAAgB;AAC1B,aAAa,OAAO;AACpB,mBAAmB,YAAY;AAC/B;AACA,UAAU,YAAY;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU,qBAAqB;AAC/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrCA,iBAAiB,UAAU,eAAe,gBAAgB;;ACA1D,iBAAiB,cAAc,UAAU,OAAO,YAAY,qBAAqB;;ACAjF,iBAAiB,UAAU,IAAI,YAAY;;ACD3C,iBAAiB,QAAQ,UAAU,OAAO,GAAG,OAAO;;ACApD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe,QAAQ,IAAI,WAAW,IAAI,KAAK,IAAI;;ACVpE,iBAAiB,YAAY,OAAO,IAAI,YAAY,QAAQ;AAC5D,iBAAiB,oBAAoB,OAAO,IAAI,YAAY,QAAQ;AACpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,iBAAiB,QAAQ,IAAI,6BAA6B,QAAQ,GAAG,IAAI;AAC1F;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,sBAAsB,MAAM,IAAI,6BAA6B,QAAQ,GAAG,IAAI;AAC7F;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,IAAI,YAAY,QAAQ,GAAG,IAAI;AAC9E;AACA;AACA;AACA;AACA,iBAAiB,2BAA2B,OAAO,IAAI,YAAY,QAAQ,GAAG,IAAI;AAClF;AACA;AACA;AACA;AACA;AACA,iBAAiB,qBAAqB,IAAI,IAAI,KAAK,IAAI,YAAY,QAAQ;;;;","names":[]}
package/dist/index.js CHANGED
@@ -1907,7 +1907,7 @@ function reducer(state, action) {
1907
1907
  };
1908
1908
  }
1909
1909
 
1910
- __insertCSS(".construction-gantt-non-working{background:rgba(241,245,249,.65)}.construction-gantt-marker-today{background:#ef4444;color:#fff;font-weight:600}.construction-gantt-marker-milestone{background:#2563eb;color:#fff;font-weight:500}");
1910
+ __insertCSS(".construction-gantt-non-working{background:rgba(241,245,249,.65)}.construction-gantt-marker-today{background:#ef4444;color:#fff;font-weight:600}.construction-gantt-marker-milestone{background:#2563eb;color:#fff;font-weight:500}.bode-baseline-ghost{display:flex;align-items:center;gap:6px;height:60%;margin-top:15%;padding:0 8px;border-radius:3px;font-size:9px;font-style:italic;color:#fff;opacity:.85;white-space:nowrap}.bode-baseline-0{background:#6b7280}.bode-baseline-1{background:#3b82f6}.bode-baseline-2{background:#8b5cf6}.bode-baseline-3{background:#14b8a6;color:#0f172a}.bode-baseline-4{background:#f59e0b;color:#0f172a}.bode-baseline-5{background:#ec4899}.bode-baseline-6{background:#06b6d4;color:#0f172a}.bode-baseline-7{background:#84cc16;color:#0f172a}.bode-baseline-8{background:#f97316;color:#0f172a}.bode-baseline-9{background:#a855f7}.bode-baseline-10{background:#dc2626}");
1911
1911
 
1912
1912
  // Render-only visibility filter for <Gantt> tasks.
1913
1913
  //
@@ -1938,8 +1938,12 @@ __insertCSS(".construction-gantt-non-working{background:rgba(241,245,249,.65)}.c
1938
1938
  return tasks.filter((t)=>visibleTaskIds.has(t.id));
1939
1939
  }
1940
1940
 
1941
- const Gantt = /*#__PURE__*/ forwardRef(function Gantt({ project, height = 500, cellWidth = 48, cellHeight = 42, preScheduled = false, markers, baselineIndex, showBaselineBars, columns, visibleTaskIds }, ref) {
1941
+ const Gantt = /*#__PURE__*/ forwardRef(function Gantt({ project, height = 500, cellWidth = 48, cellHeight = 42, preScheduled = false, markers, baselineIndex, baselineIndices, showBaselineBars, columns, visibleTaskIds }, ref) {
1942
1942
  const containerRef = useRef(null);
1943
+ const effectiveBaselineIndices = useMemo(()=>resolveEffectiveBaselineIndices(baselineIndices, baselineIndex), [
1944
+ baselineIndices,
1945
+ baselineIndex
1946
+ ]);
1943
1947
  const scheduled = useMemo(()=>preScheduled ? project : schedule(project), [
1944
1948
  project,
1945
1949
  preScheduled
@@ -1954,32 +1958,17 @@ const Gantt = /*#__PURE__*/ forwardRef(function Gantt({ project, height = 500, c
1954
1958
  scheduled.calendars,
1955
1959
  scheduled.defaultCalendarId
1956
1960
  ]);
1957
- const baseline = useMemo(()=>{
1958
- if (baselineIndex === undefined) return undefined;
1959
- return scheduled.baselines.find((b)=>b.index === baselineIndex);
1960
- }, [
1961
+ // Resolve effective indices → actual Baseline records, dropping any that
1962
+ // don't exist on the project. Preserves caller order so phantom rows
1963
+ // render in the array order the consumer passed.
1964
+ const resolvedBaselines = useMemo(()=>resolveBaselines(scheduled.baselines, effectiveBaselineIndices), [
1961
1965
  scheduled.baselines,
1962
- baselineIndex
1966
+ effectiveBaselineIndices
1963
1967
  ]);
1964
- const ghostBarsEnabled = baseline !== undefined && (showBaselineBars ?? true);
1965
- const svarTasks = useMemo(()=>{
1966
- if (!ghostBarsEnabled || !baseline) {
1967
- return renderableTasks.map((t)=>toSvarTask(t, baseline, calendar));
1968
- }
1969
- // Interleave each real task with its baseline-snapshot ghost row.
1970
- // Phantoms share the real task's `parent` so they stay grouped under
1971
- // the same summary in hierarchy views.
1972
- const out = [];
1973
- for (const t of renderableTasks){
1974
- out.push(toSvarTask(t, baseline, calendar));
1975
- if (t.type === 'summary') continue;
1976
- const phantom = makeBaselinePhantom(t, baseline);
1977
- if (phantom) out.push(phantom);
1978
- }
1979
- return out;
1980
- }, [
1968
+ const ghostBarsEnabled = resolvedBaselines.length > 0 && (showBaselineBars ?? true);
1969
+ const svarTasks = useMemo(()=>buildSvarTasks(renderableTasks, resolvedBaselines, calendar, ghostBarsEnabled), [
1981
1970
  renderableTasks,
1982
- baseline,
1971
+ resolvedBaselines,
1983
1972
  calendar,
1984
1973
  ghostBarsEnabled
1985
1974
  ]);
@@ -2010,7 +1999,7 @@ const Gantt = /*#__PURE__*/ forwardRef(function Gantt({ project, height = 500, c
2010
1999
  ]);
2011
2000
  useImperativeHandle(ref, ()=>({
2012
2001
  async exportPNG (options) {
2013
- const { exportPNG } = await import('./png-DKZeKnRh.js');
2002
+ const { exportPNG } = await import('./png-C2poOLQo.js');
2014
2003
  return exportPNG({
2015
2004
  scheduled,
2016
2005
  ganttProps: {
@@ -2018,6 +2007,7 @@ const Gantt = /*#__PURE__*/ forwardRef(function Gantt({ project, height = 500, c
2018
2007
  cellHeight,
2019
2008
  markers,
2020
2009
  baselineIndex,
2010
+ baselineIndices,
2021
2011
  showBaselineBars,
2022
2012
  columns,
2023
2013
  height,
@@ -2027,7 +2017,7 @@ const Gantt = /*#__PURE__*/ forwardRef(function Gantt({ project, height = 500, c
2027
2017
  });
2028
2018
  },
2029
2019
  async exportPDF (options) {
2030
- const { exportPDF } = await import('./pdf-CBaoJRTI.js');
2020
+ const { exportPDF } = await import('./pdf-BsFqo2UW.js');
2031
2021
  return exportPDF({
2032
2022
  scheduled,
2033
2023
  ganttProps: {
@@ -2035,6 +2025,7 @@ const Gantt = /*#__PURE__*/ forwardRef(function Gantt({ project, height = 500, c
2035
2025
  cellHeight,
2036
2026
  markers,
2037
2027
  baselineIndex,
2028
+ baselineIndices,
2038
2029
  showBaselineBars,
2039
2030
  columns,
2040
2031
  height,
@@ -2056,6 +2047,7 @@ const Gantt = /*#__PURE__*/ forwardRef(function Gantt({ project, height = 500, c
2056
2047
  cellHeight,
2057
2048
  markers,
2058
2049
  baselineIndex,
2050
+ baselineIndices,
2059
2051
  showBaselineBars,
2060
2052
  columns,
2061
2053
  height,
@@ -2085,23 +2077,56 @@ const Gantt = /*#__PURE__*/ forwardRef(function Gantt({ project, height = 500, c
2085
2077
  const ConstructionBar = ({ data })=>{
2086
2078
  // Phantom baseline row — render a slim outlined ghost bar.
2087
2079
  if (data.is_baseline_ghost) {
2088
- return /*#__PURE__*/ jsx("div", {
2089
- style: {
2090
- height: '60%',
2091
- marginTop: '15%',
2092
- border: '1.5px dashed #94a3b8',
2093
- background: 'transparent',
2094
- borderRadius: 3,
2095
- fontSize: 9,
2096
- color: '#64748b',
2097
- display: 'flex',
2098
- alignItems: 'center',
2099
- padding: '0 6px',
2100
- fontStyle: 'italic',
2101
- whiteSpace: 'nowrap'
2102
- },
2080
+ const baselineIdx = data.baseline_index ?? 0;
2081
+ const phantomSlipped = data.is_slipped ?? false;
2082
+ const phantomAhead = data.is_ahead ?? false;
2083
+ return /*#__PURE__*/ jsxs("div", {
2084
+ className: `bode-baseline-ghost bode-baseline-${baselineIdx}`,
2103
2085
  title: "Baseline position — where this task was when the baseline was captured",
2104
- children: "baseline"
2086
+ children: [
2087
+ /*#__PURE__*/ jsx("span", {
2088
+ style: {
2089
+ flex: 1,
2090
+ overflow: 'hidden',
2091
+ textOverflow: 'ellipsis'
2092
+ },
2093
+ children: data.text
2094
+ }),
2095
+ phantomSlipped && /*#__PURE__*/ jsxs("span", {
2096
+ style: {
2097
+ padding: '0 6px',
2098
+ background: '#fed7aa',
2099
+ color: '#7c2d12',
2100
+ borderRadius: 3,
2101
+ fontSize: 10,
2102
+ fontWeight: 700,
2103
+ lineHeight: '16px',
2104
+ whiteSpace: 'nowrap'
2105
+ },
2106
+ title: "Drifted later than the baseline",
2107
+ children: [
2108
+ "+",
2109
+ workingMinutesToShortLabel(data.start_variance ?? 0)
2110
+ ]
2111
+ }),
2112
+ phantomAhead && /*#__PURE__*/ jsxs("span", {
2113
+ style: {
2114
+ padding: '0 6px',
2115
+ background: '#bbf7d0',
2116
+ color: '#14532d',
2117
+ borderRadius: 3,
2118
+ fontSize: 10,
2119
+ fontWeight: 700,
2120
+ lineHeight: '16px',
2121
+ whiteSpace: 'nowrap'
2122
+ },
2123
+ title: "Ahead of the baseline",
2124
+ children: [
2125
+ "−",
2126
+ workingMinutesToShortLabel(data.start_variance ?? 0)
2127
+ ]
2128
+ })
2129
+ ]
2105
2130
  });
2106
2131
  }
2107
2132
  const isCritical = data.is_critical ?? false;
@@ -2252,19 +2277,25 @@ function toSvarTask(t, baseline, calendar) {
2252
2277
  if (t.type === 'summary') base.open = t.open ?? true;
2253
2278
  return base;
2254
2279
  }
2255
- function makeBaselinePhantom(t, baseline) {
2280
+ function makeBaselinePhantom(t, baseline, calendar) {
2256
2281
  const snap = baseline.tasks.get(t.id);
2257
2282
  if (!snap) return null;
2283
+ const variance = calendar ? getTaskBaselineVariance(t, baseline, calendar) : undefined;
2284
+ const startVariance = variance?.startVariance ?? 0;
2258
2285
  return {
2259
- id: `${t.id}-baseline-${baseline.index}`,
2260
- text: '(baseline)',
2286
+ id: `${t.id}__baseline_${baseline.index}`,
2287
+ text: formatBaselineLabel(baseline),
2261
2288
  start: snap.start,
2262
2289
  end: snap.end,
2263
2290
  duration: snap.duration,
2264
2291
  progress: 0,
2265
2292
  type: 'task',
2266
2293
  parent: t.parent,
2267
- is_baseline_ghost: true
2294
+ is_baseline_ghost: true,
2295
+ baseline_index: baseline.index,
2296
+ start_variance: startVariance,
2297
+ is_slipped: startVariance >= 30,
2298
+ is_ahead: startVariance <= -30
2268
2299
  };
2269
2300
  }
2270
2301
  function toSvarLink(l) {
@@ -2288,6 +2319,26 @@ function dependencyTypeToSvar(t) {
2288
2319
  return 's2e';
2289
2320
  }
2290
2321
  }
2322
+ /**
2323
+ * Format a baseline's metadata for display in the phantom row's label.
2324
+ * Returns "${name ?? `Baseline ${index}`} — captured ${formatShortDate(capturedAt)}".
2325
+ *
2326
+ * Exported for testing. Not part of the public surface.
2327
+ */ function formatBaselineLabel(baseline) {
2328
+ const name = baseline.name ?? `Baseline ${baseline.index}`;
2329
+ return `${name} — captured ${formatShortDate(baseline.capturedAt)}`;
2330
+ }
2331
+ /**
2332
+ * Format a Date as YYYY-MM-DD using local-time components. Deterministic
2333
+ * across locales (no Intl.DateTimeFormat — those vary by host locale).
2334
+ *
2335
+ * Exported for testing. Not part of the public surface.
2336
+ */ function formatShortDate(d) {
2337
+ const year = d.getFullYear();
2338
+ const month = String(d.getMonth() + 1).padStart(2, '0');
2339
+ const day = String(d.getDate()).padStart(2, '0');
2340
+ return `${year}-${month}-${day}`;
2341
+ }
2291
2342
  function getProjectEnd(p) {
2292
2343
  if (p.end) return p.end;
2293
2344
  let latestMs = Number.NEGATIVE_INFINITY;
@@ -2298,6 +2349,72 @@ function getProjectEnd(p) {
2298
2349
  const cushion = 24 * 60 * 60 * 1000; // 1 day
2299
2350
  return Number.isFinite(latestMs) ? new Date(latestMs + cushion) : new Date(p.start);
2300
2351
  }
2352
+ /**
2353
+ * Resolve the effective baseline indices from the two GanttProps inputs.
2354
+ *
2355
+ * - `baselineIndices` takes precedence when set (including when empty —
2356
+ * passing `[]` is an explicit opt-out signal).
2357
+ * - When `baselineIndices` is undefined, fall back to wrapping `baselineIndex`
2358
+ * in a single-element array.
2359
+ * - When both are undefined, return an empty array.
2360
+ *
2361
+ * Exported for testing. Not part of the public surface; consumers don't
2362
+ * call this directly.
2363
+ */ function resolveEffectiveBaselineIndices(baselineIndices, baselineIndex) {
2364
+ return baselineIndices ?? (baselineIndex !== undefined ? [
2365
+ baselineIndex
2366
+ ] : []);
2367
+ }
2368
+ /**
2369
+ * Map effective baseline indices to actual Baseline records on a project,
2370
+ * preserving caller order. Indices not present on the project are silently
2371
+ * dropped (per spec — consumers can pass a fixed shape across projects
2372
+ * with varying baseline counts).
2373
+ *
2374
+ * Exported for testing. Not part of the public surface.
2375
+ */ function resolveBaselines(allBaselines, effectiveIndices) {
2376
+ if (effectiveIndices.length === 0) return [];
2377
+ const byIndex = new Map(allBaselines.map((b)=>[
2378
+ b.index,
2379
+ b
2380
+ ]));
2381
+ const out = [];
2382
+ for (const idx of effectiveIndices){
2383
+ const b = byIndex.get(idx);
2384
+ if (b) out.push(b);
2385
+ }
2386
+ return out;
2387
+ }
2388
+ /**
2389
+ * Convert the rendered tasks + resolved baselines into the SVAR ITask[]
2390
+ * shape, including phantom ghost rows for each (task × baseline) pair
2391
+ * when ghost bars are enabled.
2392
+ *
2393
+ * In single-baseline mode (resolvedBaselines.length === 1), the live row
2394
+ * carries the variance pill (`start_variance` / `is_slipped` / `is_ahead`
2395
+ * are populated relative to that one baseline). In multi-baseline mode
2396
+ * (length > 1), the live row gets no variance fields (would require a
2397
+ * "primary" baseline concept that the spec rejects); each phantom row
2398
+ * gets its own.
2399
+ *
2400
+ * Exported for testing. Not part of the public surface.
2401
+ */ function buildSvarTasks(renderableTasks, resolvedBaselines, calendar, ghostBarsEnabled) {
2402
+ if (!ghostBarsEnabled || resolvedBaselines.length === 0) {
2403
+ const primary = resolvedBaselines[0];
2404
+ return renderableTasks.map((t)=>toSvarTask(t, primary, calendar));
2405
+ }
2406
+ const out = [];
2407
+ for (const t of renderableTasks){
2408
+ const liveBarBaseline = resolvedBaselines.length === 1 ? resolvedBaselines[0] : undefined;
2409
+ out.push(toSvarTask(t, liveBarBaseline, calendar));
2410
+ if (t.type === 'summary') continue;
2411
+ for (const b of resolvedBaselines){
2412
+ const phantom = makeBaselinePhantom(t, b, calendar);
2413
+ if (phantom) out.push(phantom);
2414
+ }
2415
+ }
2416
+ return out;
2417
+ }
2301
2418
  function resolveMarkers(userMarkers, projectStart, projectEnd) {
2302
2419
  if (userMarkers) return userMarkers.map(toSvarMarker);
2303
2420
  // Default: today line, only if today falls within the project window.
@@ -8,7 +8,7 @@ function __insertCSS(code) {
8
8
  }
9
9
 
10
10
  import { jsPDF } from 'jspdf';
11
- import { exportPNG } from './png-DKZeKnRh.js';
11
+ import { exportPNG } from './png-C2poOLQo.js';
12
12
 
13
13
  // Pure-function image-fit math. Used by the PDF exporter to scale the
14
14
  // captured PNG onto a chosen page format with a uniform margin while
@@ -8,7 +8,7 @@ function __insertCSS(code) {
8
8
  }
9
9
 
10
10
  var jspdf = require('jspdf');
11
- var png = require('./png-C8t74695.cjs');
11
+ var png = require('./png-BloW1DDl.cjs');
12
12
 
13
13
  // Pure-function image-fit math. Used by the PDF exporter to scale the
14
14
  // captured PNG onto a chosen page format with a uniform margin while
@@ -29,6 +29,7 @@ async function renderOffscreen(args) {
29
29
  cellHeight: ganttProps.cellHeight,
30
30
  markers: ganttProps.markers,
31
31
  baselineIndex: ganttProps.baselineIndex,
32
+ baselineIndices: ganttProps.baselineIndices,
32
33
  showBaselineBars: ganttProps.showBaselineBars,
33
34
  columns: ganttProps.columns,
34
35
  visibleTaskIds: ganttProps.visibleTaskIds,
@@ -29,6 +29,7 @@ async function renderOffscreen(args) {
29
29
  cellHeight: ganttProps.cellHeight,
30
30
  markers: ganttProps.markers,
31
31
  baselineIndex: ganttProps.baselineIndex,
32
+ baselineIndices: ganttProps.baselineIndices,
32
33
  showBaselineBars: ganttProps.showBaselineBars,
33
34
  columns: ganttProps.columns,
34
35
  visibleTaskIds: ganttProps.visibleTaskIds,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "construction-gantt",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "MIT React Gantt for construction project management — PRO-equivalent scheduling engine + construction-vertical extensions on free SVAR React Gantt",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -83,4 +83,4 @@
83
83
  "typecheck": "tsc --noEmit",
84
84
  "test": "vitest run --project=unit"
85
85
  }
86
- }
86
+ }
package/src/Gantt.css CHANGED
@@ -21,3 +21,42 @@
21
21
  color: #ffffff;
22
22
  font-weight: 500;
23
23
  }
24
+
25
+ /* Phantom baseline-row styling. Each row gets `.bode-baseline-ghost`
26
+ * plus a `.bode-baseline-${N}` class identifying WHICH baseline.
27
+ * Consumers override colours by re-declaring `.bode-baseline-N` with
28
+ * higher specificity or `!important`. */
29
+ .bode-baseline-ghost {
30
+ display: flex;
31
+ align-items: center;
32
+ gap: 6px;
33
+ height: 60%;
34
+ margin-top: 15%;
35
+ padding: 0 8px;
36
+ border-radius: 3px;
37
+ font-size: 9px;
38
+ font-style: italic;
39
+ color: #ffffff;
40
+ opacity: 0.85;
41
+ white-space: nowrap;
42
+ }
43
+
44
+ /* Per-baseline palette. Picked for distinguishability + colourblind-
45
+ * friendly ordering: greys → cool blues → warm reds for higher indices.
46
+ * Consumers override by re-declaring with higher specificity or `!important`.
47
+ *
48
+ * Lighter backgrounds (amber, lime, cyan, teal, orange) override the default
49
+ * white text to slate `#0f172a` so the small italic label remains legible
50
+ * (WCAG AA 4.5:1 for small text). Darker backgrounds keep the ghost rule's
51
+ * white text. */
52
+ .bode-baseline-0 { background: #6b7280; } /* gray */
53
+ .bode-baseline-1 { background: #3b82f6; } /* blue */
54
+ .bode-baseline-2 { background: #8b5cf6; } /* violet */
55
+ .bode-baseline-3 { background: #14b8a6; color: #0f172a; } /* teal */
56
+ .bode-baseline-4 { background: #f59e0b; color: #0f172a; } /* amber */
57
+ .bode-baseline-5 { background: #ec4899; } /* pink */
58
+ .bode-baseline-6 { background: #06b6d4; color: #0f172a; } /* cyan */
59
+ .bode-baseline-7 { background: #84cc16; color: #0f172a; } /* lime */
60
+ .bode-baseline-8 { background: #f97316; color: #0f172a; } /* orange */
61
+ .bode-baseline-9 { background: #a855f7; } /* purple */
62
+ .bode-baseline-10 { background: #dc2626; } /* red */
package/src/Gantt.tsx CHANGED
@@ -98,6 +98,26 @@ export interface GanttProps {
98
98
  */
99
99
  markers?: GanttMarker[];
100
100
  /**
101
+ * Baselines to overlay as phantom ghost rows beneath each live task.
102
+ *
103
+ * - Single-baseline mode (length 1): the live bar carries the variance pill,
104
+ * matching the existing single-baseline behaviour.
105
+ * - Multi-baseline mode (length > 1): each phantom row carries its own
106
+ * variance pill against the live task; the live bar has no pill.
107
+ * - Indices not present on `project.baselines` are silently skipped (no
108
+ * throw) so consumers can pass a fixed shape regardless of how many
109
+ * baselines a particular project has captured.
110
+ *
111
+ * Phantom rows render in array order — consumers wanting chronological
112
+ * order should sort by `baseline.capturedAt` before passing.
113
+ */
114
+ baselineIndices?: ReadonlyArray<BaselineIndex>;
115
+ /**
116
+ * @deprecated Use `baselineIndices: [N]`. Single-index convenience prop
117
+ * kept as a no-friction alias for v0.x consumers. Removed at v1.0.
118
+ * If both `baselineIndex` and `baselineIndices` are set, `baselineIndices`
119
+ * takes precedence.
120
+ *
101
121
  * Show variance against this baseline index. If unset (or no matching
102
122
  * baseline exists on `project.baselines`), bars render without variance
103
123
  * pills. Construction-vertical use case (ADR-003): comparing the live
@@ -108,8 +128,9 @@ export interface GanttProps {
108
128
  /**
109
129
  * Render the baseline as a separate "ghost" bar beneath each live task.
110
130
  * Matches the MS Project baseline-view idiom construction PMs expect
111
- * when reviewing variation claims. Default true when `baselineIndex`
112
- * is set; pass `false` to keep variance shown only as in-bar pills.
131
+ * when reviewing variation claims. Default true when either
132
+ * `baselineIndex` or `baselineIndices` is set; pass `false` to keep
133
+ * variance shown only as in-bar pills.
113
134
  */
114
135
  showBaselineBars?: boolean;
115
136
  /**
@@ -155,6 +176,8 @@ interface SvarTaskWithComputed extends ITask {
155
176
  is_ahead?: boolean;
156
177
  /** True for phantom rows representing a baseline snapshot's position. */
157
178
  is_baseline_ghost?: boolean;
179
+ /** When set, identifies which baseline (0..10) this phantom row mirrors. */
180
+ baseline_index?: BaselineIndex;
158
181
  }
159
182
 
160
183
  interface SvarMarker {
@@ -172,6 +195,7 @@ export const Gantt = forwardRef<GanttHandle, GanttProps>(function Gantt(
172
195
  preScheduled = false,
173
196
  markers,
174
197
  baselineIndex,
198
+ baselineIndices,
175
199
  showBaselineBars,
176
200
  columns,
177
201
  visibleTaskIds,
@@ -180,6 +204,11 @@ export const Gantt = forwardRef<GanttHandle, GanttProps>(function Gantt(
180
204
  ) {
181
205
  const containerRef = useRef<HTMLDivElement>(null);
182
206
 
207
+ const effectiveBaselineIndices = useMemo<ReadonlyArray<BaselineIndex>>(
208
+ () => resolveEffectiveBaselineIndices(baselineIndices, baselineIndex),
209
+ [baselineIndices, baselineIndex],
210
+ );
211
+
183
212
  const scheduled = useMemo(
184
213
  () => (preScheduled ? project : schedule(project)),
185
214
  [project, preScheduled],
@@ -197,29 +226,20 @@ export const Gantt = forwardRef<GanttHandle, GanttProps>(function Gantt(
197
226
  [scheduled.calendars, scheduled.defaultCalendarId],
198
227
  );
199
228
 
200
- const baseline = useMemo<Baseline | undefined>(() => {
201
- if (baselineIndex === undefined) return undefined;
202
- return scheduled.baselines.find((b) => b.index === baselineIndex);
203
- }, [scheduled.baselines, baselineIndex]);
229
+ // Resolve effective indices → actual Baseline records, dropping any that
230
+ // don't exist on the project. Preserves caller order so phantom rows
231
+ // render in the array order the consumer passed.
232
+ const resolvedBaselines = useMemo<Baseline[]>(
233
+ () => resolveBaselines(scheduled.baselines, effectiveBaselineIndices),
234
+ [scheduled.baselines, effectiveBaselineIndices],
235
+ );
204
236
 
205
- const ghostBarsEnabled = baseline !== undefined && (showBaselineBars ?? true);
237
+ const ghostBarsEnabled = resolvedBaselines.length > 0 && (showBaselineBars ?? true);
206
238
 
207
- const svarTasks: ITask[] = useMemo(() => {
208
- if (!ghostBarsEnabled || !baseline) {
209
- return renderableTasks.map((t) => toSvarTask(t, baseline, calendar));
210
- }
211
- // Interleave each real task with its baseline-snapshot ghost row.
212
- // Phantoms share the real task's `parent` so they stay grouped under
213
- // the same summary in hierarchy views.
214
- const out: SvarTaskWithComputed[] = [];
215
- for (const t of renderableTasks) {
216
- out.push(toSvarTask(t, baseline, calendar));
217
- if (t.type === 'summary') continue;
218
- const phantom = makeBaselinePhantom(t, baseline);
219
- if (phantom) out.push(phantom);
220
- }
221
- return out;
222
- }, [renderableTasks, baseline, calendar, ghostBarsEnabled]);
239
+ const svarTasks: ITask[] = useMemo(
240
+ () => buildSvarTasks(renderableTasks, resolvedBaselines, calendar, ghostBarsEnabled),
241
+ [renderableTasks, resolvedBaselines, calendar, ghostBarsEnabled],
242
+ );
223
243
  const svarLinks: ILink[] = useMemo(() => scheduled.links.map(toSvarLink), [scheduled.links]);
224
244
 
225
245
  const projectEnd = useMemo(() => getProjectEnd(scheduled), [scheduled]);
@@ -255,6 +275,7 @@ export const Gantt = forwardRef<GanttHandle, GanttProps>(function Gantt(
255
275
  cellHeight,
256
276
  markers,
257
277
  baselineIndex,
278
+ baselineIndices,
258
279
  showBaselineBars,
259
280
  columns,
260
281
  height,
@@ -272,6 +293,7 @@ export const Gantt = forwardRef<GanttHandle, GanttProps>(function Gantt(
272
293
  cellHeight,
273
294
  markers,
274
295
  baselineIndex,
296
+ baselineIndices,
275
297
  showBaselineBars,
276
298
  columns,
277
299
  height,
@@ -291,6 +313,7 @@ export const Gantt = forwardRef<GanttHandle, GanttProps>(function Gantt(
291
313
  cellHeight,
292
314
  markers,
293
315
  baselineIndex,
316
+ baselineIndices,
294
317
  showBaselineBars,
295
318
  columns,
296
319
  height,
@@ -316,28 +339,52 @@ export const Gantt = forwardRef<GanttHandle, GanttProps>(function Gantt(
316
339
  );
317
340
  });
318
341
 
319
- const ConstructionBar: FC<{ data: SvarTaskWithComputed }> = ({ data }) => {
342
+ export const ConstructionBar: FC<{ data: SvarTaskWithComputed }> = ({ data }) => {
320
343
  // Phantom baseline row — render a slim outlined ghost bar.
321
344
  if (data.is_baseline_ghost) {
345
+ const baselineIdx = data.baseline_index ?? 0;
346
+ const phantomSlipped = data.is_slipped ?? false;
347
+ const phantomAhead = data.is_ahead ?? false;
322
348
  return (
323
349
  <div
324
- style={{
325
- height: '60%',
326
- marginTop: '15%',
327
- border: '1.5px dashed #94a3b8',
328
- background: 'transparent',
329
- borderRadius: 3,
330
- fontSize: 9,
331
- color: '#64748b',
332
- display: 'flex',
333
- alignItems: 'center',
334
- padding: '0 6px',
335
- fontStyle: 'italic',
336
- whiteSpace: 'nowrap',
337
- }}
350
+ className={`bode-baseline-ghost bode-baseline-${baselineIdx}`}
338
351
  title="Baseline position — where this task was when the baseline was captured"
339
352
  >
340
- baseline
353
+ <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis' }}>{data.text}</span>
354
+ {phantomSlipped && (
355
+ <span
356
+ style={{
357
+ padding: '0 6px',
358
+ background: '#fed7aa',
359
+ color: '#7c2d12',
360
+ borderRadius: 3,
361
+ fontSize: 10,
362
+ fontWeight: 700,
363
+ lineHeight: '16px',
364
+ whiteSpace: 'nowrap',
365
+ }}
366
+ title="Drifted later than the baseline"
367
+ >
368
+ +{workingMinutesToShortLabel(data.start_variance ?? 0)}
369
+ </span>
370
+ )}
371
+ {phantomAhead && (
372
+ <span
373
+ style={{
374
+ padding: '0 6px',
375
+ background: '#bbf7d0',
376
+ color: '#14532d',
377
+ borderRadius: 3,
378
+ fontSize: 10,
379
+ fontWeight: 700,
380
+ lineHeight: '16px',
381
+ whiteSpace: 'nowrap',
382
+ }}
383
+ title="Ahead of the baseline"
384
+ >
385
+ −{workingMinutesToShortLabel(data.start_variance ?? 0)}
386
+ </span>
387
+ )}
341
388
  </div>
342
389
  );
343
390
  }
@@ -496,12 +543,18 @@ function toSvarTask(
496
543
  return base;
497
544
  }
498
545
 
499
- function makeBaselinePhantom(t: Task, baseline: Baseline): SvarTaskWithComputed | null {
546
+ function makeBaselinePhantom(
547
+ t: Task,
548
+ baseline: Baseline,
549
+ calendar: Calendar | undefined,
550
+ ): SvarTaskWithComputed | null {
500
551
  const snap = baseline.tasks.get(t.id);
501
552
  if (!snap) return null;
553
+ const variance = calendar ? getTaskBaselineVariance(t, baseline, calendar) : undefined;
554
+ const startVariance = variance?.startVariance ?? 0;
502
555
  return {
503
- id: `${t.id}-baseline-${baseline.index}`,
504
- text: '(baseline)',
556
+ id: `${t.id}__baseline_${baseline.index}`,
557
+ text: formatBaselineLabel(baseline),
505
558
  start: snap.start,
506
559
  end: snap.end,
507
560
  duration: snap.duration,
@@ -509,6 +562,10 @@ function makeBaselinePhantom(t: Task, baseline: Baseline): SvarTaskWithComputed
509
562
  type: 'task',
510
563
  parent: t.parent,
511
564
  is_baseline_ghost: true,
565
+ baseline_index: baseline.index,
566
+ start_variance: startVariance,
567
+ is_slipped: startVariance >= 30,
568
+ is_ahead: startVariance <= -30,
512
569
  };
513
570
  }
514
571
 
@@ -535,6 +592,30 @@ function dependencyTypeToSvar(t: DependencyType): ILink['type'] {
535
592
  }
536
593
  }
537
594
 
595
+ /**
596
+ * Format a baseline's metadata for display in the phantom row's label.
597
+ * Returns "${name ?? `Baseline ${index}`} — captured ${formatShortDate(capturedAt)}".
598
+ *
599
+ * Exported for testing. Not part of the public surface.
600
+ */
601
+ export function formatBaselineLabel(baseline: Baseline): string {
602
+ const name = baseline.name ?? `Baseline ${baseline.index}`;
603
+ return `${name} — captured ${formatShortDate(baseline.capturedAt)}`;
604
+ }
605
+
606
+ /**
607
+ * Format a Date as YYYY-MM-DD using local-time components. Deterministic
608
+ * across locales (no Intl.DateTimeFormat — those vary by host locale).
609
+ *
610
+ * Exported for testing. Not part of the public surface.
611
+ */
612
+ export function formatShortDate(d: Date): string {
613
+ const year = d.getFullYear();
614
+ const month = String(d.getMonth() + 1).padStart(2, '0');
615
+ const day = String(d.getDate()).padStart(2, '0');
616
+ return `${year}-${month}-${day}`;
617
+ }
618
+
538
619
  function getProjectEnd(p: Project): Date {
539
620
  if (p.end) return p.end;
540
621
  let latestMs = Number.NEGATIVE_INFINITY;
@@ -546,6 +627,84 @@ function getProjectEnd(p: Project): Date {
546
627
  return Number.isFinite(latestMs) ? new Date(latestMs + cushion) : new Date(p.start);
547
628
  }
548
629
 
630
+ /**
631
+ * Resolve the effective baseline indices from the two GanttProps inputs.
632
+ *
633
+ * - `baselineIndices` takes precedence when set (including when empty —
634
+ * passing `[]` is an explicit opt-out signal).
635
+ * - When `baselineIndices` is undefined, fall back to wrapping `baselineIndex`
636
+ * in a single-element array.
637
+ * - When both are undefined, return an empty array.
638
+ *
639
+ * Exported for testing. Not part of the public surface; consumers don't
640
+ * call this directly.
641
+ */
642
+ export function resolveEffectiveBaselineIndices(
643
+ baselineIndices: ReadonlyArray<BaselineIndex> | undefined,
644
+ baselineIndex: BaselineIndex | undefined,
645
+ ): ReadonlyArray<BaselineIndex> {
646
+ return baselineIndices ?? (baselineIndex !== undefined ? [baselineIndex] : []);
647
+ }
648
+
649
+ /**
650
+ * Map effective baseline indices to actual Baseline records on a project,
651
+ * preserving caller order. Indices not present on the project are silently
652
+ * dropped (per spec — consumers can pass a fixed shape across projects
653
+ * with varying baseline counts).
654
+ *
655
+ * Exported for testing. Not part of the public surface.
656
+ */
657
+ export function resolveBaselines(
658
+ allBaselines: Baseline[],
659
+ effectiveIndices: ReadonlyArray<BaselineIndex>,
660
+ ): Baseline[] {
661
+ if (effectiveIndices.length === 0) return [];
662
+ const byIndex = new Map(allBaselines.map((b) => [b.index, b]));
663
+ const out: Baseline[] = [];
664
+ for (const idx of effectiveIndices) {
665
+ const b = byIndex.get(idx);
666
+ if (b) out.push(b);
667
+ }
668
+ return out;
669
+ }
670
+
671
+ /**
672
+ * Convert the rendered tasks + resolved baselines into the SVAR ITask[]
673
+ * shape, including phantom ghost rows for each (task × baseline) pair
674
+ * when ghost bars are enabled.
675
+ *
676
+ * In single-baseline mode (resolvedBaselines.length === 1), the live row
677
+ * carries the variance pill (`start_variance` / `is_slipped` / `is_ahead`
678
+ * are populated relative to that one baseline). In multi-baseline mode
679
+ * (length > 1), the live row gets no variance fields (would require a
680
+ * "primary" baseline concept that the spec rejects); each phantom row
681
+ * gets its own.
682
+ *
683
+ * Exported for testing. Not part of the public surface.
684
+ */
685
+ export function buildSvarTasks(
686
+ renderableTasks: Task[],
687
+ resolvedBaselines: Baseline[],
688
+ calendar: Calendar | undefined,
689
+ ghostBarsEnabled: boolean,
690
+ ): SvarTaskWithComputed[] {
691
+ if (!ghostBarsEnabled || resolvedBaselines.length === 0) {
692
+ const primary = resolvedBaselines[0];
693
+ return renderableTasks.map((t) => toSvarTask(t, primary, calendar));
694
+ }
695
+ const out: SvarTaskWithComputed[] = [];
696
+ for (const t of renderableTasks) {
697
+ const liveBarBaseline = resolvedBaselines.length === 1 ? resolvedBaselines[0] : undefined;
698
+ out.push(toSvarTask(t, liveBarBaseline, calendar));
699
+ if (t.type === 'summary') continue;
700
+ for (const b of resolvedBaselines) {
701
+ const phantom = makeBaselinePhantom(t, b, calendar);
702
+ if (phantom) out.push(phantom);
703
+ }
704
+ }
705
+ return out;
706
+ }
707
+
549
708
  function resolveMarkers(
550
709
  userMarkers: GanttMarker[] | undefined,
551
710
  projectStart: Date,
@@ -14,6 +14,7 @@ export type OffscreenGanttProps = Pick<
14
14
  | 'cellHeight'
15
15
  | 'markers'
16
16
  | 'baselineIndex'
17
+ | 'baselineIndices'
17
18
  | 'showBaselineBars'
18
19
  | 'columns'
19
20
  | 'height'
@@ -50,6 +51,7 @@ export async function renderOffscreen(args: {
50
51
  cellHeight={ganttProps.cellHeight}
51
52
  markers={ganttProps.markers}
52
53
  baselineIndex={ganttProps.baselineIndex}
54
+ baselineIndices={ganttProps.baselineIndices}
53
55
  showBaselineBars={ganttProps.showBaselineBars}
54
56
  columns={ganttProps.columns}
55
57
  visibleTaskIds={ganttProps.visibleTaskIds}
package/src/export/pdf.ts CHANGED
@@ -13,6 +13,7 @@ export async function exportPDF(args: {
13
13
  | 'cellHeight'
14
14
  | 'markers'
15
15
  | 'baselineIndex'
16
+ | 'baselineIndices'
16
17
  | 'showBaselineBars'
17
18
  | 'columns'
18
19
  | 'height'
package/src/export/png.ts CHANGED
@@ -12,6 +12,7 @@ export async function exportPNG(args: {
12
12
  | 'cellHeight'
13
13
  | 'markers'
14
14
  | 'baselineIndex'
15
+ | 'baselineIndices'
15
16
  | 'showBaselineBars'
16
17
  | 'columns'
17
18
  | 'height'