@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
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
export type SizeBucket = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
2
|
+
export type ShadowKind = 'none' | 'subtle' | 'soft' | 'hard';
|
|
3
|
+
export type BorderKind = 'solid' | 'dashed' | 'dotted';
|
|
4
|
+
export type FontFamily = 'sans' | 'serif' | 'mono';
|
|
5
|
+
export type FontWeight = 'thin' | 'light' | 'normal' | 'bold';
|
|
6
|
+
export type BracketKind = 'none' | 'solid' | 'dashed';
|
|
7
|
+
export type HeaderPosition = 'beside' | 'above';
|
|
8
|
+
export type TimelinePosition = 'top' | 'bottom' | 'both';
|
|
9
|
+
export type StatusKind = 'planned' | 'in-progress' | 'done' | 'at-risk' | 'blocked' | 'neutral';
|
|
10
|
+
export interface ResolvedStyle {
|
|
11
|
+
bg: string;
|
|
12
|
+
fg: string;
|
|
13
|
+
text: string;
|
|
14
|
+
border: BorderKind;
|
|
15
|
+
icon: string;
|
|
16
|
+
shadow: ShadowKind;
|
|
17
|
+
font: FontFamily;
|
|
18
|
+
weight: FontWeight;
|
|
19
|
+
italic: boolean;
|
|
20
|
+
textSize: SizeBucket;
|
|
21
|
+
padding: SizeBucket;
|
|
22
|
+
spacing: SizeBucket;
|
|
23
|
+
headerHeight: SizeBucket;
|
|
24
|
+
cornerRadius: SizeBucket;
|
|
25
|
+
bracket: BracketKind;
|
|
26
|
+
headerPosition: HeaderPosition;
|
|
27
|
+
/**
|
|
28
|
+
* Glyph used as the suffix on capacity numbers (`5×`, `5 [person]`, etc.).
|
|
29
|
+
* Stores the raw value as the author wrote it: a built-in icon name
|
|
30
|
+
* (`'multiplier'`, `'person'`, ...), a custom symbol id declared via
|
|
31
|
+
* `symbol` in config, or an inline Unicode literal (`'💰'`). The renderer
|
|
32
|
+
* resolves built-in vs custom vs literal at paint time using
|
|
33
|
+
* `ResolvedConfig.symbols` and the `BUILTIN_CAPACITY_ICONS` set.
|
|
34
|
+
* Default `'multiplier'`.
|
|
35
|
+
*/
|
|
36
|
+
capacityIcon: string;
|
|
37
|
+
timelinePosition: TimelinePosition;
|
|
38
|
+
minorGrid: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface BoundingBox {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Calendar-resolved view of a `size NAME ["TITLE"] effort:LITERAL`
|
|
48
|
+
* declaration. Built once per layout from the AST `SizeDeclaration`s
|
|
49
|
+
* collected by the include-resolver and the active calendar (which
|
|
50
|
+
* decides how many days a literal like `2w` maps to). Items reference
|
|
51
|
+
* these via `LayoutContext.sizes` rather than walking the AST +
|
|
52
|
+
* calendar each time they need an effort.
|
|
53
|
+
*
|
|
54
|
+
* `effortLiteral` keeps the raw literal so callers can show the size's
|
|
55
|
+
* effort string (`"2w"`) on tooltips without re-formatting from
|
|
56
|
+
* `effortDays`.
|
|
57
|
+
*
|
|
58
|
+
* `title` is the optional author-provided display label. The on-bar
|
|
59
|
+
* size chip prefers `title` when present, falling back to `name` (the
|
|
60
|
+
* id) verbatim — both rendered with the case the author typed. See
|
|
61
|
+
* specs/rendering.md § Item size chip.
|
|
62
|
+
*/
|
|
63
|
+
export interface ResolvedSize {
|
|
64
|
+
name: string;
|
|
65
|
+
title?: string;
|
|
66
|
+
effortDays: number;
|
|
67
|
+
effortLiteral: string;
|
|
68
|
+
}
|
|
69
|
+
export interface Point {
|
|
70
|
+
x: number;
|
|
71
|
+
y: number;
|
|
72
|
+
}
|
|
73
|
+
export interface PositionedLogo {
|
|
74
|
+
box: BoundingBox;
|
|
75
|
+
assetRef?: string;
|
|
76
|
+
}
|
|
77
|
+
export interface PositionedHeader {
|
|
78
|
+
box: BoundingBox;
|
|
79
|
+
position: HeaderPosition;
|
|
80
|
+
title: string;
|
|
81
|
+
author?: string;
|
|
82
|
+
titleLines: string[];
|
|
83
|
+
authorLines: string[];
|
|
84
|
+
cardBox: BoundingBox;
|
|
85
|
+
logo?: PositionedLogo;
|
|
86
|
+
style: ResolvedStyle;
|
|
87
|
+
attributionBox: BoundingBox;
|
|
88
|
+
}
|
|
89
|
+
export interface PositionedTick {
|
|
90
|
+
x: number;
|
|
91
|
+
labelX?: number;
|
|
92
|
+
label?: string;
|
|
93
|
+
major: boolean;
|
|
94
|
+
}
|
|
95
|
+
export interface PositionedTimelineScale {
|
|
96
|
+
box: BoundingBox;
|
|
97
|
+
ticks: PositionedTick[];
|
|
98
|
+
pixelsPerDay: number;
|
|
99
|
+
originX: number;
|
|
100
|
+
startDate: Date;
|
|
101
|
+
endDate: Date;
|
|
102
|
+
labelStyle: ResolvedStyle;
|
|
103
|
+
pillRowHeight: number;
|
|
104
|
+
tickPanelY: number;
|
|
105
|
+
tickPanelHeight: number;
|
|
106
|
+
markerRow: {
|
|
107
|
+
y: number;
|
|
108
|
+
height: number;
|
|
109
|
+
collisionY: number;
|
|
110
|
+
};
|
|
111
|
+
bottomTickPanelY?: number;
|
|
112
|
+
bottomTickPanelHeight?: number;
|
|
113
|
+
minorGrid: boolean;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* How the now-pill is positioned relative to the now-line.
|
|
117
|
+
*
|
|
118
|
+
* - `center` — pill centered on the line (default). Used when both
|
|
119
|
+
* edges have at least `NOW_PILL_WIDTH_PX/2` of clearance from the
|
|
120
|
+
* chart's left/right edges.
|
|
121
|
+
* - `flag-right` — line at the pill's left edge, pill extends to the
|
|
122
|
+
* right with the right side rounded and the label left-aligned.
|
|
123
|
+
* Used when the line lands close enough to `chartLeftX` that a
|
|
124
|
+
* centered pill would overlap the header card / canvas left edge.
|
|
125
|
+
* - `flag-left` — line at the pill's right edge, pill extends to the
|
|
126
|
+
* left with the left side rounded and the label right-aligned.
|
|
127
|
+
* Used when the line lands close enough to `chartRightX` that a
|
|
128
|
+
* centered pill would clip past the canvas right edge.
|
|
129
|
+
*
|
|
130
|
+
* In both flag modes the squared edge IS the now-line, so the pill
|
|
131
|
+
* visually anchors to the line without growing the canvas.
|
|
132
|
+
*/
|
|
133
|
+
export type NowPillMode = 'center' | 'flag-right' | 'flag-left';
|
|
134
|
+
export interface PositionedNowline {
|
|
135
|
+
x: number;
|
|
136
|
+
topY: number;
|
|
137
|
+
bottomY: number;
|
|
138
|
+
pillTopY: number;
|
|
139
|
+
/** How the pill aligns to the line (see `NowPillMode`). */
|
|
140
|
+
pillMode: NowPillMode;
|
|
141
|
+
/**
|
|
142
|
+
* Locale-resolved string painted inside the pill (`'now'` for `en-US`,
|
|
143
|
+
* `'maint.'` for `fr`). Layout owns the locale lookup; the renderer
|
|
144
|
+
* just paints the string. See `packages/layout/src/i18n.ts`.
|
|
145
|
+
*/
|
|
146
|
+
label: string;
|
|
147
|
+
/**
|
|
148
|
+
* Width (px) of the pill. Floored at `NOW_PILL_WIDTH_PX` so en-US
|
|
149
|
+
* (`'now'`) keeps its byte-stable 36 px footprint; longer locale
|
|
150
|
+
* strings (e.g. `'maint.'` for fr) grow the pill to fit instead of
|
|
151
|
+
* clipping. Computed in `buildNowline` from `estimateTextWidth(label)`.
|
|
152
|
+
*/
|
|
153
|
+
pillWidth: number;
|
|
154
|
+
style: ResolvedStyle;
|
|
155
|
+
}
|
|
156
|
+
export interface PositionedItem {
|
|
157
|
+
kind: 'item';
|
|
158
|
+
id?: string;
|
|
159
|
+
title: string;
|
|
160
|
+
box: BoundingBox;
|
|
161
|
+
status: StatusKind;
|
|
162
|
+
progressFraction: number;
|
|
163
|
+
footnoteIndicators: number[];
|
|
164
|
+
labelChips: PositionedLabelChip[];
|
|
165
|
+
/** True when the chip row's natural total width exceeded the bar's
|
|
166
|
+
* effective inner width and the whole row spilled past the bar's
|
|
167
|
+
* right edge. The chips' `box.x` already reflects the spilled
|
|
168
|
+
* position; this flag exists for the row-packer to reserve the
|
|
169
|
+
* spilled extent and for the renderer / debug overlays to know
|
|
170
|
+
* the row sits outside the bar's painted footprint. */
|
|
171
|
+
chipsOutside: boolean;
|
|
172
|
+
/** Logical right x reached by the chip row, INCLUDING the chips
|
|
173
|
+
* whether painted inside or outside the bar. Equals the start x
|
|
174
|
+
* when there are no chips. The row-packer's spill reservation
|
|
175
|
+
* uses this to grow the chart canvas / bump siblings. */
|
|
176
|
+
chipsRightX: number;
|
|
177
|
+
linkIcon?: LinkIconKind;
|
|
178
|
+
linkHref?: string;
|
|
179
|
+
hasOverflow: boolean;
|
|
180
|
+
overflowBox?: BoundingBox;
|
|
181
|
+
overflowAnchorId?: string;
|
|
182
|
+
owner?: string;
|
|
183
|
+
description?: string;
|
|
184
|
+
metaText?: string;
|
|
185
|
+
textSpills: boolean;
|
|
186
|
+
/** True when the bar is too narrow to host the status dot inside
|
|
187
|
+
* with its full inset (`MIN_BAR_WIDTH_FOR_DOT_PX`). The dot
|
|
188
|
+
* renders in the spill column to the right of the bar instead
|
|
189
|
+
* of overshooting the bar's left edge. */
|
|
190
|
+
dotSpills: boolean;
|
|
191
|
+
/** True when the bar is too narrow to host the link-icon tile
|
|
192
|
+
* inside without colliding with the status dot column
|
|
193
|
+
* (`MIN_BAR_WIDTH_FOR_LINK_AND_DOT_PX`). The icon spills out and
|
|
194
|
+
* renders ahead of the (also-spilled) title so the icon stays
|
|
195
|
+
* visually attached to the title text. Implies `textSpills`. */
|
|
196
|
+
iconSpills: boolean;
|
|
197
|
+
/** True when the bar is too narrow to host the footnote
|
|
198
|
+
* superscript at its inset-right position. The indicator(s)
|
|
199
|
+
* render in the spill column trailing the title text instead
|
|
200
|
+
* of at the bar's upper-right corner. */
|
|
201
|
+
footnoteSpills: boolean;
|
|
202
|
+
/** Pre-computed x positions for the spilled decorations. `null`
|
|
203
|
+
* when the matching `*Spills` flag is false (decoration stays
|
|
204
|
+
* inside the bar at its inset-anchored position). */
|
|
205
|
+
dotSpillCx: number | null;
|
|
206
|
+
iconSpillX: number | null;
|
|
207
|
+
/** First footnote indicator's left edge in the spill column.
|
|
208
|
+
* Subsequent indicators walk right by `ITEM_FOOTNOTE_INDICATOR_STEP_PX`. */
|
|
209
|
+
footnoteSpillStartX: number | null;
|
|
210
|
+
/** Right edge of the spilled-decoration cluster (inclusive of
|
|
211
|
+
* spilled title and footnote glyphs). Used by the row-packer
|
|
212
|
+
* to size the row's spill reservation so the next chained item
|
|
213
|
+
* doesn't land underneath. */
|
|
214
|
+
decorationsRightX: number;
|
|
215
|
+
/**
|
|
216
|
+
* Capacity suffix data when the item declares `capacity:N`. Null when
|
|
217
|
+
* the item has no capacity, the value is non-positive, or the resolved
|
|
218
|
+
* `capacity-icon` is `none` and no number should render either way.
|
|
219
|
+
*
|
|
220
|
+
* Renders alongside the item's `metaText` (or stand-alone when no meta
|
|
221
|
+
* is present) per specs/rendering.md § Item capacity suffix. The `text`
|
|
222
|
+
* is the formatted number (`'5'`, `'0.5'`, `'1.25'`); `icon` tells the
|
|
223
|
+
* renderer which glyph to draw and whether to use the SVG library, the
|
|
224
|
+
* `×` text node, or an inline literal.
|
|
225
|
+
*/
|
|
226
|
+
capacity: PositionedCapacity | null;
|
|
227
|
+
/**
|
|
228
|
+
* Resolved size when the item declared `size:NAME`. Null when the item
|
|
229
|
+
* sized itself with a literal `duration:` or didn't declare a size at
|
|
230
|
+
* all. Used by the renderer for the size chip on the meta line (m6) and
|
|
231
|
+
* by the layout for capacity-aware duration derivation (m5).
|
|
232
|
+
*/
|
|
233
|
+
size: ResolvedSize | null;
|
|
234
|
+
style: ResolvedStyle;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Positioned capacity suffix shared by `PositionedItem` and (in m7) the lane
|
|
238
|
+
* frame-tab badge. The shape stays small and serializable: a formatted number
|
|
239
|
+
* string plus a discriminated union for the glyph. The renderer paints both.
|
|
240
|
+
*
|
|
241
|
+
* `icon === null` means the resolved `capacity-icon` was `'none'` — render
|
|
242
|
+
* the bare number with no glyph or separator.
|
|
243
|
+
*/
|
|
244
|
+
export interface PositionedCapacity {
|
|
245
|
+
/** Numeric capacity, post-percent-sugar conversion (e.g. `50%` → 0.5). */
|
|
246
|
+
value: number;
|
|
247
|
+
/** Display string per spec number-formatting rules (`'5'`, `'0.5'`). */
|
|
248
|
+
text: string;
|
|
249
|
+
/** Resolved glyph instruction, or `null` when the icon is `'none'`. */
|
|
250
|
+
icon: ResolvedCapacityIconRef | null;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Renderer-facing capacity-icon reference. Mirrors the layout-internal
|
|
254
|
+
* `ResolvedCapacityIcon` from `capacity.ts` but lives in the shared
|
|
255
|
+
* positioned-model types so the renderer can read it without importing
|
|
256
|
+
* layout internals.
|
|
257
|
+
*/
|
|
258
|
+
export type ResolvedCapacityIconRef = {
|
|
259
|
+
kind: 'builtin';
|
|
260
|
+
name: 'multiplier' | 'person' | 'people' | 'points' | 'time';
|
|
261
|
+
} | {
|
|
262
|
+
kind: 'literal';
|
|
263
|
+
text: string;
|
|
264
|
+
};
|
|
265
|
+
export type LinkIconKind = 'linear' | 'github' | 'jira' | 'generic' | 'none';
|
|
266
|
+
export interface PositionedLabelChip {
|
|
267
|
+
text: string;
|
|
268
|
+
style: ResolvedStyle;
|
|
269
|
+
box: BoundingBox;
|
|
270
|
+
}
|
|
271
|
+
export interface PositionedGroup {
|
|
272
|
+
kind: 'group';
|
|
273
|
+
id?: string;
|
|
274
|
+
title?: string;
|
|
275
|
+
box: BoundingBox;
|
|
276
|
+
children: PositionedTrackChild[];
|
|
277
|
+
style: ResolvedStyle;
|
|
278
|
+
}
|
|
279
|
+
export interface PositionedParallel {
|
|
280
|
+
kind: 'parallel';
|
|
281
|
+
id?: string;
|
|
282
|
+
title?: string;
|
|
283
|
+
box: BoundingBox;
|
|
284
|
+
children: PositionedTrackChild[];
|
|
285
|
+
style: ResolvedStyle;
|
|
286
|
+
}
|
|
287
|
+
export type PositionedTrackChild = PositionedItem | PositionedParallel | PositionedGroup;
|
|
288
|
+
export interface PositionedSwimlane {
|
|
289
|
+
id?: string;
|
|
290
|
+
title: string;
|
|
291
|
+
box: BoundingBox;
|
|
292
|
+
bandIndex: number;
|
|
293
|
+
children: PositionedTrackChild[];
|
|
294
|
+
nested: PositionedSwimlane[];
|
|
295
|
+
style: ResolvedStyle;
|
|
296
|
+
owner?: string;
|
|
297
|
+
footnoteIndicators: number[];
|
|
298
|
+
/**
|
|
299
|
+
* Lane-level capacity badge data when the swimlane declares
|
|
300
|
+
* `capacity:N`. Null when no capacity is set or the value parses to
|
|
301
|
+
* zero. The renderer paints the badge inside the frame tab after the
|
|
302
|
+
* owner badge (or after the lane title when no owner is present),
|
|
303
|
+
* per specs/rendering.md § Lane capacity badge. m8 (overload sweep)
|
|
304
|
+
* also reads `value` to compute load against item capacities.
|
|
305
|
+
*
|
|
306
|
+
* `capacity-icon:none` resolves to `icon: null` here (just the bare
|
|
307
|
+
* number renders, no glyph) but the badge still appears. Authors who
|
|
308
|
+
* want the badge fully hidden simply omit `capacity:`.
|
|
309
|
+
*/
|
|
310
|
+
capacity: PositionedCapacity | null;
|
|
311
|
+
/**
|
|
312
|
+
* Tri-state utilization underline data when the lane declares
|
|
313
|
+
* `capacity:` AND has at least one item contributing load AND has not
|
|
314
|
+
* opted out of every color band via `utilization-*-at:none`. Null
|
|
315
|
+
* otherwise — the renderer paints no underline.
|
|
316
|
+
*
|
|
317
|
+
* Computed in m12 by `computeLaneUtilization` (see
|
|
318
|
+
* `lane-utilization.ts`) per specs/rendering.md § Lane utilization
|
|
319
|
+
* underline. The renderer paints one rectangle per coalesced segment
|
|
320
|
+
* along the bottom edge of the band, colored from the resolved theme's
|
|
321
|
+
* `swimlane.utilizationOk` / `…Warn` / `…Over` tokens.
|
|
322
|
+
*/
|
|
323
|
+
utilization: PositionedLaneUtilization | null;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* One classification for a lane utilization segment. `green` includes the
|
|
327
|
+
* zero-load case so the underline reads as a continuous health bar; the spec
|
|
328
|
+
* intentionally avoids a separate "idle" color.
|
|
329
|
+
*/
|
|
330
|
+
export type UtilizationClassification = 'green' | 'yellow' | 'red';
|
|
331
|
+
/**
|
|
332
|
+
* One half-open `[startX, endX)` segment of the lane's utilization underline,
|
|
333
|
+
* pre-classified for the renderer. Adjacent same-classification segments are
|
|
334
|
+
* coalesced upstream so the renderer paints one rectangle per visible color
|
|
335
|
+
* band rather than per event boundary.
|
|
336
|
+
*
|
|
337
|
+
* `load` is the absolute concurrent capacity at this segment (sum of active
|
|
338
|
+
* items' `capacity:` values). Useful for tooltips and debug overlays; the
|
|
339
|
+
* paint color is determined by `classification` alone.
|
|
340
|
+
*/
|
|
341
|
+
export interface PositionedUtilizationSegment {
|
|
342
|
+
startX: number;
|
|
343
|
+
endX: number;
|
|
344
|
+
load: number;
|
|
345
|
+
classification: UtilizationClassification;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Resolved utilization model for a swimlane. Carries the segment list plus
|
|
349
|
+
* the resolved thresholds and capacity so downstream consumers (renderer,
|
|
350
|
+
* tooltips, exporters) can describe the model without re-resolving config.
|
|
351
|
+
*
|
|
352
|
+
* `warnFraction` / `overFraction`: each is either a positive fraction of
|
|
353
|
+
* `capacityValue` (the lane paints that color band when load reaches the
|
|
354
|
+
* fraction) or `null` to mean "this color band is opted out via
|
|
355
|
+
* `utilization-*-at:none`".
|
|
356
|
+
*/
|
|
357
|
+
export interface PositionedLaneUtilization {
|
|
358
|
+
segments: PositionedUtilizationSegment[];
|
|
359
|
+
capacityValue: number;
|
|
360
|
+
warnFraction: number | null;
|
|
361
|
+
overFraction: number | null;
|
|
362
|
+
}
|
|
363
|
+
export interface PositionedAnchor {
|
|
364
|
+
id?: string;
|
|
365
|
+
title: string;
|
|
366
|
+
center: Point;
|
|
367
|
+
radius: number;
|
|
368
|
+
style: ResolvedStyle;
|
|
369
|
+
predecessorPoints: Point[];
|
|
370
|
+
cutTopY: number;
|
|
371
|
+
cutBottomY: number;
|
|
372
|
+
bumpedUp: boolean;
|
|
373
|
+
labelBox: BoundingBox;
|
|
374
|
+
labelSide: 'left' | 'right';
|
|
375
|
+
}
|
|
376
|
+
export interface PositionedMilestone {
|
|
377
|
+
id?: string;
|
|
378
|
+
title: string;
|
|
379
|
+
center: Point;
|
|
380
|
+
radius: number;
|
|
381
|
+
fixed: boolean;
|
|
382
|
+
slackArrows?: Array<{
|
|
383
|
+
x: number;
|
|
384
|
+
y: number;
|
|
385
|
+
}>;
|
|
386
|
+
isOverrun: boolean;
|
|
387
|
+
style: ResolvedStyle;
|
|
388
|
+
cutTopY: number;
|
|
389
|
+
cutBottomY: number;
|
|
390
|
+
labelBox: BoundingBox;
|
|
391
|
+
labelSide: 'left' | 'right';
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Result of packing a marker-row entity (anchor or milestone) into the
|
|
395
|
+
* dynamic row stack. `rowIndex == 0` is the in-row baseline; positive
|
|
396
|
+
* indices push the diamond DOWN by `step` px each. The label box is
|
|
397
|
+
* absolute and already accounts for left/right side flipping when the
|
|
398
|
+
* preferred side would overflow the chart.
|
|
399
|
+
*/
|
|
400
|
+
export interface MarkerRowPlacement {
|
|
401
|
+
rowIndex: number;
|
|
402
|
+
centerY: number;
|
|
403
|
+
labelBox: BoundingBox;
|
|
404
|
+
labelSide: 'left' | 'right';
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Horizontal corridor occupied by a milestone's slack arrow. Sits at
|
|
408
|
+
* the slack predecessor's row Y, running from the predecessor's right
|
|
409
|
+
* edge to the milestone's column. Items whose natural placement would
|
|
410
|
+
* intersect this band must drop to a row whose Y does not match `y`,
|
|
411
|
+
* so the arrow has clear horizontal space to travel.
|
|
412
|
+
*/
|
|
413
|
+
export interface SlackCorridor {
|
|
414
|
+
xStart: number;
|
|
415
|
+
xEnd: number;
|
|
416
|
+
y: number;
|
|
417
|
+
slackPredId: string;
|
|
418
|
+
milestoneId: string;
|
|
419
|
+
}
|
|
420
|
+
export interface PositionedDependencyEdge {
|
|
421
|
+
fromId: string;
|
|
422
|
+
toId: string;
|
|
423
|
+
waypoints: Point[];
|
|
424
|
+
/**
|
|
425
|
+
* - `normal` — orthogonal arrow drawn AFTER swimlane / item /
|
|
426
|
+
* marker fills so it sits on top of lane bands.
|
|
427
|
+
* - `overflow` — currently unused at construction; reserved for the
|
|
428
|
+
* red `before:` overrun annotation arrow.
|
|
429
|
+
* - `underBar` — channel router could not find a clear vertical
|
|
430
|
+
* gutter between the source and target columns; the arrow's
|
|
431
|
+
* vertical leg crosses one or more item bars. The renderer paints
|
|
432
|
+
* these edges BEFORE bar fills with a thinner stroke so the bar
|
|
433
|
+
* stays the visual foreground.
|
|
434
|
+
*/
|
|
435
|
+
kind: 'normal' | 'overflow' | 'underBar';
|
|
436
|
+
style: ResolvedStyle;
|
|
437
|
+
}
|
|
438
|
+
export interface PositionedFootnoteIndicator {
|
|
439
|
+
number: number;
|
|
440
|
+
hostItemId: string;
|
|
441
|
+
style: ResolvedStyle;
|
|
442
|
+
}
|
|
443
|
+
export interface PositionedFootnoteArea {
|
|
444
|
+
box: BoundingBox;
|
|
445
|
+
entries: PositionedFootnoteEntry[];
|
|
446
|
+
}
|
|
447
|
+
export interface PositionedFootnoteEntry {
|
|
448
|
+
number: number;
|
|
449
|
+
title: string;
|
|
450
|
+
description?: string;
|
|
451
|
+
style: ResolvedStyle;
|
|
452
|
+
}
|
|
453
|
+
export interface PositionedIncludeRegion {
|
|
454
|
+
sourcePath: string;
|
|
455
|
+
label: string;
|
|
456
|
+
box: BoundingBox;
|
|
457
|
+
nestedSwimlanes: PositionedSwimlane[];
|
|
458
|
+
style: ResolvedStyle;
|
|
459
|
+
}
|
|
460
|
+
export interface PositionedRoadmap {
|
|
461
|
+
width: number;
|
|
462
|
+
height: number;
|
|
463
|
+
theme: 'light' | 'dark';
|
|
464
|
+
/**
|
|
465
|
+
* Resolved palette — every color the renderer reads. m2.5d moved
|
|
466
|
+
* theme resolution into the layout side, so the renderer no longer
|
|
467
|
+
* branches on `theme === 'dark'`. The `theme` field above stays for
|
|
468
|
+
* `data-theme` SVG attribution; all color decisions read `palette`.
|
|
469
|
+
*/
|
|
470
|
+
palette: import('./themes/index.js').Theme;
|
|
471
|
+
backgroundColor: string;
|
|
472
|
+
header: PositionedHeader;
|
|
473
|
+
timeline: PositionedTimelineScale;
|
|
474
|
+
nowline: PositionedNowline | null;
|
|
475
|
+
swimlanes: PositionedSwimlane[];
|
|
476
|
+
anchors: PositionedAnchor[];
|
|
477
|
+
milestones: PositionedMilestone[];
|
|
478
|
+
edges: PositionedDependencyEdge[];
|
|
479
|
+
footnotes: PositionedFootnoteArea;
|
|
480
|
+
includes: PositionedIncludeRegion[];
|
|
481
|
+
chartBox: BoundingBox;
|
|
482
|
+
}
|
|
483
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Positioned-model types. One type per entity in specs/rendering.md §
|
|
2
|
+
// The Positioned Model. Coordinates are in SVG user units with origin at
|
|
3
|
+
// top-left. All colors in `ResolvedStyle` are concrete hex strings baked
|
|
4
|
+
// in by style-resolution; the renderer is palette-dumb.
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,yEAAyE;AACzE,yEAAyE;AACzE,wDAAwD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { NowlineFile, ScaleBlock } from '@nowline/core';
|
|
2
|
+
import type { TimeScale } from './time-scale.js';
|
|
3
|
+
import type { PositionedTick } from './types.js';
|
|
4
|
+
import type { WorkingCalendar } from './working-calendar.js';
|
|
5
|
+
export type ScaleUnit = 'days' | 'weeks' | 'months' | 'quarters' | 'years';
|
|
6
|
+
export interface ViewPreset {
|
|
7
|
+
/** Tick stride unit (each tick is one `unit` apart). */
|
|
8
|
+
unit: ScaleUnit;
|
|
9
|
+
/** Show a label every N ticks (1 = every tick gets a label). */
|
|
10
|
+
labelEvery: number;
|
|
11
|
+
/** Pixels per `1 unit` worth of working days. */
|
|
12
|
+
pixelsPerUnit: number;
|
|
13
|
+
}
|
|
14
|
+
export type ScaleConfig = ViewPreset;
|
|
15
|
+
export declare function resolveScale(file: NowlineFile, scaleBlock: ScaleBlock | undefined): ViewPreset;
|
|
16
|
+
/**
|
|
17
|
+
* Build header ticks for the chart. The ith tick sits at
|
|
18
|
+
* `originX + i * stridePx`. The last tick is rendered (so the chart
|
|
19
|
+
* has a closing edge) but its label is suppressed because there's no
|
|
20
|
+
* following column.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildHeaderTicks(scale: TimeScale, preset: ViewPreset, calendar: WorkingCalendar, locale?: string): PositionedTick[];
|
|
23
|
+
//# sourceMappingURL=view-preset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view-preset.d.ts","sourceRoot":"","sources":["../src/view-preset.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAI7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;AAE3E,MAAM,WAAW,UAAU;IACvB,wDAAwD;IACxD,IAAI,EAAE,SAAS,CAAC;IAChB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;CACzB;AAKD,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC;AAMrC,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,CAoE9F;AAmBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,eAAe,EACzB,MAAM,GAAE,MAAuB,GAChC,cAAc,EAAE,CAsBlB"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// ViewPreset — declarative configuration for the timeline header
|
|
2
|
+
// (tick stride, label thinning, label format). Replaces the
|
|
3
|
+
// imperative `for` loop and `formatTickLabel` switch in the legacy
|
|
4
|
+
// `timeline.ts`.
|
|
5
|
+
//
|
|
6
|
+
// `resolveScale` parses the DSL `scale:` property (and any nested
|
|
7
|
+
// `scale` block) into a `ViewPreset`. `buildHeaderTicks` produces
|
|
8
|
+
// the `PositionedTick[]` byte-stable with the legacy generator: same
|
|
9
|
+
// x positions, same labelX positions, same major/minor flags, same
|
|
10
|
+
// label text.
|
|
11
|
+
import { addDays } from './calendar.js';
|
|
12
|
+
import { DEFAULT_LOCALE, localeStrings } from './i18n.js';
|
|
13
|
+
import { DEFAULT_PIXELS_PER_DAY, LABEL_THINNING } from './themes/shared.js';
|
|
14
|
+
function stripColon(key) {
|
|
15
|
+
return key.endsWith(':') ? key.slice(0, -1) : key;
|
|
16
|
+
}
|
|
17
|
+
export function resolveScale(file, scaleBlock) {
|
|
18
|
+
const scaleProp = file.roadmapDecl?.properties.find((p) => stripColon(p.key) === 'scale');
|
|
19
|
+
// `scale:` accepts a unit name (`days`/`weeks`/`months`/`quarters`/`years`)
|
|
20
|
+
// OR a duration literal (`1w`, `2w`, `1m`, `1q`, `1y`). The literal form
|
|
21
|
+
// is the documented default in the DSL spec; it picks the unit and uses
|
|
22
|
+
// the literal's count to size the pixels-per-unit budget.
|
|
23
|
+
const rawScale = scaleProp?.value;
|
|
24
|
+
let unit = 'weeks';
|
|
25
|
+
let pixelsPerUnitOverride;
|
|
26
|
+
let labelEveryOverride;
|
|
27
|
+
if (rawScale) {
|
|
28
|
+
if (rawScale === 'days' ||
|
|
29
|
+
rawScale === 'weeks' ||
|
|
30
|
+
rawScale === 'months' ||
|
|
31
|
+
rawScale === 'quarters' ||
|
|
32
|
+
rawScale === 'years') {
|
|
33
|
+
unit = rawScale;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const dur = /^(\d+)([dwmqy])$/.exec(rawScale);
|
|
37
|
+
if (dur) {
|
|
38
|
+
const n = Math.max(1, parseInt(dur[1], 10));
|
|
39
|
+
switch (dur[2]) {
|
|
40
|
+
case 'd':
|
|
41
|
+
unit = 'days';
|
|
42
|
+
break;
|
|
43
|
+
case 'w':
|
|
44
|
+
unit = 'weeks';
|
|
45
|
+
break;
|
|
46
|
+
case 'm':
|
|
47
|
+
unit = 'months';
|
|
48
|
+
break;
|
|
49
|
+
case 'q':
|
|
50
|
+
unit = 'quarters';
|
|
51
|
+
break;
|
|
52
|
+
case 'y':
|
|
53
|
+
unit = 'years';
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
pixelsPerUnitOverride = unitPx(unit) * n;
|
|
57
|
+
// A literal scale like `1w` says "I want exactly one label per
|
|
58
|
+
// unit." Override the default thinning so every tick is named.
|
|
59
|
+
labelEveryOverride = 1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const defaultLabelEvery = labelEveryOverride ?? LABEL_THINNING[unit] ?? 4;
|
|
64
|
+
if (scaleBlock) {
|
|
65
|
+
const unitProp = scaleBlock.properties.find((p) => stripColon(p.key) === 'unit');
|
|
66
|
+
const resolvedUnit = unitProp?.value ?? unit;
|
|
67
|
+
const labelProp = scaleBlock.properties.find((p) => stripColon(p.key) === 'label-every');
|
|
68
|
+
const pxProp = scaleBlock.properties.find((p) => stripColon(p.key) === 'pixels-per-unit');
|
|
69
|
+
const labelEvery = labelProp
|
|
70
|
+
? Math.max(1, parseInt(labelProp.value, 10) || defaultLabelEvery)
|
|
71
|
+
: defaultLabelEvery;
|
|
72
|
+
const pixelsPerUnit = pxProp
|
|
73
|
+
? Math.max(1, parseInt(pxProp.value, 10) || unitPx(resolvedUnit))
|
|
74
|
+
: (pixelsPerUnitOverride ?? unitPx(resolvedUnit));
|
|
75
|
+
return { unit: resolvedUnit, labelEvery, pixelsPerUnit };
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
unit,
|
|
79
|
+
labelEvery: defaultLabelEvery,
|
|
80
|
+
pixelsPerUnit: pixelsPerUnitOverride ?? unitPx(unit),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function unitPx(unit) {
|
|
84
|
+
// Baseline pixel widths per one unit, tuned so ~6 month roadmaps fit
|
|
85
|
+
// comfortably in a 1200 px wide chart area.
|
|
86
|
+
switch (unit) {
|
|
87
|
+
case 'days':
|
|
88
|
+
return DEFAULT_PIXELS_PER_DAY;
|
|
89
|
+
case 'weeks':
|
|
90
|
+
return 40;
|
|
91
|
+
case 'months':
|
|
92
|
+
return 80;
|
|
93
|
+
case 'quarters':
|
|
94
|
+
return 160;
|
|
95
|
+
case 'years':
|
|
96
|
+
return 320;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Build header ticks for the chart. The ith tick sits at
|
|
101
|
+
* `originX + i * stridePx`. The last tick is rendered (so the chart
|
|
102
|
+
* has a closing edge) but its label is suppressed because there's no
|
|
103
|
+
* following column.
|
|
104
|
+
*/
|
|
105
|
+
export function buildHeaderTicks(scale, preset, calendar, locale = DEFAULT_LOCALE) {
|
|
106
|
+
const dayPerTick = calendar.daysPerUnit(preset.unit);
|
|
107
|
+
const stridePx = dayPerTick * scale.pixelsPerDay;
|
|
108
|
+
const totalDays = Math.max(1, Math.round(scale.widthPx / scale.pixelsPerDay));
|
|
109
|
+
const tickCount = Math.floor(totalDays / dayPerTick) + 1;
|
|
110
|
+
const ticks = [];
|
|
111
|
+
for (let i = 0; i < tickCount; i++) {
|
|
112
|
+
const days = i * dayPerTick;
|
|
113
|
+
const x = scale.originX + days * scale.pixelsPerDay;
|
|
114
|
+
const isMajor = i % preset.labelEvery === 0;
|
|
115
|
+
const isLast = i === tickCount - 1;
|
|
116
|
+
ticks.push({
|
|
117
|
+
x,
|
|
118
|
+
labelX: isLast ? undefined : x + stridePx / 2,
|
|
119
|
+
major: isMajor,
|
|
120
|
+
label: isMajor && !isLast
|
|
121
|
+
? formatTickLabel(preset.unit, addDays(scale.domain[0], days), i, locale)
|
|
122
|
+
: undefined,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return ticks;
|
|
126
|
+
}
|
|
127
|
+
function formatTickLabel(unit, date, _index, locale) {
|
|
128
|
+
switch (unit) {
|
|
129
|
+
case 'days':
|
|
130
|
+
return `${date.getUTCMonth() + 1}/${date.getUTCDate()}`;
|
|
131
|
+
case 'weeks': {
|
|
132
|
+
const month = date.toLocaleString(locale, { month: 'short', timeZone: 'UTC' });
|
|
133
|
+
const day = date.getUTCDate().toString().padStart(2, '0');
|
|
134
|
+
return `${month} ${day}`;
|
|
135
|
+
}
|
|
136
|
+
case 'months':
|
|
137
|
+
return date.toLocaleString(locale, { month: 'short', timeZone: 'UTC' });
|
|
138
|
+
case 'quarters': {
|
|
139
|
+
const q = Math.floor(date.getUTCMonth() / 3) + 1;
|
|
140
|
+
return `${localeStrings(locale).quarterPrefix}${q} ${date.getUTCFullYear()}`;
|
|
141
|
+
}
|
|
142
|
+
case 'years':
|
|
143
|
+
return `${date.getUTCFullYear()}`;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=view-preset.js.map
|