@kerusiweb/angular 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ npm install @kerusiweb/core @kerusiweb/angular
9
9
 
10
10
  Requires Angular 22+. Standalone, signal-based, zoneless-friendly.
11
11
 
12
- This package is the Angular binding only — the components and the signal-backed
12
+ This package is the Angular binding only: the components and the signal-backed
13
13
  `KerusiStateStore`. The document types, the conformance validator, the render
14
14
  model and its geometry live in its peer
15
15
  [`@kerusiweb/core`](https://www.npmjs.com/package/@kerusiweb/core), which is
@@ -47,14 +47,14 @@ import { KerusiSeatmapComponent } from '@kerusiweb/angular';
47
47
 
48
48
  **Selection**
49
49
 
50
- | Input | Type | Default | |
51
- | -------------------- | ------------------------------- | --------------- | ------------------------------------------------------------------------------- |
52
- | `selection` | `readonly string[]` | `[]` | Selected seat ids. Two-way — `[(selection)]` — or read-only with `[selection]`. |
53
- | `selectableStatuses` | `SeatRenderStatus[]` | `['available']` | Which statuses a seat may be picked in. |
54
- | `companionMode` | `'auto' \| 'independent'` | `'auto'` | `auto` selects a seat's whole companion closure together (§4.6). |
55
- | `maxSelection` | `number` | — | Cap on selected seats. Counts a companion closure as its full size. |
56
- | `seatSelectable` | `(seat: RenderSeat) => boolean` | — | A final say, applied after the status test. |
57
- | `interactive` | `boolean` | `true` | `false` renders read-only. |
50
+ | Input | Type | Default | |
51
+ | -------------------- | ------------------------------- | --------------- | -------------------------------------------------------------------------- |
52
+ | `selection` | `readonly string[]` | `[]` | Selected seat ids. Two-way (`[(selection)]`) or read-only (`[selection]`). |
53
+ | `selectableStatuses` | `SeatRenderStatus[]` | `['available']` | Which statuses a seat may be picked in. |
54
+ | `companionMode` | `'auto' \| 'independent'` | `'auto'` | `auto` selects a seat's whole companion closure together (§4.6). |
55
+ | `maxSelection` | `number` | — | Cap on selected seats. Counts a companion closure as its full size. |
56
+ | `seatSelectable` | `(seat: RenderSeat) => boolean` | — | A final say, applied after the status test. |
57
+ | `interactive` | `boolean` | `true` | `false` renders read-only. |
58
58
 
59
59
  **Appearance**
60
60
 
@@ -100,18 +100,18 @@ import { KerusiSeatmapComponent } from '@kerusiweb/angular';
100
100
  ### The seat glyph
101
101
 
102
102
  **Colour means seat type; shape means status.** A seat's fill is always its
103
- `SeatType.color`, or the theme's `availableBg` when the type has none — it
103
+ `SeatType.color`, or the theme's `availableBg` when the type has none. It
104
104
  does not change when the seat is held or booked. Overloading fill with both
105
105
  type and status hid the type colour on exactly the seats a busy map has most
106
106
  of; see the doc comment on `seatFill` for the full argument. `selected` is the
107
- one deliberate exception: it still owns the seat's colour outright, so your own picks
107
+ one exception: it still owns the seat's colour outright, so your own picks
108
108
  are never ambiguous, and a purple fill on its own would in any case fail WCAG
109
- 1.4.1 (Use of Colour) for anyone who cannot distinguish it — hence the shape
109
+ 1.4.1 (Use of Colour) for anyone who cannot distinguish it, hence the shape
110
110
  cues below.
111
111
 
112
112
  The seat body itself carries orientation: square-ish at the front, tapered at
113
113
  the back, so which way a seat faces is legible from its outline alone.
114
- `Seat.rotation` turns the whole group, and the taper turns with it — no
114
+ `Seat.rotation` turns the whole group, and the taper turns with it; no
115
115
  separate frame is drawn on top. This is what makes a fanned lecture theatre or
116
116
  a stadium stand read at a glance. The seat number counter-rotates and stays
117
117
  upright regardless.
@@ -122,30 +122,29 @@ Status is read from two marks layered over the body, never from its colour:
122
122
  | --------- | ---------------------------------------------- | ---------- | ---------------------------------------------------- |
123
123
  | Available | type colour | — | — |
124
124
  | Selected | `selectedBg` **frame** + `selectedFg` **core** | — | solid, tinted `selectedBg` |
125
- | Held | type colour | `heldBg` | **hollow**, stroked `heldFg` — a hold is provisional |
126
- | Booked | type colour | `bookedBg` | solid, tinted `bookedFg` — settled, someone else's |
125
+ | Held | type colour | `heldBg` | **hollow**, stroked `heldFg` (a hold is provisional) |
126
+ | Booked | type colour | `bookedBg` | solid, tinted `bookedFg` (settled, someone else's) |
127
127
  | Blocked | `blockedBg` | — | — (withheld by the venue; no one is there) |
128
128
 
129
- Solid vs. hollow is the real distinction — settled vs. still in progress — and
129
+ Solid vs. hollow is the main distinction (settled vs. still in progress), and
130
130
  every figure leans with the seat rather than staying upright, so it doubles as
131
131
  an orientation cue too.
132
132
 
133
- Selection is the only state drawn from **two** tones rather than one, and that
134
- is deliberate. `selectedBg` frames the seat; `selectedFg` fills a core inside
133
+ Selection is the only state drawn from **two** tones rather than one. `selectedBg` frames the seat; `selectedFg` fills a core inside
135
134
  that frame and is what the number and the figure are read against. Because the
136
135
  treatment always holds a light tone and a dark one at once, one of the two
137
- separates from the page whichever way you have themed it — so the library never
136
+ separates from the page whichever way you have themed it, so the library never
138
137
  has to detect a colour scheme, and there is no `prefers-color-scheme` default to
139
138
  fight with your own. An earlier revision had this inverted, a near-white rim
140
139
  around a mid-purple middle, which put the low-contrast tone on the outside
141
140
  boundary: a selected seat dissolved into a light page and sank into a dark one.
142
141
 
143
- Both tokens therefore carry real visual weight — override them as a pair. Swapping
142
+ Both tokens therefore carry visual weight, so override them as a pair. Swapping
144
143
  the two gives the inverse treatment, a light frame around a dark core, which works
145
144
  just as well.
146
145
 
147
146
  A theme that overrides `heldFg`/`bookedFg` owns the contrast of that mark
148
- against whatever `SeatType.color` the document supplies — the library cannot
147
+ against whatever `SeatType.color` the document supplies. The library cannot
149
148
  know the pairing in advance, since the figure tint is fixed while the type
150
149
  colour underneath it is not. The shipped defaults are light marks over a
151
150
  darkened wash, which reads across the tier colours in the demo fixtures.
@@ -154,7 +153,7 @@ The geometry is exported, if you want to draw a matching seat elsewhere:
154
153
  `seatBodyPath`, `seatSelectedFrame`, `seatOccupantPath`, `seatOccupantStroke`.
155
154
  (`seatRingStroke` is the old name for `seatSelectedFrame` and still resolves to
156
155
  it.) A seat is always square, which makes `seatBodyPath`'s `inset` an exact
157
- scaled copy about the centre — that is how the selected core is derived, and why
156
+ scaled copy about the centre. That is how the selected core is derived, and why
158
157
  marks drawn to the core's box keep every clearance they had against the body.
159
158
 
160
159
  ### Theming
@@ -162,13 +161,13 @@ marks drawn to the core's box keep every clearance they had against the body.
162
161
  Colors resolve through three tiers, highest first:
163
162
 
164
163
  1. A `--kerusi-*` custom property in **your** stylesheet.
165
- 2. The **`[colors]` input** — a partial `KerusiSeatmapColors`, merged over the defaults.
164
+ 2. The **`[colors]` input**: a partial `KerusiSeatmapColors`, merged over the defaults.
166
165
  3. The library default.
167
166
 
168
167
  The library never writes a `--kerusi-*` property onto its own host, which is
169
168
  what keeps that order true: every fill is emitted as
170
169
  `var(--kerusi-selected-bg, <the resolved input value>)`. Use whichever tier
171
- fits — the input for values known at build time, CSS for anything that has to
170
+ fits: the input for values known at build time, CSS for anything that has to
172
171
  respond to a media query or a theme class.
173
172
 
174
173
  ```css
@@ -199,15 +198,15 @@ Every key of `KerusiSeatmapColors` has a property, kebab-cased:
199
198
  | `focusRing` | `--kerusi-focus-ring` |
200
199
  | `backdrop` | `--kerusi-backdrop` |
201
200
 
202
- A `SeatType.color` from the document is deliberately **not** themable — it is
201
+ A `SeatType.color` from the document is **not** themable, because it is
203
202
  the map's own value under §4.7. Turn it off wholesale with
204
203
  `[typeColors]="false"` if you want the theme to own every fill.
205
204
 
206
205
  Overriding `selectedBg` does not cost you the selected cue: the core and the
207
- figure are shape, not color. Do set `selectedFg` alongside it, though — it is the
206
+ figure are shape, not color. Do set `selectedFg` alongside it, though. It is the
208
207
  core's fill, so it is most of a selected seat's area rather than just a label
209
208
  tint, and the two are read against each other. `heldBg`/`bookedBg` are the wash
210
- drawn over a taken seat's type colour, not the seat's own fill — see
209
+ drawn over a taken seat's type colour, not the seat's own fill. See
211
210
  [The seat glyph](#the-seat-glyph) for the full state table.
212
211
 
213
212
  If you need the pre-1.1 flat selected seat back, the core is its own node:
@@ -242,7 +241,7 @@ Inside a seat: `.kerusi-seat__box`, `__wash`, `__core`, `__occupant`
242
241
  ### Keyboard
243
242
 
244
243
  Tab moves between sections; each section keeps its own tab stop. Within a
245
- section, arrow keys follow the §4.3.1 `col` order — so an aisle, which is a
244
+ section, arrow keys follow the §4.3.1 `col` order, so an aisle, which is a
246
245
  column no seat occupies, is stepped across rather than into.
247
246
 
248
247
  | Key | |
@@ -270,7 +269,7 @@ Rendered inline by `[showLegend]="true"`, or placed anywhere yourself:
270
269
  ```
271
270
 
272
271
  It resolves swatches through the same path as the seat fills, so the two cannot
273
- drift apart. Every availability swatch — not just **Selected** — draws the
272
+ drift apart. Every availability swatch, not just **Selected**, draws the
274
273
  seat glyph itself (wash, ring, occupant, as it applies) rather than a flat
275
274
  colour, because shape is the cue the map uses for status.
276
275
 
@@ -278,7 +277,7 @@ colour, because shape is the cue the map uses for status.
278
277
 
279
278
  ## Working with the format directly
280
279
 
281
- All of this lives in `@kerusiweb/core` and is pure — no Angular import — so it
280
+ All of this lives in `@kerusiweb/core` and is pure (no Angular import), so it
282
281
  can run in a test, a build step or on a server.
283
282
 
284
283
  ```ts
@@ -327,7 +326,7 @@ Deltas that are stale, duplicate or scoped to another session are discarded.
327
326
  > **Gap detection needs a sequence.** §5.2 requires `updatedAt` to be strictly
328
327
  > increasing but not contiguous, so it cannot by itself distinguish "a delta was
329
328
  > lost" from "nothing happened for a while". The store detects gaps when the
330
- > transport supplies a monotonic sequence — `metadata.seq` by default, or your
329
+ > transport supplies a monotonic sequence: `metadata.seq` by default, or your
331
330
  > own `sequenceOf` reader. A gapped delta is still applied, so the map degrades
332
331
  > rather than freezes while you re-fetch.
333
332
 
@@ -198,6 +198,15 @@ class KerusiSectionComponent {
198
198
  style(placed) {
199
199
  return elementStyle(placed.element.kind);
200
200
  }
201
+ /** A text element with no label draws nothing, rather than an empty box. */
202
+ drawsElement(placed) {
203
+ const style = this.style(placed);
204
+ return style.shape !== 'text' || (!!placed.element.label && style.showLabel);
205
+ }
206
+ /** A label on a shape stays upright; a text element's own label is what rotates. */
207
+ labelTransform(placed) {
208
+ return this.style(placed).shape === 'text' ? null : this.counterRotate(placed);
209
+ }
201
210
  elementFillOf(placed) {
202
211
  return elementFill(this.style(placed).tone, this.colors());
203
212
  }
@@ -228,11 +237,11 @@ class KerusiSectionComponent {
228
237
  }
229
238
  }
230
239
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: KerusiSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
231
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: KerusiSectionComponent, isStandalone: true, selector: "kerusi-section", inputs: { section: { classPropertyName: "section", publicName: "section", isSignal: true, isRequired: true, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: true, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, focusedSeatId: { classPropertyName: "focusedSeatId", publicName: "focusedSeatId", isSignal: true, isRequired: false, transformFunction: null }, ariaLabels: { classPropertyName: "ariaLabels", publicName: "ariaLabels", isSignal: true, isRequired: false, transformFunction: null }, colors: { classPropertyName: "colors", publicName: "colors", isSignal: true, isRequired: false, transformFunction: null }, typeColors: { classPropertyName: "typeColors", publicName: "typeColors", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, unitScale: { classPropertyName: "unitScale", publicName: "unitScale", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { seatActivate: "seatActivate", seatFocused: "seatFocused", seatKeydown: "seatKeydown" }, ngImport: i0, template: "<svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n class=\"kerusi-section-svg\"\r\n role=\"presentation\"\r\n focusable=\"false\"\r\n [style.--kerusi-focus-ring-input]=\"colors().focusRing\"\r\n [attr.viewBox]=\"viewBox()\"\r\n [style.max-width.px]=\"naturalWidth()\"\r\n preserveAspectRatio=\"xMidYMid meet\"\r\n>\r\n <rect\r\n x=\"0\"\r\n y=\"0\"\r\n [attr.width]=\"layout().width\"\r\n [attr.height]=\"layout().height\"\r\n [style.fill]=\"backdrop()\"\r\n />\r\n\r\n <!-- Elements first, so a screen or stage sits behind the seats it faces. -->\r\n @for (placed of layout().elements; track placed.element.id) {\r\n <g\r\n class=\"kerusi-element kerusi-element--{{ placed.element.kind }}\"\r\n [attr.transform]=\"transform(placed)\"\r\n >\r\n @switch (style(placed).shape) {\r\n @case ('screen') {\r\n <path [attr.d]=\"screenPathOf(placed)\" [style.fill]=\"elementFillOf(placed)\" />\r\n }\r\n @case ('void') {\r\n <rect\r\n class=\"kerusi-element__void\"\r\n [attr.x]=\"placed.x\"\r\n [attr.y]=\"placed.y\"\r\n [attr.width]=\"placed.width\"\r\n [attr.height]=\"placed.height\"\r\n [style.stroke]=\"elementTextFillOf(placed)\"\r\n fill=\"none\"\r\n />\r\n }\r\n @default {\r\n <rect\r\n [attr.x]=\"placed.x\"\r\n [attr.y]=\"placed.y\"\r\n [attr.width]=\"placed.width\"\r\n [attr.height]=\"placed.height\"\r\n [attr.rx]=\"radiusOf(placed)\"\r\n [style.fill]=\"elementFillOf(placed)\"\r\n />\r\n }\r\n }\r\n @if (placed.element.label && style(placed).showLabel) {\r\n <text\r\n class=\"kerusi-element__label\"\r\n text-anchor=\"middle\"\r\n dominant-baseline=\"central\"\r\n [attr.x]=\"placed.centerX\"\r\n [attr.y]=\"placed.centerY\"\r\n [attr.font-size]=\"placed.fontSize\"\r\n [style.fill]=\"elementTextFillOf(placed)\"\r\n [attr.transform]=\"counterRotate(placed)\"\r\n >\r\n {{ placed.element.label }}\r\n </text>\r\n }\r\n </g>\r\n }\r\n\r\n @for (placed of layout().seats; track placed.seat.id) {\r\n <g\r\n [class]=\"seatClasses(placed)\"\r\n role=\"button\"\r\n [attr.tabindex]=\"tabIndex(placed)\"\r\n [attr.aria-label]=\"ariaLabel(placed)\"\r\n [attr.aria-pressed]=\"isSelected(placed)\"\r\n [attr.aria-disabled]=\"!placed.seat.selectable\"\r\n [attr.data-seat-id]=\"placed.seat.id\"\r\n [attr.transform]=\"transform(placed)\"\r\n (click)=\"onSeatClick(placed)\"\r\n (focus)=\"onSeatFocus(placed)\"\r\n (keydown)=\"onKeydown($event)\"\r\n >\r\n @let variant = occupantVariant(placed);\r\n <!-- Tapered toward the back, square at the front, so which way the seat\r\n faces is legible from the outline alone. -->\r\n <path class=\"kerusi-seat__box\" [attr.d]=\"bodyPath(placed)\" [style.fill]=\"fill(placed)\" />\r\n @if (variant === 'held' || variant === 'booked') {\r\n <!-- Colour stays the seat type's; a taken seat recedes under a wash\r\n instead, so the type colour still reads on a busy map. -->\r\n <path\r\n class=\"kerusi-seat__wash\"\r\n [attr.d]=\"bodyPath(placed)\"\r\n [style.fill]=\"washFill(placed)\"\r\n />\r\n }\r\n @if (variant === 'selected') {\r\n <!-- Selection is the one state drawn from two tones: the body above is\r\n the frame, this is the bright core inside it. Whichever way the\r\n host page is themed, one of the two separates from it \u2014 which is\r\n why nothing here has to know about the page. -->\r\n <path class=\"kerusi-seat__core\" [attr.d]=\"corePath(placed)\" [style.fill]=\"coreFill()\" />\r\n }\r\n @if (variant) {\r\n <!-- Deliberately not counter-rotated: the occupant leans with the\r\n seat. Solid = settled (yours, or sold); hollow = a hold still in\r\n progress. -->\r\n <path\r\n class=\"kerusi-seat__occupant kerusi-seat__occupant--{{ variant }}\"\r\n [attr.d]=\"occupantPath(placed)\"\r\n [attr.stroke-width]=\"occupantStroke(placed)\"\r\n [style.fill]=\"occupantFill(variant)\"\r\n [style.stroke]=\"occupantStrokeColor(variant)\"\r\n />\r\n }\r\n <text\r\n class=\"kerusi-seat__label\"\r\n text-anchor=\"middle\"\r\n dominant-baseline=\"central\"\r\n [attr.x]=\"placed.centerX\"\r\n [attr.y]=\"placed.centerY\"\r\n [attr.font-size]=\"placed.fontSize\"\r\n [style.fill]=\"textFill(placed)\"\r\n [style.stroke]=\"labelHalo(placed)\"\r\n [attr.stroke-width]=\"labelHaloWidth(placed)\"\r\n [attr.transform]=\"counterRotate(placed)\"\r\n >\r\n {{ placed.seat.label }}\r\n </text>\r\n @if (placed.seat.accessibility?.wheelchairAccessible) {\r\n <!-- A corner marker, not a different fill: availability owns the fill. -->\r\n <circle\r\n class=\"kerusi-seat__wheelchair\"\r\n [attr.cx]=\"wheelchairCx(placed)\"\r\n [attr.cy]=\"wheelchairCy(placed)\"\r\n [attr.r]=\"wheelchairRadius(placed)\"\r\n [style.fill]=\"textFill(placed)\"\r\n />\r\n }\r\n </g>\r\n }\r\n</svg>\r\n", styles: [":host{display:block}.kerusi-section-svg{display:block;width:100%;height:auto;max-width:100%}.kerusi-seat{cursor:pointer;outline:none}.kerusi-seat--unselectable{cursor:not-allowed}.kerusi-seat__label,.kerusi-element__label{pointer-events:none;-webkit-user-select:none;user-select:none;font-family:inherit}.kerusi-seat__label{paint-order:stroke;stroke-linejoin:round}.kerusi-seat__wash{pointer-events:none;opacity:.5}.kerusi-seat__core,.kerusi-seat__occupant{pointer-events:none}.kerusi-seat__occupant--selected{opacity:.2}.kerusi-seat__occupant--booked{opacity:.55}.kerusi-seat__occupant--held{opacity:.75;stroke-linecap:round;stroke-linejoin:round}.kerusi-seat--selected .kerusi-seat__label{font-weight:600}.kerusi-seat:focus-visible .kerusi-seat__box{stroke:var(--kerusi-focus-ring, var(--kerusi-focus-ring-input, currentColor));stroke-width:2.5;paint-order:stroke}.kerusi-element__void{stroke-width:1;stroke-dasharray:4 3;opacity:.7}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
240
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: KerusiSectionComponent, isStandalone: true, selector: "kerusi-section", inputs: { section: { classPropertyName: "section", publicName: "section", isSignal: true, isRequired: true, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: true, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, focusedSeatId: { classPropertyName: "focusedSeatId", publicName: "focusedSeatId", isSignal: true, isRequired: false, transformFunction: null }, ariaLabels: { classPropertyName: "ariaLabels", publicName: "ariaLabels", isSignal: true, isRequired: false, transformFunction: null }, colors: { classPropertyName: "colors", publicName: "colors", isSignal: true, isRequired: false, transformFunction: null }, typeColors: { classPropertyName: "typeColors", publicName: "typeColors", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, unitScale: { classPropertyName: "unitScale", publicName: "unitScale", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { seatActivate: "seatActivate", seatFocused: "seatFocused", seatKeydown: "seatKeydown" }, ngImport: i0, template: "<svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n class=\"kerusi-section-svg\"\r\n role=\"presentation\"\r\n focusable=\"false\"\r\n [style.--kerusi-focus-ring-input]=\"colors().focusRing\"\r\n [attr.viewBox]=\"viewBox()\"\r\n [style.max-width.px]=\"naturalWidth()\"\r\n preserveAspectRatio=\"xMidYMid meet\"\r\n>\r\n <rect\r\n x=\"0\"\r\n y=\"0\"\r\n [attr.width]=\"layout().width\"\r\n [attr.height]=\"layout().height\"\r\n [style.fill]=\"backdrop()\"\r\n />\r\n\r\n <!-- Elements first, so a screen or stage sits behind the seats it faces. -->\r\n @for (placed of layout().elements; track placed.element.id) {\r\n @if (drawsElement(placed)) {\r\n <g\r\n class=\"kerusi-element kerusi-element--{{ placed.element.kind }}\"\r\n [attr.transform]=\"transform(placed)\"\r\n >\r\n @switch (style(placed).shape) {\r\n @case ('screen') {\r\n <path [attr.d]=\"screenPathOf(placed)\" [style.fill]=\"elementFillOf(placed)\" />\r\n }\r\n @case ('text') {}\r\n @case ('void') {\r\n <rect\r\n class=\"kerusi-element__void\"\r\n [attr.x]=\"placed.x\"\r\n [attr.y]=\"placed.y\"\r\n [attr.width]=\"placed.width\"\r\n [attr.height]=\"placed.height\"\r\n [style.stroke]=\"elementTextFillOf(placed)\"\r\n fill=\"none\"\r\n />\r\n }\r\n @default {\r\n <rect\r\n [attr.x]=\"placed.x\"\r\n [attr.y]=\"placed.y\"\r\n [attr.width]=\"placed.width\"\r\n [attr.height]=\"placed.height\"\r\n [attr.rx]=\"radiusOf(placed)\"\r\n [style.fill]=\"elementFillOf(placed)\"\r\n />\r\n }\r\n }\r\n @if (placed.element.label && style(placed).showLabel) {\r\n <text\r\n class=\"kerusi-element__label\"\r\n text-anchor=\"middle\"\r\n dominant-baseline=\"central\"\r\n [attr.x]=\"placed.centerX\"\r\n [attr.y]=\"placed.centerY\"\r\n [attr.font-size]=\"placed.fontSize\"\r\n [style.fill]=\"elementTextFillOf(placed)\"\r\n [attr.transform]=\"labelTransform(placed)\"\r\n >\r\n {{ placed.element.label }}\r\n </text>\r\n }\r\n </g>\r\n }\r\n }\r\n\r\n @for (placed of layout().seats; track placed.seat.id) {\r\n <g\r\n [class]=\"seatClasses(placed)\"\r\n role=\"button\"\r\n [attr.tabindex]=\"tabIndex(placed)\"\r\n [attr.aria-label]=\"ariaLabel(placed)\"\r\n [attr.aria-pressed]=\"isSelected(placed)\"\r\n [attr.aria-disabled]=\"!placed.seat.selectable\"\r\n [attr.data-seat-id]=\"placed.seat.id\"\r\n [attr.transform]=\"transform(placed)\"\r\n (click)=\"onSeatClick(placed)\"\r\n (focus)=\"onSeatFocus(placed)\"\r\n (keydown)=\"onKeydown($event)\"\r\n >\r\n @let variant = occupantVariant(placed);\r\n <!-- Tapered toward the back, square at the front, so which way the seat\r\n faces is legible from the outline alone. -->\r\n <path class=\"kerusi-seat__box\" [attr.d]=\"bodyPath(placed)\" [style.fill]=\"fill(placed)\" />\r\n @if (variant === 'held' || variant === 'booked') {\r\n <!-- Colour stays the seat type's; a taken seat recedes under a wash\r\n instead, so the type colour still reads on a busy map. -->\r\n <path\r\n class=\"kerusi-seat__wash\"\r\n [attr.d]=\"bodyPath(placed)\"\r\n [style.fill]=\"washFill(placed)\"\r\n />\r\n }\r\n @if (variant === 'selected') {\r\n <!-- Selection is the one state drawn from two tones: the body above is\r\n the frame, this is the bright core inside it. Whichever way the\r\n host page is themed, one of the two separates from it \u2014 which is\r\n why nothing here has to know about the page. -->\r\n <path class=\"kerusi-seat__core\" [attr.d]=\"corePath(placed)\" [style.fill]=\"coreFill()\" />\r\n }\r\n @if (variant) {\r\n <!-- Deliberately not counter-rotated: the occupant leans with the\r\n seat. Solid = settled (yours, or sold); hollow = a hold still in\r\n progress. -->\r\n <path\r\n class=\"kerusi-seat__occupant kerusi-seat__occupant--{{ variant }}\"\r\n [attr.d]=\"occupantPath(placed)\"\r\n [attr.stroke-width]=\"occupantStroke(placed)\"\r\n [style.fill]=\"occupantFill(variant)\"\r\n [style.stroke]=\"occupantStrokeColor(variant)\"\r\n />\r\n }\r\n <text\r\n class=\"kerusi-seat__label\"\r\n text-anchor=\"middle\"\r\n dominant-baseline=\"central\"\r\n [attr.x]=\"placed.centerX\"\r\n [attr.y]=\"placed.centerY\"\r\n [attr.font-size]=\"placed.fontSize\"\r\n [style.fill]=\"textFill(placed)\"\r\n [style.stroke]=\"labelHalo(placed)\"\r\n [attr.stroke-width]=\"labelHaloWidth(placed)\"\r\n [attr.transform]=\"counterRotate(placed)\"\r\n >\r\n {{ placed.seat.label }}\r\n </text>\r\n @if (placed.seat.accessibility?.wheelchairAccessible) {\r\n <!-- A corner marker, not a different fill: availability owns the fill. -->\r\n <circle\r\n class=\"kerusi-seat__wheelchair\"\r\n [attr.cx]=\"wheelchairCx(placed)\"\r\n [attr.cy]=\"wheelchairCy(placed)\"\r\n [attr.r]=\"wheelchairRadius(placed)\"\r\n [style.fill]=\"textFill(placed)\"\r\n />\r\n }\r\n </g>\r\n }\r\n</svg>\r\n", styles: [":host{display:block}.kerusi-section-svg{display:block;width:100%;height:auto;max-width:100%}.kerusi-seat{cursor:pointer;outline:none}.kerusi-seat--unselectable{cursor:not-allowed}.kerusi-seat__label,.kerusi-element__label{pointer-events:none;-webkit-user-select:none;user-select:none;font-family:inherit}.kerusi-seat__label{paint-order:stroke;stroke-linejoin:round}.kerusi-seat__wash{pointer-events:none;opacity:.5}.kerusi-seat__core,.kerusi-seat__occupant{pointer-events:none}.kerusi-seat__occupant--selected{opacity:.2}.kerusi-seat__occupant--booked{opacity:.55}.kerusi-seat__occupant--held{opacity:.75;stroke-linecap:round;stroke-linejoin:round}.kerusi-seat--selected .kerusi-seat__label{font-weight:600}.kerusi-seat:focus-visible .kerusi-seat__box{stroke:var(--kerusi-focus-ring, var(--kerusi-focus-ring-input, currentColor));stroke-width:2.5;paint-order:stroke}.kerusi-element__void{stroke-width:1;stroke-dasharray:4 3;opacity:.7}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
232
241
  }
233
242
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: KerusiSectionComponent, decorators: [{
234
243
  type: Component,
235
- args: [{ selector: 'kerusi-section', changeDetection: ChangeDetectionStrategy.OnPush, template: "<svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n class=\"kerusi-section-svg\"\r\n role=\"presentation\"\r\n focusable=\"false\"\r\n [style.--kerusi-focus-ring-input]=\"colors().focusRing\"\r\n [attr.viewBox]=\"viewBox()\"\r\n [style.max-width.px]=\"naturalWidth()\"\r\n preserveAspectRatio=\"xMidYMid meet\"\r\n>\r\n <rect\r\n x=\"0\"\r\n y=\"0\"\r\n [attr.width]=\"layout().width\"\r\n [attr.height]=\"layout().height\"\r\n [style.fill]=\"backdrop()\"\r\n />\r\n\r\n <!-- Elements first, so a screen or stage sits behind the seats it faces. -->\r\n @for (placed of layout().elements; track placed.element.id) {\r\n <g\r\n class=\"kerusi-element kerusi-element--{{ placed.element.kind }}\"\r\n [attr.transform]=\"transform(placed)\"\r\n >\r\n @switch (style(placed).shape) {\r\n @case ('screen') {\r\n <path [attr.d]=\"screenPathOf(placed)\" [style.fill]=\"elementFillOf(placed)\" />\r\n }\r\n @case ('void') {\r\n <rect\r\n class=\"kerusi-element__void\"\r\n [attr.x]=\"placed.x\"\r\n [attr.y]=\"placed.y\"\r\n [attr.width]=\"placed.width\"\r\n [attr.height]=\"placed.height\"\r\n [style.stroke]=\"elementTextFillOf(placed)\"\r\n fill=\"none\"\r\n />\r\n }\r\n @default {\r\n <rect\r\n [attr.x]=\"placed.x\"\r\n [attr.y]=\"placed.y\"\r\n [attr.width]=\"placed.width\"\r\n [attr.height]=\"placed.height\"\r\n [attr.rx]=\"radiusOf(placed)\"\r\n [style.fill]=\"elementFillOf(placed)\"\r\n />\r\n }\r\n }\r\n @if (placed.element.label && style(placed).showLabel) {\r\n <text\r\n class=\"kerusi-element__label\"\r\n text-anchor=\"middle\"\r\n dominant-baseline=\"central\"\r\n [attr.x]=\"placed.centerX\"\r\n [attr.y]=\"placed.centerY\"\r\n [attr.font-size]=\"placed.fontSize\"\r\n [style.fill]=\"elementTextFillOf(placed)\"\r\n [attr.transform]=\"counterRotate(placed)\"\r\n >\r\n {{ placed.element.label }}\r\n </text>\r\n }\r\n </g>\r\n }\r\n\r\n @for (placed of layout().seats; track placed.seat.id) {\r\n <g\r\n [class]=\"seatClasses(placed)\"\r\n role=\"button\"\r\n [attr.tabindex]=\"tabIndex(placed)\"\r\n [attr.aria-label]=\"ariaLabel(placed)\"\r\n [attr.aria-pressed]=\"isSelected(placed)\"\r\n [attr.aria-disabled]=\"!placed.seat.selectable\"\r\n [attr.data-seat-id]=\"placed.seat.id\"\r\n [attr.transform]=\"transform(placed)\"\r\n (click)=\"onSeatClick(placed)\"\r\n (focus)=\"onSeatFocus(placed)\"\r\n (keydown)=\"onKeydown($event)\"\r\n >\r\n @let variant = occupantVariant(placed);\r\n <!-- Tapered toward the back, square at the front, so which way the seat\r\n faces is legible from the outline alone. -->\r\n <path class=\"kerusi-seat__box\" [attr.d]=\"bodyPath(placed)\" [style.fill]=\"fill(placed)\" />\r\n @if (variant === 'held' || variant === 'booked') {\r\n <!-- Colour stays the seat type's; a taken seat recedes under a wash\r\n instead, so the type colour still reads on a busy map. -->\r\n <path\r\n class=\"kerusi-seat__wash\"\r\n [attr.d]=\"bodyPath(placed)\"\r\n [style.fill]=\"washFill(placed)\"\r\n />\r\n }\r\n @if (variant === 'selected') {\r\n <!-- Selection is the one state drawn from two tones: the body above is\r\n the frame, this is the bright core inside it. Whichever way the\r\n host page is themed, one of the two separates from it \u2014 which is\r\n why nothing here has to know about the page. -->\r\n <path class=\"kerusi-seat__core\" [attr.d]=\"corePath(placed)\" [style.fill]=\"coreFill()\" />\r\n }\r\n @if (variant) {\r\n <!-- Deliberately not counter-rotated: the occupant leans with the\r\n seat. Solid = settled (yours, or sold); hollow = a hold still in\r\n progress. -->\r\n <path\r\n class=\"kerusi-seat__occupant kerusi-seat__occupant--{{ variant }}\"\r\n [attr.d]=\"occupantPath(placed)\"\r\n [attr.stroke-width]=\"occupantStroke(placed)\"\r\n [style.fill]=\"occupantFill(variant)\"\r\n [style.stroke]=\"occupantStrokeColor(variant)\"\r\n />\r\n }\r\n <text\r\n class=\"kerusi-seat__label\"\r\n text-anchor=\"middle\"\r\n dominant-baseline=\"central\"\r\n [attr.x]=\"placed.centerX\"\r\n [attr.y]=\"placed.centerY\"\r\n [attr.font-size]=\"placed.fontSize\"\r\n [style.fill]=\"textFill(placed)\"\r\n [style.stroke]=\"labelHalo(placed)\"\r\n [attr.stroke-width]=\"labelHaloWidth(placed)\"\r\n [attr.transform]=\"counterRotate(placed)\"\r\n >\r\n {{ placed.seat.label }}\r\n </text>\r\n @if (placed.seat.accessibility?.wheelchairAccessible) {\r\n <!-- A corner marker, not a different fill: availability owns the fill. -->\r\n <circle\r\n class=\"kerusi-seat__wheelchair\"\r\n [attr.cx]=\"wheelchairCx(placed)\"\r\n [attr.cy]=\"wheelchairCy(placed)\"\r\n [attr.r]=\"wheelchairRadius(placed)\"\r\n [style.fill]=\"textFill(placed)\"\r\n />\r\n }\r\n </g>\r\n }\r\n</svg>\r\n", styles: [":host{display:block}.kerusi-section-svg{display:block;width:100%;height:auto;max-width:100%}.kerusi-seat{cursor:pointer;outline:none}.kerusi-seat--unselectable{cursor:not-allowed}.kerusi-seat__label,.kerusi-element__label{pointer-events:none;-webkit-user-select:none;user-select:none;font-family:inherit}.kerusi-seat__label{paint-order:stroke;stroke-linejoin:round}.kerusi-seat__wash{pointer-events:none;opacity:.5}.kerusi-seat__core,.kerusi-seat__occupant{pointer-events:none}.kerusi-seat__occupant--selected{opacity:.2}.kerusi-seat__occupant--booked{opacity:.55}.kerusi-seat__occupant--held{opacity:.75;stroke-linecap:round;stroke-linejoin:round}.kerusi-seat--selected .kerusi-seat__label{font-weight:600}.kerusi-seat:focus-visible .kerusi-seat__box{stroke:var(--kerusi-focus-ring, var(--kerusi-focus-ring-input, currentColor));stroke-width:2.5;paint-order:stroke}.kerusi-element__void{stroke-width:1;stroke-dasharray:4 3;opacity:.7}\n"] }]
244
+ args: [{ selector: 'kerusi-section', changeDetection: ChangeDetectionStrategy.OnPush, template: "<svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n class=\"kerusi-section-svg\"\r\n role=\"presentation\"\r\n focusable=\"false\"\r\n [style.--kerusi-focus-ring-input]=\"colors().focusRing\"\r\n [attr.viewBox]=\"viewBox()\"\r\n [style.max-width.px]=\"naturalWidth()\"\r\n preserveAspectRatio=\"xMidYMid meet\"\r\n>\r\n <rect\r\n x=\"0\"\r\n y=\"0\"\r\n [attr.width]=\"layout().width\"\r\n [attr.height]=\"layout().height\"\r\n [style.fill]=\"backdrop()\"\r\n />\r\n\r\n <!-- Elements first, so a screen or stage sits behind the seats it faces. -->\r\n @for (placed of layout().elements; track placed.element.id) {\r\n @if (drawsElement(placed)) {\r\n <g\r\n class=\"kerusi-element kerusi-element--{{ placed.element.kind }}\"\r\n [attr.transform]=\"transform(placed)\"\r\n >\r\n @switch (style(placed).shape) {\r\n @case ('screen') {\r\n <path [attr.d]=\"screenPathOf(placed)\" [style.fill]=\"elementFillOf(placed)\" />\r\n }\r\n @case ('text') {}\r\n @case ('void') {\r\n <rect\r\n class=\"kerusi-element__void\"\r\n [attr.x]=\"placed.x\"\r\n [attr.y]=\"placed.y\"\r\n [attr.width]=\"placed.width\"\r\n [attr.height]=\"placed.height\"\r\n [style.stroke]=\"elementTextFillOf(placed)\"\r\n fill=\"none\"\r\n />\r\n }\r\n @default {\r\n <rect\r\n [attr.x]=\"placed.x\"\r\n [attr.y]=\"placed.y\"\r\n [attr.width]=\"placed.width\"\r\n [attr.height]=\"placed.height\"\r\n [attr.rx]=\"radiusOf(placed)\"\r\n [style.fill]=\"elementFillOf(placed)\"\r\n />\r\n }\r\n }\r\n @if (placed.element.label && style(placed).showLabel) {\r\n <text\r\n class=\"kerusi-element__label\"\r\n text-anchor=\"middle\"\r\n dominant-baseline=\"central\"\r\n [attr.x]=\"placed.centerX\"\r\n [attr.y]=\"placed.centerY\"\r\n [attr.font-size]=\"placed.fontSize\"\r\n [style.fill]=\"elementTextFillOf(placed)\"\r\n [attr.transform]=\"labelTransform(placed)\"\r\n >\r\n {{ placed.element.label }}\r\n </text>\r\n }\r\n </g>\r\n }\r\n }\r\n\r\n @for (placed of layout().seats; track placed.seat.id) {\r\n <g\r\n [class]=\"seatClasses(placed)\"\r\n role=\"button\"\r\n [attr.tabindex]=\"tabIndex(placed)\"\r\n [attr.aria-label]=\"ariaLabel(placed)\"\r\n [attr.aria-pressed]=\"isSelected(placed)\"\r\n [attr.aria-disabled]=\"!placed.seat.selectable\"\r\n [attr.data-seat-id]=\"placed.seat.id\"\r\n [attr.transform]=\"transform(placed)\"\r\n (click)=\"onSeatClick(placed)\"\r\n (focus)=\"onSeatFocus(placed)\"\r\n (keydown)=\"onKeydown($event)\"\r\n >\r\n @let variant = occupantVariant(placed);\r\n <!-- Tapered toward the back, square at the front, so which way the seat\r\n faces is legible from the outline alone. -->\r\n <path class=\"kerusi-seat__box\" [attr.d]=\"bodyPath(placed)\" [style.fill]=\"fill(placed)\" />\r\n @if (variant === 'held' || variant === 'booked') {\r\n <!-- Colour stays the seat type's; a taken seat recedes under a wash\r\n instead, so the type colour still reads on a busy map. -->\r\n <path\r\n class=\"kerusi-seat__wash\"\r\n [attr.d]=\"bodyPath(placed)\"\r\n [style.fill]=\"washFill(placed)\"\r\n />\r\n }\r\n @if (variant === 'selected') {\r\n <!-- Selection is the one state drawn from two tones: the body above is\r\n the frame, this is the bright core inside it. Whichever way the\r\n host page is themed, one of the two separates from it \u2014 which is\r\n why nothing here has to know about the page. -->\r\n <path class=\"kerusi-seat__core\" [attr.d]=\"corePath(placed)\" [style.fill]=\"coreFill()\" />\r\n }\r\n @if (variant) {\r\n <!-- Deliberately not counter-rotated: the occupant leans with the\r\n seat. Solid = settled (yours, or sold); hollow = a hold still in\r\n progress. -->\r\n <path\r\n class=\"kerusi-seat__occupant kerusi-seat__occupant--{{ variant }}\"\r\n [attr.d]=\"occupantPath(placed)\"\r\n [attr.stroke-width]=\"occupantStroke(placed)\"\r\n [style.fill]=\"occupantFill(variant)\"\r\n [style.stroke]=\"occupantStrokeColor(variant)\"\r\n />\r\n }\r\n <text\r\n class=\"kerusi-seat__label\"\r\n text-anchor=\"middle\"\r\n dominant-baseline=\"central\"\r\n [attr.x]=\"placed.centerX\"\r\n [attr.y]=\"placed.centerY\"\r\n [attr.font-size]=\"placed.fontSize\"\r\n [style.fill]=\"textFill(placed)\"\r\n [style.stroke]=\"labelHalo(placed)\"\r\n [attr.stroke-width]=\"labelHaloWidth(placed)\"\r\n [attr.transform]=\"counterRotate(placed)\"\r\n >\r\n {{ placed.seat.label }}\r\n </text>\r\n @if (placed.seat.accessibility?.wheelchairAccessible) {\r\n <!-- A corner marker, not a different fill: availability owns the fill. -->\r\n <circle\r\n class=\"kerusi-seat__wheelchair\"\r\n [attr.cx]=\"wheelchairCx(placed)\"\r\n [attr.cy]=\"wheelchairCy(placed)\"\r\n [attr.r]=\"wheelchairRadius(placed)\"\r\n [style.fill]=\"textFill(placed)\"\r\n />\r\n }\r\n </g>\r\n }\r\n</svg>\r\n", styles: [":host{display:block}.kerusi-section-svg{display:block;width:100%;height:auto;max-width:100%}.kerusi-seat{cursor:pointer;outline:none}.kerusi-seat--unselectable{cursor:not-allowed}.kerusi-seat__label,.kerusi-element__label{pointer-events:none;-webkit-user-select:none;user-select:none;font-family:inherit}.kerusi-seat__label{paint-order:stroke;stroke-linejoin:round}.kerusi-seat__wash{pointer-events:none;opacity:.5}.kerusi-seat__core,.kerusi-seat__occupant{pointer-events:none}.kerusi-seat__occupant--selected{opacity:.2}.kerusi-seat__occupant--booked{opacity:.55}.kerusi-seat__occupant--held{opacity:.75;stroke-linecap:round;stroke-linejoin:round}.kerusi-seat--selected .kerusi-seat__label{font-weight:600}.kerusi-seat:focus-visible .kerusi-seat__box{stroke:var(--kerusi-focus-ring, var(--kerusi-focus-ring-input, currentColor));stroke-width:2.5;paint-order:stroke}.kerusi-element__void{stroke-width:1;stroke-dasharray:4 3;opacity:.7}\n"] }]
236
245
  }], propDecorators: { section: [{ type: i0.Input, args: [{ isSignal: true, alias: "section", required: true }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: true }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], focusedSeatId: [{ type: i0.Input, args: [{ isSignal: true, alias: "focusedSeatId", required: false }] }], ariaLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabels", required: false }] }], colors: [{ type: i0.Input, args: [{ isSignal: true, alias: "colors", required: false }] }], typeColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeColors", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], unitScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "unitScale", required: false }] }], seatActivate: [{ type: i0.Output, args: ["seatActivate"] }], seatFocused: [{ type: i0.Output, args: ["seatFocused"] }], seatKeydown: [{ type: i0.Output, args: ["seatKeydown"] }] } });
237
246
 
238
247
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"kerusiweb-angular.mjs","sources":["../../../projects/angular/src/lib/kerusi-seatmap/kerusi-section.component.ts","../../../projects/angular/src/lib/kerusi-seatmap/kerusi-section.component.html","../../../projects/angular/src/lib/kerusi-seatmap/kerusi-legend.component.ts","../../../projects/angular/src/lib/kerusi-seatmap/kerusi-legend.component.html","../../../projects/angular/src/lib/kerusi-seatmap/kerusi-seatmap.component.ts","../../../projects/angular/src/lib/kerusi-seatmap/kerusi-seatmap.component.html","../../../projects/angular/src/lib/kerusi-seatmap/index.ts","../../../projects/angular/src/lib/kerusi-state-store.ts","../../../projects/angular/src/public-api.ts","../../../projects/angular/src/kerusiweb-angular.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';\r\nimport {\r\n cssVar,\r\n DEFAULT_KERUSI_COLORS,\r\n elementFill,\r\n elementStyle,\r\n elementTextFill,\r\n KerusiSeatmapColors,\r\n occupantFillFor,\r\n occupantStrokeFor,\r\n PlacedElement,\r\n PlacedSeat,\r\n RenderSection,\r\n screenPath,\r\n seatBodyPath,\r\n SeatOccupantVariant,\r\n seatCoreFill,\r\n seatFill,\r\n seatOccupantPath,\r\n seatOccupantStroke,\r\n seatOccupantVariant,\r\n seatSelectedFrame,\r\n seatTextFill,\r\n seatWashFill,\r\n SectionLayout,\r\n} from '@kerusiweb/core';\r\n\r\n/**\r\n * Draws one Kerusi `Section` as a single `<svg>`.\r\n *\r\n * One SVG per section — rather than section groups inside a shared canvas — is\r\n * what makes per-section `aspectRatio` and mixed grid/freeform modes work: each\r\n * gets its own `viewBox` and its own intrinsic proportions, and CSS is free to\r\n * stack, wrap or column them. It also puts the section heading in real DOM,\r\n * where a screen reader can use it as a landmark.\r\n *\r\n * This component holds no state. It takes a placed layout and emits intent.\r\n */\r\n@Component({\r\n selector: 'kerusi-section',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n templateUrl: './kerusi-section.component.html',\r\n styleUrl: './kerusi-section.component.css',\r\n})\r\nexport class KerusiSectionComponent {\r\n readonly section = input.required<RenderSection>();\r\n readonly layout = input.required<SectionLayout>();\r\n\r\n /** Currently selected seat ids. */\r\n readonly selection = input<ReadonlySet<string>>(new Set());\r\n /** The seat holding the section's single tab stop, if any. */\r\n readonly focusedSeatId = input<string | null>(null);\r\n /** Per-seat accessible names, keyed by seat id. */\r\n readonly ariaLabels = input<ReadonlyMap<string, string>>(new Map());\r\n\r\n readonly colors = input<Required<KerusiSeatmapColors>>(DEFAULT_KERUSI_COLORS);\r\n /** Whether an available seat may take its `SeatType.color` (§4.7). */\r\n readonly typeColors = input<boolean>(true);\r\n /** When false, seats render but do not respond to pointer or keyboard. */\r\n readonly interactive = input<boolean>(true);\r\n /**\r\n * CSS pixels per viewBox unit, at the section's natural size.\r\n *\r\n * Without a cap every section stretches to the full container width, so in a\r\n * four-tier theatre the narrow box row would draw seats several times the\r\n * size of the orchestra's. Capping each section at its own intrinsic width\r\n * keeps a seat the same size everywhere; a section too wide to fit still\r\n * scales down to the container.\r\n */\r\n readonly unitScale = input<number>(1);\r\n\r\n readonly seatActivate = output<string>();\r\n readonly seatFocused = output<string>();\r\n readonly seatKeydown = output<KeyboardEvent>();\r\n\r\n protected readonly viewBox = computed(() => `0 0 ${this.layout().width} ${this.layout().height}`);\r\n\r\n /** The section's natural width in CSS pixels; it never draws wider. */\r\n protected readonly naturalWidth = computed(() => this.layout().width * this.unitScale());\r\n\r\n protected isSelected(placed: PlacedSeat): boolean {\r\n return this.selection().has(placed.seat.id);\r\n }\r\n\r\n protected fill(placed: PlacedSeat): string {\r\n return seatFill(placed.seat, this.isSelected(placed), this.colors(), this.typeColors());\r\n }\r\n\r\n protected textFill(placed: PlacedSeat): string {\r\n return seatTextFill(placed.seat, this.isSelected(placed), this.colors(), this.typeColors());\r\n }\r\n\r\n protected backdrop(): string {\r\n return cssVar('backdrop', this.colors().backdrop);\r\n }\r\n\r\n // --- the seat glyph -------------------------------------------------------\r\n\r\n /** The seat's outline: square-ish front, tapered back — shape carries facing. */\r\n protected bodyPath(placed: PlacedSeat): string {\r\n return seatBodyPath(placed.x, placed.y, placed.width, placed.height);\r\n }\r\n\r\n /** `null` for an available or blocked seat — nothing to mark. */\r\n protected occupantVariant(placed: PlacedSeat): SeatOccupantVariant | null {\r\n return seatOccupantVariant(placed.seat, this.isSelected(placed));\r\n }\r\n\r\n /**\r\n * The box a seat's decorative marks are drawn to: the seat's own, or — when\r\n * it is selected — the core plate's, which is inset by the frame width.\r\n *\r\n * Everything over a selected seat sits on the core, so it all has to be\r\n * measured from the core. Drawing to the seat's box instead leaves the\r\n * occupant's hips crossing the frame, where a figure tinted `selectedBg`\r\n * lands on a frame filled `selectedBg` and reads as a hard nub. Since the\r\n * core is an exact scaled copy of the body (see {@link seatSelectedFrame}),\r\n * routing through here reproduces every clearance the mark had against the\r\n * body, scaled — so the frame width stays free to change.\r\n */\r\n private markBox(placed: PlacedSeat): { x: number; y: number; width: number; height: number } {\r\n if (!this.isSelected(placed)) {\r\n return placed;\r\n }\r\n const frame = seatSelectedFrame(placed.width, placed.height);\r\n return {\r\n x: placed.x + frame,\r\n y: placed.y + frame,\r\n width: placed.width - frame * 2,\r\n height: placed.height - frame * 2,\r\n };\r\n }\r\n\r\n /** The bright plate inside a selected seat's frame. */\r\n protected corePath(placed: PlacedSeat): string {\r\n const box = this.markBox(placed);\r\n return seatBodyPath(box.x, box.y, box.width, box.height);\r\n }\r\n\r\n protected coreFill(): string {\r\n return seatCoreFill(this.colors());\r\n }\r\n\r\n /** The dimming wash over a held or booked seat's type colour. */\r\n protected washFill(placed: PlacedSeat): string {\r\n // Only called for held/booked seats — see the template's @if.\r\n return seatWashFill(placed.seat.status as 'held' | 'booked', this.colors());\r\n }\r\n\r\n /** The occupant figure for a marked seat, sized to whatever it sits on. */\r\n protected occupantPath(placed: PlacedSeat): string {\r\n const box = this.markBox(placed);\r\n return seatOccupantPath(box.x, box.y, box.width, box.height);\r\n }\r\n\r\n protected occupantStroke(placed: PlacedSeat): number {\r\n const box = this.markBox(placed);\r\n return seatOccupantStroke(box.width, box.height);\r\n }\r\n\r\n // The wheelchair marker keeps the same relative corner it always had; only\r\n // the box it is relative to changes, so a selected seat's dot stays whole\r\n // instead of being bitten off by the frame.\r\n protected wheelchairCx(placed: PlacedSeat): number {\r\n const box = this.markBox(placed);\r\n return box.x + box.width * 0.82;\r\n }\r\n\r\n protected wheelchairCy(placed: PlacedSeat): number {\r\n const box = this.markBox(placed);\r\n return box.y + box.height * 0.18;\r\n }\r\n\r\n protected wheelchairRadius(placed: PlacedSeat): number {\r\n return this.markBox(placed).width * 0.13;\r\n }\r\n\r\n /**\r\n * A core-coloured outline behind a selected seat's number, painted under the\r\n * glyphs via `paint-order`.\r\n *\r\n * The label is centred on the occupant's shoulder line, so without this it is\r\n * `selectedBg` over a `selectedBg` silhouette — the figure, not the plate, is\r\n * what it actually has to contrast against. The halo also rescues a label too\r\n * long for the core, which would otherwise run onto the frame and vanish into\r\n * it.\r\n *\r\n * Bound here rather than in the stylesheet on purpose: a CSS\r\n * `var(--kerusi-selected-fg, …)` rule would carry the library default as its\r\n * fallback and silently skip the `[colors]` input tier, which only the\r\n * TypeScript side can resolve.\r\n */\r\n protected labelHalo(placed: PlacedSeat): string {\r\n return this.isSelected(placed) ? seatCoreFill(this.colors()) : 'none';\r\n }\r\n\r\n protected labelHaloWidth(placed: PlacedSeat): number {\r\n return this.isSelected(placed) ? placed.fontSize * 0.22 : 0;\r\n }\r\n\r\n /** Solid for selected/booked; `none` for held, which draws hollow instead. */\r\n protected occupantFill(variant: SeatOccupantVariant): string {\r\n return occupantFillFor(variant, this.colors());\r\n }\r\n\r\n protected occupantStrokeColor(variant: SeatOccupantVariant): string {\r\n return occupantStrokeFor(variant, this.colors());\r\n }\r\n\r\n /** The section's single tab stop; every other seat is reachable by arrow key. */\r\n protected tabIndex(placed: PlacedSeat): number {\r\n if (!this.interactive()) {\r\n return -1;\r\n }\r\n return placed.seat.id === this.focusedSeatId() ? 0 : -1;\r\n }\r\n\r\n protected ariaLabel(placed: PlacedSeat): string {\r\n return this.ariaLabels().get(placed.seat.id) ?? placed.seat.label;\r\n }\r\n\r\n protected seatClasses(placed: PlacedSeat): string {\r\n const seat = placed.seat;\r\n return [\r\n 'kerusi-seat',\r\n `kerusi-seat--${seat.status}`,\r\n this.isSelected(placed) ? 'kerusi-seat--selected' : '',\r\n seat.selectable ? '' : 'kerusi-seat--unselectable',\r\n seat.accessibility?.wheelchairAccessible ? 'kerusi-seat--wheelchair' : '',\r\n seat.companions.length > 0 ? 'kerusi-seat--companion' : '',\r\n ]\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n /** SVG rotate() about the item's center; null when it does not rotate. */\r\n protected transform(item: PlacedSeat | PlacedElement): string | null {\r\n return item.rotation ? `rotate(${item.rotation} ${item.centerX} ${item.centerY})` : null;\r\n }\r\n\r\n /** Cancels the parent <g>'s rotation so the label stays upright and stays put. */\r\n protected counterRotate(item: PlacedSeat | PlacedElement): string | null {\r\n return item.rotation ? `rotate(${-item.rotation} ${item.centerX} ${item.centerY})` : null;\r\n }\r\n\r\n // --- elements -------------------------------------------------------------\r\n\r\n protected style(placed: PlacedElement) {\r\n return elementStyle(placed.element.kind);\r\n }\r\n\r\n protected elementFillOf(placed: PlacedElement): string {\r\n return elementFill(this.style(placed).tone, this.colors());\r\n }\r\n\r\n protected elementTextFillOf(placed: PlacedElement): string {\r\n return elementTextFill(this.style(placed).tone, this.colors());\r\n }\r\n\r\n protected screenPathOf(placed: PlacedElement): string {\r\n return screenPath(placed.x, placed.y, placed.width, placed.height);\r\n }\r\n\r\n /** The corner radius for a rectangular element; a stage reads as squarer. */\r\n protected radiusOf(placed: PlacedElement): number {\r\n return this.style(placed).shape === 'stage' ? 2 : Math.min(placed.height * 0.25, 6);\r\n }\r\n\r\n // --- interaction ----------------------------------------------------------\r\n\r\n protected onSeatClick(placed: PlacedSeat): void {\r\n if (this.interactive()) {\r\n this.seatActivate.emit(placed.seat.id);\r\n }\r\n }\r\n\r\n protected onSeatFocus(placed: PlacedSeat): void {\r\n if (this.interactive()) {\r\n this.seatFocused.emit(placed.seat.id);\r\n }\r\n }\r\n\r\n protected onKeydown(event: KeyboardEvent): void {\r\n if (this.interactive()) {\r\n this.seatKeydown.emit(event);\r\n }\r\n }\r\n}\r\n","<svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n class=\"kerusi-section-svg\"\r\n role=\"presentation\"\r\n focusable=\"false\"\r\n [style.--kerusi-focus-ring-input]=\"colors().focusRing\"\r\n [attr.viewBox]=\"viewBox()\"\r\n [style.max-width.px]=\"naturalWidth()\"\r\n preserveAspectRatio=\"xMidYMid meet\"\r\n>\r\n <rect\r\n x=\"0\"\r\n y=\"0\"\r\n [attr.width]=\"layout().width\"\r\n [attr.height]=\"layout().height\"\r\n [style.fill]=\"backdrop()\"\r\n />\r\n\r\n <!-- Elements first, so a screen or stage sits behind the seats it faces. -->\r\n @for (placed of layout().elements; track placed.element.id) {\r\n <g\r\n class=\"kerusi-element kerusi-element--{{ placed.element.kind }}\"\r\n [attr.transform]=\"transform(placed)\"\r\n >\r\n @switch (style(placed).shape) {\r\n @case ('screen') {\r\n <path [attr.d]=\"screenPathOf(placed)\" [style.fill]=\"elementFillOf(placed)\" />\r\n }\r\n @case ('void') {\r\n <rect\r\n class=\"kerusi-element__void\"\r\n [attr.x]=\"placed.x\"\r\n [attr.y]=\"placed.y\"\r\n [attr.width]=\"placed.width\"\r\n [attr.height]=\"placed.height\"\r\n [style.stroke]=\"elementTextFillOf(placed)\"\r\n fill=\"none\"\r\n />\r\n }\r\n @default {\r\n <rect\r\n [attr.x]=\"placed.x\"\r\n [attr.y]=\"placed.y\"\r\n [attr.width]=\"placed.width\"\r\n [attr.height]=\"placed.height\"\r\n [attr.rx]=\"radiusOf(placed)\"\r\n [style.fill]=\"elementFillOf(placed)\"\r\n />\r\n }\r\n }\r\n @if (placed.element.label && style(placed).showLabel) {\r\n <text\r\n class=\"kerusi-element__label\"\r\n text-anchor=\"middle\"\r\n dominant-baseline=\"central\"\r\n [attr.x]=\"placed.centerX\"\r\n [attr.y]=\"placed.centerY\"\r\n [attr.font-size]=\"placed.fontSize\"\r\n [style.fill]=\"elementTextFillOf(placed)\"\r\n [attr.transform]=\"counterRotate(placed)\"\r\n >\r\n {{ placed.element.label }}\r\n </text>\r\n }\r\n </g>\r\n }\r\n\r\n @for (placed of layout().seats; track placed.seat.id) {\r\n <g\r\n [class]=\"seatClasses(placed)\"\r\n role=\"button\"\r\n [attr.tabindex]=\"tabIndex(placed)\"\r\n [attr.aria-label]=\"ariaLabel(placed)\"\r\n [attr.aria-pressed]=\"isSelected(placed)\"\r\n [attr.aria-disabled]=\"!placed.seat.selectable\"\r\n [attr.data-seat-id]=\"placed.seat.id\"\r\n [attr.transform]=\"transform(placed)\"\r\n (click)=\"onSeatClick(placed)\"\r\n (focus)=\"onSeatFocus(placed)\"\r\n (keydown)=\"onKeydown($event)\"\r\n >\r\n @let variant = occupantVariant(placed);\r\n <!-- Tapered toward the back, square at the front, so which way the seat\r\n faces is legible from the outline alone. -->\r\n <path class=\"kerusi-seat__box\" [attr.d]=\"bodyPath(placed)\" [style.fill]=\"fill(placed)\" />\r\n @if (variant === 'held' || variant === 'booked') {\r\n <!-- Colour stays the seat type's; a taken seat recedes under a wash\r\n instead, so the type colour still reads on a busy map. -->\r\n <path\r\n class=\"kerusi-seat__wash\"\r\n [attr.d]=\"bodyPath(placed)\"\r\n [style.fill]=\"washFill(placed)\"\r\n />\r\n }\r\n @if (variant === 'selected') {\r\n <!-- Selection is the one state drawn from two tones: the body above is\r\n the frame, this is the bright core inside it. Whichever way the\r\n host page is themed, one of the two separates from it — which is\r\n why nothing here has to know about the page. -->\r\n <path class=\"kerusi-seat__core\" [attr.d]=\"corePath(placed)\" [style.fill]=\"coreFill()\" />\r\n }\r\n @if (variant) {\r\n <!-- Deliberately not counter-rotated: the occupant leans with the\r\n seat. Solid = settled (yours, or sold); hollow = a hold still in\r\n progress. -->\r\n <path\r\n class=\"kerusi-seat__occupant kerusi-seat__occupant--{{ variant }}\"\r\n [attr.d]=\"occupantPath(placed)\"\r\n [attr.stroke-width]=\"occupantStroke(placed)\"\r\n [style.fill]=\"occupantFill(variant)\"\r\n [style.stroke]=\"occupantStrokeColor(variant)\"\r\n />\r\n }\r\n <text\r\n class=\"kerusi-seat__label\"\r\n text-anchor=\"middle\"\r\n dominant-baseline=\"central\"\r\n [attr.x]=\"placed.centerX\"\r\n [attr.y]=\"placed.centerY\"\r\n [attr.font-size]=\"placed.fontSize\"\r\n [style.fill]=\"textFill(placed)\"\r\n [style.stroke]=\"labelHalo(placed)\"\r\n [attr.stroke-width]=\"labelHaloWidth(placed)\"\r\n [attr.transform]=\"counterRotate(placed)\"\r\n >\r\n {{ placed.seat.label }}\r\n </text>\r\n @if (placed.seat.accessibility?.wheelchairAccessible) {\r\n <!-- A corner marker, not a different fill: availability owns the fill. -->\r\n <circle\r\n class=\"kerusi-seat__wheelchair\"\r\n [attr.cx]=\"wheelchairCx(placed)\"\r\n [attr.cy]=\"wheelchairCy(placed)\"\r\n [attr.r]=\"wheelchairRadius(placed)\"\r\n [style.fill]=\"textFill(placed)\"\r\n />\r\n }\r\n </g>\r\n }\r\n</svg>\r\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\r\nimport {\r\n cssVar,\r\n DEFAULT_KERUSI_COLORS,\r\n formatMoney,\r\n KerusiSeatmapColors,\r\n occupantFillFor,\r\n occupantStrokeFor,\r\n readableOn,\r\n RenderLegendEntry,\r\n seatBodyPath,\r\n SeatOccupantVariant,\r\n seatCoreFill,\r\n seatOccupantPath,\r\n seatOccupantStroke,\r\n seatSelectedFrame,\r\n seatWashFill,\r\n} from '@kerusiweb/core';\r\n\r\n/** Which marks, beyond the body fill, a status swatch draws. */\r\ntype StatusKind = 'plain' | 'blocked' | SeatOccupantVariant;\r\n\r\n/** One availability swatch in the legend's second block. */\r\ninterface StatusEntry {\r\n key: string;\r\n label: string;\r\n kind: StatusKind;\r\n /** The swatch body's fill. */\r\n fill: string;\r\n}\r\n\r\n/**\r\n * A key for a seat map: its seat types on one side, its availability states on\r\n * the other.\r\n *\r\n * This ships in the library rather than being left to each application because\r\n * the swatch colors must come from the *same* resolution path as the seat\r\n * fills — `SeatType.color` when the document supplies one, the theme's\r\n * available color when it does not. Re-deriving that in application code is how\r\n * a legend ends up disagreeing with the map it describes.\r\n *\r\n * It is opt-in: `<kerusi-seatmap [showLegend]=\"true\">` renders it inline, or\r\n * use `<kerusi-legend>` directly to place it anywhere.\r\n */\r\n@Component({\r\n selector: 'kerusi-legend',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n templateUrl: './kerusi-legend.component.html',\r\n styleUrl: './kerusi-legend.component.css',\r\n})\r\nexport class KerusiLegendComponent {\r\n /** From `RenderMap.legend`. */\r\n readonly legend = input.required<readonly RenderLegendEntry[]>();\r\n readonly locale = input<string>('en');\r\n readonly colors = input<Required<KerusiSeatmapColors>>(DEFAULT_KERUSI_COLORS);\r\n /** Whether an available seat takes its type color — mirrors the seatmap input. */\r\n readonly typeColors = input<boolean>(true);\r\n readonly showPrices = input<boolean>(true);\r\n /** Hide types no seat in the rendered map actually uses. */\r\n readonly hideUnusedTypes = input<boolean>(true);\r\n\r\n readonly headingSeatTypes = input<string>('Seat types');\r\n readonly headingAvailability = input<string>('Availability');\r\n\r\n protected readonly types = computed(() =>\r\n this.legend().filter((entry) => !this.hideUnusedTypes() || entry.seatCount > 0),\r\n );\r\n\r\n // Colour means seat type; shape means status (see `seatFill`'s doc comment).\r\n // Held and booked swatches share the plain \"available\" body colour — the\r\n // map's colour there is always whatever the seat's own type supplies, which\r\n // the legend has no single value for — and are told apart by their wash and\r\n // occupant figure instead, same as on the map.\r\n protected readonly statuses = computed<StatusEntry[]>(() => {\r\n const c = this.colors();\r\n const available = cssVar('availableBg', c.availableBg);\r\n return [\r\n { key: 'available', label: 'Available', kind: 'plain', fill: available },\r\n {\r\n key: 'selected',\r\n label: 'Selected',\r\n kind: 'selected',\r\n fill: cssVar('selectedBg', c.selectedBg),\r\n },\r\n { key: 'held', label: 'On hold', kind: 'held', fill: available },\r\n { key: 'booked', label: 'Booked', kind: 'booked', fill: available },\r\n { key: 'blocked', label: 'Blocked', kind: 'blocked', fill: cssVar('blockedBg', c.blockedBg) },\r\n ];\r\n });\r\n\r\n protected swatch(entry: RenderLegendEntry): string {\r\n return (this.typeColors() && entry.color) || cssVar('availableBg', this.colors().availableBg);\r\n }\r\n\r\n protected swatchText(entry: RenderLegendEntry): string {\r\n const typeColor = this.typeColors() && entry.color;\r\n // Mirrors `seatTextFill`: only a document-supplied color needs guessing at.\r\n return typeColor ? readableOn(typeColor) : cssVar('availableFg', this.colors().availableFg);\r\n }\r\n\r\n // --- status swatches -------------------------------------------------------\r\n //\r\n // Availability is now signalled by shape, not colour, so every status swatch\r\n // (bar \"available\" and \"blocked\") draws the same glyph the map does rather\r\n // than a flat square. A legend that showed only flat colour would teach a\r\n // cue the map no longer uses.\r\n\r\n /** The swatch's viewBox edge; the seat glyph is drawn to fill it. */\r\n protected readonly glyphSize = 16;\r\n\r\n protected readonly glyphBody = seatBodyPath(0, 0, this.glyphSize, this.glyphSize);\r\n\r\n /** The selected swatch's frame width, and the core it leaves inside itself. */\r\n private readonly glyphFrame = seatSelectedFrame(this.glyphSize, this.glyphSize);\r\n private readonly glyphCoreSize = this.glyphSize - this.glyphFrame * 2;\r\n protected readonly glyphCore = seatBodyPath(\r\n 0,\r\n 0,\r\n this.glyphSize,\r\n this.glyphSize,\r\n this.glyphFrame,\r\n );\r\n\r\n protected readonly glyphOccupant = seatOccupantPath(0, 0, this.glyphSize, this.glyphSize);\r\n protected readonly glyphOccupantStroke = seatOccupantStroke(this.glyphSize, this.glyphSize);\r\n\r\n /** The selected swatch's figure sits on the core, so it is drawn to the core. */\r\n private readonly glyphCoreOccupant = seatOccupantPath(\r\n this.glyphFrame,\r\n this.glyphFrame,\r\n this.glyphCoreSize,\r\n this.glyphCoreSize,\r\n );\r\n private readonly glyphCoreOccupantStroke = seatOccupantStroke(\r\n this.glyphCoreSize,\r\n this.glyphCoreSize,\r\n );\r\n\r\n protected occupantPathOf(variant: SeatOccupantVariant): string {\r\n return variant === 'selected' ? this.glyphCoreOccupant : this.glyphOccupant;\r\n }\r\n\r\n protected occupantStrokeWidthOf(variant: SeatOccupantVariant): number {\r\n return variant === 'selected' ? this.glyphCoreOccupantStroke : this.glyphOccupantStroke;\r\n }\r\n\r\n protected hasWash(kind: StatusKind): kind is 'held' | 'booked' {\r\n return kind === 'held' || kind === 'booked';\r\n }\r\n\r\n protected hasOccupant(kind: StatusKind): kind is SeatOccupantVariant {\r\n return kind === 'selected' || kind === 'held' || kind === 'booked';\r\n }\r\n\r\n protected washFillOf(kind: 'held' | 'booked'): string {\r\n return seatWashFill(kind, this.colors());\r\n }\r\n\r\n protected occupantFillOf(variant: SeatOccupantVariant): string {\r\n return occupantFillFor(variant, this.colors());\r\n }\r\n\r\n protected occupantStrokeOf(variant: SeatOccupantVariant): string {\r\n return occupantStrokeFor(variant, this.colors());\r\n }\r\n\r\n protected coreFill(): string {\r\n return seatCoreFill(this.colors());\r\n }\r\n\r\n protected price(entry: RenderLegendEntry): string {\r\n return entry.price ? formatMoney(entry.price, this.locale()) : '';\r\n }\r\n}\r\n","<div class=\"kerusi-legend\">\r\n @if (types().length) {\r\n <div class=\"kerusi-legend__block\">\r\n <h4 class=\"kerusi-legend__heading\">{{ headingSeatTypes() }}</h4>\r\n <ul class=\"kerusi-legend__list\">\r\n @for (entry of types(); track entry.id) {\r\n <li class=\"kerusi-legend__item\">\r\n <span\r\n class=\"kerusi-legend__swatch\"\r\n aria-hidden=\"true\"\r\n [style.background]=\"swatch(entry)\"\r\n [style.color]=\"swatchText(entry)\"\r\n ></span>\r\n <span class=\"kerusi-legend__label\">{{ entry.label }}</span>\r\n @if (showPrices() && price(entry)) {\r\n <span class=\"kerusi-legend__price\">{{ price(entry) }}</span>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n </div>\r\n }\r\n\r\n <div class=\"kerusi-legend__block\">\r\n <h4 class=\"kerusi-legend__heading\">{{ headingAvailability() }}</h4>\r\n <ul class=\"kerusi-legend__list\">\r\n @for (status of statuses(); track status.key) {\r\n <li class=\"kerusi-legend__item\">\r\n <!-- Shape carries status now, so every swatch draws the seat glyph\r\n rather than a flat colour — a flat swatch would teach a cue the\r\n map itself no longer uses. -->\r\n <svg\r\n class=\"kerusi-legend__swatch kerusi-legend__swatch--glyph\"\r\n aria-hidden=\"true\"\r\n focusable=\"false\"\r\n [attr.viewBox]=\"'0 0 ' + glyphSize + ' ' + glyphSize\"\r\n >\r\n <path [attr.d]=\"glyphBody\" [style.fill]=\"status.fill\" />\r\n @if (hasWash(status.kind)) {\r\n <path [attr.d]=\"glyphBody\" [style.fill]=\"washFillOf(status.kind)\" opacity=\"0.5\" />\r\n }\r\n @if (status.kind === 'selected') {\r\n <!-- Two tones, same as the map: the body above is the frame, this\r\n is the core inside it. -->\r\n <path [attr.d]=\"glyphCore\" [style.fill]=\"coreFill()\" />\r\n }\r\n @if (hasOccupant(status.kind)) {\r\n <path\r\n [attr.d]=\"occupantPathOf(status.kind)\"\r\n class=\"kerusi-legend__occupant kerusi-legend__occupant--{{ status.kind }}\"\r\n [attr.stroke-width]=\"occupantStrokeWidthOf(status.kind)\"\r\n [style.fill]=\"occupantFillOf(status.kind)\"\r\n [style.stroke]=\"occupantStrokeOf(status.kind)\"\r\n />\r\n }\r\n </svg>\r\n <span class=\"kerusi-legend__label\">{{ status.label }}</span>\r\n </li>\r\n }\r\n </ul>\r\n </div>\r\n</div>\r\n","import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n computed,\r\n effect,\r\n ElementRef,\r\n inject,\r\n input,\r\n model,\r\n output,\r\n signal,\r\n} from '@angular/core';\r\nimport {\r\n buildNavigationGraph,\r\n buildRenderModel,\r\n computeSectionLayout,\r\n DEFAULT_SEAT_ARIA_STRINGS,\r\n DisallowedReason,\r\n disallowedAnnouncement,\r\n errorsOf,\r\n expireHolds,\r\n formatMoney,\r\n isRtlLocale,\r\n KerusiMap,\r\n KerusiSeatmapColors,\r\n KerusiSession,\r\n KerusiState,\r\n KerusiValidationError,\r\n KerusiViolation,\r\n Money,\r\n NavigationGraph,\r\n RenderMap,\r\n RenderSeat,\r\n RenderSection,\r\n resolveColors,\r\n resolveMapLocale,\r\n SeatAriaStrings,\r\n seatAriaLabel,\r\n SeatRenderStatus,\r\n SectionLayout,\r\n summarizeSelection,\r\n toggleSeatSelection,\r\n validateDocumentSet,\r\n} from '@kerusiweb/core';\r\nimport { KerusiSectionComponent } from './kerusi-section.component';\r\nimport { KerusiLegendComponent } from './kerusi-legend.component';\r\n\r\n/** How a document with validation errors is handled. */\r\nexport type ValidationMode =\r\n /** Report through `validationIssues` and render anyway. The default. */\r\n | 'collect'\r\n /** Throw a `KerusiValidationError`, as `validateKerusiMap` does. */\r\n | 'throw'\r\n /** Skip validation entirely, for a document validated upstream. */\r\n | 'off';\r\n\r\n/** Per-section rendering overrides, keyed by `Section.id`. */\r\nexport interface SectionRenderOptions {\r\n hidden?: boolean;\r\n /** Overrides `Section.aspectRatio`. */\r\n aspectRatio?: string;\r\n /** Overrides the seat size for this section only. */\r\n seatSize?: number;\r\n /** Overrides the localized `Section.label`. */\r\n label?: string;\r\n}\r\n\r\n/** What a seat event carries. Everything the document knows, already resolved. */\r\nexport interface SeatInteraction {\r\n seat: RenderSeat;\r\n /** The selection after this event. */\r\n selection: readonly string[];\r\n /** Seats whose membership changed, including companions (§4.6). */\r\n changed: readonly string[];\r\n}\r\n\r\nexport interface SeatDisallowed {\r\n seat: RenderSeat;\r\n reason: DisallowedReason;\r\n}\r\n\r\n/**\r\n * Renders a Kerusi seat map.\r\n *\r\n * Takes a `KerusiMap` and its `KerusiState` directly — no adapter, no\r\n * intermediate row model. Each `Section` becomes its own `<svg>` with its own\r\n * layout mode and proportions, seats take their color from the map's legend,\r\n * every seat is keyboard-reachable and announced with its type, price, status\r\n * and §4.3.4 accessibility properties, and selection is an immutable set of\r\n * seat ids the caller can two-way bind.\r\n *\r\n * ```html\r\n * <kerusi-seatmap [map]=\"map\" [state]=\"state\" [(selection)]=\"picked\" />\r\n * ```\r\n */\r\n@Component({\r\n selector: 'kerusi-seatmap',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [KerusiSectionComponent, KerusiLegendComponent],\r\n templateUrl: './kerusi-seatmap.component.html',\r\n styleUrl: './kerusi-seatmap.component.css',\r\n})\r\nexport class KerusiSeatmapComponent {\r\n private readonly host = inject(ElementRef<HTMLElement>);\r\n\r\n // --- documents ------------------------------------------------------------\r\n\r\n readonly map = input.required<KerusiMap>();\r\n readonly state = input<KerusiState | undefined>(undefined);\r\n /** The optional map↔event join (§5.3). Validated against the map and state. */\r\n readonly session = input<KerusiSession | undefined>(undefined);\r\n\r\n // --- selection ------------------------------------------------------------\r\n\r\n /** Selected seat ids. Two-way: `[(selection)]`, or read-only with `[selection]`. */\r\n readonly selection = model<readonly string[]>([]);\r\n\r\n // --- localization ---------------------------------------------------------\r\n\r\n /** Overrides `KerusiMap.locale` (BCP-47). */\r\n readonly locale = input<string | undefined>(undefined);\r\n /** Right-to-left rendering. `auto` derives it from the resolved locale. */\r\n readonly rtl = input<boolean | 'auto'>('auto');\r\n /** Overrides the built-in English announcement strings. */\r\n readonly ariaStrings = input<SeatAriaStrings>(DEFAULT_SEAT_ARIA_STRINGS);\r\n\r\n // --- appearance -----------------------------------------------------------\r\n\r\n readonly colors = input<KerusiSeatmapColors>({});\r\n /** Let an available seat take its `SeatType.color` (§4.7). */\r\n readonly typeColors = input<boolean>(true);\r\n /** Grid cell edge, in viewBox units. */\r\n readonly seatSize = input<number>(28);\r\n /** Gap between grid cells, in viewBox units. */\r\n readonly seatGap = input<number>(6);\r\n /** Freeform viewBox width; height follows the section's aspect ratio. */\r\n readonly freeformBasis = input<number>(1000);\r\n /**\r\n * CSS pixels per viewBox unit. Caps each section at its natural size so a\r\n * narrow section and a wide one in the same map draw seats the same size.\r\n */\r\n readonly unitScale = input<number>(1);\r\n readonly showSectionLabels = input<boolean>(true);\r\n readonly showLegend = input<boolean>(false);\r\n readonly showLegendPrices = input<boolean>(true);\r\n\r\n // --- section control ------------------------------------------------------\r\n\r\n /** Render only these sections, in this order. Default: all, by `Section.index`. */\r\n readonly sectionIds = input<readonly string[] | undefined>(undefined);\r\n readonly sectionOverrides = input<Readonly<Record<string, SectionRenderOptions>>>({});\r\n\r\n // --- interaction ----------------------------------------------------------\r\n\r\n /** Statuses in which a seat may be picked. Default: `['available']`. */\r\n readonly selectableStatuses = input<readonly SeatRenderStatus[]>(['available']);\r\n /** `auto` selects a seat's companion closure together (§4.6). */\r\n readonly companionMode = input<'auto' | 'independent'>('auto');\r\n readonly maxSelection = input<number | undefined>(undefined);\r\n /** A final say on selectability, applied after the status test. */\r\n readonly seatSelectable = input<((seat: RenderSeat) => boolean) | undefined>(undefined);\r\n /** When false the map renders read-only: no pointer or keyboard interaction. */\r\n readonly interactive = input<boolean>(true);\r\n /**\r\n * Revert a lapsed `holdExpires` to available on a ticker (§5.1). Off by\r\n * default — a renderer should stay passive unless asked.\r\n */\r\n readonly expireHolds = input<boolean>(false);\r\n readonly expiryIntervalMs = input<number>(1000);\r\n\r\n // --- validation -----------------------------------------------------------\r\n\r\n readonly validate = input<ValidationMode>('collect');\r\n\r\n // --- outputs --------------------------------------------------------------\r\n\r\n readonly seatSelect = output<SeatInteraction>();\r\n readonly seatDeselect = output<SeatInteraction>();\r\n readonly seatDisallowed = output<SeatDisallowed>();\r\n readonly seatFocus = output<RenderSeat>();\r\n /** Emitted whenever the documents change, in `collect` mode. */\r\n readonly validationIssues = output<readonly KerusiViolation[]>();\r\n\r\n // --- internal state -------------------------------------------------------\r\n\r\n /**\r\n * The seat holding each section's tab stop, keyed by section id.\r\n *\r\n * Per section, not per map: arrow keys never cross a section boundary, so a\r\n * single map-wide tab stop would leave every section after the first\r\n * unreachable by keyboard. Tab moves between sections, arrows move within.\r\n */\r\n private readonly focusedSeatIds = signal<Readonly<Record<string, string>>>({});\r\n private readonly announcement = signal('');\r\n /** Bumped by the hold ticker to force the state to be re-derived. */\r\n private readonly expiryTick = signal(0);\r\n\r\n constructor() {\r\n effect(() => {\r\n const issues = this.issues();\r\n if (this.validate() !== 'off') {\r\n this.validationIssues.emit(issues);\r\n }\r\n if (this.validate() === 'throw') {\r\n const [first] = errorsOf(issues);\r\n if (first) {\r\n throw new KerusiValidationError(first.message, first.rule, first.id, issues);\r\n }\r\n }\r\n });\r\n\r\n // Keep every section's tab stop pointing at a seat that still exists.\r\n effect(() => {\r\n const model = this.renderMap();\r\n const current = this.focusedSeatIds();\r\n const next: Record<string, string> = {};\r\n let changed = Object.keys(current).length !== model.sections.length;\r\n\r\n for (const section of model.sections) {\r\n const held = current[section.id];\r\n const valid = held && section.seats.some((seat) => seat.id === held);\r\n const resolved = valid ? held : section.seats[0]?.id;\r\n if (resolved) {\r\n next[section.id] = resolved;\r\n changed ||= resolved !== held;\r\n }\r\n }\r\n\r\n if (changed) {\r\n this.focusedSeatIds.set(next);\r\n }\r\n });\r\n\r\n effect((onCleanup) => {\r\n if (!this.expireHolds()) {\r\n return;\r\n }\r\n const handle = setInterval(\r\n () => this.expiryTick.update((n) => n + 1),\r\n this.expiryIntervalMs(),\r\n );\r\n onCleanup(() => clearInterval(handle));\r\n });\r\n }\r\n\r\n // --- derived model --------------------------------------------------------\r\n\r\n protected readonly issues = computed<readonly KerusiViolation[]>(() =>\r\n this.validate() === 'off'\r\n ? []\r\n : validateDocumentSet({\r\n map: this.map(),\r\n state: this.state(),\r\n session: this.session(),\r\n }),\r\n );\r\n\r\n /** The state actually rendered — with lapsed holds reverted when asked. */\r\n private readonly effectiveState = computed<KerusiState | undefined>(() => {\r\n const state = this.state();\r\n if (!state || !this.expireHolds()) {\r\n return state;\r\n }\r\n this.expiryTick();\r\n return expireHolds(state);\r\n });\r\n\r\n protected readonly renderMap = computed<RenderMap>(() =>\r\n buildRenderModel(this.map(), this.effectiveState(), {\r\n locale: this.locale(),\r\n sectionIds: this.visibleSectionIds(),\r\n selectableStatuses: this.selectableStatuses(),\r\n seatSelectable: this.seatSelectable(),\r\n }),\r\n );\r\n\r\n protected readonly resolvedLocale = computed(() => resolveMapLocale(this.map(), this.locale()));\r\n\r\n protected readonly isRtl = computed(() =>\r\n this.rtl() === 'auto' ? isRtlLocale(this.resolvedLocale()) : this.rtl() === true,\r\n );\r\n\r\n protected readonly resolvedColors = computed<Required<KerusiSeatmapColors>>(() =>\r\n resolveColors(this.colors()),\r\n );\r\n\r\n protected readonly selectionSet = computed(() => new Set(this.selection()));\r\n\r\n /** Placed geometry per section, keyed by section id. */\r\n protected readonly layouts = computed<ReadonlyMap<string, SectionLayout>>(() => {\r\n const overrides = this.sectionOverrides();\r\n return new Map(\r\n this.renderMap().sections.map((section) => {\r\n const override = overrides[section.id] ?? {};\r\n return [\r\n section.id,\r\n computeSectionLayout(section, {\r\n seatSize: override.seatSize ?? this.seatSize(),\r\n seatGap: this.seatGap(),\r\n freeformBasis: this.freeformBasis(),\r\n aspectRatio: override.aspectRatio,\r\n rtl: this.isRtl(),\r\n }),\r\n ];\r\n }),\r\n );\r\n });\r\n\r\n /** Navigation graphs per section — arrows move within, Tab moves between. */\r\n private readonly graphs = computed<ReadonlyMap<string, NavigationGraph>>(\r\n () =>\r\n new Map(\r\n this.renderMap().sections.map((section) => [\r\n section.id,\r\n buildNavigationGraph(section, { rtl: this.isRtl() }),\r\n ]),\r\n ),\r\n );\r\n\r\n protected readonly ariaLabels = computed<ReadonlyMap<string, string>>(() => {\r\n const locale = this.resolvedLocale();\r\n const strings = this.ariaStrings();\r\n return new Map(\r\n [...this.renderMap().seatsById.values()].map((seat) => [\r\n seat.id,\r\n seatAriaLabel(seat, locale, strings),\r\n ]),\r\n );\r\n });\r\n\r\n protected readonly mapLabel = computed(\r\n () => this.renderMap().name ?? `Seat map ${this.renderMap().id}`,\r\n );\r\n\r\n /** Live-region text: the running selection, or the last disallowed reason. */\r\n protected readonly liveMessage = computed(() => this.announcement());\r\n\r\n /** The selection's seats and total, for callers that want it off the template. */\r\n readonly summary = computed(() => summarizeSelection(this.renderMap(), this.selection()));\r\n\r\n // --- template helpers -----------------------------------------------------\r\n\r\n protected layoutFor(section: RenderSection): SectionLayout {\r\n return this.layouts().get(section.id)!;\r\n }\r\n\r\n protected labelFor(section: RenderSection): string | undefined {\r\n return this.sectionOverrides()[section.id]?.label ?? section.label;\r\n }\r\n\r\n protected labelId(section: RenderSection): string {\r\n return `kerusi-section-${this.renderMap().id}-${section.id}`;\r\n }\r\n\r\n /** The seat holding this section's tab stop. */\r\n protected focusedSeatIn(section: RenderSection): string | null {\r\n return this.focusedSeatIds()[section.id] ?? null;\r\n }\r\n\r\n // --- interaction ----------------------------------------------------------\r\n\r\n protected onSeatActivate(seatId: string): void {\r\n const model = this.renderMap();\r\n const seat = model.seatsById.get(seatId);\r\n if (!seat) {\r\n return;\r\n }\r\n\r\n const outcome = toggleSeatSelection(this.selection(), seatId, {\r\n seatsById: model.seatsById,\r\n companionMode: this.companionMode(),\r\n maxSelection: this.maxSelection(),\r\n });\r\n\r\n if (outcome.kind === 'disallowed') {\r\n this.announce(disallowedAnnouncement(seat, outcome.reason!, this.ariaStrings()));\r\n this.seatDisallowed.emit({ seat, reason: outcome.reason! });\r\n return;\r\n }\r\n\r\n this.selection.set(outcome.selection);\r\n const event: SeatInteraction = {\r\n seat,\r\n selection: outcome.selection,\r\n changed: outcome.changed,\r\n };\r\n (outcome.kind === 'select' ? this.seatSelect : this.seatDeselect).emit(event);\r\n this.announceSelection(outcome.selection);\r\n }\r\n\r\n protected onSeatFocused(seatId: string): void {\r\n const seat = this.renderMap().seatsById.get(seatId);\r\n if (!seat) {\r\n return;\r\n }\r\n this.setFocusedSeat(seat.sectionId, seatId);\r\n this.seatFocus.emit(seat);\r\n }\r\n\r\n /**\r\n * Arrow keys walk the navigation graph, which orders seats by `col` in grid\r\n * and mixed sections — §4.3.1's logical adjacency — so the aisle that is a\r\n * skipped column is stepped across rather than into.\r\n */\r\n protected onKeydown(section: RenderSection, event: KeyboardEvent): void {\r\n const seatId = (event.target as HTMLElement)?.dataset?.['seatId'];\r\n if (!seatId) {\r\n return;\r\n }\r\n\r\n const graph = this.graphs().get(section.id);\r\n const neighbours = graph?.neighbours.get(seatId);\r\n if (!graph) {\r\n return;\r\n }\r\n\r\n let target: string | undefined;\r\n switch (event.key) {\r\n case 'ArrowLeft':\r\n target = neighbours?.left;\r\n break;\r\n case 'ArrowRight':\r\n target = neighbours?.right;\r\n break;\r\n case 'ArrowUp':\r\n target = neighbours?.up;\r\n break;\r\n case 'ArrowDown':\r\n target = neighbours?.down;\r\n break;\r\n case 'Home':\r\n target = event.ctrlKey ? graph.first : neighbours?.rowStart;\r\n break;\r\n case 'End':\r\n target = event.ctrlKey ? graph.last : neighbours?.rowEnd;\r\n break;\r\n case 'PageUp':\r\n target = graph.first;\r\n break;\r\n case 'PageDown':\r\n target = graph.last;\r\n break;\r\n case 'Enter':\r\n case ' ':\r\n event.preventDefault();\r\n this.onSeatActivate(seatId);\r\n return;\r\n case 'Escape':\r\n if (this.selection().length > 0) {\r\n event.preventDefault();\r\n this.selection.set([]);\r\n this.announceSelection([]);\r\n }\r\n return;\r\n default:\r\n return;\r\n }\r\n\r\n if (target) {\r\n event.preventDefault();\r\n this.moveFocusTo(section.id, target);\r\n }\r\n }\r\n\r\n private setFocusedSeat(sectionId: string, seatId: string): void {\r\n if (this.focusedSeatIds()[sectionId] === seatId) {\r\n return;\r\n }\r\n this.focusedSeatIds.update((all) => ({ ...all, [sectionId]: seatId }));\r\n }\r\n\r\n /**\r\n * Moves the roving tab stop, then the DOM focus that follows it.\r\n *\r\n * Seat ids come from the document, so they cannot be interpolated into a\r\n * selector unescaped — and `CSS.escape` is absent in jsdom and under SSR.\r\n * Scanning the rendered nodes avoids both problems.\r\n */\r\n private moveFocusTo(sectionId: string, seatId: string): void {\r\n this.setFocusedSeat(sectionId, seatId);\r\n const nodes = (this.host.nativeElement as HTMLElement).querySelectorAll<SVGGElement>(\r\n '[data-seat-id]',\r\n );\r\n for (const node of Array.from(nodes)) {\r\n if (node.dataset['seatId'] === seatId) {\r\n node.focus?.();\r\n return;\r\n }\r\n }\r\n }\r\n\r\n private announceSelection(selection: readonly string[]): void {\r\n const strings = this.ariaStrings();\r\n if (selection.length === 0) {\r\n this.announce(strings.selectionEmpty);\r\n return;\r\n }\r\n const { total } = summarizeSelection(this.renderMap(), selection);\r\n this.announce(\r\n strings.selectionSummary(selection.length, formatTotal(total, this.resolvedLocale())),\r\n );\r\n }\r\n\r\n private announce(message: string): void {\r\n // Re-announce an identical message by clearing first; a live region that\r\n // sees no text change stays silent.\r\n this.announcement.set('');\r\n queueMicrotask(() => this.announcement.set(message));\r\n }\r\n\r\n private visibleSectionIds(): readonly string[] | undefined {\r\n const explicit = this.sectionIds();\r\n const overrides = this.sectionOverrides();\r\n const hidden = Object.entries(overrides)\r\n .filter(([, o]) => o.hidden)\r\n .map(([id]) => id);\r\n\r\n if (explicit) {\r\n return explicit.filter((id) => !hidden.includes(id));\r\n }\r\n if (hidden.length === 0) {\r\n return undefined;\r\n }\r\n return (this.map().sections ?? []).map((s) => s.id).filter((id) => !hidden.includes(id));\r\n }\r\n}\r\n\r\nfunction formatTotal(total: Money | undefined, locale: string): string {\r\n return total ? formatMoney(total, locale) : '';\r\n}\r\n","<div\r\n class=\"kerusi-seatmap\"\r\n role=\"group\"\r\n [attr.aria-label]=\"mapLabel()\"\r\n [attr.dir]=\"isRtl() ? 'rtl' : null\"\r\n [attr.lang]=\"resolvedLocale()\"\r\n>\r\n @for (section of renderMap().sections; track section.id) {\r\n <section\r\n class=\"kerusi-seatmap__section kerusi-seatmap__section--{{ section.layoutMode }}\"\r\n role=\"group\"\r\n [attr.aria-labelledby]=\"labelFor(section) ? labelId(section) : null\"\r\n [attr.aria-label]=\"labelFor(section) ? null : section.id\"\r\n >\r\n @if (showSectionLabels() && labelFor(section)) {\r\n <h3 class=\"kerusi-section-label\" [id]=\"labelId(section)\">{{ labelFor(section) }}</h3>\r\n }\r\n\r\n <kerusi-section\r\n [section]=\"section\"\r\n [layout]=\"layoutFor(section)\"\r\n [selection]=\"selectionSet()\"\r\n [focusedSeatId]=\"focusedSeatIn(section)\"\r\n [ariaLabels]=\"ariaLabels()\"\r\n [colors]=\"resolvedColors()\"\r\n [typeColors]=\"typeColors()\"\r\n [unitScale]=\"unitScale()\"\r\n [interactive]=\"interactive()\"\r\n (seatActivate)=\"onSeatActivate($event)\"\r\n (seatFocused)=\"onSeatFocused($event)\"\r\n (seatKeydown)=\"onKeydown(section, $event)\"\r\n />\r\n </section>\r\n }\r\n\r\n @if (showLegend()) {\r\n <kerusi-legend\r\n [legend]=\"renderMap().legend\"\r\n [locale]=\"resolvedLocale()\"\r\n [colors]=\"resolvedColors()\"\r\n [typeColors]=\"typeColors()\"\r\n [showPrices]=\"showLegendPrices()\"\r\n />\r\n }\r\n\r\n <p class=\"kerusi-sr-only\" aria-live=\"polite\" aria-atomic=\"true\">{{ liveMessage() }}</p>\r\n</div>\r\n","/**\r\n * The Kerusi-native seat map: `<kerusi-seatmap>`, its section renderer, and an\r\n * optional legend.\r\n *\r\n * The selection, theming and announcement helpers they are built from are\r\n * framework-free and live in `@kerusiweb/core`.\r\n */\r\n\r\nexport * from './kerusi-seatmap.component';\r\nexport * from './kerusi-section.component';\r\nexport * from './kerusi-legend.component';\r\n","import { computed, signal, Signal } from '@angular/core';\r\nimport {\r\n applyStateDeltaOrdered,\r\n DEFAULT_SEQUENCE_READER,\r\n DeltaApplication,\r\n expireHolds,\r\n HeldSeat,\r\n heldSeats as heldSeatsOf,\r\n KerusiState,\r\n KerusiStateDelta,\r\n SequenceReader,\r\n} from '@kerusiweb/core';\r\n\r\n/**\r\n * A signal-backed live view of a {@link KerusiState}.\r\n *\r\n * Construct it directly — `new KerusiStateStore(initialState)` — and feed it\r\n * deltas from whatever transport you use. It is deliberately not an\r\n * `@Injectable`: this is a rendering library, and an application that already\r\n * owns its availability state should keep owning it.\r\n */\r\nexport class KerusiStateStore {\r\n private readonly _state;\r\n private readonly _needsRefetch = signal(false);\r\n private readonly _now;\r\n private readonly clock: () => Date;\r\n private readonly sequenceOf: SequenceReader;\r\n private lastSequence: number | undefined;\r\n\r\n constructor(initial: KerusiState, opts: { now?: () => Date; sequenceOf?: SequenceReader } = {}) {\r\n this._state = signal(initial);\r\n this.clock = opts.now ?? (() => new Date());\r\n this.sequenceOf = opts.sequenceOf ?? DEFAULT_SEQUENCE_READER;\r\n this._now = signal(this.clock());\r\n }\r\n\r\n /** The current availability snapshot. */\r\n get state(): Signal<KerusiState> {\r\n return this._state.asReadonly();\r\n }\r\n\r\n /** The timestamp of the most recently applied document. */\r\n readonly updatedAt = computed(() => this._state().updatedAt);\r\n\r\n /**\r\n * True once a gap was detected in the delta stream. The consumer SHOULD\r\n * re-fetch a full `KerusiState` and hand it to {@link reset} (§5.2).\r\n */\r\n get needsRefetch(): Signal<boolean> {\r\n return this._needsRefetch.asReadonly();\r\n }\r\n\r\n /** Every currently-held seat, with the time left on its hold. */\r\n readonly heldSeats = computed<readonly HeldSeat[]>(() => heldSeatsOf(this._state(), this._now()));\r\n\r\n /** Offers a delta to the store. Returns what happened to it. */\r\n apply(delta: KerusiStateDelta): DeltaApplication {\r\n const result = applyStateDeltaOrdered(this._state(), delta, {\r\n sequenceOf: this.sequenceOf,\r\n lastSequence: this.lastSequence,\r\n });\r\n\r\n if (result.outcome === 'applied' || result.outcome === 'gap') {\r\n this._state.set(result.state);\r\n const seq = this.sequenceOf(delta);\r\n if (seq !== undefined) {\r\n this.lastSequence = seq;\r\n }\r\n }\r\n if (result.outcome === 'gap') {\r\n this._needsRefetch.set(true);\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Replaces the state wholesale — the post-refetch path. Clears the\r\n * refetch flag and forgets the delta sequence.\r\n */\r\n reset(state: KerusiState): void {\r\n this._state.set(state);\r\n this._needsRefetch.set(false);\r\n this.lastSequence = undefined;\r\n this._now.set(this.clock());\r\n }\r\n\r\n /** Reverts lapsed holds and refreshes `heldSeats` countdowns. */\r\n tick(): void {\r\n const now = this.clock();\r\n this._now.set(now);\r\n const next = expireHolds(this._state(), now);\r\n if (next !== this._state()) {\r\n this._state.set(next);\r\n }\r\n }\r\n\r\n /**\r\n * Runs {@link tick} on an interval. Returns a function that stops it — call\r\n * that from `DestroyRef.onDestroy` or `ngOnDestroy`.\r\n */\r\n startExpiryTicker(intervalMs = 1000): () => void {\r\n const handle = setInterval(() => this.tick(), intervalMs);\r\n return () => clearInterval(handle);\r\n }\r\n}\r\n","/*\r\n * Public API Surface of @kerusiweb/angular\r\n *\r\n * The Angular binding only. Document types, the render model and the view\r\n * helpers live in @kerusiweb/core — import them from there.\r\n */\r\n\r\n// --- Components -------------------------------------------------------------\r\nexport * from './lib/kerusi-seatmap';\r\n\r\n// --- Signal-backed live availability store ---------------------------------\r\nexport * from './lib/kerusi-state-store';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["heldSeatsOf"],"mappings":";;;;AA2BA;;;;;;;;;;AAUG;MAOU,sBAAsB,CAAA;IACxB,OAAO,GAAG,KAAK,CAAC,QAAQ;gFAAiB;IACzC,MAAM,GAAG,KAAK,CAAC,QAAQ;+EAAiB;;AAGxC,IAAA,SAAS,GAAG,KAAK,CAAsB,IAAI,GAAG,EAAE;kFAAC;;IAEjD,aAAa,GAAG,KAAK,CAAgB,IAAI;sFAAC;;AAE1C,IAAA,UAAU,GAAG,KAAK,CAA8B,IAAI,GAAG,EAAE;mFAAC;IAE1D,MAAM,GAAG,KAAK,CAAgC,qBAAqB;+EAAC;;IAEpE,UAAU,GAAG,KAAK,CAAU,IAAI;mFAAC;;IAEjC,WAAW,GAAG,KAAK,CAAU,IAAI;oFAAC;AAC3C;;;;;;;;AAQG;IACM,SAAS,GAAG,KAAK,CAAS,CAAC;kFAAC;IAE5B,YAAY,GAAG,MAAM,EAAU;IAC/B,WAAW,GAAG,MAAM,EAAU;IAC9B,WAAW,GAAG,MAAM,EAAiB;IAE3B,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAA,IAAA,EAAO,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAA,CAAE;gFAAC;;AAG9E,IAAA,YAAY,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE;qFAAC;AAE9E,IAAA,UAAU,CAAC,MAAkB,EAAA;AACrC,QAAA,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;IAC7C;AAEU,IAAA,IAAI,CAAC,MAAkB,EAAA;QAC/B,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;IACzF;AAEU,IAAA,QAAQ,CAAC,MAAkB,EAAA;QACnC,OAAO,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;IAC7F;IAEU,QAAQ,GAAA;QAChB,OAAO,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC;IACnD;;;AAKU,IAAA,QAAQ,CAAC,MAAkB,EAAA;AACnC,QAAA,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC;IACtE;;AAGU,IAAA,eAAe,CAAC,MAAkB,EAAA;AAC1C,QAAA,OAAO,mBAAmB,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAClE;AAEA;;;;;;;;;;;AAWG;AACK,IAAA,OAAO,CAAC,MAAkB,EAAA;QAChC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE;AAC5B,YAAA,OAAO,MAAM;QACf;AACA,QAAA,MAAM,KAAK,GAAG,iBAAiB,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC;QAC5D,OAAO;AACL,YAAA,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,KAAK;AACnB,YAAA,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,KAAK;AACnB,YAAA,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,KAAK,GAAG,CAAC;AAC/B,YAAA,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,KAAK,GAAG,CAAC;SAClC;IACH;;AAGU,IAAA,QAAQ,CAAC,MAAkB,EAAA;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;AAChC,QAAA,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC;IAC1D;IAEU,QAAQ,GAAA;AAChB,QAAA,OAAO,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;IACpC;;AAGU,IAAA,QAAQ,CAAC,MAAkB,EAAA;;AAEnC,QAAA,OAAO,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,MAA2B,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAC7E;;AAGU,IAAA,YAAY,CAAC,MAAkB,EAAA;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;AAChC,QAAA,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC;IAC9D;AAEU,IAAA,cAAc,CAAC,MAAkB,EAAA;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAChC,OAAO,kBAAkB,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC;IAClD;;;;AAKU,IAAA,YAAY,CAAC,MAAkB,EAAA;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAChC,OAAO,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,IAAI;IACjC;AAEU,IAAA,YAAY,CAAC,MAAkB,EAAA;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAChC,OAAO,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,IAAI;IAClC;AAEU,IAAA,gBAAgB,CAAC,MAAkB,EAAA;QAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,GAAG,IAAI;IAC1C;AAEA;;;;;;;;;;;;;;AAcG;AACO,IAAA,SAAS,CAAC,MAAkB,EAAA;QACpC,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM;IACvE;AAEU,IAAA,cAAc,CAAC,MAAkB,EAAA;AACzC,QAAA,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,GAAG,IAAI,GAAG,CAAC;IAC7D;;AAGU,IAAA,YAAY,CAAC,OAA4B,EAAA;QACjD,OAAO,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAChD;AAEU,IAAA,mBAAmB,CAAC,OAA4B,EAAA;QACxD,OAAO,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAClD;;AAGU,IAAA,QAAQ,CAAC,MAAkB,EAAA;AACnC,QAAA,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE;YACvB,OAAO,CAAC,CAAC;QACX;AACA,QAAA,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IACzD;AAEU,IAAA,SAAS,CAAC,MAAkB,EAAA;AACpC,QAAA,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK;IACnE;AAEU,IAAA,WAAW,CAAC,MAAkB,EAAA;AACtC,QAAA,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI;QACxB,OAAO;YACL,aAAa;YACb,CAAA,aAAA,EAAgB,IAAI,CAAC,MAAM,CAAA,CAAE;AAC7B,YAAA,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,uBAAuB,GAAG,EAAE;YACtD,IAAI,CAAC,UAAU,GAAG,EAAE,GAAG,2BAA2B;YAClD,IAAI,CAAC,aAAa,EAAE,oBAAoB,GAAG,yBAAyB,GAAG,EAAE;AACzE,YAAA,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,GAAG,wBAAwB,GAAG,EAAE;AAC3D;aACE,MAAM,CAAC,OAAO;aACd,IAAI,CAAC,GAAG,CAAC;IACd;;AAGU,IAAA,SAAS,CAAC,IAAgC,EAAA;QAClD,OAAO,IAAI,CAAC,QAAQ,GAAG,CAAA,OAAA,EAAU,IAAI,CAAC,QAAQ,CAAA,CAAA,EAAI,IAAI,CAAC,OAAO,CAAA,CAAA,EAAI,IAAI,CAAC,OAAO,GAAG,GAAG,IAAI;IAC1F;;AAGU,IAAA,aAAa,CAAC,IAAgC,EAAA;QACtD,OAAO,IAAI,CAAC,QAAQ,GAAG,CAAA,OAAA,EAAU,CAAC,IAAI,CAAC,QAAQ,CAAA,CAAA,EAAI,IAAI,CAAC,OAAO,CAAA,CAAA,EAAI,IAAI,CAAC,OAAO,GAAG,GAAG,IAAI;IAC3F;;AAIU,IAAA,KAAK,CAAC,MAAqB,EAAA;QACnC,OAAO,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;IAC1C;AAEU,IAAA,aAAa,CAAC,MAAqB,EAAA;AAC3C,QAAA,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAC5D;AAEU,IAAA,iBAAiB,CAAC,MAAqB,EAAA;AAC/C,QAAA,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAChE;AAEU,IAAA,YAAY,CAAC,MAAqB,EAAA;AAC1C,QAAA,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC;IACpE;;AAGU,IAAA,QAAQ,CAAC,MAAqB,EAAA;AACtC,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC;IACrF;;AAIU,IAAA,WAAW,CAAC,MAAkB,EAAA;AACtC,QAAA,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE;YACtB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC;IACF;AAEU,IAAA,WAAW,CAAC,MAAkB,EAAA;AACtC,QAAA,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE;YACtB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC;IACF;AAEU,IAAA,SAAS,CAAC,KAAoB,EAAA;AACtC,QAAA,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE;AACtB,YAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;QAC9B;IACF;uGAlPW,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAtB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,sBAAsB,21CC5CnC,+4KA4IA,EAAA,MAAA,EAAA,CAAA,26BAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FDhGa,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBANlC,SAAS;+BACE,gBAAgB,EAAA,eAAA,EACT,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,+4KAAA,EAAA,MAAA,EAAA,CAAA,26BAAA,CAAA,EAAA;;;AETjD;;;;;;;;;;;;AAYG;MAOU,qBAAqB,CAAA;;IAEvB,MAAM,GAAG,KAAK,CAAC,QAAQ;+EAAgC;IACvD,MAAM,GAAG,KAAK,CAAS,IAAI;+EAAC;IAC5B,MAAM,GAAG,KAAK,CAAgC,qBAAqB;+EAAC;;IAEpE,UAAU,GAAG,KAAK,CAAU,IAAI;mFAAC;IACjC,UAAU,GAAG,KAAK,CAAU,IAAI;mFAAC;;IAEjC,eAAe,GAAG,KAAK,CAAU,IAAI;wFAAC;IAEtC,gBAAgB,GAAG,KAAK,CAAS,YAAY;yFAAC;IAC9C,mBAAmB,GAAG,KAAK,CAAS,cAAc;4FAAC;AAEzC,IAAA,KAAK,GAAG,QAAQ,CAAC,MAClC,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;8EAChF;;;;;;AAOkB,IAAA,QAAQ,GAAG,QAAQ,CAAgB,MAAK;AACzD,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE;QACvB,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,WAAW,CAAC;QACtD,OAAO;AACL,YAAA,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;AACxE,YAAA;AACE,gBAAA,GAAG,EAAE,UAAU;AACf,gBAAA,KAAK,EAAE,UAAU;AACjB,gBAAA,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,UAAU,CAAC;AACzC,aAAA;AACD,YAAA,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE;AAChE,YAAA,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE;YACnE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,EAAE;SAC9F;IACH,CAAC;iFAAC;AAEQ,IAAA,MAAM,CAAC,KAAwB,EAAA;QACvC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC;IAC/F;AAEU,IAAA,UAAU,CAAC,KAAwB,EAAA;QAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,KAAK,CAAC,KAAK;;QAElD,OAAO,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC;IAC7F;;;;;;;;IAUmB,SAAS,GAAG,EAAE;AAEd,IAAA,SAAS,GAAG,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC;;IAGhE,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC;IAC9D,aAAa,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,UAAU,GAAG,CAAC;AAClD,IAAA,SAAS,GAAG,YAAY,CACzC,CAAC,EACD,CAAC,EACD,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,UAAU,CAChB;AAEkB,IAAA,aAAa,GAAG,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC;IACtE,mBAAmB,GAAG,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC;;AAG1E,IAAA,iBAAiB,GAAG,gBAAgB,CACnD,IAAI,CAAC,UAAU,EACf,IAAI,CAAC,UAAU,EACf,IAAI,CAAC,aAAa,EAClB,IAAI,CAAC,aAAa,CACnB;IACgB,uBAAuB,GAAG,kBAAkB,CAC3D,IAAI,CAAC,aAAa,EAClB,IAAI,CAAC,aAAa,CACnB;AAES,IAAA,cAAc,CAAC,OAA4B,EAAA;AACnD,QAAA,OAAO,OAAO,KAAK,UAAU,GAAG,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,aAAa;IAC7E;AAEU,IAAA,qBAAqB,CAAC,OAA4B,EAAA;AAC1D,QAAA,OAAO,OAAO,KAAK,UAAU,GAAG,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC,mBAAmB;IACzF;AAEU,IAAA,OAAO,CAAC,IAAgB,EAAA;AAChC,QAAA,OAAO,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,QAAQ;IAC7C;AAEU,IAAA,WAAW,CAAC,IAAgB,EAAA;QACpC,OAAO,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,QAAQ;IACpE;AAEU,IAAA,UAAU,CAAC,IAAuB,EAAA;QAC1C,OAAO,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAC1C;AAEU,IAAA,cAAc,CAAC,OAA4B,EAAA;QACnD,OAAO,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAChD;AAEU,IAAA,gBAAgB,CAAC,OAA4B,EAAA;QACrD,OAAO,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAClD;IAEU,QAAQ,GAAA;AAChB,QAAA,OAAO,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;IACpC;AAEU,IAAA,KAAK,CAAC,KAAwB,EAAA;QACtC,OAAO,KAAK,CAAC,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACnE;uGA1HW,qBAAqB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAArB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,qBAAqB,oqCClDlC,8rFA8DA,EAAA,MAAA,EAAA,CAAA,22BAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FDZa,qBAAqB,EAAA,UAAA,EAAA,CAAA;kBANjC,SAAS;+BACE,eAAe,EAAA,eAAA,EACR,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,8rFAAA,EAAA,MAAA,EAAA,CAAA,22BAAA,CAAA,EAAA;;;AEmCjD;;;;;;;;;;;;;AAaG;MAQU,sBAAsB,CAAA;AAChB,IAAA,IAAI,GAAG,MAAM,EAAC,UAAuB,EAAC;;IAI9C,GAAG,GAAG,KAAK,CAAC,QAAQ;4EAAa;IACjC,KAAK,GAAG,KAAK,CAA0B,SAAS;8EAAC;;IAEjD,OAAO,GAAG,KAAK,CAA4B,SAAS;gFAAC;;;IAKrD,SAAS,GAAG,KAAK,CAAoB,EAAE;kFAAC;;;IAKxC,MAAM,GAAG,KAAK,CAAqB,SAAS;+EAAC;;IAE7C,GAAG,GAAG,KAAK,CAAmB,MAAM;4EAAC;;IAErC,WAAW,GAAG,KAAK,CAAkB,yBAAyB;oFAAC;;IAI/D,MAAM,GAAG,KAAK,CAAsB,EAAE;+EAAC;;IAEvC,UAAU,GAAG,KAAK,CAAU,IAAI;mFAAC;;IAEjC,QAAQ,GAAG,KAAK,CAAS,EAAE;iFAAC;;IAE5B,OAAO,GAAG,KAAK,CAAS,CAAC;gFAAC;;IAE1B,aAAa,GAAG,KAAK,CAAS,IAAI;sFAAC;AAC5C;;;AAGG;IACM,SAAS,GAAG,KAAK,CAAS,CAAC;kFAAC;IAC5B,iBAAiB,GAAG,KAAK,CAAU,IAAI;0FAAC;IACxC,UAAU,GAAG,KAAK,CAAU,KAAK;mFAAC;IAClC,gBAAgB,GAAG,KAAK,CAAU,IAAI;yFAAC;;;IAKvC,UAAU,GAAG,KAAK,CAAgC,SAAS;mFAAC;IAC5D,gBAAgB,GAAG,KAAK,CAAiD,EAAE;yFAAC;;;AAK5E,IAAA,kBAAkB,GAAG,KAAK,CAA8B,CAAC,WAAW,CAAC;2FAAC;;IAEtE,aAAa,GAAG,KAAK,CAAyB,MAAM;sFAAC;IACrD,YAAY,GAAG,KAAK,CAAqB,SAAS;qFAAC;;IAEnD,cAAc,GAAG,KAAK,CAA8C,SAAS;uFAAC;;IAE9E,WAAW,GAAG,KAAK,CAAU,IAAI;oFAAC;AAC3C;;;AAGG;IACM,WAAW,GAAG,KAAK,CAAU,KAAK;oFAAC;IACnC,gBAAgB,GAAG,KAAK,CAAS,IAAI;yFAAC;;IAItC,QAAQ,GAAG,KAAK,CAAiB,SAAS;iFAAC;;IAI3C,UAAU,GAAG,MAAM,EAAmB;IACtC,YAAY,GAAG,MAAM,EAAmB;IACxC,cAAc,GAAG,MAAM,EAAkB;IACzC,SAAS,GAAG,MAAM,EAAc;;IAEhC,gBAAgB,GAAG,MAAM,EAA8B;;AAIhE;;;;;;AAMG;IACc,cAAc,GAAG,MAAM,CAAmC,EAAE;uFAAC;IAC7D,YAAY,GAAG,MAAM,CAAC,EAAE;qFAAC;;IAEzB,UAAU,GAAG,MAAM,CAAC,CAAC;mFAAC;AAEvC,IAAA,WAAA,GAAA;QACE,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE;AAC5B,YAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,KAAK,KAAK,EAAE;AAC7B,gBAAA,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC;YACpC;AACA,YAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,KAAK,OAAO,EAAE;gBAC/B,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;gBAChC,IAAI,KAAK,EAAE;AACT,oBAAA,MAAM,IAAI,qBAAqB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC;gBAC9E;YACF;AACF,QAAA,CAAC,CAAC;;QAGF,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE;AAC9B,YAAA,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE;YACrC,MAAM,IAAI,GAA2B,EAAE;AACvC,YAAA,IAAI,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,QAAQ,CAAC,MAAM;AAEnE,YAAA,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE;gBACpC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBAChC,MAAM,KAAK,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC;AACpE,gBAAA,MAAM,QAAQ,GAAG,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;gBACpD,IAAI,QAAQ,EAAE;AACZ,oBAAA,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,QAAQ;AAC3B,oBAAA,OAAO,KAAK,QAAQ,KAAK,IAAI;gBAC/B;YACF;YAEA,IAAI,OAAO,EAAE;AACX,gBAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC;YAC/B;AACF,QAAA,CAAC,CAAC;AAEF,QAAA,MAAM,CAAC,CAAC,SAAS,KAAI;AACnB,YAAA,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE;gBACvB;YACF;AACA,YAAA,MAAM,MAAM,GAAG,WAAW,CACxB,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAC1C,IAAI,CAAC,gBAAgB,EAAE,CACxB;YACD,SAAS,CAAC,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;AACxC,QAAA,CAAC,CAAC;IACJ;;IAImB,MAAM,GAAG,QAAQ,CAA6B,MAC/D,IAAI,CAAC,QAAQ,EAAE,KAAK;AAClB,UAAE;UACA,mBAAmB,CAAC;AAClB,YAAA,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE;AACf,YAAA,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;AACnB,YAAA,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;SACxB,CAAC;+EACP;;AAGgB,IAAA,cAAc,GAAG,QAAQ,CAA0B,MAAK;AACvE,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE;QAC1B,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE;AACjC,YAAA,OAAO,KAAK;QACd;QACA,IAAI,CAAC,UAAU,EAAE;AACjB,QAAA,OAAO,WAAW,CAAC,KAAK,CAAC;IAC3B,CAAC;uFAAC;AAEiB,IAAA,SAAS,GAAG,QAAQ,CAAY,MACjD,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,cAAc,EAAE,EAAE;AAClD,QAAA,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;AACrB,QAAA,UAAU,EAAE,IAAI,CAAC,iBAAiB,EAAE;AACpC,QAAA,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,EAAE;AAC7C,QAAA,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE;KACtC,CAAC;kFACH;AAEkB,IAAA,cAAc,GAAG,QAAQ,CAAC,MAAM,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;uFAAC;AAE5E,IAAA,KAAK,GAAG,QAAQ,CAAC,MAClC,IAAI,CAAC,GAAG,EAAE,KAAK,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;8EACjF;AAEkB,IAAA,cAAc,GAAG,QAAQ,CAAgC,MAC1E,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;uFAC7B;AAEkB,IAAA,YAAY,GAAG,QAAQ,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;qFAAC;;AAGxD,IAAA,OAAO,GAAG,QAAQ,CAAqC,MAAK;AAC7E,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE;AACzC,QAAA,OAAO,IAAI,GAAG,CACZ,IAAI,CAAC,SAAS,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,KAAI;YACxC,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;YAC5C,OAAO;AACL,gBAAA,OAAO,CAAC,EAAE;gBACV,oBAAoB,CAAC,OAAO,EAAE;oBAC5B,QAAQ,EAAE,QAAQ,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE;AAC9C,oBAAA,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;AACvB,oBAAA,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE;oBACnC,WAAW,EAAE,QAAQ,CAAC,WAAW;AACjC,oBAAA,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE;iBAClB,CAAC;aACH;QACH,CAAC,CAAC,CACH;IACH,CAAC;gFAAC;;IAGe,MAAM,GAAG,QAAQ,CAChC,MACE,IAAI,GAAG,CACL,IAAI,CAAC,SAAS,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK;AACzC,QAAA,OAAO,CAAC,EAAE;QACV,oBAAoB,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;AACrD,KAAA,CAAC,CACH;+EACJ;AAEkB,IAAA,UAAU,GAAG,QAAQ,CAA8B,MAAK;AACzE,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE;AACpC,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE;QAClC,OAAO,IAAI,GAAG,CACZ,CAAC,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK;AACrD,YAAA,IAAI,CAAC,EAAE;AACP,YAAA,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;AACrC,SAAA,CAAC,CACH;IACH,CAAC;mFAAC;IAEiB,QAAQ,GAAG,QAAQ,CACpC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,IAAI,YAAY,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,CAAA,CAAE;iFACjE;;IAGkB,WAAW,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE;oFAAC;;AAG3D,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAM,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;gFAAC;;AAI/E,IAAA,SAAS,CAAC,OAAsB,EAAA;QACxC,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAE;IACxC;AAEU,IAAA,QAAQ,CAAC,OAAsB,EAAA;AACvC,QAAA,OAAO,IAAI,CAAC,gBAAgB,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI,OAAO,CAAC,KAAK;IACpE;AAEU,IAAA,OAAO,CAAC,OAAsB,EAAA;AACtC,QAAA,OAAO,CAAA,eAAA,EAAkB,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,CAAA,CAAA,EAAI,OAAO,CAAC,EAAE,CAAA,CAAE;IAC9D;;AAGU,IAAA,aAAa,CAAC,OAAsB,EAAA;QAC5C,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,IAAI;IAClD;;AAIU,IAAA,cAAc,CAAC,MAAc,EAAA;AACrC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE;QAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC;QACxC,IAAI,CAAC,IAAI,EAAE;YACT;QACF;QAEA,MAAM,OAAO,GAAG,mBAAmB,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE;YAC5D,SAAS,EAAE,KAAK,CAAC,SAAS;AAC1B,YAAA,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE;AACnC,YAAA,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE;AAClC,SAAA,CAAC;AAEF,QAAA,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,EAAE;AACjC,YAAA,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,IAAI,EAAE,OAAO,CAAC,MAAO,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;AAChF,YAAA,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAO,EAAE,CAAC;YAC3D;QACF;QAEA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC;AACrC,QAAA,MAAM,KAAK,GAAoB;YAC7B,IAAI;YACJ,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB;QACD,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC;AAC7E,QAAA,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,SAAS,CAAC;IAC3C;AAEU,IAAA,aAAa,CAAC,MAAc,EAAA;AACpC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC;QACnD,IAAI,CAAC,IAAI,EAAE;YACT;QACF;QACA,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC;AAC3C,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;IAC3B;AAEA;;;;AAIG;IACO,SAAS,CAAC,OAAsB,EAAE,KAAoB,EAAA;QAC9D,MAAM,MAAM,GAAI,KAAK,CAAC,MAAsB,EAAE,OAAO,GAAG,QAAQ,CAAC;QACjE,IAAI,CAAC,MAAM,EAAE;YACX;QACF;AAEA,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,MAAM,UAAU,GAAG,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC;QAChD,IAAI,CAAC,KAAK,EAAE;YACV;QACF;AAEA,QAAA,IAAI,MAA0B;AAC9B,QAAA,QAAQ,KAAK,CAAC,GAAG;AACf,YAAA,KAAK,WAAW;AACd,gBAAA,MAAM,GAAG,UAAU,EAAE,IAAI;gBACzB;AACF,YAAA,KAAK,YAAY;AACf,gBAAA,MAAM,GAAG,UAAU,EAAE,KAAK;gBAC1B;AACF,YAAA,KAAK,SAAS;AACZ,gBAAA,MAAM,GAAG,UAAU,EAAE,EAAE;gBACvB;AACF,YAAA,KAAK,WAAW;AACd,gBAAA,MAAM,GAAG,UAAU,EAAE,IAAI;gBACzB;AACF,YAAA,KAAK,MAAM;AACT,gBAAA,MAAM,GAAG,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,GAAG,UAAU,EAAE,QAAQ;gBAC3D;AACF,YAAA,KAAK,KAAK;AACR,gBAAA,MAAM,GAAG,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,GAAG,UAAU,EAAE,MAAM;gBACxD;AACF,YAAA,KAAK,QAAQ;AACX,gBAAA,MAAM,GAAG,KAAK,CAAC,KAAK;gBACpB;AACF,YAAA,KAAK,UAAU;AACb,gBAAA,MAAM,GAAG,KAAK,CAAC,IAAI;gBACnB;AACF,YAAA,KAAK,OAAO;AACZ,YAAA,KAAK,GAAG;gBACN,KAAK,CAAC,cAAc,EAAE;AACtB,gBAAA,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;gBAC3B;AACF,YAAA,KAAK,QAAQ;gBACX,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE;oBAC/B,KAAK,CAAC,cAAc,EAAE;AACtB,oBAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;AACtB,oBAAA,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBAC5B;gBACA;AACF,YAAA;gBACE;;QAGJ,IAAI,MAAM,EAAE;YACV,KAAK,CAAC,cAAc,EAAE;YACtB,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC;QACtC;IACF;IAEQ,cAAc,CAAC,SAAiB,EAAE,MAAc,EAAA;QACtD,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC,SAAS,CAAC,KAAK,MAAM,EAAE;YAC/C;QACF;QACA,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,GAAG,EAAE,CAAC,SAAS,GAAG,MAAM,EAAE,CAAC,CAAC;IACxE;AAEA;;;;;;AAMG;IACK,WAAW,CAAC,SAAiB,EAAE,MAAc,EAAA;AACnD,QAAA,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC;AACtC,QAAA,MAAM,KAAK,GAAI,IAAI,CAAC,IAAI,CAAC,aAA6B,CAAC,gBAAgB,CACrE,gBAAgB,CACjB;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YACpC,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,MAAM,EAAE;AACrC,gBAAA,IAAI,CAAC,KAAK,IAAI;gBACd;YACF;QACF;IACF;AAEQ,IAAA,iBAAiB,CAAC,SAA4B,EAAA;AACpD,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE;AAClC,QAAA,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE;AAC1B,YAAA,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,cAAc,CAAC;YACrC;QACF;AACA,QAAA,MAAM,EAAE,KAAK,EAAE,GAAG,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,SAAS,CAAC;QACjE,IAAI,CAAC,QAAQ,CACX,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CACtF;IACH;AAEQ,IAAA,QAAQ,CAAC,OAAe,EAAA;;;AAG9B,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;AACzB,QAAA,cAAc,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACtD;IAEQ,iBAAiB,GAAA;AACvB,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE;AAClC,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE;AACzC,QAAA,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS;AACpC,aAAA,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM;aAC1B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;QAEpB,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACtD;AACA,QAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;AACvB,YAAA,OAAO,SAAS;QAClB;AACA,QAAA,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC1F;uGAtaW,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAtB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,sBAAsB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,gBAAA,EAAA,MAAA,EAAA,EAAA,GAAA,EAAA,EAAA,iBAAA,EAAA,KAAA,EAAA,UAAA,EAAA,KAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,GAAA,EAAA,EAAA,iBAAA,EAAA,KAAA,EAAA,UAAA,EAAA,KAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,aAAA,EAAA,EAAA,iBAAA,EAAA,eAAA,EAAA,UAAA,EAAA,eAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,EAAA,iBAAA,EAAA,mBAAA,EAAA,UAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,gBAAA,EAAA,EAAA,iBAAA,EAAA,kBAAA,EAAA,UAAA,EAAA,kBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,gBAAA,EAAA,EAAA,iBAAA,EAAA,kBAAA,EAAA,UAAA,EAAA,kBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,kBAAA,EAAA,EAAA,iBAAA,EAAA,oBAAA,EAAA,UAAA,EAAA,oBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,aAAA,EAAA,EAAA,iBAAA,EAAA,eAAA,EAAA,UAAA,EAAA,eAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,EAAA,iBAAA,EAAA,gBAAA,EAAA,UAAA,EAAA,gBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,gBAAA,EAAA,EAAA,iBAAA,EAAA,kBAAA,EAAA,UAAA,EAAA,kBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,SAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,YAAA,EAAA,YAAA,EAAA,cAAA,EAAA,cAAA,EAAA,gBAAA,EAAA,SAAA,EAAA,WAAA,EAAA,gBAAA,EAAA,kBAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECtGnC,iqDA+CA,EAAA,MAAA,EAAA,CAAA,wbAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDmDY,sBAAsB,mPAAE,qBAAqB,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,QAAA,EAAA,QAAA,EAAA,YAAA,EAAA,YAAA,EAAA,iBAAA,EAAA,kBAAA,EAAA,qBAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAI5C,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBAPlC,SAAS;+BACE,gBAAgB,EAAA,eAAA,EACT,uBAAuB,CAAC,MAAM,WACtC,CAAC,sBAAsB,EAAE,qBAAqB,CAAC,EAAA,QAAA,EAAA,iqDAAA,EAAA,MAAA,EAAA,CAAA,wbAAA,CAAA,EAAA;;AA6a1D,SAAS,WAAW,CAAC,KAAwB,EAAE,MAAc,EAAA;AAC3D,IAAA,OAAO,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,EAAE;AAChD;;AEjhBA;;;;;;AAMG;;ACOH;;;;;;;AAOG;MACU,gBAAgB,CAAA;AACV,IAAA,MAAM;IACN,aAAa,GAAG,MAAM,CAAC,KAAK;sFAAC;AAC7B,IAAA,IAAI;AACJ,IAAA,KAAK;AACL,IAAA,UAAU;AACnB,IAAA,YAAY;IAEpB,WAAA,CAAY,OAAoB,EAAE,IAAA,GAA0D,EAAE,EAAA;AAC5F,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,OAAO;mFAAC;AAC7B,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC;QAC3C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,uBAAuB;QAC5D,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE;iFAAC;IAClC;;AAGA,IAAA,IAAI,KAAK,GAAA;AACP,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE;IACjC;;IAGS,SAAS,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,SAAS;kFAAC;AAE5D;;;AAGG;AACH,IAAA,IAAI,YAAY,GAAA;AACd,QAAA,OAAO,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE;IACxC;;AAGS,IAAA,SAAS,GAAG,QAAQ,CAAsB,MAAMA,SAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;kFAAC;;AAGjG,IAAA,KAAK,CAAC,KAAuB,EAAA;QAC3B,MAAM,MAAM,GAAG,sBAAsB,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE;YAC1D,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,YAAY,EAAE,IAAI,CAAC,YAAY;AAChC,SAAA,CAAC;AAEF,QAAA,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,EAAE;YAC5D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;YAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;AAClC,YAAA,IAAI,GAAG,KAAK,SAAS,EAAE;AACrB,gBAAA,IAAI,CAAC,YAAY,GAAG,GAAG;YACzB;QACF;AACA,QAAA,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,EAAE;AAC5B,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;QAC9B;AAEA,QAAA,OAAO,MAAM;IACf;AAEA;;;AAGG;AACH,IAAA,KAAK,CAAC,KAAkB,EAAA;AACtB,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;AACtB,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;AAC7B,QAAA,IAAI,CAAC,YAAY,GAAG,SAAS;QAC7B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IAC7B;;IAGA,IAAI,GAAA;AACF,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE;AACxB,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QAClB,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC;AAC5C,QAAA,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,EAAE;AAC1B,YAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;QACvB;IACF;AAEA;;;AAGG;IACH,iBAAiB,CAAC,UAAU,GAAG,IAAI,EAAA;AACjC,QAAA,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,UAAU,CAAC;AACzD,QAAA,OAAO,MAAM,aAAa,CAAC,MAAM,CAAC;IACpC;AACD;;ACzGD;;;;;AAKG;AAEH;;ACPA;;AAEG;;;;"}
1
+ {"version":3,"file":"kerusiweb-angular.mjs","sources":["../../../projects/angular/src/lib/kerusi-seatmap/kerusi-section.component.ts","../../../projects/angular/src/lib/kerusi-seatmap/kerusi-section.component.html","../../../projects/angular/src/lib/kerusi-seatmap/kerusi-legend.component.ts","../../../projects/angular/src/lib/kerusi-seatmap/kerusi-legend.component.html","../../../projects/angular/src/lib/kerusi-seatmap/kerusi-seatmap.component.ts","../../../projects/angular/src/lib/kerusi-seatmap/kerusi-seatmap.component.html","../../../projects/angular/src/lib/kerusi-seatmap/index.ts","../../../projects/angular/src/lib/kerusi-state-store.ts","../../../projects/angular/src/public-api.ts","../../../projects/angular/src/kerusiweb-angular.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';\r\nimport {\r\n cssVar,\r\n DEFAULT_KERUSI_COLORS,\r\n elementFill,\r\n elementStyle,\r\n elementTextFill,\r\n KerusiSeatmapColors,\r\n occupantFillFor,\r\n occupantStrokeFor,\r\n PlacedElement,\r\n PlacedSeat,\r\n RenderSection,\r\n screenPath,\r\n seatBodyPath,\r\n SeatOccupantVariant,\r\n seatCoreFill,\r\n seatFill,\r\n seatOccupantPath,\r\n seatOccupantStroke,\r\n seatOccupantVariant,\r\n seatSelectedFrame,\r\n seatTextFill,\r\n seatWashFill,\r\n SectionLayout,\r\n} from '@kerusiweb/core';\r\n\r\n/**\r\n * Draws one Kerusi `Section` as a single `<svg>`.\r\n *\r\n * One SVG per section — rather than section groups inside a shared canvas — is\r\n * what makes per-section `aspectRatio` and mixed grid/freeform modes work: each\r\n * gets its own `viewBox` and its own intrinsic proportions, and CSS is free to\r\n * stack, wrap or column them. It also puts the section heading in real DOM,\r\n * where a screen reader can use it as a landmark.\r\n *\r\n * This component holds no state. It takes a placed layout and emits intent.\r\n */\r\n@Component({\r\n selector: 'kerusi-section',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n templateUrl: './kerusi-section.component.html',\r\n styleUrl: './kerusi-section.component.css',\r\n})\r\nexport class KerusiSectionComponent {\r\n readonly section = input.required<RenderSection>();\r\n readonly layout = input.required<SectionLayout>();\r\n\r\n /** Currently selected seat ids. */\r\n readonly selection = input<ReadonlySet<string>>(new Set());\r\n /** The seat holding the section's single tab stop, if any. */\r\n readonly focusedSeatId = input<string | null>(null);\r\n /** Per-seat accessible names, keyed by seat id. */\r\n readonly ariaLabels = input<ReadonlyMap<string, string>>(new Map());\r\n\r\n readonly colors = input<Required<KerusiSeatmapColors>>(DEFAULT_KERUSI_COLORS);\r\n /** Whether an available seat may take its `SeatType.color` (§4.7). */\r\n readonly typeColors = input<boolean>(true);\r\n /** When false, seats render but do not respond to pointer or keyboard. */\r\n readonly interactive = input<boolean>(true);\r\n /**\r\n * CSS pixels per viewBox unit, at the section's natural size.\r\n *\r\n * Without a cap every section stretches to the full container width, so in a\r\n * four-tier theatre the narrow box row would draw seats several times the\r\n * size of the orchestra's. Capping each section at its own intrinsic width\r\n * keeps a seat the same size everywhere; a section too wide to fit still\r\n * scales down to the container.\r\n */\r\n readonly unitScale = input<number>(1);\r\n\r\n readonly seatActivate = output<string>();\r\n readonly seatFocused = output<string>();\r\n readonly seatKeydown = output<KeyboardEvent>();\r\n\r\n protected readonly viewBox = computed(() => `0 0 ${this.layout().width} ${this.layout().height}`);\r\n\r\n /** The section's natural width in CSS pixels; it never draws wider. */\r\n protected readonly naturalWidth = computed(() => this.layout().width * this.unitScale());\r\n\r\n protected isSelected(placed: PlacedSeat): boolean {\r\n return this.selection().has(placed.seat.id);\r\n }\r\n\r\n protected fill(placed: PlacedSeat): string {\r\n return seatFill(placed.seat, this.isSelected(placed), this.colors(), this.typeColors());\r\n }\r\n\r\n protected textFill(placed: PlacedSeat): string {\r\n return seatTextFill(placed.seat, this.isSelected(placed), this.colors(), this.typeColors());\r\n }\r\n\r\n protected backdrop(): string {\r\n return cssVar('backdrop', this.colors().backdrop);\r\n }\r\n\r\n // --- the seat glyph -------------------------------------------------------\r\n\r\n /** The seat's outline: square-ish front, tapered back — shape carries facing. */\r\n protected bodyPath(placed: PlacedSeat): string {\r\n return seatBodyPath(placed.x, placed.y, placed.width, placed.height);\r\n }\r\n\r\n /** `null` for an available or blocked seat — nothing to mark. */\r\n protected occupantVariant(placed: PlacedSeat): SeatOccupantVariant | null {\r\n return seatOccupantVariant(placed.seat, this.isSelected(placed));\r\n }\r\n\r\n /**\r\n * The box a seat's decorative marks are drawn to: the seat's own, or — when\r\n * it is selected — the core plate's, which is inset by the frame width.\r\n *\r\n * Everything over a selected seat sits on the core, so it all has to be\r\n * measured from the core. Drawing to the seat's box instead leaves the\r\n * occupant's hips crossing the frame, where a figure tinted `selectedBg`\r\n * lands on a frame filled `selectedBg` and reads as a hard nub. Since the\r\n * core is an exact scaled copy of the body (see {@link seatSelectedFrame}),\r\n * routing through here reproduces every clearance the mark had against the\r\n * body, scaled — so the frame width stays free to change.\r\n */\r\n private markBox(placed: PlacedSeat): { x: number; y: number; width: number; height: number } {\r\n if (!this.isSelected(placed)) {\r\n return placed;\r\n }\r\n const frame = seatSelectedFrame(placed.width, placed.height);\r\n return {\r\n x: placed.x + frame,\r\n y: placed.y + frame,\r\n width: placed.width - frame * 2,\r\n height: placed.height - frame * 2,\r\n };\r\n }\r\n\r\n /** The bright plate inside a selected seat's frame. */\r\n protected corePath(placed: PlacedSeat): string {\r\n const box = this.markBox(placed);\r\n return seatBodyPath(box.x, box.y, box.width, box.height);\r\n }\r\n\r\n protected coreFill(): string {\r\n return seatCoreFill(this.colors());\r\n }\r\n\r\n /** The dimming wash over a held or booked seat's type colour. */\r\n protected washFill(placed: PlacedSeat): string {\r\n // Only called for held/booked seats — see the template's @if.\r\n return seatWashFill(placed.seat.status as 'held' | 'booked', this.colors());\r\n }\r\n\r\n /** The occupant figure for a marked seat, sized to whatever it sits on. */\r\n protected occupantPath(placed: PlacedSeat): string {\r\n const box = this.markBox(placed);\r\n return seatOccupantPath(box.x, box.y, box.width, box.height);\r\n }\r\n\r\n protected occupantStroke(placed: PlacedSeat): number {\r\n const box = this.markBox(placed);\r\n return seatOccupantStroke(box.width, box.height);\r\n }\r\n\r\n // The wheelchair marker keeps the same relative corner it always had; only\r\n // the box it is relative to changes, so a selected seat's dot stays whole\r\n // instead of being bitten off by the frame.\r\n protected wheelchairCx(placed: PlacedSeat): number {\r\n const box = this.markBox(placed);\r\n return box.x + box.width * 0.82;\r\n }\r\n\r\n protected wheelchairCy(placed: PlacedSeat): number {\r\n const box = this.markBox(placed);\r\n return box.y + box.height * 0.18;\r\n }\r\n\r\n protected wheelchairRadius(placed: PlacedSeat): number {\r\n return this.markBox(placed).width * 0.13;\r\n }\r\n\r\n /**\r\n * A core-coloured outline behind a selected seat's number, painted under the\r\n * glyphs via `paint-order`.\r\n *\r\n * The label is centred on the occupant's shoulder line, so without this it is\r\n * `selectedBg` over a `selectedBg` silhouette — the figure, not the plate, is\r\n * what it actually has to contrast against. The halo also rescues a label too\r\n * long for the core, which would otherwise run onto the frame and vanish into\r\n * it.\r\n *\r\n * Bound here rather than in the stylesheet on purpose: a CSS\r\n * `var(--kerusi-selected-fg, …)` rule would carry the library default as its\r\n * fallback and silently skip the `[colors]` input tier, which only the\r\n * TypeScript side can resolve.\r\n */\r\n protected labelHalo(placed: PlacedSeat): string {\r\n return this.isSelected(placed) ? seatCoreFill(this.colors()) : 'none';\r\n }\r\n\r\n protected labelHaloWidth(placed: PlacedSeat): number {\r\n return this.isSelected(placed) ? placed.fontSize * 0.22 : 0;\r\n }\r\n\r\n /** Solid for selected/booked; `none` for held, which draws hollow instead. */\r\n protected occupantFill(variant: SeatOccupantVariant): string {\r\n return occupantFillFor(variant, this.colors());\r\n }\r\n\r\n protected occupantStrokeColor(variant: SeatOccupantVariant): string {\r\n return occupantStrokeFor(variant, this.colors());\r\n }\r\n\r\n /** The section's single tab stop; every other seat is reachable by arrow key. */\r\n protected tabIndex(placed: PlacedSeat): number {\r\n if (!this.interactive()) {\r\n return -1;\r\n }\r\n return placed.seat.id === this.focusedSeatId() ? 0 : -1;\r\n }\r\n\r\n protected ariaLabel(placed: PlacedSeat): string {\r\n return this.ariaLabels().get(placed.seat.id) ?? placed.seat.label;\r\n }\r\n\r\n protected seatClasses(placed: PlacedSeat): string {\r\n const seat = placed.seat;\r\n return [\r\n 'kerusi-seat',\r\n `kerusi-seat--${seat.status}`,\r\n this.isSelected(placed) ? 'kerusi-seat--selected' : '',\r\n seat.selectable ? '' : 'kerusi-seat--unselectable',\r\n seat.accessibility?.wheelchairAccessible ? 'kerusi-seat--wheelchair' : '',\r\n seat.companions.length > 0 ? 'kerusi-seat--companion' : '',\r\n ]\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n /** SVG rotate() about the item's center; null when it does not rotate. */\r\n protected transform(item: PlacedSeat | PlacedElement): string | null {\r\n return item.rotation ? `rotate(${item.rotation} ${item.centerX} ${item.centerY})` : null;\r\n }\r\n\r\n /** Cancels the parent <g>'s rotation so the label stays upright and stays put. */\r\n protected counterRotate(item: PlacedSeat | PlacedElement): string | null {\r\n return item.rotation ? `rotate(${-item.rotation} ${item.centerX} ${item.centerY})` : null;\r\n }\r\n\r\n // --- elements -------------------------------------------------------------\r\n\r\n protected style(placed: PlacedElement) {\r\n return elementStyle(placed.element.kind);\r\n }\r\n\r\n /** A text element with no label draws nothing, rather than an empty box. */\r\n protected drawsElement(placed: PlacedElement): boolean {\r\n const style = this.style(placed);\r\n return style.shape !== 'text' || (!!placed.element.label && style.showLabel);\r\n }\r\n\r\n /** A label on a shape stays upright; a text element's own label is what rotates. */\r\n protected labelTransform(placed: PlacedElement): string | null {\r\n return this.style(placed).shape === 'text' ? null : this.counterRotate(placed);\r\n }\r\n\r\n protected elementFillOf(placed: PlacedElement): string {\r\n return elementFill(this.style(placed).tone, this.colors());\r\n }\r\n\r\n protected elementTextFillOf(placed: PlacedElement): string {\r\n return elementTextFill(this.style(placed).tone, this.colors());\r\n }\r\n\r\n protected screenPathOf(placed: PlacedElement): string {\r\n return screenPath(placed.x, placed.y, placed.width, placed.height);\r\n }\r\n\r\n /** The corner radius for a rectangular element; a stage reads as squarer. */\r\n protected radiusOf(placed: PlacedElement): number {\r\n return this.style(placed).shape === 'stage' ? 2 : Math.min(placed.height * 0.25, 6);\r\n }\r\n\r\n // --- interaction ----------------------------------------------------------\r\n\r\n protected onSeatClick(placed: PlacedSeat): void {\r\n if (this.interactive()) {\r\n this.seatActivate.emit(placed.seat.id);\r\n }\r\n }\r\n\r\n protected onSeatFocus(placed: PlacedSeat): void {\r\n if (this.interactive()) {\r\n this.seatFocused.emit(placed.seat.id);\r\n }\r\n }\r\n\r\n protected onKeydown(event: KeyboardEvent): void {\r\n if (this.interactive()) {\r\n this.seatKeydown.emit(event);\r\n }\r\n }\r\n}\r\n","<svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n class=\"kerusi-section-svg\"\r\n role=\"presentation\"\r\n focusable=\"false\"\r\n [style.--kerusi-focus-ring-input]=\"colors().focusRing\"\r\n [attr.viewBox]=\"viewBox()\"\r\n [style.max-width.px]=\"naturalWidth()\"\r\n preserveAspectRatio=\"xMidYMid meet\"\r\n>\r\n <rect\r\n x=\"0\"\r\n y=\"0\"\r\n [attr.width]=\"layout().width\"\r\n [attr.height]=\"layout().height\"\r\n [style.fill]=\"backdrop()\"\r\n />\r\n\r\n <!-- Elements first, so a screen or stage sits behind the seats it faces. -->\r\n @for (placed of layout().elements; track placed.element.id) {\r\n @if (drawsElement(placed)) {\r\n <g\r\n class=\"kerusi-element kerusi-element--{{ placed.element.kind }}\"\r\n [attr.transform]=\"transform(placed)\"\r\n >\r\n @switch (style(placed).shape) {\r\n @case ('screen') {\r\n <path [attr.d]=\"screenPathOf(placed)\" [style.fill]=\"elementFillOf(placed)\" />\r\n }\r\n @case ('text') {}\r\n @case ('void') {\r\n <rect\r\n class=\"kerusi-element__void\"\r\n [attr.x]=\"placed.x\"\r\n [attr.y]=\"placed.y\"\r\n [attr.width]=\"placed.width\"\r\n [attr.height]=\"placed.height\"\r\n [style.stroke]=\"elementTextFillOf(placed)\"\r\n fill=\"none\"\r\n />\r\n }\r\n @default {\r\n <rect\r\n [attr.x]=\"placed.x\"\r\n [attr.y]=\"placed.y\"\r\n [attr.width]=\"placed.width\"\r\n [attr.height]=\"placed.height\"\r\n [attr.rx]=\"radiusOf(placed)\"\r\n [style.fill]=\"elementFillOf(placed)\"\r\n />\r\n }\r\n }\r\n @if (placed.element.label && style(placed).showLabel) {\r\n <text\r\n class=\"kerusi-element__label\"\r\n text-anchor=\"middle\"\r\n dominant-baseline=\"central\"\r\n [attr.x]=\"placed.centerX\"\r\n [attr.y]=\"placed.centerY\"\r\n [attr.font-size]=\"placed.fontSize\"\r\n [style.fill]=\"elementTextFillOf(placed)\"\r\n [attr.transform]=\"labelTransform(placed)\"\r\n >\r\n {{ placed.element.label }}\r\n </text>\r\n }\r\n </g>\r\n }\r\n }\r\n\r\n @for (placed of layout().seats; track placed.seat.id) {\r\n <g\r\n [class]=\"seatClasses(placed)\"\r\n role=\"button\"\r\n [attr.tabindex]=\"tabIndex(placed)\"\r\n [attr.aria-label]=\"ariaLabel(placed)\"\r\n [attr.aria-pressed]=\"isSelected(placed)\"\r\n [attr.aria-disabled]=\"!placed.seat.selectable\"\r\n [attr.data-seat-id]=\"placed.seat.id\"\r\n [attr.transform]=\"transform(placed)\"\r\n (click)=\"onSeatClick(placed)\"\r\n (focus)=\"onSeatFocus(placed)\"\r\n (keydown)=\"onKeydown($event)\"\r\n >\r\n @let variant = occupantVariant(placed);\r\n <!-- Tapered toward the back, square at the front, so which way the seat\r\n faces is legible from the outline alone. -->\r\n <path class=\"kerusi-seat__box\" [attr.d]=\"bodyPath(placed)\" [style.fill]=\"fill(placed)\" />\r\n @if (variant === 'held' || variant === 'booked') {\r\n <!-- Colour stays the seat type's; a taken seat recedes under a wash\r\n instead, so the type colour still reads on a busy map. -->\r\n <path\r\n class=\"kerusi-seat__wash\"\r\n [attr.d]=\"bodyPath(placed)\"\r\n [style.fill]=\"washFill(placed)\"\r\n />\r\n }\r\n @if (variant === 'selected') {\r\n <!-- Selection is the one state drawn from two tones: the body above is\r\n the frame, this is the bright core inside it. Whichever way the\r\n host page is themed, one of the two separates from it — which is\r\n why nothing here has to know about the page. -->\r\n <path class=\"kerusi-seat__core\" [attr.d]=\"corePath(placed)\" [style.fill]=\"coreFill()\" />\r\n }\r\n @if (variant) {\r\n <!-- Deliberately not counter-rotated: the occupant leans with the\r\n seat. Solid = settled (yours, or sold); hollow = a hold still in\r\n progress. -->\r\n <path\r\n class=\"kerusi-seat__occupant kerusi-seat__occupant--{{ variant }}\"\r\n [attr.d]=\"occupantPath(placed)\"\r\n [attr.stroke-width]=\"occupantStroke(placed)\"\r\n [style.fill]=\"occupantFill(variant)\"\r\n [style.stroke]=\"occupantStrokeColor(variant)\"\r\n />\r\n }\r\n <text\r\n class=\"kerusi-seat__label\"\r\n text-anchor=\"middle\"\r\n dominant-baseline=\"central\"\r\n [attr.x]=\"placed.centerX\"\r\n [attr.y]=\"placed.centerY\"\r\n [attr.font-size]=\"placed.fontSize\"\r\n [style.fill]=\"textFill(placed)\"\r\n [style.stroke]=\"labelHalo(placed)\"\r\n [attr.stroke-width]=\"labelHaloWidth(placed)\"\r\n [attr.transform]=\"counterRotate(placed)\"\r\n >\r\n {{ placed.seat.label }}\r\n </text>\r\n @if (placed.seat.accessibility?.wheelchairAccessible) {\r\n <!-- A corner marker, not a different fill: availability owns the fill. -->\r\n <circle\r\n class=\"kerusi-seat__wheelchair\"\r\n [attr.cx]=\"wheelchairCx(placed)\"\r\n [attr.cy]=\"wheelchairCy(placed)\"\r\n [attr.r]=\"wheelchairRadius(placed)\"\r\n [style.fill]=\"textFill(placed)\"\r\n />\r\n }\r\n </g>\r\n }\r\n</svg>\r\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\r\nimport {\r\n cssVar,\r\n DEFAULT_KERUSI_COLORS,\r\n formatMoney,\r\n KerusiSeatmapColors,\r\n occupantFillFor,\r\n occupantStrokeFor,\r\n readableOn,\r\n RenderLegendEntry,\r\n seatBodyPath,\r\n SeatOccupantVariant,\r\n seatCoreFill,\r\n seatOccupantPath,\r\n seatOccupantStroke,\r\n seatSelectedFrame,\r\n seatWashFill,\r\n} from '@kerusiweb/core';\r\n\r\n/** Which marks, beyond the body fill, a status swatch draws. */\r\ntype StatusKind = 'plain' | 'blocked' | SeatOccupantVariant;\r\n\r\n/** One availability swatch in the legend's second block. */\r\ninterface StatusEntry {\r\n key: string;\r\n label: string;\r\n kind: StatusKind;\r\n /** The swatch body's fill. */\r\n fill: string;\r\n}\r\n\r\n/**\r\n * A key for a seat map: its seat types on one side, its availability states on\r\n * the other.\r\n *\r\n * This ships in the library rather than being left to each application because\r\n * the swatch colors must come from the *same* resolution path as the seat\r\n * fills — `SeatType.color` when the document supplies one, the theme's\r\n * available color when it does not. Re-deriving that in application code is how\r\n * a legend ends up disagreeing with the map it describes.\r\n *\r\n * It is opt-in: `<kerusi-seatmap [showLegend]=\"true\">` renders it inline, or\r\n * use `<kerusi-legend>` directly to place it anywhere.\r\n */\r\n@Component({\r\n selector: 'kerusi-legend',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n templateUrl: './kerusi-legend.component.html',\r\n styleUrl: './kerusi-legend.component.css',\r\n})\r\nexport class KerusiLegendComponent {\r\n /** From `RenderMap.legend`. */\r\n readonly legend = input.required<readonly RenderLegendEntry[]>();\r\n readonly locale = input<string>('en');\r\n readonly colors = input<Required<KerusiSeatmapColors>>(DEFAULT_KERUSI_COLORS);\r\n /** Whether an available seat takes its type color — mirrors the seatmap input. */\r\n readonly typeColors = input<boolean>(true);\r\n readonly showPrices = input<boolean>(true);\r\n /** Hide types no seat in the rendered map actually uses. */\r\n readonly hideUnusedTypes = input<boolean>(true);\r\n\r\n readonly headingSeatTypes = input<string>('Seat types');\r\n readonly headingAvailability = input<string>('Availability');\r\n\r\n protected readonly types = computed(() =>\r\n this.legend().filter((entry) => !this.hideUnusedTypes() || entry.seatCount > 0),\r\n );\r\n\r\n // Colour means seat type; shape means status (see `seatFill`'s doc comment).\r\n // Held and booked swatches share the plain \"available\" body colour — the\r\n // map's colour there is always whatever the seat's own type supplies, which\r\n // the legend has no single value for — and are told apart by their wash and\r\n // occupant figure instead, same as on the map.\r\n protected readonly statuses = computed<StatusEntry[]>(() => {\r\n const c = this.colors();\r\n const available = cssVar('availableBg', c.availableBg);\r\n return [\r\n { key: 'available', label: 'Available', kind: 'plain', fill: available },\r\n {\r\n key: 'selected',\r\n label: 'Selected',\r\n kind: 'selected',\r\n fill: cssVar('selectedBg', c.selectedBg),\r\n },\r\n { key: 'held', label: 'On hold', kind: 'held', fill: available },\r\n { key: 'booked', label: 'Booked', kind: 'booked', fill: available },\r\n { key: 'blocked', label: 'Blocked', kind: 'blocked', fill: cssVar('blockedBg', c.blockedBg) },\r\n ];\r\n });\r\n\r\n protected swatch(entry: RenderLegendEntry): string {\r\n return (this.typeColors() && entry.color) || cssVar('availableBg', this.colors().availableBg);\r\n }\r\n\r\n protected swatchText(entry: RenderLegendEntry): string {\r\n const typeColor = this.typeColors() && entry.color;\r\n // Mirrors `seatTextFill`: only a document-supplied color needs guessing at.\r\n return typeColor ? readableOn(typeColor) : cssVar('availableFg', this.colors().availableFg);\r\n }\r\n\r\n // --- status swatches -------------------------------------------------------\r\n //\r\n // Availability is now signalled by shape, not colour, so every status swatch\r\n // (bar \"available\" and \"blocked\") draws the same glyph the map does rather\r\n // than a flat square. A legend that showed only flat colour would teach a\r\n // cue the map no longer uses.\r\n\r\n /** The swatch's viewBox edge; the seat glyph is drawn to fill it. */\r\n protected readonly glyphSize = 16;\r\n\r\n protected readonly glyphBody = seatBodyPath(0, 0, this.glyphSize, this.glyphSize);\r\n\r\n /** The selected swatch's frame width, and the core it leaves inside itself. */\r\n private readonly glyphFrame = seatSelectedFrame(this.glyphSize, this.glyphSize);\r\n private readonly glyphCoreSize = this.glyphSize - this.glyphFrame * 2;\r\n protected readonly glyphCore = seatBodyPath(\r\n 0,\r\n 0,\r\n this.glyphSize,\r\n this.glyphSize,\r\n this.glyphFrame,\r\n );\r\n\r\n protected readonly glyphOccupant = seatOccupantPath(0, 0, this.glyphSize, this.glyphSize);\r\n protected readonly glyphOccupantStroke = seatOccupantStroke(this.glyphSize, this.glyphSize);\r\n\r\n /** The selected swatch's figure sits on the core, so it is drawn to the core. */\r\n private readonly glyphCoreOccupant = seatOccupantPath(\r\n this.glyphFrame,\r\n this.glyphFrame,\r\n this.glyphCoreSize,\r\n this.glyphCoreSize,\r\n );\r\n private readonly glyphCoreOccupantStroke = seatOccupantStroke(\r\n this.glyphCoreSize,\r\n this.glyphCoreSize,\r\n );\r\n\r\n protected occupantPathOf(variant: SeatOccupantVariant): string {\r\n return variant === 'selected' ? this.glyphCoreOccupant : this.glyphOccupant;\r\n }\r\n\r\n protected occupantStrokeWidthOf(variant: SeatOccupantVariant): number {\r\n return variant === 'selected' ? this.glyphCoreOccupantStroke : this.glyphOccupantStroke;\r\n }\r\n\r\n protected hasWash(kind: StatusKind): kind is 'held' | 'booked' {\r\n return kind === 'held' || kind === 'booked';\r\n }\r\n\r\n protected hasOccupant(kind: StatusKind): kind is SeatOccupantVariant {\r\n return kind === 'selected' || kind === 'held' || kind === 'booked';\r\n }\r\n\r\n protected washFillOf(kind: 'held' | 'booked'): string {\r\n return seatWashFill(kind, this.colors());\r\n }\r\n\r\n protected occupantFillOf(variant: SeatOccupantVariant): string {\r\n return occupantFillFor(variant, this.colors());\r\n }\r\n\r\n protected occupantStrokeOf(variant: SeatOccupantVariant): string {\r\n return occupantStrokeFor(variant, this.colors());\r\n }\r\n\r\n protected coreFill(): string {\r\n return seatCoreFill(this.colors());\r\n }\r\n\r\n protected price(entry: RenderLegendEntry): string {\r\n return entry.price ? formatMoney(entry.price, this.locale()) : '';\r\n }\r\n}\r\n","<div class=\"kerusi-legend\">\r\n @if (types().length) {\r\n <div class=\"kerusi-legend__block\">\r\n <h4 class=\"kerusi-legend__heading\">{{ headingSeatTypes() }}</h4>\r\n <ul class=\"kerusi-legend__list\">\r\n @for (entry of types(); track entry.id) {\r\n <li class=\"kerusi-legend__item\">\r\n <span\r\n class=\"kerusi-legend__swatch\"\r\n aria-hidden=\"true\"\r\n [style.background]=\"swatch(entry)\"\r\n [style.color]=\"swatchText(entry)\"\r\n ></span>\r\n <span class=\"kerusi-legend__label\">{{ entry.label }}</span>\r\n @if (showPrices() && price(entry)) {\r\n <span class=\"kerusi-legend__price\">{{ price(entry) }}</span>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n </div>\r\n }\r\n\r\n <div class=\"kerusi-legend__block\">\r\n <h4 class=\"kerusi-legend__heading\">{{ headingAvailability() }}</h4>\r\n <ul class=\"kerusi-legend__list\">\r\n @for (status of statuses(); track status.key) {\r\n <li class=\"kerusi-legend__item\">\r\n <!-- Shape carries status now, so every swatch draws the seat glyph\r\n rather than a flat colour — a flat swatch would teach a cue the\r\n map itself no longer uses. -->\r\n <svg\r\n class=\"kerusi-legend__swatch kerusi-legend__swatch--glyph\"\r\n aria-hidden=\"true\"\r\n focusable=\"false\"\r\n [attr.viewBox]=\"'0 0 ' + glyphSize + ' ' + glyphSize\"\r\n >\r\n <path [attr.d]=\"glyphBody\" [style.fill]=\"status.fill\" />\r\n @if (hasWash(status.kind)) {\r\n <path [attr.d]=\"glyphBody\" [style.fill]=\"washFillOf(status.kind)\" opacity=\"0.5\" />\r\n }\r\n @if (status.kind === 'selected') {\r\n <!-- Two tones, same as the map: the body above is the frame, this\r\n is the core inside it. -->\r\n <path [attr.d]=\"glyphCore\" [style.fill]=\"coreFill()\" />\r\n }\r\n @if (hasOccupant(status.kind)) {\r\n <path\r\n [attr.d]=\"occupantPathOf(status.kind)\"\r\n class=\"kerusi-legend__occupant kerusi-legend__occupant--{{ status.kind }}\"\r\n [attr.stroke-width]=\"occupantStrokeWidthOf(status.kind)\"\r\n [style.fill]=\"occupantFillOf(status.kind)\"\r\n [style.stroke]=\"occupantStrokeOf(status.kind)\"\r\n />\r\n }\r\n </svg>\r\n <span class=\"kerusi-legend__label\">{{ status.label }}</span>\r\n </li>\r\n }\r\n </ul>\r\n </div>\r\n</div>\r\n","import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n computed,\r\n effect,\r\n ElementRef,\r\n inject,\r\n input,\r\n model,\r\n output,\r\n signal,\r\n} from '@angular/core';\r\nimport {\r\n buildNavigationGraph,\r\n buildRenderModel,\r\n computeSectionLayout,\r\n DEFAULT_SEAT_ARIA_STRINGS,\r\n DisallowedReason,\r\n disallowedAnnouncement,\r\n errorsOf,\r\n expireHolds,\r\n formatMoney,\r\n isRtlLocale,\r\n KerusiMap,\r\n KerusiSeatmapColors,\r\n KerusiSession,\r\n KerusiState,\r\n KerusiValidationError,\r\n KerusiViolation,\r\n Money,\r\n NavigationGraph,\r\n RenderMap,\r\n RenderSeat,\r\n RenderSection,\r\n resolveColors,\r\n resolveMapLocale,\r\n SeatAriaStrings,\r\n seatAriaLabel,\r\n SeatRenderStatus,\r\n SectionLayout,\r\n summarizeSelection,\r\n toggleSeatSelection,\r\n validateDocumentSet,\r\n} from '@kerusiweb/core';\r\nimport { KerusiSectionComponent } from './kerusi-section.component';\r\nimport { KerusiLegendComponent } from './kerusi-legend.component';\r\n\r\n/** How a document with validation errors is handled. */\r\nexport type ValidationMode =\r\n /** Report through `validationIssues` and render anyway. The default. */\r\n | 'collect'\r\n /** Throw a `KerusiValidationError`, as `validateKerusiMap` does. */\r\n | 'throw'\r\n /** Skip validation entirely, for a document validated upstream. */\r\n | 'off';\r\n\r\n/** Per-section rendering overrides, keyed by `Section.id`. */\r\nexport interface SectionRenderOptions {\r\n hidden?: boolean;\r\n /** Overrides `Section.aspectRatio`. */\r\n aspectRatio?: string;\r\n /** Overrides the seat size for this section only. */\r\n seatSize?: number;\r\n /** Overrides the localized `Section.label`. */\r\n label?: string;\r\n}\r\n\r\n/** What a seat event carries. Everything the document knows, already resolved. */\r\nexport interface SeatInteraction {\r\n seat: RenderSeat;\r\n /** The selection after this event. */\r\n selection: readonly string[];\r\n /** Seats whose membership changed, including companions (§4.6). */\r\n changed: readonly string[];\r\n}\r\n\r\nexport interface SeatDisallowed {\r\n seat: RenderSeat;\r\n reason: DisallowedReason;\r\n}\r\n\r\n/**\r\n * Renders a Kerusi seat map.\r\n *\r\n * Takes a `KerusiMap` and its `KerusiState` directly — no adapter, no\r\n * intermediate row model. Each `Section` becomes its own `<svg>` with its own\r\n * layout mode and proportions, seats take their color from the map's legend,\r\n * every seat is keyboard-reachable and announced with its type, price, status\r\n * and §4.3.4 accessibility properties, and selection is an immutable set of\r\n * seat ids the caller can two-way bind.\r\n *\r\n * ```html\r\n * <kerusi-seatmap [map]=\"map\" [state]=\"state\" [(selection)]=\"picked\" />\r\n * ```\r\n */\r\n@Component({\r\n selector: 'kerusi-seatmap',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [KerusiSectionComponent, KerusiLegendComponent],\r\n templateUrl: './kerusi-seatmap.component.html',\r\n styleUrl: './kerusi-seatmap.component.css',\r\n})\r\nexport class KerusiSeatmapComponent {\r\n private readonly host = inject(ElementRef<HTMLElement>);\r\n\r\n // --- documents ------------------------------------------------------------\r\n\r\n readonly map = input.required<KerusiMap>();\r\n readonly state = input<KerusiState | undefined>(undefined);\r\n /** The optional map↔event join (§5.3). Validated against the map and state. */\r\n readonly session = input<KerusiSession | undefined>(undefined);\r\n\r\n // --- selection ------------------------------------------------------------\r\n\r\n /** Selected seat ids. Two-way: `[(selection)]`, or read-only with `[selection]`. */\r\n readonly selection = model<readonly string[]>([]);\r\n\r\n // --- localization ---------------------------------------------------------\r\n\r\n /** Overrides `KerusiMap.locale` (BCP-47). */\r\n readonly locale = input<string | undefined>(undefined);\r\n /** Right-to-left rendering. `auto` derives it from the resolved locale. */\r\n readonly rtl = input<boolean | 'auto'>('auto');\r\n /** Overrides the built-in English announcement strings. */\r\n readonly ariaStrings = input<SeatAriaStrings>(DEFAULT_SEAT_ARIA_STRINGS);\r\n\r\n // --- appearance -----------------------------------------------------------\r\n\r\n readonly colors = input<KerusiSeatmapColors>({});\r\n /** Let an available seat take its `SeatType.color` (§4.7). */\r\n readonly typeColors = input<boolean>(true);\r\n /** Grid cell edge, in viewBox units. */\r\n readonly seatSize = input<number>(28);\r\n /** Gap between grid cells, in viewBox units. */\r\n readonly seatGap = input<number>(6);\r\n /** Freeform viewBox width; height follows the section's aspect ratio. */\r\n readonly freeformBasis = input<number>(1000);\r\n /**\r\n * CSS pixels per viewBox unit. Caps each section at its natural size so a\r\n * narrow section and a wide one in the same map draw seats the same size.\r\n */\r\n readonly unitScale = input<number>(1);\r\n readonly showSectionLabels = input<boolean>(true);\r\n readonly showLegend = input<boolean>(false);\r\n readonly showLegendPrices = input<boolean>(true);\r\n\r\n // --- section control ------------------------------------------------------\r\n\r\n /** Render only these sections, in this order. Default: all, by `Section.index`. */\r\n readonly sectionIds = input<readonly string[] | undefined>(undefined);\r\n readonly sectionOverrides = input<Readonly<Record<string, SectionRenderOptions>>>({});\r\n\r\n // --- interaction ----------------------------------------------------------\r\n\r\n /** Statuses in which a seat may be picked. Default: `['available']`. */\r\n readonly selectableStatuses = input<readonly SeatRenderStatus[]>(['available']);\r\n /** `auto` selects a seat's companion closure together (§4.6). */\r\n readonly companionMode = input<'auto' | 'independent'>('auto');\r\n readonly maxSelection = input<number | undefined>(undefined);\r\n /** A final say on selectability, applied after the status test. */\r\n readonly seatSelectable = input<((seat: RenderSeat) => boolean) | undefined>(undefined);\r\n /** When false the map renders read-only: no pointer or keyboard interaction. */\r\n readonly interactive = input<boolean>(true);\r\n /**\r\n * Revert a lapsed `holdExpires` to available on a ticker (§5.1). Off by\r\n * default — a renderer should stay passive unless asked.\r\n */\r\n readonly expireHolds = input<boolean>(false);\r\n readonly expiryIntervalMs = input<number>(1000);\r\n\r\n // --- validation -----------------------------------------------------------\r\n\r\n readonly validate = input<ValidationMode>('collect');\r\n\r\n // --- outputs --------------------------------------------------------------\r\n\r\n readonly seatSelect = output<SeatInteraction>();\r\n readonly seatDeselect = output<SeatInteraction>();\r\n readonly seatDisallowed = output<SeatDisallowed>();\r\n readonly seatFocus = output<RenderSeat>();\r\n /** Emitted whenever the documents change, in `collect` mode. */\r\n readonly validationIssues = output<readonly KerusiViolation[]>();\r\n\r\n // --- internal state -------------------------------------------------------\r\n\r\n /**\r\n * The seat holding each section's tab stop, keyed by section id.\r\n *\r\n * Per section, not per map: arrow keys never cross a section boundary, so a\r\n * single map-wide tab stop would leave every section after the first\r\n * unreachable by keyboard. Tab moves between sections, arrows move within.\r\n */\r\n private readonly focusedSeatIds = signal<Readonly<Record<string, string>>>({});\r\n private readonly announcement = signal('');\r\n /** Bumped by the hold ticker to force the state to be re-derived. */\r\n private readonly expiryTick = signal(0);\r\n\r\n constructor() {\r\n effect(() => {\r\n const issues = this.issues();\r\n if (this.validate() !== 'off') {\r\n this.validationIssues.emit(issues);\r\n }\r\n if (this.validate() === 'throw') {\r\n const [first] = errorsOf(issues);\r\n if (first) {\r\n throw new KerusiValidationError(first.message, first.rule, first.id, issues);\r\n }\r\n }\r\n });\r\n\r\n // Keep every section's tab stop pointing at a seat that still exists.\r\n effect(() => {\r\n const model = this.renderMap();\r\n const current = this.focusedSeatIds();\r\n const next: Record<string, string> = {};\r\n let changed = Object.keys(current).length !== model.sections.length;\r\n\r\n for (const section of model.sections) {\r\n const held = current[section.id];\r\n const valid = held && section.seats.some((seat) => seat.id === held);\r\n const resolved = valid ? held : section.seats[0]?.id;\r\n if (resolved) {\r\n next[section.id] = resolved;\r\n changed ||= resolved !== held;\r\n }\r\n }\r\n\r\n if (changed) {\r\n this.focusedSeatIds.set(next);\r\n }\r\n });\r\n\r\n effect((onCleanup) => {\r\n if (!this.expireHolds()) {\r\n return;\r\n }\r\n const handle = setInterval(\r\n () => this.expiryTick.update((n) => n + 1),\r\n this.expiryIntervalMs(),\r\n );\r\n onCleanup(() => clearInterval(handle));\r\n });\r\n }\r\n\r\n // --- derived model --------------------------------------------------------\r\n\r\n protected readonly issues = computed<readonly KerusiViolation[]>(() =>\r\n this.validate() === 'off'\r\n ? []\r\n : validateDocumentSet({\r\n map: this.map(),\r\n state: this.state(),\r\n session: this.session(),\r\n }),\r\n );\r\n\r\n /** The state actually rendered — with lapsed holds reverted when asked. */\r\n private readonly effectiveState = computed<KerusiState | undefined>(() => {\r\n const state = this.state();\r\n if (!state || !this.expireHolds()) {\r\n return state;\r\n }\r\n this.expiryTick();\r\n return expireHolds(state);\r\n });\r\n\r\n protected readonly renderMap = computed<RenderMap>(() =>\r\n buildRenderModel(this.map(), this.effectiveState(), {\r\n locale: this.locale(),\r\n sectionIds: this.visibleSectionIds(),\r\n selectableStatuses: this.selectableStatuses(),\r\n seatSelectable: this.seatSelectable(),\r\n }),\r\n );\r\n\r\n protected readonly resolvedLocale = computed(() => resolveMapLocale(this.map(), this.locale()));\r\n\r\n protected readonly isRtl = computed(() =>\r\n this.rtl() === 'auto' ? isRtlLocale(this.resolvedLocale()) : this.rtl() === true,\r\n );\r\n\r\n protected readonly resolvedColors = computed<Required<KerusiSeatmapColors>>(() =>\r\n resolveColors(this.colors()),\r\n );\r\n\r\n protected readonly selectionSet = computed(() => new Set(this.selection()));\r\n\r\n /** Placed geometry per section, keyed by section id. */\r\n protected readonly layouts = computed<ReadonlyMap<string, SectionLayout>>(() => {\r\n const overrides = this.sectionOverrides();\r\n return new Map(\r\n this.renderMap().sections.map((section) => {\r\n const override = overrides[section.id] ?? {};\r\n return [\r\n section.id,\r\n computeSectionLayout(section, {\r\n seatSize: override.seatSize ?? this.seatSize(),\r\n seatGap: this.seatGap(),\r\n freeformBasis: this.freeformBasis(),\r\n aspectRatio: override.aspectRatio,\r\n rtl: this.isRtl(),\r\n }),\r\n ];\r\n }),\r\n );\r\n });\r\n\r\n /** Navigation graphs per section — arrows move within, Tab moves between. */\r\n private readonly graphs = computed<ReadonlyMap<string, NavigationGraph>>(\r\n () =>\r\n new Map(\r\n this.renderMap().sections.map((section) => [\r\n section.id,\r\n buildNavigationGraph(section, { rtl: this.isRtl() }),\r\n ]),\r\n ),\r\n );\r\n\r\n protected readonly ariaLabels = computed<ReadonlyMap<string, string>>(() => {\r\n const locale = this.resolvedLocale();\r\n const strings = this.ariaStrings();\r\n return new Map(\r\n [...this.renderMap().seatsById.values()].map((seat) => [\r\n seat.id,\r\n seatAriaLabel(seat, locale, strings),\r\n ]),\r\n );\r\n });\r\n\r\n protected readonly mapLabel = computed(\r\n () => this.renderMap().name ?? `Seat map ${this.renderMap().id}`,\r\n );\r\n\r\n /** Live-region text: the running selection, or the last disallowed reason. */\r\n protected readonly liveMessage = computed(() => this.announcement());\r\n\r\n /** The selection's seats and total, for callers that want it off the template. */\r\n readonly summary = computed(() => summarizeSelection(this.renderMap(), this.selection()));\r\n\r\n // --- template helpers -----------------------------------------------------\r\n\r\n protected layoutFor(section: RenderSection): SectionLayout {\r\n return this.layouts().get(section.id)!;\r\n }\r\n\r\n protected labelFor(section: RenderSection): string | undefined {\r\n return this.sectionOverrides()[section.id]?.label ?? section.label;\r\n }\r\n\r\n protected labelId(section: RenderSection): string {\r\n return `kerusi-section-${this.renderMap().id}-${section.id}`;\r\n }\r\n\r\n /** The seat holding this section's tab stop. */\r\n protected focusedSeatIn(section: RenderSection): string | null {\r\n return this.focusedSeatIds()[section.id] ?? null;\r\n }\r\n\r\n // --- interaction ----------------------------------------------------------\r\n\r\n protected onSeatActivate(seatId: string): void {\r\n const model = this.renderMap();\r\n const seat = model.seatsById.get(seatId);\r\n if (!seat) {\r\n return;\r\n }\r\n\r\n const outcome = toggleSeatSelection(this.selection(), seatId, {\r\n seatsById: model.seatsById,\r\n companionMode: this.companionMode(),\r\n maxSelection: this.maxSelection(),\r\n });\r\n\r\n if (outcome.kind === 'disallowed') {\r\n this.announce(disallowedAnnouncement(seat, outcome.reason!, this.ariaStrings()));\r\n this.seatDisallowed.emit({ seat, reason: outcome.reason! });\r\n return;\r\n }\r\n\r\n this.selection.set(outcome.selection);\r\n const event: SeatInteraction = {\r\n seat,\r\n selection: outcome.selection,\r\n changed: outcome.changed,\r\n };\r\n (outcome.kind === 'select' ? this.seatSelect : this.seatDeselect).emit(event);\r\n this.announceSelection(outcome.selection);\r\n }\r\n\r\n protected onSeatFocused(seatId: string): void {\r\n const seat = this.renderMap().seatsById.get(seatId);\r\n if (!seat) {\r\n return;\r\n }\r\n this.setFocusedSeat(seat.sectionId, seatId);\r\n this.seatFocus.emit(seat);\r\n }\r\n\r\n /**\r\n * Arrow keys walk the navigation graph, which orders seats by `col` in grid\r\n * and mixed sections — §4.3.1's logical adjacency — so the aisle that is a\r\n * skipped column is stepped across rather than into.\r\n */\r\n protected onKeydown(section: RenderSection, event: KeyboardEvent): void {\r\n const seatId = (event.target as HTMLElement)?.dataset?.['seatId'];\r\n if (!seatId) {\r\n return;\r\n }\r\n\r\n const graph = this.graphs().get(section.id);\r\n const neighbours = graph?.neighbours.get(seatId);\r\n if (!graph) {\r\n return;\r\n }\r\n\r\n let target: string | undefined;\r\n switch (event.key) {\r\n case 'ArrowLeft':\r\n target = neighbours?.left;\r\n break;\r\n case 'ArrowRight':\r\n target = neighbours?.right;\r\n break;\r\n case 'ArrowUp':\r\n target = neighbours?.up;\r\n break;\r\n case 'ArrowDown':\r\n target = neighbours?.down;\r\n break;\r\n case 'Home':\r\n target = event.ctrlKey ? graph.first : neighbours?.rowStart;\r\n break;\r\n case 'End':\r\n target = event.ctrlKey ? graph.last : neighbours?.rowEnd;\r\n break;\r\n case 'PageUp':\r\n target = graph.first;\r\n break;\r\n case 'PageDown':\r\n target = graph.last;\r\n break;\r\n case 'Enter':\r\n case ' ':\r\n event.preventDefault();\r\n this.onSeatActivate(seatId);\r\n return;\r\n case 'Escape':\r\n if (this.selection().length > 0) {\r\n event.preventDefault();\r\n this.selection.set([]);\r\n this.announceSelection([]);\r\n }\r\n return;\r\n default:\r\n return;\r\n }\r\n\r\n if (target) {\r\n event.preventDefault();\r\n this.moveFocusTo(section.id, target);\r\n }\r\n }\r\n\r\n private setFocusedSeat(sectionId: string, seatId: string): void {\r\n if (this.focusedSeatIds()[sectionId] === seatId) {\r\n return;\r\n }\r\n this.focusedSeatIds.update((all) => ({ ...all, [sectionId]: seatId }));\r\n }\r\n\r\n /**\r\n * Moves the roving tab stop, then the DOM focus that follows it.\r\n *\r\n * Seat ids come from the document, so they cannot be interpolated into a\r\n * selector unescaped — and `CSS.escape` is absent in jsdom and under SSR.\r\n * Scanning the rendered nodes avoids both problems.\r\n */\r\n private moveFocusTo(sectionId: string, seatId: string): void {\r\n this.setFocusedSeat(sectionId, seatId);\r\n const nodes = (this.host.nativeElement as HTMLElement).querySelectorAll<SVGGElement>(\r\n '[data-seat-id]',\r\n );\r\n for (const node of Array.from(nodes)) {\r\n if (node.dataset['seatId'] === seatId) {\r\n node.focus?.();\r\n return;\r\n }\r\n }\r\n }\r\n\r\n private announceSelection(selection: readonly string[]): void {\r\n const strings = this.ariaStrings();\r\n if (selection.length === 0) {\r\n this.announce(strings.selectionEmpty);\r\n return;\r\n }\r\n const { total } = summarizeSelection(this.renderMap(), selection);\r\n this.announce(\r\n strings.selectionSummary(selection.length, formatTotal(total, this.resolvedLocale())),\r\n );\r\n }\r\n\r\n private announce(message: string): void {\r\n // Re-announce an identical message by clearing first; a live region that\r\n // sees no text change stays silent.\r\n this.announcement.set('');\r\n queueMicrotask(() => this.announcement.set(message));\r\n }\r\n\r\n private visibleSectionIds(): readonly string[] | undefined {\r\n const explicit = this.sectionIds();\r\n const overrides = this.sectionOverrides();\r\n const hidden = Object.entries(overrides)\r\n .filter(([, o]) => o.hidden)\r\n .map(([id]) => id);\r\n\r\n if (explicit) {\r\n return explicit.filter((id) => !hidden.includes(id));\r\n }\r\n if (hidden.length === 0) {\r\n return undefined;\r\n }\r\n return (this.map().sections ?? []).map((s) => s.id).filter((id) => !hidden.includes(id));\r\n }\r\n}\r\n\r\nfunction formatTotal(total: Money | undefined, locale: string): string {\r\n return total ? formatMoney(total, locale) : '';\r\n}\r\n","<div\r\n class=\"kerusi-seatmap\"\r\n role=\"group\"\r\n [attr.aria-label]=\"mapLabel()\"\r\n [attr.dir]=\"isRtl() ? 'rtl' : null\"\r\n [attr.lang]=\"resolvedLocale()\"\r\n>\r\n @for (section of renderMap().sections; track section.id) {\r\n <section\r\n class=\"kerusi-seatmap__section kerusi-seatmap__section--{{ section.layoutMode }}\"\r\n role=\"group\"\r\n [attr.aria-labelledby]=\"labelFor(section) ? labelId(section) : null\"\r\n [attr.aria-label]=\"labelFor(section) ? null : section.id\"\r\n >\r\n @if (showSectionLabels() && labelFor(section)) {\r\n <h3 class=\"kerusi-section-label\" [id]=\"labelId(section)\">{{ labelFor(section) }}</h3>\r\n }\r\n\r\n <kerusi-section\r\n [section]=\"section\"\r\n [layout]=\"layoutFor(section)\"\r\n [selection]=\"selectionSet()\"\r\n [focusedSeatId]=\"focusedSeatIn(section)\"\r\n [ariaLabels]=\"ariaLabels()\"\r\n [colors]=\"resolvedColors()\"\r\n [typeColors]=\"typeColors()\"\r\n [unitScale]=\"unitScale()\"\r\n [interactive]=\"interactive()\"\r\n (seatActivate)=\"onSeatActivate($event)\"\r\n (seatFocused)=\"onSeatFocused($event)\"\r\n (seatKeydown)=\"onKeydown(section, $event)\"\r\n />\r\n </section>\r\n }\r\n\r\n @if (showLegend()) {\r\n <kerusi-legend\r\n [legend]=\"renderMap().legend\"\r\n [locale]=\"resolvedLocale()\"\r\n [colors]=\"resolvedColors()\"\r\n [typeColors]=\"typeColors()\"\r\n [showPrices]=\"showLegendPrices()\"\r\n />\r\n }\r\n\r\n <p class=\"kerusi-sr-only\" aria-live=\"polite\" aria-atomic=\"true\">{{ liveMessage() }}</p>\r\n</div>\r\n","/**\r\n * The Kerusi-native seat map: `<kerusi-seatmap>`, its section renderer, and an\r\n * optional legend.\r\n *\r\n * The selection, theming and announcement helpers they are built from are\r\n * framework-free and live in `@kerusiweb/core`.\r\n */\r\n\r\nexport * from './kerusi-seatmap.component';\r\nexport * from './kerusi-section.component';\r\nexport * from './kerusi-legend.component';\r\n","import { computed, signal, Signal } from '@angular/core';\r\nimport {\r\n applyStateDeltaOrdered,\r\n DEFAULT_SEQUENCE_READER,\r\n DeltaApplication,\r\n expireHolds,\r\n HeldSeat,\r\n heldSeats as heldSeatsOf,\r\n KerusiState,\r\n KerusiStateDelta,\r\n SequenceReader,\r\n} from '@kerusiweb/core';\r\n\r\n/**\r\n * A signal-backed live view of a {@link KerusiState}.\r\n *\r\n * Construct it directly — `new KerusiStateStore(initialState)` — and feed it\r\n * deltas from whatever transport you use. It is deliberately not an\r\n * `@Injectable`: this is a rendering library, and an application that already\r\n * owns its availability state should keep owning it.\r\n */\r\nexport class KerusiStateStore {\r\n private readonly _state;\r\n private readonly _needsRefetch = signal(false);\r\n private readonly _now;\r\n private readonly clock: () => Date;\r\n private readonly sequenceOf: SequenceReader;\r\n private lastSequence: number | undefined;\r\n\r\n constructor(initial: KerusiState, opts: { now?: () => Date; sequenceOf?: SequenceReader } = {}) {\r\n this._state = signal(initial);\r\n this.clock = opts.now ?? (() => new Date());\r\n this.sequenceOf = opts.sequenceOf ?? DEFAULT_SEQUENCE_READER;\r\n this._now = signal(this.clock());\r\n }\r\n\r\n /** The current availability snapshot. */\r\n get state(): Signal<KerusiState> {\r\n return this._state.asReadonly();\r\n }\r\n\r\n /** The timestamp of the most recently applied document. */\r\n readonly updatedAt = computed(() => this._state().updatedAt);\r\n\r\n /**\r\n * True once a gap was detected in the delta stream. The consumer SHOULD\r\n * re-fetch a full `KerusiState` and hand it to {@link reset} (§5.2).\r\n */\r\n get needsRefetch(): Signal<boolean> {\r\n return this._needsRefetch.asReadonly();\r\n }\r\n\r\n /** Every currently-held seat, with the time left on its hold. */\r\n readonly heldSeats = computed<readonly HeldSeat[]>(() => heldSeatsOf(this._state(), this._now()));\r\n\r\n /** Offers a delta to the store. Returns what happened to it. */\r\n apply(delta: KerusiStateDelta): DeltaApplication {\r\n const result = applyStateDeltaOrdered(this._state(), delta, {\r\n sequenceOf: this.sequenceOf,\r\n lastSequence: this.lastSequence,\r\n });\r\n\r\n if (result.outcome === 'applied' || result.outcome === 'gap') {\r\n this._state.set(result.state);\r\n const seq = this.sequenceOf(delta);\r\n if (seq !== undefined) {\r\n this.lastSequence = seq;\r\n }\r\n }\r\n if (result.outcome === 'gap') {\r\n this._needsRefetch.set(true);\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Replaces the state wholesale — the post-refetch path. Clears the\r\n * refetch flag and forgets the delta sequence.\r\n */\r\n reset(state: KerusiState): void {\r\n this._state.set(state);\r\n this._needsRefetch.set(false);\r\n this.lastSequence = undefined;\r\n this._now.set(this.clock());\r\n }\r\n\r\n /** Reverts lapsed holds and refreshes `heldSeats` countdowns. */\r\n tick(): void {\r\n const now = this.clock();\r\n this._now.set(now);\r\n const next = expireHolds(this._state(), now);\r\n if (next !== this._state()) {\r\n this._state.set(next);\r\n }\r\n }\r\n\r\n /**\r\n * Runs {@link tick} on an interval. Returns a function that stops it — call\r\n * that from `DestroyRef.onDestroy` or `ngOnDestroy`.\r\n */\r\n startExpiryTicker(intervalMs = 1000): () => void {\r\n const handle = setInterval(() => this.tick(), intervalMs);\r\n return () => clearInterval(handle);\r\n }\r\n}\r\n","/*\r\n * Public API Surface of @kerusiweb/angular\r\n *\r\n * The Angular binding only. Document types, the render model and the view\r\n * helpers live in @kerusiweb/core — import them from there.\r\n */\r\n\r\n// --- Components -------------------------------------------------------------\r\nexport * from './lib/kerusi-seatmap';\r\n\r\n// --- Signal-backed live availability store ---------------------------------\r\nexport * from './lib/kerusi-state-store';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["heldSeatsOf"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kerusiweb/angular",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "An Angular seat-map component that renders Kerusi Seat Map & Availability Format documents as interactive, accessible inline SVG.",
5
5
  "keywords": [
6
6
  "angular",
@@ -27,7 +27,7 @@
27
27
  "peerDependencies": {
28
28
  "@angular/common": "^22.1.0",
29
29
  "@angular/core": "^22.1.0",
30
- "@kerusiweb/core": "^2.0.0"
30
+ "@kerusiweb/core": "^2.1.0"
31
31
  },
32
32
  "dependencies": {
33
33
  "tslib": "^2.3.0"
@@ -264,6 +264,10 @@ declare class KerusiSectionComponent {
264
264
  /** Cancels the parent <g>'s rotation so the label stays upright and stays put. */
265
265
  protected counterRotate(item: PlacedSeat | PlacedElement): string | null;
266
266
  protected style(placed: PlacedElement): _kerusiweb_core.ElementStyle;
267
+ /** A text element with no label draws nothing, rather than an empty box. */
268
+ protected drawsElement(placed: PlacedElement): boolean;
269
+ /** A label on a shape stays upright; a text element's own label is what rotates. */
270
+ protected labelTransform(placed: PlacedElement): string | null;
267
271
  protected elementFillOf(placed: PlacedElement): string;
268
272
  protected elementTextFillOf(placed: PlacedElement): string;
269
273
  protected screenPathOf(placed: PlacedElement): string;