@kerusiweb/core 2.0.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.
Files changed (95) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +68 -0
  3. package/dist/kerusi/index.d.ts +19 -0
  4. package/dist/kerusi/index.d.ts.map +1 -0
  5. package/dist/kerusi/index.js +19 -0
  6. package/dist/kerusi/index.js.map +1 -0
  7. package/dist/kerusi/kerusi-layout-mode.d.ts +58 -0
  8. package/dist/kerusi/kerusi-layout-mode.d.ts.map +1 -0
  9. package/dist/kerusi/kerusi-layout-mode.js +117 -0
  10. package/dist/kerusi/kerusi-layout-mode.js.map +1 -0
  11. package/dist/kerusi/kerusi-locale.d.ts +43 -0
  12. package/dist/kerusi/kerusi-locale.d.ts.map +1 -0
  13. package/dist/kerusi/kerusi-locale.js +111 -0
  14. package/dist/kerusi/kerusi-locale.js.map +1 -0
  15. package/dist/kerusi/kerusi-map.model.d.ts +258 -0
  16. package/dist/kerusi/kerusi-map.model.d.ts.map +1 -0
  17. package/dist/kerusi/kerusi-map.model.js +11 -0
  18. package/dist/kerusi/kerusi-map.model.js.map +1 -0
  19. package/dist/kerusi/kerusi-price.d.ts +22 -0
  20. package/dist/kerusi/kerusi-price.d.ts.map +1 -0
  21. package/dist/kerusi/kerusi-price.js +48 -0
  22. package/dist/kerusi/kerusi-price.js.map +1 -0
  23. package/dist/kerusi/kerusi-rows.d.ts +25 -0
  24. package/dist/kerusi/kerusi-rows.d.ts.map +1 -0
  25. package/dist/kerusi/kerusi-rows.js +36 -0
  26. package/dist/kerusi/kerusi-rows.js.map +1 -0
  27. package/dist/kerusi/kerusi-state.d.ts +85 -0
  28. package/dist/kerusi/kerusi-state.d.ts.map +1 -0
  29. package/dist/kerusi/kerusi-state.js +117 -0
  30. package/dist/kerusi/kerusi-state.js.map +1 -0
  31. package/dist/kerusi/kerusi-state.model.d.ts +79 -0
  32. package/dist/kerusi/kerusi-state.model.d.ts.map +1 -0
  33. package/dist/kerusi/kerusi-state.model.js +7 -0
  34. package/dist/kerusi/kerusi-state.model.js.map +1 -0
  35. package/dist/kerusi/kerusi-validator.d.ts +96 -0
  36. package/dist/kerusi/kerusi-validator.d.ts.map +1 -0
  37. package/dist/kerusi/kerusi-validator.js +525 -0
  38. package/dist/kerusi/kerusi-validator.js.map +1 -0
  39. package/dist/kerusi/kerusi-violation.d.ts +36 -0
  40. package/dist/kerusi/kerusi-violation.d.ts.map +1 -0
  41. package/dist/kerusi/kerusi-violation.js +13 -0
  42. package/dist/kerusi/kerusi-violation.js.map +1 -0
  43. package/dist/public-api.d.ts +4 -0
  44. package/dist/public-api.d.ts.map +1 -0
  45. package/dist/public-api.js +13 -0
  46. package/dist/public-api.js.map +1 -0
  47. package/dist/render/build-render-model.d.ts +36 -0
  48. package/dist/render/build-render-model.d.ts.map +1 -0
  49. package/dist/render/build-render-model.js +258 -0
  50. package/dist/render/build-render-model.js.map +1 -0
  51. package/dist/render/element-shapes.d.ts +34 -0
  52. package/dist/render/element-shapes.d.ts.map +1 -0
  53. package/dist/render/element-shapes.js +41 -0
  54. package/dist/render/element-shapes.js.map +1 -0
  55. package/dist/render/index.d.ts +14 -0
  56. package/dist/render/index.d.ts.map +1 -0
  57. package/dist/render/index.js +14 -0
  58. package/dist/render/index.js.map +1 -0
  59. package/dist/render/navigation-order.d.ts +49 -0
  60. package/dist/render/navigation-order.d.ts.map +1 -0
  61. package/dist/render/navigation-order.js +109 -0
  62. package/dist/render/navigation-order.js.map +1 -0
  63. package/dist/render/render-model.d.ts +151 -0
  64. package/dist/render/render-model.d.ts.map +1 -0
  65. package/dist/render/render-model.js +2 -0
  66. package/dist/render/render-model.js.map +1 -0
  67. package/dist/render/seat-shapes.d.ts +72 -0
  68. package/dist/render/seat-shapes.d.ts.map +1 -0
  69. package/dist/render/seat-shapes.js +161 -0
  70. package/dist/render/seat-shapes.js.map +1 -0
  71. package/dist/render/section-layout.d.ts +66 -0
  72. package/dist/render/section-layout.d.ts.map +1 -0
  73. package/dist/render/section-layout.js +173 -0
  74. package/dist/render/section-layout.js.map +1 -0
  75. package/dist/view/index.d.ts +12 -0
  76. package/dist/view/index.d.ts.map +1 -0
  77. package/dist/view/index.js +12 -0
  78. package/dist/view/index.js.map +1 -0
  79. package/dist/view/kerusi-seatmap-colors.d.ts +176 -0
  80. package/dist/view/kerusi-seatmap-colors.d.ts.map +1 -0
  81. package/dist/view/kerusi-seatmap-colors.js +215 -0
  82. package/dist/view/kerusi-seatmap-colors.js.map +1 -0
  83. package/dist/view/rtl.d.ts +17 -0
  84. package/dist/view/rtl.d.ts.map +1 -0
  85. package/dist/view/rtl.js +29 -0
  86. package/dist/view/rtl.js.map +1 -0
  87. package/dist/view/seat-aria.d.ts +44 -0
  88. package/dist/view/seat-aria.d.ts.map +1 -0
  89. package/dist/view/seat-aria.js +103 -0
  90. package/dist/view/seat-aria.js.map +1 -0
  91. package/dist/view/selection.d.ts +84 -0
  92. package/dist/view/selection.d.ts.map +1 -0
  93. package/dist/view/selection.js +98 -0
  94. package/dist/view/selection.js.map +1 -0
  95. package/package.json +42 -0
@@ -0,0 +1,36 @@
1
+ import { KerusiMap } from '../kerusi/kerusi-map.model.js';
2
+ import { KerusiState } from '../kerusi/kerusi-state.model.js';
3
+ import { RenderMap, RenderSeat, SeatRenderStatus } from './render-model.js';
4
+ /** Which statuses a seat may be picked in, unless the caller says otherwise. */
5
+ export declare const DEFAULT_SELECTABLE_STATUSES: readonly SeatRenderStatus[];
6
+ export interface BuildRenderModelOptions {
7
+ /** Overrides `KerusiMap.locale` for label resolution. */
8
+ locale?: string;
9
+ /** Render only these sections, in this order. Default: all, by `Section.index`. */
10
+ sectionIds?: readonly string[];
11
+ /** Statuses in which a seat may be picked. Default: `['available']`. */
12
+ selectableStatuses?: readonly SeatRenderStatus[];
13
+ /** A final say on whether a seat is selectable, applied after the status test. */
14
+ seatSelectable?: (seat: RenderSeat) => boolean;
15
+ }
16
+ /**
17
+ * Builds the renderer's view of a `KerusiMap` with its `KerusiState` merged in.
18
+ *
19
+ * Everything the standard makes a consumer resolve happens here, once:
20
+ * - sections ordered by `Section.index`, and each given its own layout mode
21
+ * per §4.5 — this is what lets a grid balcony and a freeform orchestra
22
+ * coexist in one map;
23
+ * - every row a section declares materialized, empty rows included, in the
24
+ * §4.2.1 order, with seats grouped into them by `Seat.row` and ordered by
25
+ * `col` (grid, mixed) or `x` (freeform);
26
+ * - availability merged by `Seat.id`, honoring §5.1's sparse rule that an
27
+ * absent seat is available;
28
+ * - price resolved through the §4.9 precedence order;
29
+ * - `Section.label` and `SeatType.label` localized against the map's locale.
30
+ *
31
+ * It does NOT validate — call `validateKerusiMap` or `checkKerusiMap` first.
32
+ * Given an invalid document it builds the best model it can rather than
33
+ * throwing, so a caller in "collect" mode can render and report at once.
34
+ */
35
+ export declare function buildRenderModel(map: KerusiMap, state?: KerusiState, options?: BuildRenderModelOptions): RenderMap;
36
+ //# sourceMappingURL=build-render-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-render-model.d.ts","sourceRoot":"","sources":["../../src/render/build-render-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA0B,MAAM,+BAA+B,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAK9D,OAAO,EAIL,SAAS,EAET,UAAU,EAEV,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAK3B,gFAAgF;AAChF,eAAO,MAAM,2BAA2B,EAAE,SAAS,gBAAgB,EAAkB,CAAC;AAEtF,MAAM,WAAW,uBAAuB;IACtC,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACjD,kFAAkF;IAClF,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,SAAS,EACd,KAAK,CAAC,EAAE,WAAW,EACnB,OAAO,GAAE,uBAA4B,GACpC,SAAS,CA6CX"}
@@ -0,0 +1,258 @@
1
+ import { resolveSectionLayoutMode } from '../kerusi/kerusi-layout-mode.js';
2
+ import { resolveLocalizedText, resolveMapLocale } from '../kerusi/kerusi-locale.js';
3
+ import { resolveSeatPrice, resolveTierPrice } from '../kerusi/kerusi-price.js';
4
+ import { orderRowMeta } from '../kerusi/kerusi-rows.js';
5
+ /** The default section proportions when none is declared (§4.5). */
6
+ const DEFAULT_ASPECT_RATIO = '1:1';
7
+ /** Which statuses a seat may be picked in, unless the caller says otherwise. */
8
+ export const DEFAULT_SELECTABLE_STATUSES = ['available'];
9
+ /**
10
+ * Builds the renderer's view of a `KerusiMap` with its `KerusiState` merged in.
11
+ *
12
+ * Everything the standard makes a consumer resolve happens here, once:
13
+ * - sections ordered by `Section.index`, and each given its own layout mode
14
+ * per §4.5 — this is what lets a grid balcony and a freeform orchestra
15
+ * coexist in one map;
16
+ * - every row a section declares materialized, empty rows included, in the
17
+ * §4.2.1 order, with seats grouped into them by `Seat.row` and ordered by
18
+ * `col` (grid, mixed) or `x` (freeform);
19
+ * - availability merged by `Seat.id`, honoring §5.1's sparse rule that an
20
+ * absent seat is available;
21
+ * - price resolved through the §4.9 precedence order;
22
+ * - `Section.label` and `SeatType.label` localized against the map's locale.
23
+ *
24
+ * It does NOT validate — call `validateKerusiMap` or `checkKerusiMap` first.
25
+ * Given an invalid document it builds the best model it can rather than
26
+ * throwing, so a caller in "collect" mode can render and report at once.
27
+ */
28
+ export function buildRenderModel(map, state, options = {}) {
29
+ const locale = resolveMapLocale(map, options.locale);
30
+ const selectableStatuses = new Set(options.selectableStatuses ?? DEFAULT_SELECTABLE_STATUSES);
31
+ const legendById = new Map((map.legend ?? []).map((t) => [t.id, t]));
32
+ const seatsById = new Map();
33
+ const typeCounts = new Map();
34
+ const currencies = new Set();
35
+ const sections = selectSections(map, options.sectionIds).map((section) => buildSection(section, {
36
+ map,
37
+ state,
38
+ locale,
39
+ selectableStatuses,
40
+ seatSelectable: options.seatSelectable,
41
+ legendById,
42
+ seatsById,
43
+ typeCounts,
44
+ currencies,
45
+ }));
46
+ const legend = (map.legend ?? []).map((type) => {
47
+ const price = resolveTierPrice(map, type.defaultPriceTier);
48
+ return {
49
+ id: type.id,
50
+ label: resolveLocalizedText(type.label, locale) ?? type.id,
51
+ color: type.color,
52
+ defaultTier: map.priceTiers?.find((t) => t.id === type.defaultPriceTier),
53
+ price,
54
+ seatCount: typeCounts.get(type.id) ?? 0,
55
+ };
56
+ });
57
+ return {
58
+ id: map.id,
59
+ name: map.name,
60
+ locale,
61
+ domain: map.domain,
62
+ sections,
63
+ legend,
64
+ // §4.9 forbids mixing currencies, so the first is the map's.
65
+ currency: [...currencies][0],
66
+ seatsById,
67
+ };
68
+ }
69
+ function buildSection(section, ctx) {
70
+ const layoutMode = resolveSectionLayoutMode(section);
71
+ const rows = buildRows(section, layoutMode, ctx);
72
+ return {
73
+ id: section.id,
74
+ label: resolveLocalizedText(section.label, ctx.locale),
75
+ layoutMode,
76
+ aspectRatio: section.aspectRatio ?? DEFAULT_ASPECT_RATIO,
77
+ rows,
78
+ seats: rows.flatMap((r) => r.seats),
79
+ elements: buildElements(section, rows),
80
+ directions: buildDirections(section, ctx.locale),
81
+ source: section,
82
+ };
83
+ }
84
+ /**
85
+ * Localizes `Section.directions` (§4.10) and carries it through. Both ends of a
86
+ * `Direction` may be a locale map, exactly as `Section.label` may be.
87
+ *
88
+ * Nothing downstream reads this — §4.10 forbids a renderer deciding layout or
89
+ * screen placement from it. It exists so an application can print "front of
90
+ * train" beside the axis the document says means that.
91
+ */
92
+ function buildDirections(section, locale) {
93
+ if (!section.directions?.length) {
94
+ return undefined;
95
+ }
96
+ return section.directions.map((direction) => ({
97
+ axis: direction.axis,
98
+ low: resolveLocalizedText(direction.low, locale) ?? '',
99
+ high: resolveLocalizedText(direction.high, locale) ?? '',
100
+ }));
101
+ }
102
+ /**
103
+ * Materializes a section's rows and groups its flat seat list into them.
104
+ *
105
+ * `Section.rows` is not a container (§4.2) — seats never live inside a row — but
106
+ * where it is present it *is* the section's complete row registry (§4.2.2), so
107
+ * the rows come from it rather than from the seats that happen to reference
108
+ * one. That is what makes an **empty row** real: a `RowMeta` no seat mentions
109
+ * still takes a slot, and the grid layout reserves a row of vertical space for
110
+ * it. Without that, a document could not open the throw between a cinema screen
111
+ * and row A, because rows would exist only where seats did.
112
+ *
113
+ * With no registry, a seat's `row` is opaque free text (§4.6) and the rows are
114
+ * exactly those the seats name, in first-appearance order — a document that
115
+ * declares no row metadata still renders in the order its author wrote it.
116
+ */
117
+ function buildRows(section, layoutMode, ctx) {
118
+ const byRowKey = new Map();
119
+ const firstSeen = new Map();
120
+ (section.seats ?? []).forEach((seat, i) => {
121
+ const key = seat.row ?? '';
122
+ if (!byRowKey.has(key)) {
123
+ byRowKey.set(key, []);
124
+ firstSeen.set(key, i);
125
+ }
126
+ byRowKey.get(key).push(seat);
127
+ });
128
+ const registry = section.rows;
129
+ const ordered = registry
130
+ ? orderRowMeta(registry).map((meta) => ({ key: meta.id, meta }))
131
+ : [...byRowKey.keys()]
132
+ .sort((a, b) => firstSeen.get(a) - firstSeen.get(b))
133
+ .map((key) => ({ key }));
134
+ if (registry) {
135
+ // A seat naming a row the registry does not declare is a §4.6 violation the
136
+ // validator reports. Render it anyway, after the declared rows — dropping
137
+ // seats would hide the error rather than show it.
138
+ const declared = new Set(registry.map((r) => r.id));
139
+ for (const key of byRowKey.keys()) {
140
+ if (!declared.has(key)) {
141
+ ordered.push({ key });
142
+ }
143
+ }
144
+ }
145
+ return ordered.map(({ key, meta }, rowIndex) => {
146
+ const seats = byRowKey.get(key) ?? [];
147
+ const label = meta?.label ?? (key || undefined);
148
+ const row = {
149
+ id: key,
150
+ label,
151
+ index: rowIndex,
152
+ seats: sortSeats(seats, layoutMode).map((seat) => buildSeat(seat, section, { rowIndex, rowLabel: label }, ctx)),
153
+ empty: seats.length === 0,
154
+ };
155
+ return row;
156
+ });
157
+ }
158
+ /**
159
+ * Orders seats within a row. `col` is the adjacency ordinal in grid and mixed
160
+ * sections — including mixed, where `x`/`y` places the seat but `col` is what
161
+ * "the seat to the right" means (§4.3.1). Freeform rows have no columns, so
162
+ * they order by `x`.
163
+ */
164
+ function sortSeats(seats, layoutMode) {
165
+ const byCol = layoutMode !== 'freeform';
166
+ return [...seats]
167
+ .map((seat, order) => ({ seat, order }))
168
+ .sort((a, b) => {
169
+ const left = byCol ? a.seat.col : a.seat.x;
170
+ const right = byCol ? b.seat.col : b.seat.x;
171
+ if (left === undefined || right === undefined || left === right) {
172
+ return (a.seat.y ?? 0) - (b.seat.y ?? 0) || a.order - b.order;
173
+ }
174
+ return left - right;
175
+ })
176
+ .map((entry) => entry.seat);
177
+ }
178
+ function buildSeat(seat, section, row, ctx) {
179
+ const type = ctx.legendById.get(seat.type) ?? { id: seat.type };
180
+ const price = resolveSeatPrice(seat, ctx.map);
181
+ if (price?.currency) {
182
+ ctx.currencies.add(price.currency);
183
+ }
184
+ ctx.typeCounts.set(seat.type, (ctx.typeCounts.get(seat.type) ?? 0) + 1);
185
+ // §5.1: a seat absent from the state document is available.
186
+ const status = ctx.state?.seats?.[seat.id];
187
+ const rendered = {
188
+ id: seat.id,
189
+ sectionId: section.id,
190
+ label: seat.label ?? seat.id,
191
+ row: seat.row,
192
+ rowLabel: row.rowLabel,
193
+ rowIndex: row.rowIndex,
194
+ col: seat.col,
195
+ x: seat.x,
196
+ y: seat.y,
197
+ rotation: seat.rotation,
198
+ type,
199
+ typeLabel: resolveLocalizedText(type.label, ctx.locale) ?? type.id,
200
+ typeColor: type.color,
201
+ attributes: seat.attributes ?? [],
202
+ accessibility: seat.accessibility,
203
+ price,
204
+ companions: seat.companions ?? [],
205
+ status: status?.status ?? 'available',
206
+ holdExpires: status?.holdExpires,
207
+ selectable: false,
208
+ source: seat,
209
+ };
210
+ rendered.selectable =
211
+ ctx.selectableStatuses.has(rendered.status) && (ctx.seatSelectable?.(rendered) ?? true);
212
+ ctx.seatsById.set(rendered.id, rendered);
213
+ return rendered;
214
+ }
215
+ /**
216
+ * Carries elements through with their `id` intact — the pre-1.0 adapter dropped
217
+ * it, leaving the render model with no element identity. A grid-addressed
218
+ * element gets its row's index so the layout can place it in the cell grid.
219
+ */
220
+ function buildElements(section, rows) {
221
+ const rowIndexById = new Map(rows.map((r) => [r.id, r.index]));
222
+ return (section.elements ?? []).map((element) => ({
223
+ id: element.id,
224
+ kind: element.kind,
225
+ label: element.label,
226
+ row: element.row,
227
+ rowIndex: element.row !== undefined ? rowIndexById.get(element.row) : undefined,
228
+ col: element.col,
229
+ x: element.x,
230
+ y: element.y,
231
+ width: element.width,
232
+ height: element.height,
233
+ rotation: element.rotation,
234
+ source: element,
235
+ }));
236
+ }
237
+ /** Sections in `Section.index` order, optionally filtered to an explicit set. */
238
+ function selectSections(map, sectionIds) {
239
+ const all = map.sections ?? [];
240
+ if (sectionIds) {
241
+ // An explicit list is also an explicit order — honor it verbatim.
242
+ return sectionIds
243
+ .map((id) => all.find((s) => s.id === id))
244
+ .filter((s) => s !== undefined);
245
+ }
246
+ return [...all]
247
+ .map((section, order) => ({ section, order }))
248
+ .sort((a, b) => orderKey(a.section.index, a.order) - orderKey(b.section.index, b.order))
249
+ .map((entry) => entry.section);
250
+ }
251
+ /**
252
+ * A sort key that keeps entries without an explicit `index` in declaration
253
+ * order, and after every entry that has one.
254
+ */
255
+ function orderKey(index, fallbackOrder) {
256
+ return index ?? Number.MAX_SAFE_INTEGER - 1_000_000 + fallbackOrder;
257
+ }
258
+ //# sourceMappingURL=build-render-model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-render-model.js","sourceRoot":"","sources":["../../src/render/build-render-model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * How each `Element.kind` is drawn.
3
+ *
4
+ * `kind` is open — the spec lists "screen" | "stage" | "exit" | "lavatory" |
5
+ * "gap" | "aisle" and explicitly allows implementation-defined values (§4.4) —
6
+ * so this is a lookup with a sensible default, never an exhaustive switch. An
7
+ * unrecognized kind draws as a labelled rectangle, which is what the pre-1.0
8
+ * renderer did for every kind including the ones it should have distinguished.
9
+ */
10
+ export type ElementShape =
11
+ /** A rounded rectangle. The default. */
12
+ 'rect'
13
+ /** A shallow arc bulging toward the audience — a cinema or theatre screen. */
14
+ | 'screen'
15
+ /** A rectangle with a heavier outline, reading as a raised platform. */
16
+ | 'stage'
17
+ /** A dashed outline: space that is deliberately empty. */
18
+ | 'void';
19
+ export interface ElementStyle {
20
+ shape: ElementShape;
21
+ /** Which themed color the element takes. */
22
+ tone: 'neutral' | 'accent' | 'muted';
23
+ /** Whether the element's label is drawn on it. */
24
+ showLabel: boolean;
25
+ }
26
+ /** The style for an element kind. Unknown kinds get the labelled rectangle. */
27
+ export declare function elementStyle(kind: string): ElementStyle;
28
+ /**
29
+ * An SVG path for a screen: a shallow arc across the top of its box, bulging
30
+ * downward toward the seats. Drawn as a filled sliver rather than a stroke so
31
+ * it reads at any size.
32
+ */
33
+ export declare function screenPath(x: number, y: number, width: number, height: number): string;
34
+ //# sourceMappingURL=element-shapes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-shapes.d.ts","sourceRoot":"","sources":["../../src/render/element-shapes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,YAAY;AACtB,wCAAwC;AACtC,MAAM;AACR,8EAA8E;GAC5E,QAAQ;AACV,wEAAwE;GACtE,OAAO;AACT,0DAA0D;GACxD,MAAM,CAAC;AAEX,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,YAAY,CAAC;IACpB,4CAA4C;IAC5C,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;IACrC,kDAAkD;IAClD,SAAS,EAAE,OAAO,CAAC;CACpB;AAmBD,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQtF"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * How each `Element.kind` is drawn.
3
+ *
4
+ * `kind` is open — the spec lists "screen" | "stage" | "exit" | "lavatory" |
5
+ * "gap" | "aisle" and explicitly allows implementation-defined values (§4.4) —
6
+ * so this is a lookup with a sensible default, never an exhaustive switch. An
7
+ * unrecognized kind draws as a labelled rectangle, which is what the pre-1.0
8
+ * renderer did for every kind including the ones it should have distinguished.
9
+ */
10
+ const DEFAULT_STYLE = { shape: 'rect', tone: 'neutral', showLabel: true };
11
+ const STYLES = {
12
+ screen: { shape: 'screen', tone: 'neutral', showLabel: true },
13
+ stage: { shape: 'stage', tone: 'neutral', showLabel: true },
14
+ pitch: { shape: 'stage', tone: 'muted', showLabel: true },
15
+ field: { shape: 'stage', tone: 'muted', showLabel: true },
16
+ exit: { shape: 'rect', tone: 'accent', showLabel: true },
17
+ door: { shape: 'rect', tone: 'accent', showLabel: true },
18
+ lavatory: { shape: 'rect', tone: 'muted', showLabel: true },
19
+ galley: { shape: 'rect', tone: 'muted', showLabel: true },
20
+ table: { shape: 'rect', tone: 'muted', showLabel: true },
21
+ aisle: { shape: 'void', tone: 'muted', showLabel: false },
22
+ gap: { shape: 'void', tone: 'muted', showLabel: true },
23
+ stairs: { shape: 'void', tone: 'muted', showLabel: true },
24
+ };
25
+ /** The style for an element kind. Unknown kinds get the labelled rectangle. */
26
+ export function elementStyle(kind) {
27
+ return STYLES[kind?.toLowerCase()] ?? DEFAULT_STYLE;
28
+ }
29
+ /**
30
+ * An SVG path for a screen: a shallow arc across the top of its box, bulging
31
+ * downward toward the seats. Drawn as a filled sliver rather than a stroke so
32
+ * it reads at any size.
33
+ */
34
+ export function screenPath(x, y, width, height) {
35
+ const bulge = height * 1.6;
36
+ return (`M ${x} ${y} ` +
37
+ `Q ${x + width / 2} ${y + bulge} ${x + width} ${y} ` +
38
+ `L ${x + width} ${y + height} ` +
39
+ `Q ${x + width / 2} ${y + height + bulge} ${x} ${y + height} Z`);
40
+ }
41
+ //# sourceMappingURL=element-shapes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-shapes.js","sourceRoot":"","sources":["../../src/render/element-shapes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoBH,MAAM,aAAa,GAAiB,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAExF,MAAM,MAAM,GAA2C;IACrD,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE;IAC7D,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE;IAC3D,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IACzD,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IACzD,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE;IACxD,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE;IACxD,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IAC3D,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IACzD,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IACxD,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE;IACzD,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;IACtD,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;CAC1D,CAAC;AAEF,+EAA+E;AAC/E,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,IAAI,aAAa,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,CAAS,EAAE,CAAS,EAAE,KAAa,EAAE,MAAc;IAC5E,MAAM,KAAK,GAAG,MAAM,GAAG,GAAG,CAAC;IAC3B,OAAO,CACL,KAAK,CAAC,IAAI,CAAC,GAAG;QACd,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG;QACpD,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,MAAM,GAAG;QAC/B,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAChE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The pure render model: a `KerusiMap` + `KerusiState` resolved into everything
3
+ * a renderer needs, and the geometry to place it.
4
+ *
5
+ * Nothing here imports a framework — the model can be built in a test, a
6
+ * build step, or on a server.
7
+ */
8
+ export * from './render-model.js';
9
+ export * from './build-render-model.js';
10
+ export * from './section-layout.js';
11
+ export * from './navigation-order.js';
12
+ export * from './element-shapes.js';
13
+ export * from './seat-shapes.js';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The pure render model: a `KerusiMap` + `KerusiState` resolved into everything
3
+ * a renderer needs, and the geometry to place it.
4
+ *
5
+ * Nothing here imports a framework — the model can be built in a test, a
6
+ * build step, or on a server.
7
+ */
8
+ export * from './render-model.js';
9
+ export * from './build-render-model.js';
10
+ export * from './section-layout.js';
11
+ export * from './navigation-order.js';
12
+ export * from './element-shapes.js';
13
+ export * from './seat-shapes.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { RenderSection } from './render-model.js';
2
+ /**
3
+ * Keyboard navigation order over a seat map.
4
+ *
5
+ * §4.3.1 gives `col` a second job beyond drawing: it is the *logical adjacency
6
+ * ordinal*, which is exactly what "the seat to my right" must mean for a
7
+ * keyboard or a screen reader. That holds in `mixed` sections too, where
8
+ * `x`/`y` places the seat but `col` still orders it — so a stand curving away
9
+ * from the pitch still reads seat 1, 2, 3.
10
+ *
11
+ * Freeform sections have no columns. Where their seats carry a `row` label,
12
+ * that grouping is used; where they carry nothing, seats are banded by `y` and
13
+ * ordered by `x`. That last case is a heuristic, and documented as one.
14
+ */
15
+ /** The seats reachable from a given seat, by id. */
16
+ export interface SeatNeighbours {
17
+ left?: string;
18
+ right?: string;
19
+ up?: string;
20
+ down?: string;
21
+ rowStart?: string;
22
+ rowEnd?: string;
23
+ }
24
+ export interface NavigationGraph {
25
+ /** Seats in traversal order: row by row, position by position. */
26
+ order: readonly string[];
27
+ /** Per-seat neighbours. */
28
+ neighbours: ReadonlyMap<string, SeatNeighbours>;
29
+ /** The first seat in the section, or `undefined` when it has none. */
30
+ first?: string;
31
+ /** The last seat in the section. */
32
+ last?: string;
33
+ }
34
+ export interface NavigationOptions {
35
+ /** Swaps left/right so arrow keys stay visually correct in RTL locales. */
36
+ rtl?: boolean;
37
+ /**
38
+ * Restrict traversal to seats passing this test — e.g. to skip unselectable
39
+ * seats. Default: every seat is reachable, since a screen-reader user needs
40
+ * to hear that a seat is booked, not have it hidden.
41
+ */
42
+ includes?: (seatId: string) => boolean;
43
+ }
44
+ /**
45
+ * Builds the navigation graph for one section. Sections are navigated
46
+ * independently: Tab moves between them, arrows move within.
47
+ */
48
+ export declare function buildNavigationGraph(section: RenderSection, options?: NavigationOptions): NavigationGraph;
49
+ //# sourceMappingURL=navigation-order.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation-order.d.ts","sourceRoot":"","sources":["../../src/render/navigation-order.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE9D;;;;;;;;;;;;GAYG;AAEH,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,2BAA2B;IAC3B,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAChD,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;CACxC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE,iBAAsB,GAC9B,eAAe,CAkCjB"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Builds the navigation graph for one section. Sections are navigated
3
+ * independently: Tab moves between them, arrows move within.
4
+ */
5
+ export function buildNavigationGraph(section, options = {}) {
6
+ const bands = section.layoutMode === 'freeform' ? freeformBands(section) : gridBands(section);
7
+ const included = options.includes;
8
+ const filtered = bands
9
+ .map((band) => band.filter((s) => !included || included(s.id)))
10
+ .filter((band) => band.length > 0);
11
+ const neighbours = new Map();
12
+ const order = [];
13
+ filtered.forEach((band, bandIndex) => {
14
+ band.forEach((seat, i) => {
15
+ order.push(seat.id);
16
+ const prev = band[i - 1]?.id;
17
+ const next = band[i + 1]?.id;
18
+ neighbours.set(seat.id, {
19
+ left: options.rtl ? next : prev,
20
+ right: options.rtl ? prev : next,
21
+ up: nearest(filtered[bandIndex - 1], seat),
22
+ down: nearest(filtered[bandIndex + 1], seat),
23
+ rowStart: band[0]?.id,
24
+ rowEnd: band[band.length - 1]?.id,
25
+ });
26
+ });
27
+ });
28
+ return {
29
+ order,
30
+ neighbours,
31
+ first: order[0],
32
+ last: order[order.length - 1],
33
+ };
34
+ }
35
+ /**
36
+ * Grid and mixed sections: the rows the render model already grouped, with
37
+ * seats ordered by `col`. `RenderRow.seats` is already in that order.
38
+ *
39
+ * Empty rows (§4.2.2) are skipped: they reserve space on the canvas but hold
40
+ * nothing to move to, and an empty band would strand an arrow press between two
41
+ * rows of seats.
42
+ */
43
+ function gridBands(section) {
44
+ return section.rows
45
+ .filter((row) => row.seats.length > 0)
46
+ .map((row) => row.seats.map((seat, i) => ({ id: seat.id, ordinal: seat.col ?? i })));
47
+ }
48
+ /**
49
+ * Freeform sections: group by the seats' `row` labels when they have them,
50
+ * since §4.5 permits `row` as a pure label in freeform mode and an author who
51
+ * supplied one has told us how the seats group.
52
+ *
53
+ * With no row labels at all, seats are banded by `y`: sorted top to bottom and
54
+ * split wherever the vertical step exceeds the median step, which separates
55
+ * distinct arcs without assuming they are level. This is a heuristic; a
56
+ * document that wants exact reading order should carry `row` labels.
57
+ */
58
+ function freeformBands(section) {
59
+ const toNav = (seats) => [...seats]
60
+ .sort((a, b) => (a.x ?? 0) - (b.x ?? 0))
61
+ .map((seat) => ({ id: seat.id, ordinal: seat.x ?? 0 }));
62
+ const hasRowLabels = section.seats.some((s) => s.row !== undefined && s.row !== '');
63
+ if (hasRowLabels) {
64
+ return section.rows.filter((row) => row.seats.length > 0).map((row) => toNav(row.seats));
65
+ }
66
+ const sorted = [...section.seats].sort((a, b) => (a.y ?? 0) - (b.y ?? 0));
67
+ if (sorted.length === 0) {
68
+ return [];
69
+ }
70
+ // Split where the vertical step is far larger than a typical one. "Typical"
71
+ // is the 25th percentile rather than the median: the between-row jumps are
72
+ // themselves steps, and in a section with few rows they drag the median up
73
+ // until no jump looks unusual any more.
74
+ const steps = sorted.slice(1).map((seat, i) => (seat.y ?? 0) - (sorted[i].y ?? 0));
75
+ const threshold = Math.max(percentile(steps, 0.25) * 4, 1);
76
+ const bands = [[sorted[0]]];
77
+ for (let i = 1; i < sorted.length; i++) {
78
+ const gap = (sorted[i].y ?? 0) - (sorted[i - 1].y ?? 0);
79
+ if (gap > threshold) {
80
+ bands.push([]);
81
+ }
82
+ bands[bands.length - 1].push(sorted[i]);
83
+ }
84
+ return bands.map(toNav);
85
+ }
86
+ /** The seat in `band` whose ordinal is closest to `from`'s — vertical movement. */
87
+ function nearest(band, from) {
88
+ if (!band || band.length === 0) {
89
+ return undefined;
90
+ }
91
+ let best = band[0];
92
+ let bestDistance = Math.abs(best.ordinal - from.ordinal);
93
+ for (const candidate of band.slice(1)) {
94
+ const distance = Math.abs(candidate.ordinal - from.ordinal);
95
+ if (distance < bestDistance) {
96
+ best = candidate;
97
+ bestDistance = distance;
98
+ }
99
+ }
100
+ return best.id;
101
+ }
102
+ function percentile(values, fraction) {
103
+ if (values.length === 0) {
104
+ return 0;
105
+ }
106
+ const sorted = [...values].sort((a, b) => a - b);
107
+ return sorted[Math.min(Math.floor(sorted.length * fraction), sorted.length - 1)];
108
+ }
109
+ //# sourceMappingURL=navigation-order.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation-order.js","sourceRoot":"","sources":["../../src/render/navigation-order.ts"],"names":[],"mappings":"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"}