@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
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015-2026 Arsyad "Shad Ahm" Ahmad
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # @kerusiweb/core
2
+
3
+ Framework-agnostic core for the **[Kerusi Seat Map & Availability
4
+ Format](https://github.com/ShadAhm/kerusi)**: the document types, a conformance
5
+ validator, and the render model and geometry a seat-map renderer consumes.
6
+
7
+ Pure TypeScript. No Angular, no React, no RxJS, no DOM — so it runs in a
8
+ browser, in a build step, or on a server.
9
+
10
+ ```bash
11
+ npm install @kerusiweb/core
12
+ ```
13
+
14
+ For a UI on top of it, add the binding for your framework:
15
+ [`@kerusiweb/angular`](https://www.npmjs.com/package/@kerusiweb/angular) or
16
+ [`@kerusiweb/react`](https://www.npmjs.com/package/@kerusiweb/react). Both
17
+ expose the same surface and emit the same SVG.
18
+
19
+ ## What's in it
20
+
21
+ **`kerusi/` — the format.** `KerusiMap`, `KerusiState`, `KerusiSession`, `Seat`,
22
+ `Section`, `Money` and friends, plus the logic the standard defines over them:
23
+
24
+ ```ts
25
+ import { validateDocumentSet, errorsOf, resolveSeatPrice, expireHolds } from '@kerusiweb/core';
26
+
27
+ const violations = validateDocumentSet({ map, state, session });
28
+ if (errorsOf(violations).length) throw new Error('non-conformant documents');
29
+ ```
30
+
31
+ **`render/` — the resolved view and its geometry.** `buildRenderModel()` merges
32
+ a state onto a map and resolves prices, locale, row order and layout mode into a
33
+ `RenderMap`; `computeSectionLayout()` turns a section into placed seats with
34
+ coordinates; `seatBodyPath()` and friends return SVG path `d` strings.
35
+
36
+ ```ts
37
+ import { buildRenderModel, computeSectionLayout, seatBodyPath } from '@kerusiweb/core';
38
+
39
+ const model = buildRenderModel(map, state);
40
+ const layout = computeSectionLayout(model.sections[0], { seatSize: 28, seatGap: 6 });
41
+ // layout.width / layout.height → the section's intrinsic viewBox
42
+ // layout.seats[0] → { seat, x, y, width, height, centerX, centerY, ... }
43
+ const { x, y, width, height } = layout.seats[0];
44
+ const d = seatBodyPath(x, y, width, height); // → 'M4 8 …', ready for <path d>
45
+ ```
46
+
47
+ **`view/` — presentation policy, still framework-free.** The colour system
48
+ (`DEFAULT_KERUSI_COLORS`, `seatFill`, `readableOn`), the ARIA strings
49
+ (`seatAriaLabel`, `disallowedAnnouncement`), and the selection rules
50
+ (`toggleSeatSelection`, `summarizeSelection`, `DisallowedReason`).
51
+
52
+ ## Server-side use
53
+
54
+ Core is `"type": "module"` and its emitted imports carry explicit extensions, so
55
+ it loads under plain Node ESM with no bundler:
56
+
57
+ ```js
58
+ import { validateDocumentSet } from '@kerusiweb/core';
59
+ ```
60
+
61
+ ## Contributing
62
+
63
+ See [docs/architecture.md](../../docs/architecture.md) for what belongs here and
64
+ what belongs in a framework binding.
65
+
66
+ ---
67
+
68
+ MIT © Arsyad "Shad Ahm" Ahmad
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Kerusi Seat Map & Availability Format support.
3
+ *
4
+ * The document types, a conformance validator, and the price, locale and
5
+ * layout-mode resolution the renderer is built on. Everything here is pure —
6
+ * no framework import — so it can be used server-side or in a build step.
7
+ *
8
+ * See docs/kerusi.md and the full standard for details.
9
+ */
10
+ export * from './kerusi-map.model.js';
11
+ export * from './kerusi-state.model.js';
12
+ export * from './kerusi-violation.js';
13
+ export * from './kerusi-layout-mode.js';
14
+ export * from './kerusi-rows.js';
15
+ export * from './kerusi-locale.js';
16
+ export * from './kerusi-price.js';
17
+ export * from './kerusi-validator.js';
18
+ export * from './kerusi-state.js';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/kerusi/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Kerusi Seat Map & Availability Format support.
3
+ *
4
+ * The document types, a conformance validator, and the price, locale and
5
+ * layout-mode resolution the renderer is built on. Everything here is pure —
6
+ * no framework import — so it can be used server-side or in a build step.
7
+ *
8
+ * See docs/kerusi.md and the full standard for details.
9
+ */
10
+ export * from './kerusi-map.model.js';
11
+ export * from './kerusi-state.model.js';
12
+ export * from './kerusi-violation.js';
13
+ export * from './kerusi-layout-mode.js';
14
+ export * from './kerusi-rows.js';
15
+ export * from './kerusi-locale.js';
16
+ export * from './kerusi-price.js';
17
+ export * from './kerusi-validator.js';
18
+ export * from './kerusi-state.js';
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/kerusi/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,58 @@
1
+ import { Section } from './kerusi-map.model.js';
2
+ import { KerusiViolation } from './kerusi-violation.js';
3
+ /** The three positioning modes a section may use (§4.5). */
4
+ export type SectionLayoutMode = 'grid' | 'freeform' | 'mixed';
5
+ /**
6
+ * `Section.layout` is a strict, validated constraint — not a rendering hint.
7
+ * This module is the single implementation of §4.5, shared by the validator and
8
+ * the render-model builder so the two can never disagree about what mode a
9
+ * section is in.
10
+ *
11
+ * Per §4.5:
12
+ * - `grid` — every seat MUST have `col`; no seat may have `x` or `y`.
13
+ * - `freeform` — every seat MUST have both `x` and `y`; no seat may have `col`.
14
+ * - `mixed` — every seat MUST have `col` AND both `x` and `y`.
15
+ *
16
+ * A free-text `row` is permitted in every mode: it carries no positional
17
+ * information, so it never participates in the constraint.
18
+ */
19
+ /**
20
+ * The positional members a seat or element may carry. Elements are positioned
21
+ * the same way seats are (§4.4), so these predicates take the shape rather than
22
+ * the full `Seat`.
23
+ */
24
+ export interface Positioned {
25
+ col?: number;
26
+ x?: number;
27
+ y?: number;
28
+ }
29
+ /** True when the item carries a usable grid column. */
30
+ export declare function hasCol(item: Positioned): boolean;
31
+ /** True when the item carries both freeform coordinates. Neither alone counts. */
32
+ export declare function hasXY(item: Positioned): boolean;
33
+ /** True when the item carries either coordinate — the `grid` prohibition. */
34
+ export declare function hasAnyCoordinate(item: Positioned): boolean;
35
+ /**
36
+ * Infers a section's mode from its seats when `Section.layout` is omitted
37
+ * (§4.5). Returns `undefined` when the seats are inconsistent — the caller MUST
38
+ * reject such a section.
39
+ *
40
+ * An empty section infers `grid`: it satisfies "every seat has `col`" vacuously
41
+ * and grid is the cheaper mode to render.
42
+ */
43
+ export declare function inferSectionLayoutMode(section: Section): SectionLayoutMode | undefined;
44
+ /**
45
+ * The mode a section renders in: its declared `layout`, or the inferred one.
46
+ * Falls back to `grid` for an inconsistent section so a renderer can still draw
47
+ * *something* after the validator has reported the fault. Callers that must not
48
+ * render an invalid document should check {@link checkSectionLayout} first.
49
+ */
50
+ export declare function resolveSectionLayoutMode(section: Section): SectionLayoutMode;
51
+ /**
52
+ * Validates a section against §4.5. Returns every violation found, in seat
53
+ * declaration order; an empty array means the section conforms.
54
+ *
55
+ * @param path Document path prefix for the violations, e.g. `sections[1]`.
56
+ */
57
+ export declare function checkSectionLayout(section: Section, path?: string): KerusiViolation[];
58
+ //# sourceMappingURL=kerusi-layout-mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kerusi-layout-mode.d.ts","sourceRoot":"","sources":["../../src/kerusi/kerusi-layout-mode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAS,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE/D,4DAA4D;AAC5D,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC;AAE9D;;;;;;;;;;;;;GAaG;AAEH;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;CACZ;AAED,uDAAuD;AACvD,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAEhD;AAED,kFAAkF;AAClF,wBAAgB,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAE/C;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAE1D;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,GAAG,SAAS,CActF;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,CAE5E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE,CA4FrF"}
@@ -0,0 +1,117 @@
1
+ import { error } from './kerusi-violation.js';
2
+ /** True when the item carries a usable grid column. */
3
+ export function hasCol(item) {
4
+ return item.col !== undefined && item.col !== null;
5
+ }
6
+ /** True when the item carries both freeform coordinates. Neither alone counts. */
7
+ export function hasXY(item) {
8
+ return item.x !== undefined && item.x !== null && item.y !== undefined && item.y !== null;
9
+ }
10
+ /** True when the item carries either coordinate — the `grid` prohibition. */
11
+ export function hasAnyCoordinate(item) {
12
+ return (item.x !== undefined && item.x !== null) || (item.y !== undefined && item.y !== null);
13
+ }
14
+ /**
15
+ * Infers a section's mode from its seats when `Section.layout` is omitted
16
+ * (§4.5). Returns `undefined` when the seats are inconsistent — the caller MUST
17
+ * reject such a section.
18
+ *
19
+ * An empty section infers `grid`: it satisfies "every seat has `col`" vacuously
20
+ * and grid is the cheaper mode to render.
21
+ */
22
+ export function inferSectionLayoutMode(section) {
23
+ const seats = section.seats ?? [];
24
+ if (seats.length === 0) {
25
+ return 'grid';
26
+ }
27
+ if (seats.every((s) => hasCol(s) && !hasAnyCoordinate(s))) {
28
+ return 'grid';
29
+ }
30
+ if (seats.every((s) => hasXY(s) && !hasCol(s))) {
31
+ return 'freeform';
32
+ }
33
+ // The spec defines no inference for `mixed`; declaring it is required. Every
34
+ // other shape is inconsistent.
35
+ return undefined;
36
+ }
37
+ /**
38
+ * The mode a section renders in: its declared `layout`, or the inferred one.
39
+ * Falls back to `grid` for an inconsistent section so a renderer can still draw
40
+ * *something* after the validator has reported the fault. Callers that must not
41
+ * render an invalid document should check {@link checkSectionLayout} first.
42
+ */
43
+ export function resolveSectionLayoutMode(section) {
44
+ return section.layout ?? inferSectionLayoutMode(section) ?? 'grid';
45
+ }
46
+ /**
47
+ * Validates a section against §4.5. Returns every violation found, in seat
48
+ * declaration order; an empty array means the section conforms.
49
+ *
50
+ * @param path Document path prefix for the violations, e.g. `sections[1]`.
51
+ */
52
+ export function checkSectionLayout(section, path) {
53
+ const seats = section.seats ?? [];
54
+ if (section.layout === undefined) {
55
+ if (inferSectionLayoutMode(section) === undefined) {
56
+ const offender = firstInconsistentSeat(seats);
57
+ return [
58
+ error('section-layout-inconsistent', `Section "${section.id}" omits "layout" and its seats mix positioning ` +
59
+ `methods, so no mode can be inferred: seat "${offender?.id}" does not ` +
60
+ 'match the section\'s prevailing method. Declare "layout" explicitly, ' +
61
+ 'or make every seat use the same method (§4.5).', { id: section.id, path }),
62
+ ];
63
+ }
64
+ // An inferred mode is, by construction, satisfied by every seat.
65
+ return [];
66
+ }
67
+ const violations = [];
68
+ seats.forEach((seat, i) => {
69
+ const seatPath = path ? `${path}.seats[${i}]` : undefined;
70
+ switch (section.layout) {
71
+ case 'grid':
72
+ if (!hasCol(seat)) {
73
+ violations.push(error('section-layout-grid', `Seat "${seat.id}" is in grid section "${section.id}" but has no ` +
74
+ '"col". Every seat in a grid section must declare "col" ' +
75
+ 'explicitly; it is never inferred from array order (§4.5).', { id: seat.id, path: seatPath }));
76
+ }
77
+ if (hasAnyCoordinate(seat)) {
78
+ violations.push(error('section-layout-grid', `Seat "${seat.id}" is in grid section "${section.id}" but carries ` +
79
+ '"x"/"y". No seat in a grid section may have freeform ' +
80
+ 'coordinates; use layout "mixed" to combine both (§4.5).', { id: seat.id, path: seatPath }));
81
+ }
82
+ break;
83
+ case 'freeform':
84
+ if (!hasXY(seat)) {
85
+ violations.push(error('section-layout-freeform', `Seat "${seat.id}" is in freeform section "${section.id}" but is ` +
86
+ 'missing "x" or "y". Every seat in a freeform section must have ' +
87
+ 'both (§4.5).', { id: seat.id, path: seatPath }));
88
+ }
89
+ if (hasCol(seat)) {
90
+ violations.push(error('section-layout-freeform', `Seat "${seat.id}" is in freeform section "${section.id}" but ` +
91
+ 'carries "col". No seat in a freeform section may have a grid ' +
92
+ 'column; use layout "mixed" to combine both (§4.5).', { id: seat.id, path: seatPath }));
93
+ }
94
+ break;
95
+ case 'mixed':
96
+ if (!hasCol(seat) || !hasXY(seat)) {
97
+ violations.push(error('section-layout-mixed', `Seat "${seat.id}" is in mixed section "${section.id}" but does not ` +
98
+ 'have all of "col", "x" and "y". A mixed section requires every ' +
99
+ 'seat to carry both addressing methods (§4.5).', { id: seat.id, path: seatPath }));
100
+ }
101
+ break;
102
+ }
103
+ });
104
+ return violations;
105
+ }
106
+ /**
107
+ * The first seat that breaks the section's prevailing positioning method, used
108
+ * to make an "inconsistent" message point at something concrete.
109
+ */
110
+ function firstInconsistentSeat(seats) {
111
+ const gridLike = seats.filter((s) => hasCol(s) && !hasAnyCoordinate(s)).length;
112
+ const freeformLike = seats.filter((s) => hasXY(s) && !hasCol(s)).length;
113
+ return gridLike >= freeformLike
114
+ ? seats.find((s) => !(hasCol(s) && !hasAnyCoordinate(s)))
115
+ : seats.find((s) => !(hasXY(s) && !hasCol(s)));
116
+ }
117
+ //# sourceMappingURL=kerusi-layout-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kerusi-layout-mode.js","sourceRoot":"","sources":["../../src/kerusi/kerusi-layout-mode.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { KerusiMap, LocalizedText, Money } from './kerusi-map.model.js';
2
+ /**
3
+ * Label and money resolution for Kerusi documents.
4
+ *
5
+ * The standard lets `Section.label` and `SeatType.label` be either a plain
6
+ * string or a BCP-47 locale map (§4.1, §4.7), and states amounts in minor units
7
+ * (§4.8). Both need resolving before anything can be rendered or announced.
8
+ */
9
+ /** The default when a map declares no `locale` (§4). */
10
+ export declare const DEFAULT_LOCALE = "en";
11
+ /** The locale a map renders in: an explicit override, else the map's, else "en". */
12
+ export declare function resolveMapLocale(map: KerusiMap | undefined, override?: string): string;
13
+ /**
14
+ * Resolves a `string | Record<string, string>` label for the given locale.
15
+ *
16
+ * A plain string is returned as-is. A locale map is matched, in order, against:
17
+ * the exact tag, progressively shorter prefixes of it (`ms-MY` → `ms`), the
18
+ * fallback locale and its prefixes, and finally the first key in insertion
19
+ * order — so a map that localizes into languages the caller didn't ask for
20
+ * still renders something rather than nothing. Tag matching is
21
+ * case-insensitive.
22
+ *
23
+ * Returns `undefined` for an absent or empty label, so callers can fall back to
24
+ * an id.
25
+ */
26
+ export declare function resolveLocalizedText(value: LocalizedText | undefined, locale: string, fallbackLocale?: string): string | undefined;
27
+ /**
28
+ * The number of minor-unit digits for an ISO 4217 code — 2 for MYR/USD, 0 for
29
+ * JPY, 3 for KWD/BHD. Read from `Intl` rather than hardcoded, because
30
+ * `Money.amount` is in minor units and dividing everything by 100 renders JPY
31
+ * 100× too small and KWD 10× too large (§4.8).
32
+ */
33
+ export declare function minorUnitDigits(currency: string): number;
34
+ /** A `Money` as a major-unit number, e.g. `{ amount: 4500, currency: 'MYR' }` → `45`. */
35
+ export declare function toMajorUnits(money: Money): number;
36
+ /**
37
+ * Formats a `Money` for display, e.g. `RM 45.00`, `¥1,200`, `KD 12.500`.
38
+ * Falls back to `"<amount> <currency>"` when the runtime rejects the code.
39
+ */
40
+ export declare function formatMoney(money: Money | undefined, locale?: string): string;
41
+ /** Sums money of one currency. Returns `undefined` for an empty list. */
42
+ export declare function sumMoney(amounts: readonly Money[]): Money | undefined;
43
+ //# sourceMappingURL=kerusi-locale.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kerusi-locale.d.ts","sourceRoot":"","sources":["../../src/kerusi/kerusi-locale.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAExE;;;;;;GAMG;AAEH,wDAAwD;AACxD,eAAO,MAAM,cAAc,OAAO,CAAC;AAEnC,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,aAAa,GAAG,SAAS,EAChC,MAAM,EAAE,MAAM,EACd,cAAc,GAAE,MAAuB,GACtC,MAAM,GAAG,SAAS,CAyBpB;AAeD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUxD;AAED,yFAAyF;AACzF,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAEjD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,EAAE,MAAM,GAAE,MAAuB,GAAG,MAAM,CAa7F;AAED,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,SAAS,KAAK,EAAE,GAAG,KAAK,GAAG,SAAS,CAQrE"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Label and money resolution for Kerusi documents.
3
+ *
4
+ * The standard lets `Section.label` and `SeatType.label` be either a plain
5
+ * string or a BCP-47 locale map (§4.1, §4.7), and states amounts in minor units
6
+ * (§4.8). Both need resolving before anything can be rendered or announced.
7
+ */
8
+ /** The default when a map declares no `locale` (§4). */
9
+ export const DEFAULT_LOCALE = 'en';
10
+ /** The locale a map renders in: an explicit override, else the map's, else "en". */
11
+ export function resolveMapLocale(map, override) {
12
+ return override ?? map?.locale ?? DEFAULT_LOCALE;
13
+ }
14
+ /**
15
+ * Resolves a `string | Record<string, string>` label for the given locale.
16
+ *
17
+ * A plain string is returned as-is. A locale map is matched, in order, against:
18
+ * the exact tag, progressively shorter prefixes of it (`ms-MY` → `ms`), the
19
+ * fallback locale and its prefixes, and finally the first key in insertion
20
+ * order — so a map that localizes into languages the caller didn't ask for
21
+ * still renders something rather than nothing. Tag matching is
22
+ * case-insensitive.
23
+ *
24
+ * Returns `undefined` for an absent or empty label, so callers can fall back to
25
+ * an id.
26
+ */
27
+ export function resolveLocalizedText(value, locale, fallbackLocale = DEFAULT_LOCALE) {
28
+ if (value === undefined || value === null) {
29
+ return undefined;
30
+ }
31
+ if (typeof value === 'string') {
32
+ return value;
33
+ }
34
+ const byLowerKey = new Map();
35
+ for (const [key, text] of Object.entries(value)) {
36
+ // First key wins, so insertion order decides between "en" and "EN".
37
+ if (!byLowerKey.has(key.toLowerCase())) {
38
+ byLowerKey.set(key.toLowerCase(), text);
39
+ }
40
+ }
41
+ for (const candidate of [...tagChain(locale), ...tagChain(fallbackLocale)]) {
42
+ const hit = byLowerKey.get(candidate);
43
+ if (hit !== undefined) {
44
+ return hit;
45
+ }
46
+ }
47
+ const first = Object.values(value)[0];
48
+ return first ?? undefined;
49
+ }
50
+ /**
51
+ * A BCP-47 tag and its progressively shorter prefixes, lowercased:
52
+ * `"zh-Hant-TW"` → `["zh-hant-tw", "zh-hant", "zh"]`.
53
+ */
54
+ function tagChain(tag) {
55
+ const parts = tag.toLowerCase().split('-').filter(Boolean);
56
+ const chain = [];
57
+ for (let i = parts.length; i > 0; i--) {
58
+ chain.push(parts.slice(0, i).join('-'));
59
+ }
60
+ return chain;
61
+ }
62
+ /**
63
+ * The number of minor-unit digits for an ISO 4217 code — 2 for MYR/USD, 0 for
64
+ * JPY, 3 for KWD/BHD. Read from `Intl` rather than hardcoded, because
65
+ * `Money.amount` is in minor units and dividing everything by 100 renders JPY
66
+ * 100× too small and KWD 10× too large (§4.8).
67
+ */
68
+ export function minorUnitDigits(currency) {
69
+ try {
70
+ return (new Intl.NumberFormat('en', { style: 'currency', currency }).resolvedOptions()
71
+ .maximumFractionDigits ?? 2);
72
+ }
73
+ catch {
74
+ // An unknown or malformed code: assume the common case rather than throw.
75
+ return 2;
76
+ }
77
+ }
78
+ /** A `Money` as a major-unit number, e.g. `{ amount: 4500, currency: 'MYR' }` → `45`. */
79
+ export function toMajorUnits(money) {
80
+ return money.amount / 10 ** minorUnitDigits(money.currency);
81
+ }
82
+ /**
83
+ * Formats a `Money` for display, e.g. `RM 45.00`, `¥1,200`, `KD 12.500`.
84
+ * Falls back to `"<amount> <currency>"` when the runtime rejects the code.
85
+ */
86
+ export function formatMoney(money, locale = DEFAULT_LOCALE) {
87
+ if (!money) {
88
+ return '';
89
+ }
90
+ const major = toMajorUnits(money);
91
+ try {
92
+ return new Intl.NumberFormat(locale, {
93
+ style: 'currency',
94
+ currency: money.currency,
95
+ }).format(major);
96
+ }
97
+ catch {
98
+ return `${major} ${money.currency}`;
99
+ }
100
+ }
101
+ /** Sums money of one currency. Returns `undefined` for an empty list. */
102
+ export function sumMoney(amounts) {
103
+ if (amounts.length === 0) {
104
+ return undefined;
105
+ }
106
+ return {
107
+ amount: amounts.reduce((sum, m) => sum + m.amount, 0),
108
+ currency: amounts[0].currency,
109
+ };
110
+ }
111
+ //# sourceMappingURL=kerusi-locale.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kerusi-locale.js","sourceRoot":"","sources":["../../src/kerusi/kerusi-locale.ts"],"names":[],"mappings":"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"}