@nowline/layout 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/LICENSE +190 -0
- package/README.md +103 -0
- package/dist/band-scale.d.ts +56 -0
- package/dist/band-scale.d.ts.map +1 -0
- package/dist/band-scale.js +86 -0
- package/dist/band-scale.js.map +1 -0
- package/dist/calendar.d.ts +79 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +210 -0
- package/dist/calendar.js.map +1 -0
- package/dist/capacity.d.ts +72 -0
- package/dist/capacity.d.ts.map +1 -0
- package/dist/capacity.js +163 -0
- package/dist/capacity.js.map +1 -0
- package/dist/dsl-utils.d.ts +5 -0
- package/dist/dsl-utils.d.ts.map +1 -0
- package/dist/dsl-utils.js +28 -0
- package/dist/dsl-utils.js.map +1 -0
- package/dist/edge-routing.d.ts +89 -0
- package/dist/edge-routing.d.ts.map +1 -0
- package/dist/edge-routing.js +435 -0
- package/dist/edge-routing.js.map +1 -0
- package/dist/frame-tab-geometry.d.ts +78 -0
- package/dist/frame-tab-geometry.d.ts.map +1 -0
- package/dist/frame-tab-geometry.js +115 -0
- package/dist/frame-tab-geometry.js.map +1 -0
- package/dist/header-card-geometry.d.ts +29 -0
- package/dist/header-card-geometry.d.ts.map +1 -0
- package/dist/header-card-geometry.js +41 -0
- package/dist/header-card-geometry.js.map +1 -0
- package/dist/i18n.d.ts +48 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +114 -0
- package/dist/i18n.js.map +1 -0
- package/dist/include-chrome-geometry.d.ts +86 -0
- package/dist/include-chrome-geometry.d.ts.map +1 -0
- package/dist/include-chrome-geometry.js +104 -0
- package/dist/include-chrome-geometry.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/item-bar-geometry.d.ts +127 -0
- package/dist/item-bar-geometry.d.ts.map +1 -0
- package/dist/item-bar-geometry.js +173 -0
- package/dist/item-bar-geometry.js.map +1 -0
- package/dist/lane-utilization.d.ts +90 -0
- package/dist/lane-utilization.d.ts.map +1 -0
- package/dist/lane-utilization.js +206 -0
- package/dist/lane-utilization.js.map +1 -0
- package/dist/layout-context.d.ts +143 -0
- package/dist/layout-context.d.ts.map +1 -0
- package/dist/layout-context.js +8 -0
- package/dist/layout-context.js.map +1 -0
- package/dist/layout.d.ts +18 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +1213 -0
- package/dist/layout.js.map +1 -0
- package/dist/nodes/anchor-node.d.ts +16 -0
- package/dist/nodes/anchor-node.d.ts.map +1 -0
- package/dist/nodes/anchor-node.js +68 -0
- package/dist/nodes/anchor-node.js.map +1 -0
- package/dist/nodes/footnote-node.d.ts +10 -0
- package/dist/nodes/footnote-node.d.ts.map +1 -0
- package/dist/nodes/footnote-node.js +41 -0
- package/dist/nodes/footnote-node.js.map +1 -0
- package/dist/nodes/group-node.d.ts +29 -0
- package/dist/nodes/group-node.d.ts.map +1 -0
- package/dist/nodes/group-node.js +187 -0
- package/dist/nodes/group-node.js.map +1 -0
- package/dist/nodes/include-node.d.ts +16 -0
- package/dist/nodes/include-node.d.ts.map +1 -0
- package/dist/nodes/include-node.js +117 -0
- package/dist/nodes/include-node.js.map +1 -0
- package/dist/nodes/item-node.d.ts +51 -0
- package/dist/nodes/item-node.d.ts.map +1 -0
- package/dist/nodes/item-node.js +108 -0
- package/dist/nodes/item-node.js.map +1 -0
- package/dist/nodes/marker-geometry.d.ts +22 -0
- package/dist/nodes/marker-geometry.d.ts.map +1 -0
- package/dist/nodes/marker-geometry.js +38 -0
- package/dist/nodes/marker-geometry.js.map +1 -0
- package/dist/nodes/milestone-node.d.ts +48 -0
- package/dist/nodes/milestone-node.d.ts.map +1 -0
- package/dist/nodes/milestone-node.js +210 -0
- package/dist/nodes/milestone-node.js.map +1 -0
- package/dist/nodes/parallel-node.d.ts +21 -0
- package/dist/nodes/parallel-node.d.ts.map +1 -0
- package/dist/nodes/parallel-node.js +80 -0
- package/dist/nodes/parallel-node.js.map +1 -0
- package/dist/nodes/roadmap-node.d.ts +76 -0
- package/dist/nodes/roadmap-node.d.ts.map +1 -0
- package/dist/nodes/roadmap-node.js +788 -0
- package/dist/nodes/roadmap-node.js.map +1 -0
- package/dist/nodes/swimlane-node.d.ts +38 -0
- package/dist/nodes/swimlane-node.d.ts.map +1 -0
- package/dist/nodes/swimlane-node.js +308 -0
- package/dist/nodes/swimlane-node.js.map +1 -0
- package/dist/renderable.d.ts +44 -0
- package/dist/renderable.d.ts.map +1 -0
- package/dist/renderable.js +21 -0
- package/dist/renderable.js.map +1 -0
- package/dist/row-packer.d.ts +125 -0
- package/dist/row-packer.d.ts.map +1 -0
- package/dist/row-packer.js +169 -0
- package/dist/row-packer.js.map +1 -0
- package/dist/style-resolution.d.ts +14 -0
- package/dist/style-resolution.d.ts.map +1 -0
- package/dist/style-resolution.js +191 -0
- package/dist/style-resolution.js.map +1 -0
- package/dist/themes/dark.d.ts +4 -0
- package/dist/themes/dark.d.ts.map +1 -0
- package/dist/themes/dark.js +241 -0
- package/dist/themes/dark.js.map +1 -0
- package/dist/themes/index.d.ts +15 -0
- package/dist/themes/index.d.ts.map +1 -0
- package/dist/themes/index.js +30 -0
- package/dist/themes/index.js.map +1 -0
- package/dist/themes/light.d.ts +4 -0
- package/dist/themes/light.d.ts.map +1 -0
- package/dist/themes/light.js +248 -0
- package/dist/themes/light.js.map +1 -0
- package/dist/themes/shape.d.ts +194 -0
- package/dist/themes/shape.d.ts.map +1 -0
- package/dist/themes/shape.js +6 -0
- package/dist/themes/shape.js.map +1 -0
- package/dist/themes/shared.d.ts +145 -0
- package/dist/themes/shared.d.ts.map +1 -0
- package/dist/themes/shared.js +310 -0
- package/dist/themes/shared.js.map +1 -0
- package/dist/time-scale.d.ts +39 -0
- package/dist/time-scale.d.ts.map +1 -0
- package/dist/time-scale.js +62 -0
- package/dist/time-scale.js.map +1 -0
- package/dist/types.d.ts +483 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/view-preset.d.ts +23 -0
- package/dist/view-preset.d.ts.map +1 -0
- package/dist/view-preset.js +146 -0
- package/dist/view-preset.js.map +1 -0
- package/dist/working-calendar.d.ts +14 -0
- package/dist/working-calendar.d.ts.map +1 -0
- package/dist/working-calendar.js +74 -0
- package/dist/working-calendar.js.map +1 -0
- package/package.json +37 -0
- package/src/band-scale.ts +115 -0
- package/src/calendar.ts +244 -0
- package/src/capacity.ts +191 -0
- package/src/dsl-utils.ts +30 -0
- package/src/edge-routing.ts +550 -0
- package/src/frame-tab-geometry.ts +165 -0
- package/src/header-card-geometry.ts +48 -0
- package/src/i18n.ts +124 -0
- package/src/include-chrome-geometry.ts +156 -0
- package/src/index.ts +116 -0
- package/src/item-bar-geometry.ts +222 -0
- package/src/lane-utilization.ts +259 -0
- package/src/layout-context.ts +182 -0
- package/src/layout.ts +1446 -0
- package/src/nodes/anchor-node.ts +77 -0
- package/src/nodes/footnote-node.ts +60 -0
- package/src/nodes/group-node.ts +252 -0
- package/src/nodes/include-node.ts +168 -0
- package/src/nodes/item-node.ts +171 -0
- package/src/nodes/marker-geometry.ts +43 -0
- package/src/nodes/milestone-node.ts +263 -0
- package/src/nodes/parallel-node.ts +101 -0
- package/src/nodes/roadmap-node.ts +957 -0
- package/src/nodes/swimlane-node.ts +423 -0
- package/src/renderable.ts +68 -0
- package/src/row-packer.ts +271 -0
- package/src/style-resolution.ts +243 -0
- package/src/themes/dark.ts +244 -0
- package/src/themes/index.ts +36 -0
- package/src/themes/light.ts +251 -0
- package/src/themes/shape.ts +230 -0
- package/src/themes/shared.ts +369 -0
- package/src/time-scale.ts +78 -0
- package/src/types.ts +607 -0
- package/src/view-preset.ts +180 -0
- package/src/working-calendar.ts +91 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"include-node.js","sourceRoot":"","sources":["../../src/nodes/include-node.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,qEAAqE;AACrE,kEAAkE;AAClE,kEAAkE;AAClE,gEAAgE;AAChE,uCAAuC;AASvC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAQtD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,WAAW,GAAG,EAAE,CAAC;AACvB,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAC/B,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,qEAAqE;AACrE,uEAAuE;AACvE,6DAA6D;AAC7D,2DAA2D;AAC3D,MAAM,4BAA4B,GAAG,EAAE,CAAC;AAyBxC,MAAM,UAAU,mBAAmB,CAC/B,OAAyB,EACzB,GAAkB,EAClB,MAAc,EACd,IAAqB;IAErB,IAAI,CAAC,GAAG,MAAM,GAAG,WAAW,CAAC;IAC7B,MAAM,GAAG,GAA8B,EAAE,CAAC;IAC1C,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO;YAAE,CAAC,IAAI,mBAAmB,CAAC;QACvC,OAAO,GAAG,KAAK,CAAC;QAChB,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,IAAI,MAAM,CAAC,UAAU,CAAC;QACjE,MAAM,WAAW,GAAG,CAAC,GAAG,gBAAgB,CAAC;QACzC,MAAM,QAAQ,GAAkB;YAC5B,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,QAAQ,EAAE;gBACN,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK;gBACzB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM;gBAC5B,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ;gBAChC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM;aAChC;YACD,iEAAiE;YACjE,gEAAgE;YAChE,4CAA4C;YAC5C,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC;YAClD,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM;YAC7B,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK;YAC3B,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO;YAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO;YAC9B,aAAa,EAAE,IAAI,GAAG,EAAE;YACxB,aAAa,EAAE,IAAI,GAAG,EAAE;YACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,eAAe,EAAE,IAAI,GAAG,EAAE;YAC1B,gBAAgB,EAAE,IAAI,GAAG,EAAE;YAC3B,eAAe,EAAE,IAAI,GAAG,EAAE;YAC1B,iBAAiB,EAAE,IAAI,GAAG,EAAE;YAC5B,kBAAkB,EAAE,IAAI,GAAG,EAAE;YAC7B,eAAe,EAAE,IAAI,GAAG,EAAE;YAC1B,WAAW,EAAE,IAAI,GAAG,EAAE;YACtB,cAAc,EAAE,EAAE;YAClB,gBAAgB,EAAE,IAAI,GAAG,EAAE;YAC3B,cAAc,EAAE,EAAE;YAClB,mBAAmB,EAAE,IAAI,GAAG,EAAE;YAC9B,SAAS,EAAE,WAAW;YACtB,YAAY,EAAE,WAAW;YACzB,eAAe,EAAE,WAAW;YAC5B,WAAW,EAAE,GAAG,CAAC,WAAW;SAC/B,CAAC;QACF,MAAM,eAAe,GAAyB,EAAE,CAAC;QACjD,IAAI,OAAO,GAAG,WAAW,CAAC;QAC1B,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,mBAAmB,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;QACpD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YACnD,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,YAAY,CAC3D,EAAE,IAAI,EAAE,SAAS,EAAE,EACnB,IAAI,CACP,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;YAChE,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACjC,OAAO,IAAI,UAAU,CAAC;YACtB,SAAS,EAAE,CAAC;YACZ,IAAI,UAAU,GAAG,mBAAmB;gBAAE,mBAAmB,GAAG,UAAU,CAAC;QAC3E,CAAC;QACD,MAAM,SAAS,GAAG,OAAO,CAAC;QAC1B,gEAAgE;QAChE,gEAAgE;QAChE,+DAA+D;QAC/D,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CACzB,GAAG,CAAC,SAAS,CAAC,SAAS,EAAE,EACzB,SAAS,GAAG,CAAC,GAAG,mBAAmB,CACtC,CAAC;QACF,iEAAiE;QACjE,6DAA6D;QAC7D,+DAA+D;QAC/D,6DAA6D;QAC7D,oDAAoD;QACpD,MAAM,IAAI,GAAG,CAAC,CAAC;QACf,MAAM,EAAE,YAAY,EAAE,GAAG,qBAAqB,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QAC/E,MAAM,aAAa,GACf,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,mBAAmB,CAAC,GAAG,4BAA4B,CAAC;QAC/E,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,GAAG,IAAI,EAAE,aAAa,GAAG,IAAI,CAAC,CAAC;QACxE,MAAM,GAAG,GAAgB;YACrB,CAAC,EAAE,IAAI;YACP,CAAC;YACD,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,YAAY;SACvB,CAAC;QACF,gEAAgE;QAChE,8DAA8D;QAC9D,iCAAiC;QACjC,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;YACjC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,QAAQ,CAAC;QAC9B,CAAC;QACD,GAAG,CAAC,IAAI,CAAC;YACL,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,KAAK;YACL,GAAG;YACH,eAAe;YACf,KAAK,EAAE,YAAY,CAAC,UAAU,EAAE,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC;SACpD,CAAC,CAAC;QACH,CAAC,IAAI,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { IntrinsicSize, MeasureContext, PlaceContext, Point, Renderable } from '../renderable.js';
|
|
2
|
+
import type { BoundingBox } from '../types.js';
|
|
3
|
+
export interface ItemNodeInput {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
/** Logical left x of the column the bar lives in. */
|
|
7
|
+
logicalLeftX: number;
|
|
8
|
+
/** Logical right x of the column the bar lives in. */
|
|
9
|
+
logicalRightX: number;
|
|
10
|
+
/** Caption text shown under the title (e.g. "1w - 50% remaining"). */
|
|
11
|
+
metaText?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Extra horizontal width (px) appended to the meta line for spill
|
|
14
|
+
* detection — covers content the renderer paints after `metaText` but
|
|
15
|
+
* that the layout assembles as a structured trailing element rather
|
|
16
|
+
* than a string (currently: the capacity suffix). The layout adds this
|
|
17
|
+
* to the meta-line's measured width before deciding whether the
|
|
18
|
+
* caption block fits inside the bar.
|
|
19
|
+
*
|
|
20
|
+
* Defaults to 0. Title spill checks ignore this value (the suffix
|
|
21
|
+
* never renders on the title line).
|
|
22
|
+
*/
|
|
23
|
+
metaTrailingWidth?: number;
|
|
24
|
+
/**
|
|
25
|
+
* True when the bar shows a link icon in its upper-left corner.
|
|
26
|
+
* Caption text indents past the icon column so the title doesn't
|
|
27
|
+
* collide with the icon.
|
|
28
|
+
*/
|
|
29
|
+
hasLinkIcon?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface PlacedItemGeometry {
|
|
32
|
+
id: string;
|
|
33
|
+
box: BoundingBox;
|
|
34
|
+
/**
|
|
35
|
+
* X for the title/meta text. Equal to `box.x + TEXT_INSET_PX`
|
|
36
|
+
* when text fits inside the bar; otherwise positioned just past
|
|
37
|
+
* the bar's right edge so the caption reads as belonging to the
|
|
38
|
+
* item rather than being clipped.
|
|
39
|
+
*/
|
|
40
|
+
textX: number;
|
|
41
|
+
/** True when text spills past the bar's right edge. */
|
|
42
|
+
textSpills: boolean;
|
|
43
|
+
}
|
|
44
|
+
export declare class ItemNode implements Renderable<PlacedItemGeometry> {
|
|
45
|
+
readonly input: ItemNodeInput;
|
|
46
|
+
constructor(input: ItemNodeInput);
|
|
47
|
+
get id(): string;
|
|
48
|
+
measure(ctx: MeasureContext): IntrinsicSize;
|
|
49
|
+
place(origin: Point, ctx: PlaceContext): PlacedItemGeometry;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=item-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"item-node.d.ts","sourceRoot":"","sources":["../../src/nodes/item-node.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACR,aAAa,EACb,cAAc,EACd,YAAY,EACZ,KAAK,EACL,UAAU,EACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAqB/C,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,WAAW,CAAC;IACjB;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,UAAU,EAAE,OAAO,CAAC;CACvB;AAWD,qBAAa,QAAS,YAAW,UAAU,CAAC,kBAAkB,CAAC;aAC/B,KAAK,EAAE,aAAa;gBAApB,KAAK,EAAE,aAAa;IAEhD,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,aAAa;IAW3C,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,GAAG,kBAAkB;CAiD9D"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// ItemNode — first Renderable entity in the m2.5c port. Owns the
|
|
2
|
+
// geometry of a single item bar: its visual box (insets + height) and
|
|
3
|
+
// its caption x (inside vs. spilled past the bar).
|
|
4
|
+
//
|
|
5
|
+
// Constructed from already-resolved inputs (start/end x, status,
|
|
6
|
+
// remaining, meta text) so this node stays small and pure. The
|
|
7
|
+
// dependency-resolution logic in layout.ts (after/before chains,
|
|
8
|
+
// cursor.x, footnote indexing) remains where it is for now; m2.5c's
|
|
9
|
+
// remaining work is to migrate the rest into sibling node files.
|
|
10
|
+
//
|
|
11
|
+
// `measure(ctx)` returns the bar's intrinsic size. `place(origin,
|
|
12
|
+
// ctx)` returns a `PositionedItem`-shaped fragment with the box, row,
|
|
13
|
+
// and `textX`. The shape is byte-stable with the legacy sequenceItem
|
|
14
|
+
// arithmetic when fed the same inputs.
|
|
15
|
+
import { ITEM_LINK_ICON_INSET_PX, ITEM_LINK_ICON_TILE_SIZE_PX } from '../item-bar-geometry.js';
|
|
16
|
+
import { ITEM_INSET_PX, MIN_ITEM_WIDTH } from '../themes/shared.js';
|
|
17
|
+
/**
|
|
18
|
+
* Inner padding applied on each side of the title text — the bar's
|
|
19
|
+
* inner-padded text area is `box.width - 2 * TEXT_INSET_PX` wide.
|
|
20
|
+
* Text spills past the bar when either the title or the meta line
|
|
21
|
+
* exceeds that area.
|
|
22
|
+
*/
|
|
23
|
+
const TEXT_INSET_PX = 12;
|
|
24
|
+
/**
|
|
25
|
+
* Gap (px) between the bar's right edge and overflow text. Smaller
|
|
26
|
+
* than `TEXT_INSET_PX` so the text reads as belonging to this bar —
|
|
27
|
+
* adjacent bars are at least `2 * ITEM_INSET_PX = 12` away, so the
|
|
28
|
+
* text still has a clear visual home.
|
|
29
|
+
*/
|
|
30
|
+
const TEXT_OUTSIDE_GAP_PX = 4;
|
|
31
|
+
const TITLE_FONT_SIZE_PX = 13;
|
|
32
|
+
const META_FONT_SIZE_PX = 11;
|
|
33
|
+
/**
|
|
34
|
+
* Approx. rendered width of `text` at `fontSizePx`. Matches the legacy
|
|
35
|
+
* `sequenceItem` heuristic (intentionally pessimistic at ~0.58 em/char so
|
|
36
|
+
* borderline-fitting captions trigger spill rather than clip).
|
|
37
|
+
*/
|
|
38
|
+
function estimateTextWidth(text, fontSizePx) {
|
|
39
|
+
return text.length * fontSizePx * 0.58;
|
|
40
|
+
}
|
|
41
|
+
export class ItemNode {
|
|
42
|
+
input;
|
|
43
|
+
constructor(input) {
|
|
44
|
+
this.input = input;
|
|
45
|
+
}
|
|
46
|
+
get id() {
|
|
47
|
+
return this.input.id;
|
|
48
|
+
}
|
|
49
|
+
measure(ctx) {
|
|
50
|
+
const naturalWidth = Math.max(MIN_ITEM_WIDTH, this.input.logicalRightX - this.input.logicalLeftX);
|
|
51
|
+
return {
|
|
52
|
+
width: naturalWidth,
|
|
53
|
+
height: ctx.bands.bandwidth(),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
place(origin, ctx) {
|
|
57
|
+
const intrinsic = this.measure(ctx);
|
|
58
|
+
const visualWidth = Math.max(MIN_ITEM_WIDTH, intrinsic.width - 2 * ITEM_INSET_PX);
|
|
59
|
+
const boxX = origin.x + ITEM_INSET_PX;
|
|
60
|
+
const box = {
|
|
61
|
+
x: boxX,
|
|
62
|
+
y: origin.y,
|
|
63
|
+
width: visualWidth,
|
|
64
|
+
height: intrinsic.height,
|
|
65
|
+
};
|
|
66
|
+
// The link icon (when present) lives in the bar's upper-left
|
|
67
|
+
// and shares the title's vertical band. The caption indents
|
|
68
|
+
// past the icon so the title doesn't render on top of it.
|
|
69
|
+
const linkColumn = this.input.hasLinkIcon
|
|
70
|
+
? ITEM_LINK_ICON_INSET_PX +
|
|
71
|
+
ITEM_LINK_ICON_TILE_SIZE_PX +
|
|
72
|
+
LINK_ICON_TO_CAPTION_GAP_PX -
|
|
73
|
+
TEXT_INSET_PX
|
|
74
|
+
: 0;
|
|
75
|
+
const captionLeftInset = TEXT_INSET_PX + Math.max(0, linkColumn);
|
|
76
|
+
const innerWidth = Math.max(0, visualWidth - captionLeftInset - TEXT_INSET_PX);
|
|
77
|
+
const titleStr = this.input.title;
|
|
78
|
+
const titleWidth = titleStr ? estimateTextWidth(titleStr, TITLE_FONT_SIZE_PX) : 0;
|
|
79
|
+
const metaTextWidth = this.input.metaText
|
|
80
|
+
? estimateTextWidth(this.input.metaText, META_FONT_SIZE_PX)
|
|
81
|
+
: 0;
|
|
82
|
+
const trailingWidth = this.input.metaTrailingWidth ?? 0;
|
|
83
|
+
// Trailing decoration (capacity suffix) renders to the right of
|
|
84
|
+
// metaText. When metaText is empty the suffix sits at the
|
|
85
|
+
// caption's leading edge, so its width is the entire meta-line
|
|
86
|
+
// budget; when both exist the suffix needs a small separator gap
|
|
87
|
+
// (rendered via `<tspan dx>` later) included in trailingWidth.
|
|
88
|
+
const metaWidth = metaTextWidth + trailingWidth;
|
|
89
|
+
const hasMetaContent = this.input.metaText !== undefined || trailingWidth > 0;
|
|
90
|
+
const textSpills = (titleStr.length > 0 && titleWidth > innerWidth) ||
|
|
91
|
+
(hasMetaContent && metaWidth > innerWidth);
|
|
92
|
+
const textX = textSpills
|
|
93
|
+
? boxX + visualWidth + TEXT_OUTSIDE_GAP_PX
|
|
94
|
+
: boxX + captionLeftInset;
|
|
95
|
+
return {
|
|
96
|
+
id: this.input.id,
|
|
97
|
+
box,
|
|
98
|
+
textX,
|
|
99
|
+
textSpills,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Horizontal gap (px) between the link-icon tile's right edge and the
|
|
105
|
+
* start of the caption text when both render inside the bar.
|
|
106
|
+
*/
|
|
107
|
+
const LINK_ICON_TO_CAPTION_GAP_PX = 4;
|
|
108
|
+
//# sourceMappingURL=item-node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"item-node.js","sourceRoot":"","sources":["../../src/nodes/item-node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Height (px) of a single marker row in the timeline header band. */
|
|
2
|
+
export declare const MARKER_ROW_PITCH_PX = 26;
|
|
3
|
+
/**
|
|
4
|
+
* Vertical center offset of a marker row from the top of its band.
|
|
5
|
+
* Always half of `MARKER_ROW_PITCH_PX` — exposed as a derived constant
|
|
6
|
+
* so call sites read as "row 0 center" instead of magic `13`.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MARKER_ROW_CENTER_OFFSET_PX: number;
|
|
9
|
+
/** Half-width of the diamond glyph drawn at every marker's centerX. */
|
|
10
|
+
export declare const MARKER_DIAMOND_RADIUS_PX = 6;
|
|
11
|
+
/** Horizontal gap between the diamond's edge and its label box. */
|
|
12
|
+
export declare const MARKER_LABEL_GAP_PX = 6;
|
|
13
|
+
/** Height of the label box (one line of marker label text). */
|
|
14
|
+
export declare const MARKER_LABEL_HEIGHT_PX = 12;
|
|
15
|
+
/**
|
|
16
|
+
* Width-estimate surcharge for bold sans-serif marker labels (milestone
|
|
17
|
+
* titles). `estimateTextWidth` is intentionally pessimistic at ~0.58
|
|
18
|
+
* em/char so this small surcharge keeps overlap detection on the safe
|
|
19
|
+
* side without overshooting layout space.
|
|
20
|
+
*/
|
|
21
|
+
export declare const MARKER_BOLD_WIDTH_FACTOR = 1.05;
|
|
22
|
+
//# sourceMappingURL=marker-geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marker-geometry.d.ts","sourceRoot":"","sources":["../../src/nodes/marker-geometry.ts"],"names":[],"mappings":"AAiBA,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,QAA0B,CAAC;AAEnE,uEAAuE;AACvE,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,+DAA+D;AAC/D,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Marker-row geometry shared across the marker stack: anchors,
|
|
2
|
+
// milestones, and the roadmap-level row packer all read from here so a
|
|
3
|
+
// font, gap, or row-height change flows from one place.
|
|
4
|
+
//
|
|
5
|
+
// Two row-band types live in this file:
|
|
6
|
+
//
|
|
7
|
+
// - `MARKER_ROW_PITCH_PX` (26 px): height of a single marker row in
|
|
8
|
+
// the timeline header. The marker band stacks N rows tall when
|
|
9
|
+
// anchors and milestones can't fit on a single row without colliding.
|
|
10
|
+
// - The bare `13` (= PITCH / 2) midpoint that used to sprinkle
|
|
11
|
+
// `roadmap-node.ts` is derived from this constant — bumping the
|
|
12
|
+
// pitch automatically re-centers labels.
|
|
13
|
+
//
|
|
14
|
+
// Diamond + label box dimensions are reused by every marker (anchor +
|
|
15
|
+
// milestone), and the `MARKER_BOLD_WIDTH_FACTOR` corrects the
|
|
16
|
+
// pessimistic 0.58 em width estimate for bold milestone labels.
|
|
17
|
+
/** Height (px) of a single marker row in the timeline header band. */
|
|
18
|
+
export const MARKER_ROW_PITCH_PX = 26;
|
|
19
|
+
/**
|
|
20
|
+
* Vertical center offset of a marker row from the top of its band.
|
|
21
|
+
* Always half of `MARKER_ROW_PITCH_PX` — exposed as a derived constant
|
|
22
|
+
* so call sites read as "row 0 center" instead of magic `13`.
|
|
23
|
+
*/
|
|
24
|
+
export const MARKER_ROW_CENTER_OFFSET_PX = MARKER_ROW_PITCH_PX / 2;
|
|
25
|
+
/** Half-width of the diamond glyph drawn at every marker's centerX. */
|
|
26
|
+
export const MARKER_DIAMOND_RADIUS_PX = 6;
|
|
27
|
+
/** Horizontal gap between the diamond's edge and its label box. */
|
|
28
|
+
export const MARKER_LABEL_GAP_PX = 6;
|
|
29
|
+
/** Height of the label box (one line of marker label text). */
|
|
30
|
+
export const MARKER_LABEL_HEIGHT_PX = 12;
|
|
31
|
+
/**
|
|
32
|
+
* Width-estimate surcharge for bold sans-serif marker labels (milestone
|
|
33
|
+
* titles). `estimateTextWidth` is intentionally pessimistic at ~0.58
|
|
34
|
+
* em/char so this small surcharge keeps overlap detection on the safe
|
|
35
|
+
* side without overshooting layout space.
|
|
36
|
+
*/
|
|
37
|
+
export const MARKER_BOLD_WIDTH_FACTOR = 1.05;
|
|
38
|
+
//# sourceMappingURL=marker-geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marker-geometry.js","sourceRoot":"","sources":["../../src/nodes/marker-geometry.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,uEAAuE;AACvE,wDAAwD;AACxD,EAAE;AACF,wCAAwC;AACxC,EAAE;AACF,sEAAsE;AACtE,mEAAmE;AACnE,0EAA0E;AAC1E,iEAAiE;AACjE,oEAAoE;AACpE,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,8DAA8D;AAC9D,gEAAgE;AAEhE,sEAAsE;AACtE,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,mBAAmB,GAAG,CAAC,CAAC;AAEnE,uEAAuE;AACvE,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAE1C,mEAAmE;AACnE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,+DAA+D;AAC/D,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { MilestoneDeclaration } from '@nowline/core';
|
|
2
|
+
import type { LayoutContext } from '../layout-context.js';
|
|
3
|
+
import type { PositionedMilestone } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* One predecessor of a milestone with everything the slack-arrow
|
|
6
|
+
* pipeline needs: its source x (visual right edge for items, marker
|
|
7
|
+
* centerX for anchors / other milestones), its attach y (bar bottom
|
|
8
|
+
* strip when text spills, row mid otherwise), and its flow key (used
|
|
9
|
+
* to dedupe so a chained-flow's siblings collapse to the last entry).
|
|
10
|
+
*/
|
|
11
|
+
export interface MilestonePredecessor {
|
|
12
|
+
ref: string;
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
flowKey: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Resolve each `after:` reference into a `MilestonePredecessor`.
|
|
19
|
+
* Items use their VISUAL right edge as the slack source so the
|
|
20
|
+
* arrow leaves the painted bar instead of landing in the inter-
|
|
21
|
+
* column gutter; markers (anchors / other milestones) fall back
|
|
22
|
+
* to their cut-line centerX. Refs whose target is unknown drop
|
|
23
|
+
* silently — matches the legacy continue path.
|
|
24
|
+
*/
|
|
25
|
+
export declare function collectMilestonePredecessors(refs: string[], ctx: LayoutContext): MilestonePredecessor[];
|
|
26
|
+
/**
|
|
27
|
+
* Keep only the rightmost predecessor per flow key. Two
|
|
28
|
+
* predecessors share a flow when they sit in the same deepest
|
|
29
|
+
* single-track container (swimlane root, sequential group, or one
|
|
30
|
+
* sub-track of a parallel) — file order already encodes their
|
|
31
|
+
* ordering, so only the latest entry contributes a slack arrow.
|
|
32
|
+
* Predecessors in different flows (e.g. two parallel sub-tracks)
|
|
33
|
+
* each survive as their flow's last entry.
|
|
34
|
+
*/
|
|
35
|
+
export declare function lastPredecessorPerFlow(preds: MilestonePredecessor[]): MilestonePredecessor[];
|
|
36
|
+
export declare class MilestoneNode {
|
|
37
|
+
readonly id: string;
|
|
38
|
+
readonly milestone: MilestoneDeclaration;
|
|
39
|
+
constructor(id: string, milestone: MilestoneDeclaration);
|
|
40
|
+
/**
|
|
41
|
+
* Returns null when the milestone is neither date-pinned nor has a
|
|
42
|
+
* resolvable `after:` predecessor (skipped silently — matches the
|
|
43
|
+
* legacy `buildMilestones` continue-paths).
|
|
44
|
+
*/
|
|
45
|
+
place(ctx: LayoutContext): PositionedMilestone | null;
|
|
46
|
+
}
|
|
47
|
+
export declare function buildMilestones(milestones: Map<string, MilestoneDeclaration>, ctx: LayoutContext): PositionedMilestone[];
|
|
48
|
+
//# sourceMappingURL=milestone-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"milestone-node.d.ts","sourceRoot":"","sources":["../../src/nodes/milestone-node.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAsB,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAQ3E;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CACxC,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,aAAa,GACnB,oBAAoB,EAAE,CAaxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,oBAAoB,EAAE,CAO5F;AAyBD,qBAAa,aAAa;aAEF,EAAE,EAAE,MAAM;aACV,SAAS,EAAE,oBAAoB;gBAD/B,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,oBAAoB;IAGnD;;;;OAIG;IACH,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,mBAAmB,GAAG,IAAI;CAoIxD;AAED,wBAAgB,eAAe,CAC3B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,EAC7C,GAAG,EAAE,aAAa,GACnB,mBAAmB,EAAE,CAOvB"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// MilestoneNode — Renderable for a single milestone + a
|
|
2
|
+
// `buildMilestones` loop helper. Each milestone sits in the marker row
|
|
3
|
+
// and either pins to a fixed `date:` or floats to the rightmost `after:`
|
|
4
|
+
// predecessor. A non-binding (second-latest) predecessor drives the
|
|
5
|
+
// "slack" arrow when present. Date-pinned milestones whose predecessors
|
|
6
|
+
// would push past the date are flagged `isOverrun`.
|
|
7
|
+
//
|
|
8
|
+
// Marker-row placement (row + label box + left/right side) is decided
|
|
9
|
+
// once by `roadmap-node.ts::packMarkerRow`. Date-pinned entities are
|
|
10
|
+
// pre-packed before swimlanes run; after-only milestones get a
|
|
11
|
+
// PROVISIONAL row=0 placement here at build time which the unified
|
|
12
|
+
// re-pack in `RoadmapNode.place` overwrites with the final tick-order
|
|
13
|
+
// bottom-first slot once every centerX is known.
|
|
14
|
+
import { parseDate, propValue, propValues } from '../dsl-utils.js';
|
|
15
|
+
import { resolveStyle } from '../style-resolution.js';
|
|
16
|
+
import { MARKER_BOLD_WIDTH_FACTOR, MARKER_DIAMOND_RADIUS_PX, MARKER_LABEL_GAP_PX, MARKER_LABEL_HEIGHT_PX, } from './marker-geometry.js';
|
|
17
|
+
/**
|
|
18
|
+
* Resolve each `after:` reference into a `MilestonePredecessor`.
|
|
19
|
+
* Items use their VISUAL right edge as the slack source so the
|
|
20
|
+
* arrow leaves the painted bar instead of landing in the inter-
|
|
21
|
+
* column gutter; markers (anchors / other milestones) fall back
|
|
22
|
+
* to their cut-line centerX. Refs whose target is unknown drop
|
|
23
|
+
* silently — matches the legacy continue path.
|
|
24
|
+
*/
|
|
25
|
+
export function collectMilestonePredecessors(refs, ctx) {
|
|
26
|
+
const out = [];
|
|
27
|
+
for (const ref of refs) {
|
|
28
|
+
const visualRight = ctx.entityVisualRightX.get(ref);
|
|
29
|
+
const x = visualRight ?? ctx.entityRightEdges.get(ref);
|
|
30
|
+
if (x === undefined)
|
|
31
|
+
continue;
|
|
32
|
+
const y = ctx.itemSlackAttachY.get(ref) ?? ctx.entityMidpoints.get(ref)?.y ?? 0;
|
|
33
|
+
// Markers don't share a flow with anything, so use their id
|
|
34
|
+
// as a unique flow key — every marker stands on its own.
|
|
35
|
+
const flowKey = ctx.itemFlowKey.get(ref) ?? `marker:${ref}`;
|
|
36
|
+
out.push({ ref, x, y, flowKey });
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Keep only the rightmost predecessor per flow key. Two
|
|
42
|
+
* predecessors share a flow when they sit in the same deepest
|
|
43
|
+
* single-track container (swimlane root, sequential group, or one
|
|
44
|
+
* sub-track of a parallel) — file order already encodes their
|
|
45
|
+
* ordering, so only the latest entry contributes a slack arrow.
|
|
46
|
+
* Predecessors in different flows (e.g. two parallel sub-tracks)
|
|
47
|
+
* each survive as their flow's last entry.
|
|
48
|
+
*/
|
|
49
|
+
export function lastPredecessorPerFlow(preds) {
|
|
50
|
+
const m = new Map();
|
|
51
|
+
for (const p of preds) {
|
|
52
|
+
const existing = m.get(p.flowKey);
|
|
53
|
+
if (!existing || p.x > existing.x)
|
|
54
|
+
m.set(p.flowKey, p);
|
|
55
|
+
}
|
|
56
|
+
return Array.from(m.values());
|
|
57
|
+
}
|
|
58
|
+
function decideLabelBoxForCanvas(centerX, centerY, radius, title, fontSize, bold, chartLeftX, chartRightX) {
|
|
59
|
+
const labelWidth = title.length * fontSize * 0.58 * (bold ? MARKER_BOLD_WIDTH_FACTOR : 1);
|
|
60
|
+
const naturalRightX = centerX + radius + MARKER_LABEL_GAP_PX;
|
|
61
|
+
const naturalLeftX = centerX - radius - MARKER_LABEL_GAP_PX - labelWidth;
|
|
62
|
+
const fitsRight = naturalRightX + labelWidth <= chartRightX;
|
|
63
|
+
const fitsLeft = naturalLeftX >= chartLeftX;
|
|
64
|
+
const side = fitsRight ? 'right' : fitsLeft ? 'left' : 'right';
|
|
65
|
+
const xLeft = side === 'right' ? naturalRightX : naturalLeftX;
|
|
66
|
+
return {
|
|
67
|
+
box: { x: xLeft, y: centerY - 4, width: labelWidth, height: MARKER_LABEL_HEIGHT_PX },
|
|
68
|
+
side,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export class MilestoneNode {
|
|
72
|
+
id;
|
|
73
|
+
milestone;
|
|
74
|
+
constructor(id, milestone) {
|
|
75
|
+
this.id = id;
|
|
76
|
+
this.milestone = milestone;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Returns null when the milestone is neither date-pinned nor has a
|
|
80
|
+
* resolvable `after:` predecessor (skipped silently — matches the
|
|
81
|
+
* legacy `buildMilestones` continue-paths).
|
|
82
|
+
*/
|
|
83
|
+
place(ctx) {
|
|
84
|
+
const m = this.milestone;
|
|
85
|
+
const style = resolveStyle('milestone', m.properties, ctx.styleCtx);
|
|
86
|
+
const dateRaw = propValue(m.properties, 'date');
|
|
87
|
+
const afterRaw = propValues(m.properties, 'after');
|
|
88
|
+
const date = parseDate(dateRaw);
|
|
89
|
+
const inRowY = ctx.timeline.markerRow.y;
|
|
90
|
+
const radius = MARKER_DIAMOND_RADIUS_PX;
|
|
91
|
+
const title = m.title ?? this.id;
|
|
92
|
+
let centerX = null;
|
|
93
|
+
let centerY = inRowY;
|
|
94
|
+
let labelBox = null;
|
|
95
|
+
let labelSide = 'right';
|
|
96
|
+
let fixed = false;
|
|
97
|
+
let slackArrows;
|
|
98
|
+
let isOverrun = false;
|
|
99
|
+
if (date) {
|
|
100
|
+
const x = ctx.scale.forwardWithinDomain(date);
|
|
101
|
+
if (x === null)
|
|
102
|
+
return null;
|
|
103
|
+
centerX = x;
|
|
104
|
+
fixed = true;
|
|
105
|
+
const placement = ctx.markerRowPlacements.get(this.id);
|
|
106
|
+
if (placement) {
|
|
107
|
+
centerY = placement.centerY;
|
|
108
|
+
labelBox = placement.labelBox;
|
|
109
|
+
labelSide = placement.labelSide;
|
|
110
|
+
}
|
|
111
|
+
// Date-pinned milestones whose `after:` predecessors finish
|
|
112
|
+
// past the pinned date are flagged `isOverrun`; the latest
|
|
113
|
+
// last-of-flow predecessor draws a single overrun arrow
|
|
114
|
+
// pointing back from its visual right edge to the
|
|
115
|
+
// milestone's column. Flow-dedupe avoids stacking redundant
|
|
116
|
+
// arrows from sibling items in one chained flow.
|
|
117
|
+
const preds = collectMilestonePredecessors(afterRaw, ctx);
|
|
118
|
+
const dedupedPreds = lastPredecessorPerFlow(preds);
|
|
119
|
+
let maxPred = null;
|
|
120
|
+
for (const p of dedupedPreds) {
|
|
121
|
+
if (!maxPred || p.x > maxPred.x)
|
|
122
|
+
maxPred = p;
|
|
123
|
+
}
|
|
124
|
+
if (maxPred && maxPred.x > x) {
|
|
125
|
+
isOverrun = true;
|
|
126
|
+
slackArrows = [
|
|
127
|
+
{
|
|
128
|
+
x: maxPred.x,
|
|
129
|
+
y: maxPred.y > 0 ? maxPred.y : centerY,
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else if (afterRaw.length > 0) {
|
|
135
|
+
// Float to the rightmost (binding) predecessor; every
|
|
136
|
+
// last-of-flow predecessor that finishes EARLIER than the
|
|
137
|
+
// binding contributes one slack arrow. Predecessors in the
|
|
138
|
+
// same single-track flow (sequential group, swimlane root,
|
|
139
|
+
// one parallel sub-track) collapse to just their last
|
|
140
|
+
// entry — file order encodes the dependency chain, so only
|
|
141
|
+
// the rightmost matters. Predecessors in different flows
|
|
142
|
+
// (e.g. two parallel sub-tracks) each contribute their own
|
|
143
|
+
// last-of-flow arrow.
|
|
144
|
+
const preds = collectMilestonePredecessors(afterRaw, ctx);
|
|
145
|
+
const dedupedPreds = lastPredecessorPerFlow(preds);
|
|
146
|
+
dedupedPreds.sort((a, b) => b.x - a.x);
|
|
147
|
+
const maxEnd = dedupedPreds[0]?.x ?? ctx.timeline.originX;
|
|
148
|
+
centerX = maxEnd;
|
|
149
|
+
fixed = false;
|
|
150
|
+
// Provisional placement — RoadmapNode runs a unified
|
|
151
|
+
// tick-order pack across all markers (date-pinned anchors,
|
|
152
|
+
// date-pinned milestones, after-only milestones) once
|
|
153
|
+
// everyone's centerX is known. That pass overwrites the
|
|
154
|
+
// rowIndex / centerY / labelBox / labelSide we set here.
|
|
155
|
+
const provisional = decideLabelBoxForCanvas(centerX, inRowY, radius, title, 10, true, ctx.timeline.box.x, ctx.chartRightX);
|
|
156
|
+
centerY = inRowY;
|
|
157
|
+
labelBox = provisional.box;
|
|
158
|
+
labelSide = provisional.side;
|
|
159
|
+
ctx.markerRowPlacements.set(this.id, {
|
|
160
|
+
rowIndex: 0,
|
|
161
|
+
centerY,
|
|
162
|
+
labelBox,
|
|
163
|
+
labelSide,
|
|
164
|
+
});
|
|
165
|
+
const arrows = [];
|
|
166
|
+
for (let i = 1; i < dedupedPreds.length; i++) {
|
|
167
|
+
const p = dedupedPreds[i];
|
|
168
|
+
if (p.x < maxEnd && p.y > 0)
|
|
169
|
+
arrows.push({ x: p.x, y: p.y });
|
|
170
|
+
}
|
|
171
|
+
if (arrows.length > 0)
|
|
172
|
+
slackArrows = arrows;
|
|
173
|
+
}
|
|
174
|
+
if (centerX === null)
|
|
175
|
+
return null;
|
|
176
|
+
if (!labelBox) {
|
|
177
|
+
const fallback = decideLabelBoxForCanvas(centerX, centerY, radius, title, 10, true, ctx.timeline.box.x, ctx.chartRightX);
|
|
178
|
+
labelBox = fallback.box;
|
|
179
|
+
labelSide = fallback.side;
|
|
180
|
+
}
|
|
181
|
+
const center = { x: centerX, y: centerY };
|
|
182
|
+
ctx.entityLeftEdges.set(this.id, center.x);
|
|
183
|
+
ctx.entityRightEdges.set(this.id, center.x);
|
|
184
|
+
ctx.entityMidpoints.set(this.id, center);
|
|
185
|
+
return {
|
|
186
|
+
id: this.id,
|
|
187
|
+
title,
|
|
188
|
+
center,
|
|
189
|
+
radius,
|
|
190
|
+
fixed,
|
|
191
|
+
slackArrows,
|
|
192
|
+
isOverrun,
|
|
193
|
+
style,
|
|
194
|
+
cutTopY: ctx.chartTopY,
|
|
195
|
+
cutBottomY: ctx.swimlaneBottomY,
|
|
196
|
+
labelBox,
|
|
197
|
+
labelSide,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
export function buildMilestones(milestones, ctx) {
|
|
202
|
+
const out = [];
|
|
203
|
+
for (const [id, m] of milestones) {
|
|
204
|
+
const positioned = new MilestoneNode(id, m).place(ctx);
|
|
205
|
+
if (positioned)
|
|
206
|
+
out.push(positioned);
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=milestone-node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"milestone-node.js","sourceRoot":"","sources":["../../src/nodes/milestone-node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { GroupBlock, ItemDeclaration, ParallelBlock } from '@nowline/core';
|
|
2
|
+
import type { LayoutContext, TrackCursor } from '../layout-context.js';
|
|
3
|
+
import type { PositionedParallel, PositionedTrackChild } from '../types.js';
|
|
4
|
+
export interface ParallelNodeDeps {
|
|
5
|
+
sequenceOne: (child: ItemDeclaration | GroupBlock | ParallelBlock, cursor: TrackCursor, ctx: LayoutContext) => PositionedTrackChild;
|
|
6
|
+
newCursor: (x: number, y: number) => TrackCursor;
|
|
7
|
+
}
|
|
8
|
+
export declare class ParallelNode {
|
|
9
|
+
readonly node: ParallelBlock;
|
|
10
|
+
private readonly deps;
|
|
11
|
+
constructor(node: ParallelBlock, deps: ParallelNodeDeps);
|
|
12
|
+
get id(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Sequence children into stacked sub-tracks, advance the parent
|
|
15
|
+
* `cursor` past the parallel's right edge plus
|
|
16
|
+
* `TRACK_BLOCK_TAIL_GUTTER_PX` of breathing room, and return a
|
|
17
|
+
* `PositionedParallel`.
|
|
18
|
+
*/
|
|
19
|
+
place(cursor: TrackCursor, ctx: LayoutContext): PositionedParallel;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=parallel-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parallel-node.d.ts","sourceRoot":"","sources":["../../src/nodes/parallel-node.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGvE,OAAO,KAAK,EAAe,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEzF,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,CACT,KAAK,EAAE,eAAe,GAAG,UAAU,GAAG,aAAa,EACnD,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,aAAa,KACjB,oBAAoB,CAAC;IAC1B,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,WAAW,CAAC;CACpD;AAED,qBAAa,YAAY;aAED,IAAI,EAAE,aAAa;IAEnC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAFL,IAAI,EAAE,aAAa,EAElB,IAAI,EAAE,gBAAgB;IAG3C,IAAI,EAAE,IAAI,MAAM,CAEf;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,aAAa,GAAG,kBAAkB;CA6DrE"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// ParallelNode — Renderable for a `parallel { ... }` block. Stacks
|
|
2
|
+
// children top-to-bottom (each child owns a fresh sub-track) and reports
|
|
3
|
+
// the union bounding box. Each child is sequenced via the injected
|
|
4
|
+
// `deps.sequenceOne` callback, which dispatches to the appropriate
|
|
5
|
+
// per-entity Renderable (or, transitionally, the legacy sequencer
|
|
6
|
+
// helpers in `layout.ts`).
|
|
7
|
+
import { resolveStyle } from '../style-resolution.js';
|
|
8
|
+
import { TRACK_BLOCK_TAIL_GUTTER_PX } from '../themes/shared.js';
|
|
9
|
+
export class ParallelNode {
|
|
10
|
+
node;
|
|
11
|
+
deps;
|
|
12
|
+
constructor(node,
|
|
13
|
+
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: accessed via `const { deps } = this` destructuring inside methods, which the analyzer does not detect.
|
|
14
|
+
deps) {
|
|
15
|
+
this.node = node;
|
|
16
|
+
this.deps = deps;
|
|
17
|
+
}
|
|
18
|
+
get id() {
|
|
19
|
+
return this.node.name ?? '';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Sequence children into stacked sub-tracks, advance the parent
|
|
23
|
+
* `cursor` past the parallel's right edge plus
|
|
24
|
+
* `TRACK_BLOCK_TAIL_GUTTER_PX` of breathing room, and return a
|
|
25
|
+
* `PositionedParallel`.
|
|
26
|
+
*/
|
|
27
|
+
place(cursor, ctx) {
|
|
28
|
+
const { node } = this;
|
|
29
|
+
const { deps } = this;
|
|
30
|
+
const style = resolveStyle('parallel', node.properties, ctx.styleCtx);
|
|
31
|
+
const startX = cursor.x;
|
|
32
|
+
const startY = cursor.y;
|
|
33
|
+
const children = [];
|
|
34
|
+
let maxRight = startX;
|
|
35
|
+
let accumulatedHeight = 0;
|
|
36
|
+
// Each child of a parallel block lives on its own sub-track,
|
|
37
|
+
// so each child starts a fresh flow segment under the parent's
|
|
38
|
+
// path. Two predecessors that sit on different parallel
|
|
39
|
+
// sub-tracks therefore stay in different flows for milestone
|
|
40
|
+
// slack-arrow dedupe.
|
|
41
|
+
const previousFlowKey = ctx.currentFlowKey;
|
|
42
|
+
const parId = node.name ?? 'p';
|
|
43
|
+
let childIndex = 0;
|
|
44
|
+
for (const child of node.content) {
|
|
45
|
+
if (child.$type === 'DescriptionDirective')
|
|
46
|
+
continue;
|
|
47
|
+
ctx.currentFlowKey = `${previousFlowKey}/par:${parId}#${childIndex}`;
|
|
48
|
+
const subCursor = deps.newCursor(startX, startY + accumulatedHeight);
|
|
49
|
+
const positioned = deps.sequenceOne(child, subCursor, ctx);
|
|
50
|
+
children.push(positioned);
|
|
51
|
+
accumulatedHeight += Math.max(ctx.bandScale.step(), subCursor.height);
|
|
52
|
+
maxRight = Math.max(maxRight, subCursor.maxX);
|
|
53
|
+
childIndex++;
|
|
54
|
+
}
|
|
55
|
+
ctx.currentFlowKey = previousFlowKey;
|
|
56
|
+
const box = {
|
|
57
|
+
x: startX,
|
|
58
|
+
y: startY,
|
|
59
|
+
width: maxRight - startX,
|
|
60
|
+
height: accumulatedHeight,
|
|
61
|
+
};
|
|
62
|
+
cursor.x = maxRight + TRACK_BLOCK_TAIL_GUTTER_PX;
|
|
63
|
+
cursor.maxX = Math.max(cursor.maxX, cursor.x);
|
|
64
|
+
cursor.height = Math.max(cursor.height, accumulatedHeight);
|
|
65
|
+
const id = node.name;
|
|
66
|
+
if (id) {
|
|
67
|
+
ctx.entityLeftEdges.set(id, box.x);
|
|
68
|
+
ctx.entityRightEdges.set(id, box.x + box.width);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
kind: 'parallel',
|
|
72
|
+
id,
|
|
73
|
+
title: node.title ?? node.name,
|
|
74
|
+
box,
|
|
75
|
+
children,
|
|
76
|
+
style,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=parallel-node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parallel-node.js","sourceRoot":"","sources":["../../src/nodes/parallel-node.ts"],"names":[],"mappings":"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"}
|