@kerusiweb/core 2.3.0 → 3.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 (61) hide show
  1. package/LICENSE +22 -22
  2. package/README.md +70 -68
  3. package/dist/kerusi/index.d.ts +4 -3
  4. package/dist/kerusi/index.d.ts.map +1 -1
  5. package/dist/kerusi/index.js +4 -3
  6. package/dist/kerusi/index.js.map +1 -1
  7. package/dist/kerusi/kerusi-geometry.d.ts +47 -0
  8. package/dist/kerusi/kerusi-geometry.d.ts.map +1 -0
  9. package/dist/kerusi/kerusi-geometry.js +111 -0
  10. package/dist/kerusi/kerusi-geometry.js.map +1 -0
  11. package/dist/kerusi/kerusi-map.model.d.ts +121 -68
  12. package/dist/kerusi/kerusi-map.model.d.ts.map +1 -1
  13. package/dist/kerusi/kerusi-map.model.js +6 -2
  14. package/dist/kerusi/kerusi-map.model.js.map +1 -1
  15. package/dist/kerusi/kerusi-rows.d.ts +2 -2
  16. package/dist/kerusi/kerusi-rows.js +2 -2
  17. package/dist/kerusi/kerusi-validator.d.ts +18 -9
  18. package/dist/kerusi/kerusi-validator.d.ts.map +1 -1
  19. package/dist/kerusi/kerusi-validator.js +301 -122
  20. package/dist/kerusi/kerusi-validator.js.map +1 -1
  21. package/dist/render/build-render-model.d.ts +8 -4
  22. package/dist/render/build-render-model.d.ts.map +1 -1
  23. package/dist/render/build-render-model.js +147 -45
  24. package/dist/render/build-render-model.js.map +1 -1
  25. package/dist/render/index.d.ts +1 -1
  26. package/dist/render/index.d.ts.map +1 -1
  27. package/dist/render/index.js +1 -1
  28. package/dist/render/index.js.map +1 -1
  29. package/dist/render/map-layout.d.ts +92 -0
  30. package/dist/render/map-layout.d.ts.map +1 -0
  31. package/dist/render/map-layout.js +93 -0
  32. package/dist/render/map-layout.js.map +1 -0
  33. package/dist/render/navigation-order.d.ts +20 -10
  34. package/dist/render/navigation-order.d.ts.map +1 -1
  35. package/dist/render/navigation-order.js +72 -57
  36. package/dist/render/navigation-order.js.map +1 -1
  37. package/dist/render/render-model.d.ts +48 -26
  38. package/dist/render/render-model.d.ts.map +1 -1
  39. package/dist/render/seat-shapes.d.ts +3 -3
  40. package/dist/render/seat-shapes.js +3 -3
  41. package/dist/view/rtl.d.ts +1 -1
  42. package/dist/view/rtl.js +1 -1
  43. package/dist/view/seat-aria.d.ts +6 -0
  44. package/dist/view/seat-aria.d.ts.map +1 -1
  45. package/dist/view/seat-aria.js +8 -0
  46. package/dist/view/seat-aria.js.map +1 -1
  47. package/dist/view/seat-glyph.d.ts +1 -1
  48. package/dist/view/seat-glyph.d.ts.map +1 -1
  49. package/dist/view/viewport.d.ts +35 -7
  50. package/dist/view/viewport.d.ts.map +1 -1
  51. package/dist/view/viewport.js +56 -7
  52. package/dist/view/viewport.js.map +1 -1
  53. package/package.json +42 -42
  54. package/dist/kerusi/kerusi-layout-mode.d.ts +0 -58
  55. package/dist/kerusi/kerusi-layout-mode.d.ts.map +0 -1
  56. package/dist/kerusi/kerusi-layout-mode.js +0 -117
  57. package/dist/kerusi/kerusi-layout-mode.js.map +0 -1
  58. package/dist/render/section-layout.d.ts +0 -66
  59. package/dist/render/section-layout.d.ts.map +0 -1
  60. package/dist/render/section-layout.js +0 -186
  61. package/dist/render/section-layout.js.map +0 -1
@@ -0,0 +1,93 @@
1
+ import { elementStyle } from './element-shapes.js';
2
+ /**
3
+ * Places every seat and element of a map on its canvas.
4
+ *
5
+ * The canvas never shrinks to fit what is drawn: a section left out of the
6
+ * render model, or hidden by a binding, leaves its space empty, because closing
7
+ * the gap would move the sections around it.
8
+ */
9
+ export function computeMapLayout(map, options = {}) {
10
+ const { width, height } = map.canvas;
11
+ const mirror = options.rtl ? (x) => width - x : (x) => x;
12
+ const turn = (rotation) => (options.rtl ? -(rotation ?? 0) : (rotation ?? 0)) + 0;
13
+ const sections = map.sections.map((section) => {
14
+ const bounds = mirrorBounds(section.bounds, options.rtl ? width : undefined);
15
+ return {
16
+ section,
17
+ seats: section.seats.map((seat) => placeSeat(seat, mirror(seat.x), seat.y, turn(seat.rotation))),
18
+ elements: section.elements.map((element) => placeElement(element, mirror(element.x), element.y, turn(element.rotation), section.seatSize)),
19
+ bounds,
20
+ labelAnchor: labelAnchorOf(bounds, section.seatSize, !!options.rtl),
21
+ };
22
+ });
23
+ return { width, height, sections };
24
+ }
25
+ function placeSeat(seat, centerX, centerY, rotation) {
26
+ const size = seat.size;
27
+ return {
28
+ seat,
29
+ x: centerX - size / 2,
30
+ y: centerY - size / 2,
31
+ width: size,
32
+ height: size,
33
+ centerX,
34
+ centerY,
35
+ fontSize: size * 0.4,
36
+ rotation,
37
+ };
38
+ }
39
+ function placeElement(element, centerX, centerY, rotation, seatSize) {
40
+ const { width, height } = element;
41
+ return {
42
+ element,
43
+ x: centerX - width / 2,
44
+ y: centerY - height / 2,
45
+ width,
46
+ height,
47
+ centerX,
48
+ centerY,
49
+ fontSize: elementFontSize(element, width, height, seatSize),
50
+ rotation,
51
+ };
52
+ }
53
+ /** The label's inset from the bounds' corner and its size, as fractions of the seat size. */
54
+ const LABEL_INSET_RATIO = 0.25;
55
+ const LABEL_FONT_RATIO = 0.5;
56
+ function labelAnchorOf(bounds, seatSize, rtl) {
57
+ const inset = seatSize * LABEL_INSET_RATIO;
58
+ const fontSize = seatSize * LABEL_FONT_RATIO;
59
+ const above = bounds.y - inset - fontSize;
60
+ const edge = above >= 0 ? 0 : inset;
61
+ return {
62
+ x: rtl ? bounds.x + bounds.width - edge : bounds.x + edge,
63
+ y: above >= 0 ? above : bounds.y + inset,
64
+ fontSize,
65
+ };
66
+ }
67
+ /** A box, mirrored about a canvas of width `canvasWidth` when one is given. */
68
+ function mirrorBounds(bounds, canvasWidth) {
69
+ if (canvasWidth === undefined) {
70
+ return { ...bounds };
71
+ }
72
+ return { ...bounds, x: canvasWidth - bounds.x - bounds.width };
73
+ }
74
+ /** Average glyph advance as a fraction of the font size, for fitting text to a width. */
75
+ const GLYPH_WIDTH = 0.6;
76
+ /**
77
+ * The smallest element font, as a fraction of the section's seat size. Canvas
78
+ * units are abstract, so a floor in units would be huge on one map and
79
+ * invisible on another; the seat size is the one length every map has.
80
+ */
81
+ const MIN_FONT_RATIO = 0.2;
82
+ function elementFontSize(element, width, height, seatSize) {
83
+ const floor = seatSize * MIN_FONT_RATIO;
84
+ if (elementStyle(element.kind).shape !== 'text') {
85
+ return Math.max(Math.min(height * 0.5, width * 0.18), floor);
86
+ }
87
+ // A text element has no box to read from, so the height sets the size and the
88
+ // width only shrinks it, so the whole label fits however long it is.
89
+ const base = Math.max(height * 0.6, floor);
90
+ const length = element.label?.length ?? 0;
91
+ return length ? Math.min(base, width / (length * GLYPH_WIDTH)) : base;
92
+ }
93
+ //# sourceMappingURL=map-layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-layout.js","sourceRoot":"","sources":["../../src/render/map-layout.ts"],"names":[],"mappings":"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"}
@@ -2,15 +2,24 @@ import { RenderSection } from './render-model.js';
2
2
  /**
3
3
  * Keyboard navigation order over a seat map.
4
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.
5
+ * §4.3.5 makes `row` and `col` the source of truth for "next to": two seats are
6
+ * neighbours when they share a row and their cols differ by one, whatever the
7
+ * drawing looks like. That is exactly what "the seat to my right" must mean for
8
+ * a keyboard or a screen reader, so a curved balcony or a round table still
9
+ * reads seat 1, 2, 3.
10
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.
11
+ * - Left/right moves along the row in `col` order. At an aisle, a skipped
12
+ * `col`, it jumps to the next seat in the row rather than stopping, so a
13
+ * keyboard user is never stranded; `RenderSeat.besideAisle` lets the seat's
14
+ * description say the aisle is there.
15
+ * - Up/down moves to the next row in the §4.2.1 order and picks the seat
16
+ * nearest on the canvas.
17
+ * - Seats with no `row` have no adjacency, so they fall back to a heuristic:
18
+ * banded by `y` and ordered by `x`.
19
+ *
20
+ * The arrows follow the screen. A row whose cols run right to left on the
21
+ * canvas, or a mirrored canvas (D3), swaps left and right; rows ordered up the
22
+ * screen swap up and down.
14
23
  */
15
24
  /** The seats reachable from a given seat, by id. */
16
25
  export interface SeatNeighbours {
@@ -32,7 +41,7 @@ export interface NavigationGraph {
32
41
  last?: string;
33
42
  }
34
43
  export interface NavigationOptions {
35
- /** Swaps left/right so arrow keys stay visually correct in RTL locales. */
44
+ /** The canvas is mirrored for a right-to-left locale, so left and right swap with it. */
36
45
  rtl?: boolean;
37
46
  /**
38
47
  * Restrict traversal to seats passing this test — e.g. to skip unselectable
@@ -43,7 +52,8 @@ export interface NavigationOptions {
43
52
  }
44
53
  /**
45
54
  * Builds the navigation graph for one section. Sections are navigated
46
- * independently: Tab moves between them, arrows move within.
55
+ * independently: Tab moves between them, arrows move within. Seats in
56
+ * different sections are never neighbours (§4.3.5).
47
57
  */
48
58
  export declare function buildNavigationGraph(section: RenderSection, options?: NavigationOptions): NavigationGraph;
49
59
  //# sourceMappingURL=navigation-order.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"navigation-order.d.ts","sourceRoot":"","sources":["../../src/render/navigation-order.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;GAqBG;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,yFAAyF;IACzF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE,iBAAsB,GAC9B,eAAe,CAwCjB"}
@@ -1,27 +1,34 @@
1
1
  /**
2
2
  * Builds the navigation graph for one section. Sections are navigated
3
- * independently: Tab moves between them, arrows move within.
3
+ * independently: Tab moves between them, arrows move within. Seats in
4
+ * different sections are never neighbours (§4.3.5).
4
5
  */
5
6
  export function buildNavigationGraph(section, options = {}) {
6
- const bands = section.layoutMode === 'freeform' ? freeformBands(section) : gridBands(section);
7
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);
8
+ const bands = section.rows
9
+ .flatMap(rowBands)
10
+ .map((band) => ({
11
+ seats: band.filter((s) => !included || included(s.id)),
12
+ ascending: runsRight(band) !== !!options.rtl,
13
+ }))
14
+ .filter((band) => band.seats.length > 0);
15
+ const downIsNext = runsDown(bands.map((b) => b.seats));
11
16
  const neighbours = new Map();
12
17
  const order = [];
13
- filtered.forEach((band, bandIndex) => {
14
- band.forEach((seat, i) => {
18
+ bands.forEach(({ seats, ascending }, bandIndex) => {
19
+ const above = bands[downIsNext ? bandIndex - 1 : bandIndex + 1]?.seats;
20
+ const below = bands[downIsNext ? bandIndex + 1 : bandIndex - 1]?.seats;
21
+ seats.forEach((seat, i) => {
15
22
  order.push(seat.id);
16
- const prev = band[i - 1]?.id;
17
- const next = band[i + 1]?.id;
23
+ const prev = seats[i - 1]?.id;
24
+ const next = seats[i + 1]?.id;
18
25
  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,
26
+ left: ascending ? prev : next,
27
+ right: ascending ? next : prev,
28
+ up: nearest(above, seat),
29
+ down: nearest(below, seat),
30
+ rowStart: seats[0]?.id,
31
+ rowEnd: seats[seats.length - 1]?.id,
25
32
  });
26
33
  });
27
34
  });
@@ -33,65 +40,73 @@ export function buildNavigationGraph(section, options = {}) {
33
40
  };
34
41
  }
35
42
  /**
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.
43
+ * The bands one row contributes. A named row is one band, in the `col`-then-`x`
44
+ * order the render model already gave it. An empty row (§4.2.2) contributes
45
+ * nothing, so an arrow press never lands between two rows of seats.
38
46
  *
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.
47
+ * The seats with no `row` are not a row at all, so they are banded by `y`:
48
+ * sorted top to bottom and split wherever the vertical step is far larger than
49
+ * a typical one, which separates distinct arcs without assuming they are
50
+ * level. This is a heuristic; a document that wants exact reading order gives
51
+ * its seats `row` and `col`.
42
52
  */
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) {
53
+ function rowBands(row) {
54
+ if (row.seats.length === 0) {
68
55
  return [];
69
56
  }
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);
57
+ if (row.id !== '') {
58
+ return [[...row.seats]];
59
+ }
60
+ const sorted = [...row.seats].sort((a, b) => a.y - b.y);
61
+ // "Typical" is the 25th percentile rather than the median: the between-band
62
+ // jumps are themselves steps, and with few bands they drag the median up
63
+ // until no jump looks unusual any more. The floor keeps seats a fraction of
64
+ // a seat apart in one band.
65
+ const steps = sorted.slice(1).map((seat, i) => seat.y - sorted[i].y);
66
+ const threshold = Math.max(percentile(steps, 0.25) * 4, sorted[0].size / 4);
76
67
  const bands = [[sorted[0]]];
77
68
  for (let i = 1; i < sorted.length; i++) {
78
- const gap = (sorted[i].y ?? 0) - (sorted[i - 1].y ?? 0);
79
- if (gap > threshold) {
69
+ if (sorted[i].y - sorted[i - 1].y > threshold) {
80
70
  bands.push([]);
81
71
  }
82
72
  bands[bands.length - 1].push(sorted[i]);
83
73
  }
84
- return bands.map(toNav);
74
+ return bands.map((band) => band.sort((a, b) => a.x - b.x));
75
+ }
76
+ /**
77
+ * Whether a band's order runs left to right on the canvas, judged from its two
78
+ * ends. A band whose ends are level, such as a round table's, counts as left
79
+ * to right.
80
+ */
81
+ function runsRight(band) {
82
+ const dx = band[band.length - 1].x - band[0].x;
83
+ return dx >= -tolerance(band[0]);
84
+ }
85
+ /**
86
+ * Whether the row order runs down the screen, judged from the first and last
87
+ * bands' mean `y`. Applied to the whole section, so up and down stay each
88
+ * other's inverse.
89
+ */
90
+ function runsDown(bands) {
91
+ if (bands.length < 2) {
92
+ return true;
93
+ }
94
+ const meanY = (band) => band.reduce((sum, s) => sum + s.y, 0) / band.length;
95
+ return meanY(bands[bands.length - 1]) - meanY(bands[0]) >= -tolerance(bands[0][0]);
96
+ }
97
+ /** Below this, two positions count as level: a small fraction of a seat. */
98
+ function tolerance(seat) {
99
+ return seat.size * 1e-6;
85
100
  }
86
- /** The seat in `band` whose ordinal is closest to `from`'s — vertical movement. */
101
+ /** The seat in `band` nearest `from` on the canvas — vertical movement. */
87
102
  function nearest(band, from) {
88
103
  if (!band || band.length === 0) {
89
104
  return undefined;
90
105
  }
91
106
  let best = band[0];
92
- let bestDistance = Math.abs(best.ordinal - from.ordinal);
107
+ let bestDistance = Math.hypot(best.x - from.x, best.y - from.y);
93
108
  for (const candidate of band.slice(1)) {
94
- const distance = Math.abs(candidate.ordinal - from.ordinal);
109
+ const distance = Math.hypot(candidate.x - from.x, candidate.y - from.y);
95
110
  if (distance < bestDistance) {
96
111
  best = candidate;
97
112
  bestDistance = distance;
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-order.js","sourceRoot":"","sources":["../../src/render/navigation-order.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"navigation-order.js","sourceRoot":"","sources":["../../src/render/navigation-order.ts"],"names":[],"mappings":"AAyDA;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAsB,EACtB,UAA6B,EAAE;IAE/B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI;SACvB,OAAO,CAAC,QAAQ,CAAC;SACjB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACd,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtD,SAAS,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG;KAC7C,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE3C,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,IAAI,GAAG,EAA0B,CAAC;IACrD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,EAAE;QAChD,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC;QACvE,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC;QAEvE,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YACxB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAEpB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC;YAC9B,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE;gBACtB,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;gBAC7B,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;gBAC9B,EAAE,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC;gBACxB,IAAI,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC;gBAC1B,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;gBACtB,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE;aACpC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK;QACL,UAAU;QACV,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QACf,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;KAC9B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,QAAQ,CAAC,GAAc;IAC9B,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QAClB,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAExD,4EAA4E;IAC5E,yEAAyE;IACzE,4EAA4E;IAC5E,4BAA4B;IAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IAE5E,MAAM,KAAK,GAAmB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,CAAC;YAC9C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,IAA2B;IAC5C,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,SAAS,QAAQ,CAAC,KAAyC;IACzD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,IAA2B,EAAE,EAAE,CAC5C,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IACtD,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,4EAA4E;AAC5E,SAAS,SAAS,CAAC,IAAgB;IACjC,OAAO,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;AAC1B,CAAC;AAED,2EAA2E;AAC3E,SAAS,OAAO,CAAC,IAAuC,EAAE,IAAgB;IACxE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,IAAI,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAChE,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACxE,IAAI,QAAQ,GAAG,YAAY,EAAE,CAAC;YAC5B,IAAI,GAAG,SAAS,CAAC;YACjB,YAAY,GAAG,QAAQ,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,EAAE,CAAC;AACjB,CAAC;AAED,SAAS,UAAU,CAAC,MAAyB,EAAE,QAAgB;IAC7D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACjD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;AACnF,CAAC"}
@@ -1,10 +1,10 @@
1
- import { Accessibility, Element, Money, PriceTier, Seat, SeatType, Section } from '../kerusi/kerusi-map.model.js';
2
- import { SectionLayoutMode } from '../kerusi/kerusi-layout-mode.js';
1
+ import { Accessibility, Bounds, Canvas, Element, Money, PriceTier, Seat, SeatType, Section } from '../kerusi/kerusi-map.model.js';
3
2
  import { SeatStatus } from '../kerusi/kerusi-state.model.js';
4
3
  /**
5
4
  * The renderer's view of a Kerusi document pair: a `KerusiMap` with its
6
5
  * `KerusiState` merged in, every reference resolved, every label localized, and
7
- * every section carrying its own layout mode.
6
+ * every size the document leaves to a default filled in. Positions stay in the
7
+ * map's canvas units (§4.5), exactly as the document gives them.
8
8
  *
9
9
  * This is deliberately not the source documents — a renderer should never have
10
10
  * to re-resolve a price tier or re-read a locale map while drawing. It is also
@@ -26,14 +26,25 @@ export interface RenderSeat {
26
26
  rowLabel?: string;
27
27
  /** The row's position among the section's rows, for vertical navigation. */
28
28
  rowIndex: number;
29
- /** `Seat.col` — the adjacency ordinal, meaningful in grid and mixed (§4.3.1). */
29
+ /**
30
+ * `Seat.col` — the adjacency ordinal within the row (§4.3.5). It says which
31
+ * seats are neighbours, never where a seat is drawn.
32
+ */
30
33
  col?: number;
31
- /** `Seat.x`, 0–100 percent of the section canvas. */
32
- x?: number;
33
- /** `Seat.y`, 0–100 percent of the section canvas. */
34
- y?: number;
35
- /** `Seat.rotation` in degrees. */
34
+ /** `Seat.x`: the centre, in canvas units. */
35
+ x: number;
36
+ /** `Seat.y`: the centre, in canvas units. */
37
+ y: number;
38
+ /** The side of the seat's square footprint, in canvas units: its section's seat size. */
39
+ size: number;
40
+ /** `Seat.rotation` in degrees, clockwise on screen about the centre. */
36
41
  rotation?: number;
42
+ /**
43
+ * Whether an aisle runs beside this seat: the next seat along its row skips
44
+ * a `col`, so the two are not neighbours (§4.3.5). Read from adjacency, never
45
+ * from the drawing.
46
+ */
47
+ besideAisle: boolean;
37
48
  /** The resolved legend entry, not merely its id (§4.7). */
38
49
  type: SeatType;
39
50
  /** `SeatType.label` localized, defaulting to the type id. */
@@ -63,13 +74,15 @@ export interface RenderElement {
63
74
  /** `Element.kind`: "screen" | "stage" | "exit" | "lavatory" | "gap" | "label" | ... */
64
75
  kind: string;
65
76
  label?: string;
66
- row?: string;
67
- rowIndex?: number;
68
- col?: number;
69
- x?: number;
70
- y?: number;
71
- width?: number;
72
- height?: number;
77
+ /** `Element.x`: the centre, in canvas units. */
78
+ x: number;
79
+ /** `Element.y`: the centre, in canvas units. */
80
+ y: number;
81
+ /** Canvas units; the section's seat size when the document gives none (§4.4.1). */
82
+ width: number;
83
+ /** Canvas units; the section's seat size when the document gives none (§4.4.1). */
84
+ height: number;
85
+ /** Degrees, clockwise on screen about the centre. */
73
86
  rotation?: number;
74
87
  source: Element;
75
88
  }
@@ -81,13 +94,12 @@ export interface RenderRow {
81
94
  label?: string;
82
95
  /** Position among the section's rows, after the §4.2.1 ordering. */
83
96
  index: number;
84
- /** The seats in this row, ordered by `col` (grid/mixed) or `x` (freeform). */
97
+ /** The seats in this row, ordered by `col`, then by `x`. */
85
98
  seats: readonly RenderSeat[];
86
99
  /**
87
- * An **empty row** (§4.2.2): declared by `Section.rows`, referenced by no
88
- * seat, and still occupying a slot — it reserves the vertical space one row
89
- * of seats would. This is what a document uses to open the throw between a
90
- * cinema screen and row A, or a cross-aisle mid-section.
100
+ * An **empty row** (§4.2.2): declared by `Section.rows` and referenced by no
101
+ * seat. It takes its place in the row order and reserves nothing: rows give
102
+ * order and adjacency, never space. Kept as information only.
91
103
  */
92
104
  empty: boolean;
93
105
  }
@@ -101,10 +113,16 @@ export interface RenderSection {
101
113
  id: string;
102
114
  /** `Section.label` localized; absent when the section declares none. */
103
115
  label?: string;
104
- /** The resolved mode — declared, or inferred per §4.5. */
105
- layoutMode: SectionLayoutMode;
106
- /** "width:height"; meaningful for freeform and mixed sections. */
107
- aspectRatio: string;
116
+ /** The seat size that applies in this section (§4.5), resolved. */
117
+ seatSize: number;
118
+ /**
119
+ * The section's extent on the canvas: `Section.bounds` when declared, else
120
+ * the box around its seats' and elements' footprints. For zooming to a
121
+ * section and revealing a focused seat; never for placing anything.
122
+ */
123
+ bounds: Bounds;
124
+ /** Whether `bounds` is the document's own `Section.bounds` rather than computed. */
125
+ boundsDeclared: boolean;
108
126
  /** Rows in display order. Grouping only — seats do not live inside rows. */
109
127
  rows: readonly RenderRow[];
110
128
  /** Every seat in the section, flat, in row-then-position order. */
@@ -114,7 +132,7 @@ export interface RenderSection {
114
132
  /**
115
133
  * `Section.directions` localized (§4.10) — what an axis means in the physical
116
134
  * world. Informational only: nothing in this library reads it to decide
117
- * layout or screen placement, exactly as nothing branches on `RenderMap.domain`.
135
+ * screen placement, exactly as nothing branches on `RenderMap.domain`.
118
136
  */
119
137
  directions?: readonly RenderDirection[];
120
138
  source: Section;
@@ -127,6 +145,10 @@ export interface RenderMap {
127
145
  locale: string;
128
146
  /** `KerusiMap.domain` — informational only; never used to reject or branch. */
129
147
  domain?: string;
148
+ /** `KerusiMap.canvas`: the one drawing surface every section shares (§4.5). */
149
+ canvas: Canvas;
150
+ /** `KerusiMap.seatSize`: the map-wide seat size, before any section overrides it. */
151
+ seatSize: number;
130
152
  /** Sections in `Section.index` order. */
131
153
  sections: readonly RenderSection[];
132
154
  /** The legend, with labels localized, for a key or legend component. */
@@ -1 +1 @@
1
- {"version":3,"file":"render-model.d.ts","sourceRoot":"","sources":["../../src/render/render-model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"render-model.d.ts","sourceRoot":"","sources":["../../src/render/render-model.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,MAAM,EACN,MAAM,EACN,OAAO,EACP,KAAK,EACL,SAAS,EACT,IAAI,EACJ,QAAQ,EACR,OAAO,EACR,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAE7D;;;;;;;;;;GAUG;AAEH,2DAA2D;AAC3D,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;AAEpD,MAAM,WAAW,UAAU;IACzB,yEAAyE;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IAGd,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,CAAC,EAAE,MAAM,CAAC;IACV,6CAA6C;IAC7C,CAAC,EAAE,MAAM,CAAC;IACV,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAC;IAGrB,2DAA2D;IAC3D,IAAI,EAAE,QAAQ,CAAC;IACf,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,oDAAoD;IACpD,aAAa,CAAC,EAAE,aAAa,CAAC;IAG9B,gFAAgF;IAChF,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,mEAAmE;IACnE,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAG9B,2EAA2E;IAC3E,MAAM,EAAE,gBAAgB,CAAC;IACzB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,UAAU,EAAE,OAAO,CAAC;IAEpB,yEAAyE;IACzE,MAAM,EAAE,IAAI,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,EAAE,EAAE,MAAM,CAAC;IACX,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,CAAC,EAAE,MAAM,CAAC;IACV,gDAAgD;IAChD,CAAC,EAAE,MAAM,CAAC;IACV,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,yEAAyE;AACzE,MAAM,WAAW,SAAS;IACxB,2EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAC;IACX,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B;;;;OAIG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,GAAG,GAAG,GAAG,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,cAAc,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,IAAI,EAAE,SAAS,SAAS,EAAE,CAAC;IAC3B,mEAAmE;IACnE,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,oCAAoC;IACpC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACxC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,wEAAwE;IACxE,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACrC,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAC5C;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,yDAAyD;IACzD,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -31,8 +31,8 @@ export declare function seatBodyPath(x: number, y: number, width: number, height
31
31
  * The width of the frame around a selected seat's core — feed it straight to
32
32
  * {@link seatBodyPath}'s `inset` to get the core.
33
33
  *
34
- * A seat is always square: `section-layout` places every one of them as
35
- * `size × size`, in both the grid and the freeform mode. That makes the inset
34
+ * A seat is always square: `map-layout` places every one of them as
35
+ * `size × size`, every seat in a section the same size. That makes the inset
36
36
  * shape an *exact* scaled copy of the body about its centre, not merely a
37
37
  * similar-looking one, so any mark that clears the body also clears the core
38
38
  * when it is drawn to the core's own box. That is what lets the frame width
@@ -62,7 +62,7 @@ export declare const seatRingStroke: typeof seatSelectedFrame;
62
62
  * though, so pass the box the figure is meant to sit *on*: a selected seat
63
63
  * draws it to the core's box, not the seat's, or the hips cross the frame.
64
64
  *
65
- * The ratios assume a square box, which is what `section-layout` always
65
+ * The ratios assume a square box, which is what `map-layout` always
66
66
  * places. On a wide box the head, sized from the width, would outgrow the
67
67
  * height.
68
68
  */
@@ -98,8 +98,8 @@ export function seatBodyPath(x, y, width, height, inset = 0) {
98
98
  * The width of the frame around a selected seat's core — feed it straight to
99
99
  * {@link seatBodyPath}'s `inset` to get the core.
100
100
  *
101
- * A seat is always square: `section-layout` places every one of them as
102
- * `size × size`, in both the grid and the freeform mode. That makes the inset
101
+ * A seat is always square: `map-layout` places every one of them as
102
+ * `size × size`, every seat in a section the same size. That makes the inset
103
103
  * shape an *exact* scaled copy of the body about its centre, not merely a
104
104
  * similar-looking one, so any mark that clears the body also clears the core
105
105
  * when it is drawn to the core's own box. That is what lets the frame width
@@ -131,7 +131,7 @@ export const seatRingStroke = seatSelectedFrame;
131
131
  * though, so pass the box the figure is meant to sit *on*: a selected seat
132
132
  * draws it to the core's box, not the seat's, or the hips cross the frame.
133
133
  *
134
- * The ratios assume a square box, which is what `section-layout` always
134
+ * The ratios assume a square box, which is what `map-layout` always
135
135
  * places. On a wide box the head, sized from the width, would outgrow the
136
136
  * height.
137
137
  */
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Which locales are written right-to-left.
3
3
  *
4
- * A binding needs this to decide whether to mirror a grid section and swap the
4
+ * A binding needs this to decide whether to mirror the whole canvas and swap the
5
5
  * left/right arrow keys, and it would be written identically in every one —
6
6
  * so it belongs here rather than in any of them.
7
7
  */
package/dist/view/rtl.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Which locales are written right-to-left.
3
3
  *
4
- * A binding needs this to decide whether to mirror a grid section and swap the
4
+ * A binding needs this to decide whether to mirror the whole canvas and swap the
5
5
  * left/right arrow keys, and it would be written identically in every one —
6
6
  * so it belongs here rather than in any of them.
7
7
  */
@@ -15,6 +15,8 @@ import { DisallowedReason } from './selection.js';
15
15
  export interface SeatAriaStrings {
16
16
  rowSeat: (row: string, seat: string) => string;
17
17
  seatOnly: (seat: string) => string;
18
+ /** Said when an aisle runs beside the seat (§4.3.5: a skipped `col`). */
19
+ besideAisle: string;
18
20
  noPrice: string;
19
21
  available: string;
20
22
  booked: string;
@@ -35,6 +37,10 @@ export declare const DEFAULT_SEAT_ARIA_STRINGS: SeatAriaStrings;
35
37
  * Builds a seat's accessible name, in the order a listener needs it: where the
36
38
  * seat is, what kind it is, what it costs, whether it can be had, what
37
39
  * accessibility it offers, and finally its descriptive tags.
40
+ *
41
+ * "Where" comes from the document's row and adjacency, never from `x`/`y`: an
42
+ * aisle is mentioned when the next seat along the row skips a `col` (§4.3.5),
43
+ * which is also where the arrow keys jump across it.
38
44
  */
39
45
  export declare function seatAriaLabel(seat: RenderSeat, locale: string, strings?: SeatAriaStrings): string;
40
46
  /** A hold expiry as a local clock time; falls back to the raw value. */
@@ -1 +1 @@
1
- {"version":3,"file":"seat-aria.d.ts","sourceRoot":"","sources":["../../src/view/seat-aria.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;;;;;;GAUG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3D,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;CAC9C;AAED,eAAO,MAAM,yBAAyB,EAAE,eAwBvC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,eAA2C,GACnD,MAAM,CAsBR;AAwCD,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAU9D;AAED,mEAAmE;AACnE,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,gBAAgB,EACxB,OAAO,GAAE,eAA2C,GACnD,MAAM,CAKR"}
1
+ {"version":3,"file":"seat-aria.d.ts","sourceRoot":"","sources":["../../src/view/seat-aria.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;;;;;;GAUG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3D,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;CAC9C;AAED,eAAO,MAAM,yBAAyB,EAAE,eAyBvC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,eAA2C,GACnD,MAAM,CAyBR;AAwCD,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAU9D;AAED,mEAAmE;AACnE,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,gBAAgB,EACxB,OAAO,GAAE,eAA2C,GACnD,MAAM,CAKR"}