@nowline/layout 0.8.5 → 0.9.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/dist/axis-columns.d.ts +70 -0
- package/dist/axis-columns.d.ts.map +1 -0
- package/dist/axis-columns.js +104 -0
- package/dist/axis-columns.js.map +1 -0
- package/dist/band-scale.d.ts.map +1 -1
- package/dist/calendar-resolver.d.ts +27 -0
- package/dist/calendar-resolver.d.ts.map +1 -0
- package/dist/calendar-resolver.js +132 -0
- package/dist/calendar-resolver.js.map +1 -0
- package/dist/calendar.d.ts +0 -19
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js +4 -44
- package/dist/calendar.js.map +1 -1
- package/dist/canvas-width.d.ts +23 -0
- package/dist/canvas-width.d.ts.map +1 -0
- package/dist/canvas-width.js +26 -0
- package/dist/canvas-width.js.map +1 -0
- package/dist/capacity.d.ts +28 -0
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +64 -29
- package/dist/capacity.js.map +1 -1
- package/dist/chart-translation.d.ts +22 -0
- package/dist/chart-translation.d.ts.map +1 -0
- package/dist/chart-translation.js +83 -0
- package/dist/chart-translation.js.map +1 -0
- package/dist/container-header-geometry.d.ts +64 -0
- package/dist/container-header-geometry.d.ts.map +1 -0
- package/dist/container-header-geometry.js +89 -0
- package/dist/container-header-geometry.js.map +1 -0
- package/dist/dsl-utils.d.ts +3 -4
- package/dist/dsl-utils.d.ts.map +1 -1
- package/dist/dsl-utils.js +8 -25
- package/dist/dsl-utils.js.map +1 -1
- package/dist/edge-routing.d.ts +69 -78
- package/dist/edge-routing.d.ts.map +1 -1
- package/dist/edge-routing.js +271 -401
- package/dist/edge-routing.js.map +1 -1
- package/dist/frame-tab-geometry.d.ts +24 -14
- package/dist/frame-tab-geometry.d.ts.map +1 -1
- package/dist/frame-tab-geometry.js +69 -23
- package/dist/frame-tab-geometry.js.map +1 -1
- package/dist/header-card-geometry.d.ts +12 -0
- package/dist/header-card-geometry.d.ts.map +1 -1
- package/dist/header-card-geometry.js +14 -2
- package/dist/header-card-geometry.js.map +1 -1
- package/dist/header-logo.d.ts +28 -0
- package/dist/header-logo.d.ts.map +1 -0
- package/dist/header-logo.js +51 -0
- package/dist/header-logo.js.map +1 -0
- package/dist/i18n.d.ts +11 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +11 -1
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts +20 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/inline-date-pin-geometry.d.ts +19 -11
- package/dist/inline-date-pin-geometry.d.ts.map +1 -1
- package/dist/inline-date-pin-geometry.js +45 -46
- package/dist/inline-date-pin-geometry.js.map +1 -1
- package/dist/item-bar-geometry.d.ts +192 -4
- package/dist/item-bar-geometry.d.ts.map +1 -1
- package/dist/item-bar-geometry.js +258 -5
- package/dist/item-bar-geometry.js.map +1 -1
- package/dist/item-port-geometry.d.ts +13 -0
- package/dist/item-port-geometry.d.ts.map +1 -0
- package/dist/item-port-geometry.js +63 -0
- package/dist/item-port-geometry.js.map +1 -0
- package/dist/layout-context.d.ts +115 -51
- package/dist/layout-context.d.ts.map +1 -1
- package/dist/layout-context.js +96 -5
- package/dist/layout-context.js.map +1 -1
- package/dist/layout-insights.d.ts.map +1 -1
- package/dist/layout-insights.js +73 -1
- package/dist/layout-insights.js.map +1 -1
- package/dist/layout.d.ts +35 -0
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +18 -1315
- package/dist/layout.js.map +1 -1
- package/dist/nodes/anchor-node.d.ts.map +1 -1
- package/dist/nodes/anchor-node.js +11 -4
- package/dist/nodes/anchor-node.js.map +1 -1
- package/dist/nodes/content-extent.d.ts +24 -0
- package/dist/nodes/content-extent.d.ts.map +1 -0
- package/dist/nodes/content-extent.js +100 -0
- package/dist/nodes/content-extent.js.map +1 -0
- package/dist/nodes/dependency-edges.d.ts +6 -0
- package/dist/nodes/dependency-edges.d.ts.map +1 -0
- package/dist/nodes/dependency-edges.js +148 -0
- package/dist/nodes/dependency-edges.js.map +1 -0
- package/dist/nodes/footnote-node.d.ts +24 -3
- package/dist/nodes/footnote-node.d.ts.map +1 -1
- package/dist/nodes/footnote-node.js +129 -25
- package/dist/nodes/footnote-node.js.map +1 -1
- package/dist/nodes/group-node.d.ts +10 -21
- package/dist/nodes/group-node.d.ts.map +1 -1
- package/dist/nodes/group-node.js +79 -154
- package/dist/nodes/group-node.js.map +1 -1
- package/dist/nodes/header-node.d.ts +56 -0
- package/dist/nodes/header-node.d.ts.map +1 -0
- package/dist/nodes/header-node.js +163 -0
- package/dist/nodes/header-node.js.map +1 -0
- package/dist/nodes/include-node.d.ts +22 -12
- package/dist/nodes/include-node.d.ts.map +1 -1
- package/dist/nodes/include-node.js +95 -62
- package/dist/nodes/include-node.js.map +1 -1
- package/dist/nodes/item-measure.d.ts +135 -0
- package/dist/nodes/item-measure.d.ts.map +1 -0
- package/dist/nodes/item-measure.js +514 -0
- package/dist/nodes/item-measure.js.map +1 -0
- package/dist/nodes/item-node.d.ts +162 -9
- package/dist/nodes/item-node.d.ts.map +1 -1
- package/dist/nodes/item-node.js +256 -69
- package/dist/nodes/item-node.js.map +1 -1
- package/dist/nodes/marker-band.d.ts +118 -0
- package/dist/nodes/marker-band.d.ts.map +1 -0
- package/dist/nodes/marker-band.js +303 -0
- package/dist/nodes/marker-band.js.map +1 -0
- package/dist/nodes/marker-geometry.js +1 -1
- package/dist/nodes/marker-geometry.js.map +1 -1
- package/dist/nodes/milestone-node.d.ts +112 -15
- package/dist/nodes/milestone-node.d.ts.map +1 -1
- package/dist/nodes/milestone-node.js +246 -95
- package/dist/nodes/milestone-node.js.map +1 -1
- package/dist/nodes/nowline-node.d.ts +8 -0
- package/dist/nodes/nowline-node.d.ts.map +1 -0
- package/dist/nodes/nowline-node.js +74 -0
- package/dist/nodes/nowline-node.js.map +1 -0
- package/dist/nodes/parallel-node.d.ts +7 -12
- package/dist/nodes/parallel-node.d.ts.map +1 -1
- package/dist/nodes/parallel-node.js +66 -32
- package/dist/nodes/parallel-node.js.map +1 -1
- package/dist/nodes/region-markers.d.ts +41 -0
- package/dist/nodes/region-markers.d.ts.map +1 -0
- package/dist/nodes/region-markers.js +130 -0
- package/dist/nodes/region-markers.js.map +1 -0
- package/dist/nodes/roadmap-node.d.ts +9 -72
- package/dist/nodes/roadmap-node.d.ts.map +1 -1
- package/dist/nodes/roadmap-node.js +38 -788
- package/dist/nodes/roadmap-node.js.map +1 -1
- package/dist/nodes/row-track.d.ts +33 -0
- package/dist/nodes/row-track.d.ts.map +1 -0
- package/dist/nodes/row-track.js +170 -0
- package/dist/nodes/row-track.js.map +1 -0
- package/dist/nodes/sequence-item.d.ts +25 -0
- package/dist/nodes/sequence-item.d.ts.map +1 -0
- package/dist/nodes/sequence-item.js +396 -0
- package/dist/nodes/sequence-item.js.map +1 -0
- package/dist/nodes/sequence-one.d.ts +5 -0
- package/dist/nodes/sequence-one.d.ts.map +1 -0
- package/dist/nodes/sequence-one.js +26 -0
- package/dist/nodes/sequence-one.js.map +1 -0
- package/dist/nodes/swimlane-node.d.ts +5 -17
- package/dist/nodes/swimlane-node.d.ts.map +1 -1
- package/dist/nodes/swimlane-node.js +62 -147
- package/dist/nodes/swimlane-node.js.map +1 -1
- package/dist/nodes/wave-node.d.ts +52 -0
- package/dist/nodes/wave-node.d.ts.map +1 -0
- package/dist/nodes/wave-node.js +483 -0
- package/dist/nodes/wave-node.js.map +1 -0
- package/dist/non-working-display.d.ts +14 -0
- package/dist/non-working-display.d.ts.map +1 -0
- package/dist/non-working-display.js +28 -0
- package/dist/non-working-display.js.map +1 -0
- package/dist/phases/date-window.d.ts +48 -0
- package/dist/phases/date-window.d.ts.map +1 -0
- package/dist/phases/date-window.js +130 -0
- package/dist/phases/date-window.js.map +1 -0
- package/dist/phases/finalize.d.ts +198 -0
- package/dist/phases/finalize.d.ts.map +1 -0
- package/dist/phases/finalize.js +1058 -0
- package/dist/phases/finalize.js.map +1 -0
- package/dist/phases/measure.d.ts +77 -0
- package/dist/phases/measure.d.ts.map +1 -0
- package/dist/phases/measure.js +222 -0
- package/dist/phases/measure.js.map +1 -0
- package/dist/phases/place.d.ts +28 -0
- package/dist/phases/place.d.ts.map +1 -0
- package/dist/phases/place.js +225 -0
- package/dist/phases/place.js.map +1 -0
- package/dist/renderable.d.ts.map +1 -1
- package/dist/renderable.js +23 -3
- package/dist/renderable.js.map +1 -1
- package/dist/resolve-today.d.ts +11 -1
- package/dist/resolve-today.d.ts.map +1 -1
- package/dist/resolve-today.js +26 -12
- package/dist/resolve-today.js.map +1 -1
- package/dist/row-packer.d.ts +42 -16
- package/dist/row-packer.d.ts.map +1 -1
- package/dist/row-packer.js +51 -78
- package/dist/row-packer.js.map +1 -1
- package/dist/schedule.d.ts +58 -1
- package/dist/schedule.d.ts.map +1 -1
- package/dist/schedule.js +280 -106
- package/dist/schedule.js.map +1 -1
- package/dist/start-rules.d.ts +27 -0
- package/dist/start-rules.d.ts.map +1 -0
- package/dist/start-rules.js +91 -0
- package/dist/start-rules.js.map +1 -0
- package/dist/start-terms.d.ts +23 -0
- package/dist/start-terms.d.ts.map +1 -0
- package/dist/start-terms.js +27 -0
- package/dist/start-terms.js.map +1 -0
- package/dist/status-dot.d.ts +46 -0
- package/dist/status-dot.d.ts.map +1 -0
- package/dist/status-dot.js +78 -0
- package/dist/status-dot.js.map +1 -0
- package/dist/style-resolution.d.ts.map +1 -1
- package/dist/style-resolution.js +23 -43
- package/dist/style-resolution.js.map +1 -1
- package/dist/text-measure.d.ts +28 -0
- package/dist/text-measure.d.ts.map +1 -0
- package/dist/text-measure.js +70 -0
- package/dist/text-measure.js.map +1 -0
- package/dist/themes/contrast.d.ts +4 -0
- package/dist/themes/contrast.d.ts.map +1 -0
- package/dist/themes/contrast.js +32 -0
- package/dist/themes/contrast.js.map +1 -0
- package/dist/themes/dark.d.ts.map +1 -1
- package/dist/themes/dark.js +28 -1
- package/dist/themes/dark.js.map +1 -1
- package/dist/themes/grayscale.d.ts.map +1 -1
- package/dist/themes/grayscale.js +28 -1
- package/dist/themes/grayscale.js.map +1 -1
- package/dist/themes/index.d.ts +4 -11
- package/dist/themes/index.d.ts.map +1 -1
- package/dist/themes/index.js +6 -4
- package/dist/themes/index.js.map +1 -1
- package/dist/themes/light.d.ts.map +1 -1
- package/dist/themes/light.js +28 -1
- package/dist/themes/light.js.map +1 -1
- package/dist/themes/shape.d.ts +31 -0
- package/dist/themes/shape.d.ts.map +1 -1
- package/dist/themes/shared.d.ts +44 -50
- package/dist/themes/shared.d.ts.map +1 -1
- package/dist/themes/shared.js +79 -83
- package/dist/themes/shared.js.map +1 -1
- package/dist/time-scale.d.ts +42 -6
- package/dist/time-scale.d.ts.map +1 -1
- package/dist/time-scale.js +109 -17
- package/dist/time-scale.js.map +1 -1
- package/dist/types.d.ts +528 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/view-preset.d.ts +61 -6
- package/dist/view-preset.d.ts.map +1 -1
- package/dist/view-preset.js +439 -24
- package/dist/view-preset.js.map +1 -1
- package/dist/wave-barrier.d.ts +107 -0
- package/dist/wave-barrier.d.ts.map +1 -0
- package/dist/wave-barrier.js +174 -0
- package/dist/wave-barrier.js.map +1 -0
- package/dist/wave-hatch.d.ts +4 -0
- package/dist/wave-hatch.d.ts.map +1 -0
- package/dist/wave-hatch.js +21 -0
- package/dist/wave-hatch.js.map +1 -0
- package/dist/wave-layout.d.ts +99 -0
- package/dist/wave-layout.d.ts.map +1 -0
- package/dist/wave-layout.js +130 -0
- package/dist/wave-layout.js.map +1 -0
- package/dist/working-calendar.d.ts +128 -4
- package/dist/working-calendar.d.ts.map +1 -1
- package/dist/working-calendar.js +725 -51
- package/dist/working-calendar.js.map +1 -1
- package/dist/working-span.d.ts +11 -0
- package/dist/working-span.d.ts.map +1 -0
- package/dist/working-span.js +18 -0
- package/dist/working-span.js.map +1 -0
- package/package.json +7 -7
- package/src/axis-columns.ts +140 -0
- package/src/calendar-resolver.ts +161 -0
- package/src/calendar.ts +4 -45
- package/src/canvas-width.ts +36 -0
- package/src/capacity.ts +71 -30
- package/src/chart-translation.ts +88 -0
- package/src/container-header-geometry.ts +116 -0
- package/src/dsl-utils.ts +8 -26
- package/src/edge-routing.ts +347 -491
- package/src/frame-tab-geometry.ts +78 -25
- package/src/header-card-geometry.ts +17 -2
- package/src/header-logo.ts +67 -0
- package/src/i18n.ts +22 -1
- package/src/index.ts +97 -12
- package/src/inline-date-pin-geometry.ts +55 -58
- package/src/item-bar-geometry.ts +341 -5
- package/src/item-port-geometry.ts +69 -0
- package/src/layout-context.ts +210 -72
- package/src/layout-insights.ts +109 -2
- package/src/layout.ts +57 -1530
- package/src/nodes/anchor-node.ts +11 -4
- package/src/nodes/content-extent.ts +107 -0
- package/src/nodes/dependency-edges.ts +171 -0
- package/src/nodes/footnote-node.ts +171 -31
- package/src/nodes/group-node.ts +92 -215
- package/src/nodes/header-node.ts +255 -0
- package/src/nodes/include-node.ts +124 -110
- package/src/nodes/item-measure.ts +723 -0
- package/src/nodes/item-node.ts +358 -80
- package/src/nodes/marker-band.ts +406 -0
- package/src/nodes/marker-geometry.ts +1 -1
- package/src/nodes/milestone-node.ts +321 -136
- package/src/nodes/nowline-node.ts +87 -0
- package/src/nodes/parallel-node.ts +69 -39
- package/src/nodes/region-markers.ts +194 -0
- package/src/nodes/roadmap-node.ts +46 -950
- package/src/nodes/row-track.ts +217 -0
- package/src/nodes/sequence-item.ts +433 -0
- package/src/nodes/sequence-one.ts +40 -0
- package/src/nodes/swimlane-node.ts +66 -206
- package/src/nodes/wave-node.ts +609 -0
- package/src/non-working-display.ts +32 -0
- package/src/phases/date-window.ts +191 -0
- package/src/phases/finalize.ts +1303 -0
- package/src/phases/measure.ts +329 -0
- package/src/phases/place.ts +318 -0
- package/src/renderable.ts +23 -3
- package/src/resolve-today.ts +28 -25
- package/src/row-packer.ts +85 -84
- package/src/schedule.ts +362 -114
- package/src/start-rules.ts +113 -0
- package/src/start-terms.ts +38 -0
- package/src/status-dot.ts +81 -0
- package/src/style-resolution.ts +27 -57
- package/src/text-measure.ts +71 -0
- package/src/themes/contrast.ts +34 -0
- package/src/themes/dark.ts +28 -1
- package/src/themes/grayscale.ts +28 -1
- package/src/themes/index.ts +10 -7
- package/src/themes/light.ts +28 -1
- package/src/themes/shape.ts +38 -0
- package/src/themes/shared.ts +85 -93
- package/src/time-scale.ts +117 -19
- package/src/types.ts +529 -20
- package/src/view-preset.ts +520 -27
- package/src/wave-barrier.ts +242 -0
- package/src/wave-hatch.ts +25 -0
- package/src/wave-layout.ts +203 -0
- package/src/working-calendar.ts +875 -57
- package/src/working-span.ts +19 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// sequenceOne — the dispatcher a track calls for each child: an item goes
|
|
2
|
+
// to `sequenceItem`, a parallel block to `ParallelNode`, a group to
|
|
3
|
+
// `GroupNode`. It is the one helper the nodes still take injected
|
|
4
|
+
// (`LayoutHelpers`): the group and parallel nodes call it for their nested
|
|
5
|
+
// blocks, so importing it there would form a cycle (specs/layout-phases.md
|
|
6
|
+
// §8, decision 4).
|
|
7
|
+
|
|
8
|
+
import type { GroupBlock, ItemDeclaration, ParallelBlock } from '@nowline/core';
|
|
9
|
+
import { isGroupBlock, isItemDeclaration, isParallelBlock } from '@nowline/core';
|
|
10
|
+
import type { LayoutContext, TrackCursor } from '../layout-context.js';
|
|
11
|
+
import type { PositionedGroup, PositionedParallel, PositionedTrackChild } from '../types.js';
|
|
12
|
+
import { GroupNode } from './group-node.js';
|
|
13
|
+
import { ParallelNode } from './parallel-node.js';
|
|
14
|
+
|
|
15
|
+
import { sequenceItem } from './sequence-item.js';
|
|
16
|
+
|
|
17
|
+
function sequenceParallel(
|
|
18
|
+
node: ParallelBlock,
|
|
19
|
+
cursor: TrackCursor,
|
|
20
|
+
ctx: LayoutContext,
|
|
21
|
+
): PositionedParallel {
|
|
22
|
+
return new ParallelNode(node, { sequenceOne }).place(cursor, ctx);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function sequenceGroup(node: GroupBlock, cursor: TrackCursor, ctx: LayoutContext): PositionedGroup {
|
|
26
|
+
return new GroupNode(node, { sequenceOne }).place(cursor, ctx);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function sequenceOne(
|
|
30
|
+
node: ItemDeclaration | GroupBlock | ParallelBlock,
|
|
31
|
+
cursor: TrackCursor,
|
|
32
|
+
ctx: LayoutContext,
|
|
33
|
+
): PositionedTrackChild {
|
|
34
|
+
if (isItemDeclaration(node)) return sequenceItem(node, cursor, ctx);
|
|
35
|
+
if (isParallelBlock(node)) return sequenceParallel(node, cursor, ctx);
|
|
36
|
+
if (isGroupBlock(node)) return sequenceGroup(node, cursor, ctx);
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Unknown swimlane child type: ${(node as { $type?: string }).$type ?? 'unknown'}`,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -4,29 +4,21 @@
|
|
|
4
4
|
// fresh rows) and emits a `PositionedSwimlane` plus the band's used
|
|
5
5
|
// height.
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// their own nodes land in step 3.
|
|
14
|
-
// - The Renderable interface's `measure(MeasureContext)` is not yet
|
|
15
|
-
// wired through the production pipeline. The composition-root work
|
|
16
|
-
// in step 5 (`RoadmapNode`) will exercise it; meanwhile the legacy
|
|
17
|
-
// two-pass "buildSwimlane returns positioned + usedHeight" shape is
|
|
18
|
-
// preserved.
|
|
7
|
+
// The lane's children go through the shared row track (`row-track.ts`):
|
|
8
|
+
// items are measured and placed by `sequenceItem`, and parallel and group
|
|
9
|
+
// children by the injected `helpers.sequenceOne`. A lane has no separate
|
|
10
|
+
// measure pass: its height is the result of packing its rows, so
|
|
11
|
+
// `place` returns the positioned lane with its used height
|
|
12
|
+
// (specs/layout-phases.md §2, non-goals).
|
|
19
13
|
|
|
20
14
|
import type {
|
|
21
|
-
EntityProperty,
|
|
22
15
|
GroupBlock,
|
|
23
16
|
ItemDeclaration,
|
|
24
17
|
ParallelBlock,
|
|
25
18
|
SwimlaneDeclaration,
|
|
26
19
|
} from '@nowline/core';
|
|
27
|
-
import { isItemDeclaration } from '@nowline/core';
|
|
28
|
-
import { deriveItemDurationDays } from '../calendar.js';
|
|
29
20
|
import {
|
|
21
|
+
capacityIconDxTerms,
|
|
30
22
|
estimateCapacitySuffixWidth,
|
|
31
23
|
formatCapacityNumber,
|
|
32
24
|
parseCapacityValue,
|
|
@@ -35,17 +27,12 @@ import {
|
|
|
35
27
|
import { propValue } from '../dsl-utils.js';
|
|
36
28
|
import { frameTabGeometry } from '../frame-tab-geometry.js';
|
|
37
29
|
import { computeLaneUtilization, resolveLaneUtilizationThresholds } from '../lane-utilization.js';
|
|
38
|
-
import type { LayoutContext,
|
|
39
|
-
import {
|
|
30
|
+
import type { LayoutContext, LayoutHelpers } from '../layout-context.js';
|
|
31
|
+
import { resolveChildStart } from '../start-rules.js';
|
|
40
32
|
import { resolveStyle } from '../style-resolution.js';
|
|
41
|
-
import {
|
|
42
|
-
import type {
|
|
43
|
-
|
|
44
|
-
PositionedCapacity,
|
|
45
|
-
PositionedItem,
|
|
46
|
-
PositionedSwimlane,
|
|
47
|
-
PositionedTrackChild,
|
|
48
|
-
} from '../types.js';
|
|
33
|
+
import { estimateTextWidth } from '../text-measure.js';
|
|
34
|
+
import type { BoundingBox, PositionedCapacity, PositionedSwimlane } from '../types.js';
|
|
35
|
+
import { sequenceRowTrack } from './row-track.js';
|
|
49
36
|
|
|
50
37
|
/**
|
|
51
38
|
* Font size (px) the renderer uses for the lane capacity badge. Mirrors
|
|
@@ -73,39 +60,13 @@ function resolveLaneCapacity(
|
|
|
73
60
|
const text = formatCapacityNumber(value);
|
|
74
61
|
const icon = resolveCapacityIcon(style.capacityIcon, symbols);
|
|
75
62
|
const badgeWidthPx = estimateCapacitySuffixWidth(text, icon, LANE_CAPACITY_BADGE_FONT_SIZE_PX);
|
|
63
|
+
const iconDxTerms = capacityIconDxTerms(text, icon, LANE_CAPACITY_BADGE_FONT_SIZE_PX);
|
|
76
64
|
return {
|
|
77
|
-
capacity: { value, text, icon },
|
|
65
|
+
capacity: { value, text, icon, ...(iconDxTerms !== undefined ? { iconDxTerms } : {}) },
|
|
78
66
|
badgeWidthPx,
|
|
79
67
|
};
|
|
80
68
|
}
|
|
81
69
|
|
|
82
|
-
/** Helpers that SwimlaneNode delegates to until the rest of m2.5c lands. */
|
|
83
|
-
export interface SwimlaneNodeDeps {
|
|
84
|
-
sequenceItem: (
|
|
85
|
-
child: ItemDeclaration,
|
|
86
|
-
cursor: TrackCursor,
|
|
87
|
-
ctx: LayoutContext,
|
|
88
|
-
ownerOverride?: string,
|
|
89
|
-
) => PositionedItem;
|
|
90
|
-
sequenceOne: (
|
|
91
|
-
child: ItemDeclaration | GroupBlock | ParallelBlock,
|
|
92
|
-
cursor: TrackCursor,
|
|
93
|
-
ctx: LayoutContext,
|
|
94
|
-
) => PositionedTrackChild;
|
|
95
|
-
resolveChildStart: (
|
|
96
|
-
props: EntityProperty[],
|
|
97
|
-
seqDefault: number,
|
|
98
|
-
laneLeftX: number,
|
|
99
|
-
ctx: LayoutContext,
|
|
100
|
-
) => number;
|
|
101
|
-
newCursor: (x: number, y: number) => TrackCursor;
|
|
102
|
-
estimateTextWidth: (text: string, fontSize: number) => number;
|
|
103
|
-
/** Predict the extra vertical height an item's wrapped label-chip
|
|
104
|
-
* rows will add to its bar; used to size the row-packer's row
|
|
105
|
-
* pitch ahead of the call to `sequenceItem`. */
|
|
106
|
-
predictItemChipExtraHeight: (item: ItemDeclaration, ctx: LayoutContext) => number;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
70
|
export interface SwimlaneNodeInput {
|
|
110
71
|
lane: SwimlaneDeclaration;
|
|
111
72
|
bandIndex: number;
|
|
@@ -129,30 +90,21 @@ const TAB_BOTTOM_Y = 38; // tab (height 22) plus 6 px breathing room
|
|
|
129
90
|
const TAB_GUTTER_PX = 8;
|
|
130
91
|
|
|
131
92
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* exactly in sync. Capacity badge and footnote-indicator widths are
|
|
136
|
-
* added when present so the first-item collision math reserves
|
|
137
|
-
* enough horizontal space for the (now-wider) chiclet.
|
|
93
|
+
* The lane's owner as the frame tab shows it: the person's or team's
|
|
94
|
+
* title (nested teams included), else the bare id, plus which glyph leads
|
|
95
|
+
* it. Undefined without `owner:`.
|
|
138
96
|
*/
|
|
139
|
-
function
|
|
97
|
+
function resolveLaneOwner(
|
|
140
98
|
lane: SwimlaneDeclaration,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
): number {
|
|
144
|
-
const title = lane.title ?? lane.name ?? '';
|
|
145
|
-
if (!title) return 0;
|
|
99
|
+
ctx: LayoutContext,
|
|
100
|
+
): { text: string; kind: 'person' | 'team' } | undefined {
|
|
146
101
|
const ownerRaw = propValue(lane.properties, 'owner');
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
capacityBadgeWidthPx,
|
|
154
|
-
footnoteIndicatorWidthPx,
|
|
155
|
-
).rightX;
|
|
102
|
+
if (!ownerRaw) return undefined;
|
|
103
|
+
const team = ctx.teams.get(ownerRaw);
|
|
104
|
+
if (team) return { text: team.title ?? ownerRaw, kind: 'team' };
|
|
105
|
+
const person = ctx.persons.get(ownerRaw);
|
|
106
|
+
if (person) return { text: person.title ?? ownerRaw, kind: 'person' };
|
|
107
|
+
return { text: ownerRaw, kind: 'team' };
|
|
156
108
|
}
|
|
157
109
|
|
|
158
110
|
/**
|
|
@@ -184,14 +136,17 @@ function firstChildStartX(
|
|
|
184
136
|
lane: SwimlaneDeclaration,
|
|
185
137
|
laneLeftX: number,
|
|
186
138
|
ctx: LayoutContext,
|
|
187
|
-
deps: Pick<SwimlaneNodeDeps, 'resolveChildStart'>,
|
|
188
139
|
): number | undefined {
|
|
189
140
|
for (const child of lane.content) {
|
|
190
141
|
if (child.$type === 'DescriptionDirective') continue;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
142
|
+
// Snapped like the start it predicts (a no-op under `hide`).
|
|
143
|
+
return ctx.scale.startX(
|
|
144
|
+
resolveChildStart(
|
|
145
|
+
child as ItemDeclaration | GroupBlock | ParallelBlock,
|
|
146
|
+
laneLeftX,
|
|
147
|
+
ctx,
|
|
148
|
+
),
|
|
149
|
+
);
|
|
195
150
|
}
|
|
196
151
|
return undefined;
|
|
197
152
|
}
|
|
@@ -199,8 +154,7 @@ function firstChildStartX(
|
|
|
199
154
|
export class SwimlaneNode {
|
|
200
155
|
constructor(
|
|
201
156
|
public readonly input: SwimlaneNodeInput,
|
|
202
|
-
|
|
203
|
-
private readonly deps: SwimlaneNodeDeps,
|
|
157
|
+
private readonly helpers: LayoutHelpers,
|
|
204
158
|
) {}
|
|
205
159
|
|
|
206
160
|
get id(): string {
|
|
@@ -209,7 +163,7 @@ export class SwimlaneNode {
|
|
|
209
163
|
|
|
210
164
|
place(origin: SwimlaneOrigin, ctx: LayoutContext): PlacedSwimlaneGeometry {
|
|
211
165
|
const { lane, bandIndex } = this.input;
|
|
212
|
-
const {
|
|
166
|
+
const { helpers } = this;
|
|
213
167
|
const style = resolveStyle('swimlane', lane.properties, ctx.styleCtx);
|
|
214
168
|
const laneLeftX = origin.x;
|
|
215
169
|
const step = ctx.bandScale.step();
|
|
@@ -234,153 +188,57 @@ export class SwimlaneNode {
|
|
|
234
188
|
const footnoteIndicators = collectFootnoteIndicators(lane, ctx);
|
|
235
189
|
const footnoteIndicatorWidthPx =
|
|
236
190
|
footnoteIndicators.length > 0
|
|
237
|
-
?
|
|
238
|
-
footnoteIndicators.join(','),
|
|
239
|
-
LANE_CAPACITY_BADGE_FONT_SIZE_PX,
|
|
240
|
-
)
|
|
191
|
+
? estimateTextWidth(footnoteIndicators.join(','), LANE_CAPACITY_BADGE_FONT_SIZE_PX)
|
|
241
192
|
: 0;
|
|
242
|
-
// Title-tab geometry
|
|
243
|
-
|
|
193
|
+
// Title-tab geometry, computed once from the owner's display
|
|
194
|
+
// title: the collision box below and the chiclet the renderer
|
|
195
|
+
// draws (`PositionedSwimlane.tab`) are the same rectangle. Every
|
|
196
|
+
// lane box sits at x 0 (`box` below); finalize sets only widths.
|
|
197
|
+
const boxX = 0;
|
|
198
|
+
const owner = resolveLaneOwner(lane, ctx);
|
|
199
|
+
const title = lane.title ?? lane.name ?? '';
|
|
200
|
+
const tab = title
|
|
201
|
+
? frameTabGeometry(boxX, title, owner?.text, badgeWidthPx, footnoteIndicatorWidthPx)
|
|
202
|
+
: null;
|
|
203
|
+
const tabRightX = tab?.rightX ?? 0;
|
|
244
204
|
// First-row Y: when the first child's desired x is past the title
|
|
245
205
|
// tab, top-align with the tab and reclaim ~28 px per lane.
|
|
246
206
|
// Otherwise drop below the tab.
|
|
247
|
-
const firstChildDesiredX = firstChildStartX(lane, laneLeftX, ctx
|
|
207
|
+
const firstChildDesiredX = firstChildStartX(lane, laneLeftX, ctx);
|
|
248
208
|
const canAlignFirstRowWithTab =
|
|
249
209
|
!lane.title ||
|
|
250
210
|
firstChildDesiredX === undefined ||
|
|
251
211
|
firstChildDesiredX >= tabRightX + TAB_GUTTER_PX;
|
|
252
212
|
const startY = origin.y + (canAlignFirstRowWithTab ? TAB_TOP_Y : TAB_BOTTOM_Y);
|
|
253
213
|
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
const
|
|
258
|
-
laneLeftX,
|
|
259
|
-
originY: startY,
|
|
260
|
-
minRowHeight: step,
|
|
261
|
-
slackCorridors: ctx.slackCorridors,
|
|
262
|
-
});
|
|
263
|
-
let timeCursorX = laneLeftX;
|
|
264
|
-
|
|
265
|
-
const children: PositionedTrackChild[] = [];
|
|
266
|
-
for (const child of lane.content) {
|
|
267
|
-
if (child.$type === 'DescriptionDirective') continue;
|
|
268
|
-
|
|
269
|
-
if (!isItemDeclaration(child)) {
|
|
270
|
-
const blockProps = (child as ParallelBlock | GroupBlock).properties ?? [];
|
|
271
|
-
const blockStart = deps.resolveChildStart(blockProps, timeCursorX, laneLeftX, ctx);
|
|
272
|
-
const { rowIndex, y: blockY } = packer.placeBlock();
|
|
273
|
-
const cursor = deps.newCursor(blockStart, blockY);
|
|
274
|
-
const positioned = deps.sequenceOne(
|
|
275
|
-
child as ItemDeclaration | GroupBlock | ParallelBlock,
|
|
276
|
-
cursor,
|
|
277
|
-
ctx,
|
|
278
|
-
);
|
|
279
|
-
children.push(positioned);
|
|
280
|
-
const blockEnd = positioned.box.x + positioned.box.width;
|
|
281
|
-
const blockHeight = Math.max(step, cursor.height);
|
|
282
|
-
packer.commitBlock({
|
|
283
|
-
rowIndex,
|
|
284
|
-
placed: positioned,
|
|
285
|
-
blockHeight,
|
|
286
|
-
blockEnd,
|
|
287
|
-
});
|
|
288
|
-
timeCursorX = Math.max(timeCursorX, blockEnd);
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const props = (child as ItemDeclaration).properties;
|
|
293
|
-
const desiredStart = deps.resolveChildStart(props, timeCursorX, laneLeftX, ctx);
|
|
294
|
-
// Predict the item's logical extent so the row-pack can decide
|
|
295
|
-
// BEFORE handing off to sequenceItem. The arithmetic mirrors
|
|
296
|
-
// the duration → width math in `sequenceItem` (see
|
|
297
|
-
// packages/layout/src/layout.ts).
|
|
298
|
-
const durationDays = deriveItemDurationDays(props, ctx.sizes, ctx.cal);
|
|
299
|
-
const naturalWidth = Math.max(MIN_ITEM_WIDTH, durationDays * ctx.timeline.pixelsPerDay);
|
|
300
|
-
const desiredEnd = desiredStart + naturalWidth;
|
|
301
|
-
const childId = (child as ItemDeclaration).name ?? '';
|
|
214
|
+
// The shared row-track loop (row-track.ts) packs the lane's
|
|
215
|
+
// children below the tab offset chosen above.
|
|
216
|
+
const track = sequenceRowTrack(lane, { leftX: laneLeftX, firstRowY: startY }, ctx, helpers);
|
|
217
|
+
const { children } = track;
|
|
302
218
|
|
|
303
|
-
|
|
304
|
-
const predictedHeight = step + chipExtra;
|
|
305
|
-
const { rowIndex, y: rowY } = packer.placeItem({
|
|
306
|
-
childId,
|
|
307
|
-
desiredStart,
|
|
308
|
-
desiredEnd,
|
|
309
|
-
// Row pitch = `step()` + extra chip-row height. Keeps
|
|
310
|
-
// the inter-row visible gap (= step - bandwidth) intact
|
|
311
|
-
// when an item's labels wrap and grow the bar.
|
|
312
|
-
predictedHeight,
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
const cursor = deps.newCursor(desiredStart, rowY);
|
|
316
|
-
const positioned = deps.sequenceItem(child as ItemDeclaration, cursor, ctx);
|
|
317
|
-
children.push(positioned);
|
|
318
|
-
|
|
319
|
-
// Item end in LOGICAL space (one ITEM_INSET_PX past the visual
|
|
320
|
-
// bar's right edge). The next chained item starts here and
|
|
321
|
-
// lands edge-to-edge in time, with a 2 × ITEM_INSET_PX visible
|
|
322
|
-
// gutter between bars.
|
|
323
|
-
const itemLogicalEnd = positioned.box.x + positioned.box.width + ITEM_INSET_PX;
|
|
324
|
-
timeCursorX = Math.max(timeCursorX, itemLogicalEnd);
|
|
325
|
-
|
|
326
|
-
let spillReservation: number | null = null;
|
|
327
|
-
const hasAnySpill =
|
|
328
|
-
positioned.textSpills ||
|
|
329
|
-
positioned.chipsOutside ||
|
|
330
|
-
positioned.dotSpills ||
|
|
331
|
-
positioned.iconSpills ||
|
|
332
|
-
positioned.footnoteSpills;
|
|
333
|
-
if (hasAnySpill) {
|
|
334
|
-
// `decorationsRightX` already aggregates the spilled
|
|
335
|
-
// dot / icon / footnote / caption right edges from
|
|
336
|
-
// `sequenceItem`; the chip column tracks separately
|
|
337
|
-
// via `chipsRightX`. Add a 6-px buffer so the next
|
|
338
|
-
// chained item's bar leaves a visible gutter past
|
|
339
|
-
// the spilled cluster instead of butting against it.
|
|
340
|
-
const farRight = Math.max(
|
|
341
|
-
positioned.decorationsRightX,
|
|
342
|
-
positioned.chipsOutside ? positioned.chipsRightX : 0,
|
|
343
|
-
);
|
|
344
|
-
spillReservation = farRight + 6;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
packer.commitItem({
|
|
348
|
-
rowIndex,
|
|
349
|
-
placed: positioned,
|
|
350
|
-
logicalEnd: itemLogicalEnd,
|
|
351
|
-
spillReservation,
|
|
352
|
-
rowHeight: predictedHeight,
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// `packer.usedHeight()` measures from `startY` (the first row,
|
|
219
|
+
// `track.packer.usedHeight()` measures from `startY` (the first row,
|
|
357
220
|
// below the title tab); the swimlane band spans from `origin.y`
|
|
358
221
|
// (the band top, above the tab). Add the tab offset back so the
|
|
359
222
|
// band height covers everything the user sees.
|
|
360
223
|
const tabOffset = startY - origin.y;
|
|
361
|
-
const bandHeight = Math.max(step + 32, tabOffset + packer.usedHeight() + 16);
|
|
224
|
+
const bandHeight = Math.max(step + 32, tabOffset + track.packer.usedHeight() + 16);
|
|
362
225
|
// Include the chiclet's right edge in the lane's reported
|
|
363
226
|
// right-extent so empty lanes still report a non-zero right edge
|
|
364
227
|
// — `buildIncludeRegions` uses this to size the include's dashed
|
|
365
228
|
// bracket around its content, and a lane with only a chiclet
|
|
366
229
|
// (no items) should still fit visibly inside that bracket.
|
|
367
|
-
const usedRightX = Math.max(packer.usedRightX(), tabRightX);
|
|
230
|
+
const usedRightX = Math.max(track.packer.usedRightX(), tabRightX);
|
|
231
|
+
// The band spans the canvas, whose width is final only after the
|
|
232
|
+
// post-placement growth: finalize (`fitWidths`, f4) sets a main
|
|
233
|
+
// lane's width to it and `fitIncludeRegionWidth` a region lane's to
|
|
234
|
+
// the region's. Placement never reads the canvas.
|
|
368
235
|
const box: BoundingBox = {
|
|
369
|
-
x:
|
|
236
|
+
x: boxX,
|
|
370
237
|
y: origin.y,
|
|
371
|
-
width:
|
|
238
|
+
width: 0,
|
|
372
239
|
height: bandHeight,
|
|
373
240
|
};
|
|
374
241
|
|
|
375
|
-
// Owner display string: id → title for teams/people; falls back to id.
|
|
376
|
-
const ownerRaw = propValue(lane.properties, 'owner');
|
|
377
|
-
let ownerDisplay: string | undefined;
|
|
378
|
-
if (ownerRaw) {
|
|
379
|
-
const team = ctx.teams.get(ownerRaw);
|
|
380
|
-
const person = ctx.persons.get(ownerRaw);
|
|
381
|
-
ownerDisplay = team?.title ?? person?.title ?? ownerRaw;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
242
|
// Lane utilization underline (m12). Computed *after* children are
|
|
385
243
|
// positioned so the contributors carry their final box.x / box.width.
|
|
386
244
|
// Returns null when the lane has no `capacity:`, no contributing
|
|
@@ -405,15 +263,17 @@ export class SwimlaneNode {
|
|
|
405
263
|
return {
|
|
406
264
|
positioned: {
|
|
407
265
|
id: lane.name,
|
|
408
|
-
title
|
|
266
|
+
title,
|
|
409
267
|
box,
|
|
410
268
|
bandIndex,
|
|
411
269
|
children,
|
|
412
270
|
nested: [],
|
|
413
271
|
style,
|
|
414
|
-
owner:
|
|
272
|
+
owner: owner?.text,
|
|
273
|
+
ownerKind: owner?.kind,
|
|
415
274
|
footnoteIndicators,
|
|
416
275
|
capacity,
|
|
276
|
+
tab,
|
|
417
277
|
utilization,
|
|
418
278
|
},
|
|
419
279
|
usedHeight: bandHeight,
|