@kerusiweb/angular 2.1.0 → 2.3.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.
@@ -1,7 +1,53 @@
1
1
  import * as _kerusiweb_core from '@kerusiweb/core';
2
- import { KerusiMap, KerusiState, KerusiSession, SeatAriaStrings, KerusiSeatmapColors, RenderSeat, DisallowedReason, KerusiViolation, RenderMap, SectionLayout, RenderSection, PlacedSeat, SeatOccupantVariant, PlacedElement, RenderLegendEntry, SequenceReader, HeldSeat, KerusiStateDelta, DeltaApplication } from '@kerusiweb/core';
2
+ import { KerusiSeatContext, KerusiMap, KerusiState, KerusiSession, SeatAriaStrings, KerusiSeatmapColors, SeatStyleFn, RenderSeat, KerusiControlStrings, DisallowedReason, KerusiViolation, KerusiViewport, RenderMap, SectionLayout, RenderSection, PlacedSeat, PlacedElement, RenderLegendEntry, SeatOccupantVariant, SequenceReader, HeldSeat, KerusiStateDelta, DeltaApplication } from '@kerusiweb/core';
3
3
  import * as _angular_core from '@angular/core';
4
- import { Signal } from '@angular/core';
4
+ import { TemplateRef, Signal } from '@angular/core';
5
+
6
+ /**
7
+ * What a `kerusiSeat` template is handed for each seat (`let-seat`): the seat,
8
+ * whether it is selected, the stock glyph core resolved for it, and the stock
9
+ * drawing itself.
10
+ */
11
+ interface SeatTemplateContext extends KerusiSeatContext {
12
+ /**
13
+ * The library's own drawing of this seat. Render it to keep the stock seat,
14
+ * or alongside your own marks to add to it:
15
+ *
16
+ * ```html
17
+ * <ng-container *ngTemplateOutlet="seat.defaultContent; context: { $implicit: seat }" />
18
+ * ```
19
+ */
20
+ defaultContent: TemplateRef<SeatTemplateOutletContext>;
21
+ }
22
+ /** The context object a seat template is stamped with. */
23
+ interface SeatTemplateOutletContext {
24
+ $implicit: SeatTemplateContext;
25
+ }
26
+ /**
27
+ * Marks an `<ng-template>` inside `<kerusi-seatmap>` as the drawing for every
28
+ * seat. The library keeps each seat's `<g>`, with its role, ARIA state, tab
29
+ * stop, rotation and event handlers, and stamps this template inside it.
30
+ *
31
+ * The template is declared outside any `<svg>`, so Angular cannot tell its
32
+ * elements are SVG: prefix each one with `svg:` (`<svg:path>`, `<svg:image>`),
33
+ * or it is created as an unknown HTML element and draws nothing.
34
+ *
35
+ * ```html
36
+ * <kerusi-seatmap [map]="map">
37
+ * <ng-template kerusiSeat let-seat>
38
+ * <ng-container *ngTemplateOutlet="seat.defaultContent; context: { $implicit: seat }" />
39
+ * <svg:circle [attr.cx]="seat.glyph.box.x" [attr.cy]="seat.glyph.box.y" r="3" />
40
+ * </ng-template>
41
+ * </kerusi-seatmap>
42
+ * ```
43
+ */
44
+ declare class KerusiSeatTemplateDirective {
45
+ readonly template: TemplateRef<SeatTemplateOutletContext>;
46
+ /** Types `let-seat` as a {@link SeatTemplateContext}. */
47
+ static ngTemplateContextGuard(_dir: KerusiSeatTemplateDirective, ctx: unknown): ctx is SeatTemplateOutletContext;
48
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<KerusiSeatTemplateDirective, never>;
49
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<KerusiSeatTemplateDirective, "ng-template[kerusiSeat]", never, {}, {}, never, never, true, never>;
50
+ }
5
51
 
6
52
  /** How a document with validation errors is handled. */
7
53
  type ValidationMode =
@@ -46,6 +92,11 @@ interface SeatDisallowed {
46
92
  * ```html
47
93
  * <kerusi-seatmap [map]="map" [state]="state" [(selection)]="picked" />
48
94
  * ```
95
+ *
96
+ * Two opt-in features sit on top, both off by default so an existing map is
97
+ * unchanged: `[zoomable]` (pinch, wheel and drag, keys `+` `-` `0`, buttons)
98
+ * and `[maximizable]` (a full-screen button). Their arithmetic is in
99
+ * `@kerusiweb/core`; this class supplies the listeners and the signals.
49
100
  */
50
101
  declare class KerusiSeatmapComponent {
51
102
  private readonly host;
@@ -64,6 +115,22 @@ declare class KerusiSeatmapComponent {
64
115
  readonly colors: _angular_core.InputSignal<KerusiSeatmapColors>;
65
116
  /** Let an available seat take its `SeatType.color` (§4.7). */
66
117
  readonly typeColors: _angular_core.InputSignal<boolean>;
118
+ /**
119
+ * Styles for every seat of a type, keyed by `SeatType.id`: fill, label colour
120
+ * and classes. Never changes a selected or blocked seat's fill. Also shown in
121
+ * the legend.
122
+ */
123
+ readonly seatTypeStyles: _angular_core.InputSignal<Readonly<Record<string, _kerusiweb_core.KerusiSeatStyle>> | undefined>;
124
+ /**
125
+ * A style for one seat, laid over its `seatTypeStyles` entry; return nothing
126
+ * to leave a seat be. Never changes a selected or blocked seat's fill.
127
+ */
128
+ readonly seatStyle: _angular_core.InputSignal<SeatStyleFn | undefined>;
129
+ /**
130
+ * A `<ng-template kerusiSeat let-seat>` placed inside the map draws the inside
131
+ * of every seat in place of the stock glyph. See {@link KerusiSeatTemplateDirective}.
132
+ */
133
+ protected readonly seatTemplate: _angular_core.Signal<KerusiSeatTemplateDirective | undefined>;
67
134
  /** Grid cell edge, in viewBox units. */
68
135
  readonly seatSize: _angular_core.InputSignal<number>;
69
136
  /** Gap between grid cells, in viewBox units. */
@@ -82,7 +149,7 @@ declare class KerusiSeatmapComponent {
82
149
  readonly sectionIds: _angular_core.InputSignal<readonly string[] | undefined>;
83
150
  readonly sectionOverrides: _angular_core.InputSignal<Readonly<Record<string, SectionRenderOptions>>>;
84
151
  /** Statuses in which a seat may be picked. Default: `['available']`. */
85
- readonly selectableStatuses: _angular_core.InputSignal<readonly ("held" | "booked" | "available" | "blocked")[]>;
152
+ readonly selectableStatuses: _angular_core.InputSignal<readonly ("available" | "blocked" | "held" | "booked")[]>;
86
153
  /** `auto` selects a seat's companion closure together (§4.6). */
87
154
  readonly companionMode: _angular_core.InputSignal<"auto" | "independent">;
88
155
  readonly maxSelection: _angular_core.InputSignal<number | undefined>;
@@ -96,6 +163,22 @@ declare class KerusiSeatmapComponent {
96
163
  */
97
164
  readonly expireHolds: _angular_core.InputSignal<boolean>;
98
165
  readonly expiryIntervalMs: _angular_core.InputSignal<number>;
166
+ /**
167
+ * Pinch, Ctrl/Cmd + wheel and drag to zoom and pan, `+` `-` `0` on the keyboard,
168
+ * and on-screen buttons. A tap still selects at every zoom level.
169
+ */
170
+ readonly zoomable: _angular_core.InputSignal<boolean>;
171
+ /** The zoom ceiling, relative to fit-to-container. The floor is always fit. */
172
+ readonly maxZoom: _angular_core.InputSignal<number>;
173
+ /**
174
+ * The built-in zoom buttons. Turn this off to supply your own, anywhere on the
175
+ * page, and call `zoomIn()`, `zoomOut()` and `resetZoom()` on the component.
176
+ */
177
+ readonly showZoomControls: _angular_core.InputSignal<boolean>;
178
+ /** A button that maximizes the map to fill the screen. */
179
+ readonly maximizable: _angular_core.InputSignal<boolean>;
180
+ /** Overrides the built-in English names of the zoom and maximize controls. */
181
+ readonly controlStrings: _angular_core.InputSignal<Partial<KerusiControlStrings>>;
99
182
  readonly validate: _angular_core.InputSignal<ValidationMode>;
100
183
  readonly seatSelect: _angular_core.OutputEmitterRef<SeatInteraction>;
101
184
  readonly seatDeselect: _angular_core.OutputEmitterRef<SeatInteraction>;
@@ -103,6 +186,9 @@ declare class KerusiSeatmapComponent {
103
186
  readonly seatFocus: _angular_core.OutputEmitterRef<RenderSeat>;
104
187
  /** Emitted whenever the documents change, in `collect` mode. */
105
188
  readonly validationIssues: _angular_core.OutputEmitterRef<readonly KerusiViolation[]>;
189
+ /** The zoom and pan, whenever they change. For a host that persists or shows them. */
190
+ readonly viewportChange: _angular_core.OutputEmitterRef<KerusiViewport>;
191
+ readonly maximizedChange: _angular_core.OutputEmitterRef<boolean>;
106
192
  /**
107
193
  * The seat holding each section's tab stop, keyed by section id.
108
194
  *
@@ -114,6 +200,20 @@ declare class KerusiSeatmapComponent {
114
200
  private readonly announcement;
115
201
  /** Bumped by the hold ticker to force the state to be re-derived. */
116
202
  private readonly expiryTick;
203
+ private readonly viewportState;
204
+ private readonly maximizedState;
205
+ /** True for a discrete change (button, key, reveal), false while a finger or wheel drives. */
206
+ protected readonly animated: _angular_core.WritableSignal<boolean>;
207
+ private readonly viewportEl;
208
+ private readonly contentEl;
209
+ private gesture;
210
+ private suppressClick;
211
+ private gestureBase;
212
+ private nativeFullscreen;
213
+ /** The current zoom and pan. Read-only: drive it with the methods below. */
214
+ readonly viewport: _angular_core.Signal<KerusiViewport>;
215
+ /** Whether the map is currently maximized. */
216
+ readonly maximized: _angular_core.Signal<boolean>;
117
217
  constructor();
118
218
  protected readonly issues: _angular_core.Signal<readonly KerusiViolation[]>;
119
219
  /** The state actually rendered — with lapsed holds reverted when asked. */
@@ -123,6 +223,14 @@ declare class KerusiSeatmapComponent {
123
223
  protected readonly isRtl: _angular_core.Signal<boolean>;
124
224
  protected readonly resolvedColors: _angular_core.Signal<Required<KerusiSeatmapColors>>;
125
225
  protected readonly selectionSet: _angular_core.Signal<Set<string>>;
226
+ protected readonly limits: _angular_core.Signal<_kerusiweb_core.ViewportLimits>;
227
+ protected readonly strings: _angular_core.Signal<KerusiControlStrings>;
228
+ protected readonly controlColors: _angular_core.Signal<_kerusiweb_core.KerusiControlStyle>;
229
+ protected readonly transform: _angular_core.Signal<string>;
230
+ protected readonly touchAction: _angular_core.Signal<string>;
231
+ protected readonly showToolbar: _angular_core.Signal<boolean>;
232
+ protected readonly atMaxZoom: _angular_core.Signal<boolean>;
233
+ protected readonly atFit: _angular_core.Signal<boolean>;
126
234
  /** Placed geometry per section, keyed by section id. */
127
235
  protected readonly layouts: _angular_core.Signal<ReadonlyMap<string, SectionLayout>>;
128
236
  /** Navigation graphs per section — arrows move within, Tab moves between. */
@@ -150,16 +258,58 @@ declare class KerusiSeatmapComponent {
150
258
  /**
151
259
  * Moves the roving tab stop, then the DOM focus that follows it.
152
260
  *
261
+ * When zoomable, the browser's own scroll-into-view is suppressed: the map
262
+ * moves by transform, so `onSeatFocused` pans it into view instead.
263
+ */
264
+ private moveFocusTo;
265
+ /**
153
266
  * Seat ids come from the document, so they cannot be interpolated into a
154
267
  * selector unescaped — and `CSS.escape` is absent in jsdom and under SSR.
155
268
  * Scanning the rendered nodes avoids both problems.
156
269
  */
157
- private moveFocusTo;
270
+ private seatNode;
271
+ /** Zooms in one step about the centre. No-op unless `zoomable`. */
272
+ zoomIn(): void;
273
+ /** Zooms out one step about the centre. No-op unless `zoomable`. */
274
+ zoomOut(): void;
275
+ /** Back to fit-to-container: the map's original view. No-op unless `zoomable`. */
276
+ resetZoom(): void;
277
+ private stepZoom;
278
+ /** Sizes in CSS pixels, read when needed: `offset*` ignores the zoom transform. */
279
+ private measure;
280
+ private commit;
281
+ private reclamp;
282
+ /** A point in the viewport's own coordinates, which is what the core functions take. */
283
+ private containerPoint;
284
+ /**
285
+ * Pans a seat that took keyboard focus into view. A click or tap also
286
+ * focuses the seat, but should not move the map under the finger, so only a
287
+ * `:focus-visible` focus counts.
288
+ */
289
+ private revealSeat;
290
+ private onPointerDown;
291
+ private onPointerMove;
292
+ private onPointerEnd;
293
+ private feedGesture;
294
+ private onClickCapture;
295
+ private onWheel;
296
+ private onGestureStart;
297
+ private onGestureChange;
298
+ private onGestureEnd;
299
+ /**
300
+ * `+` `-` `0` zoom, and arrows pan when the viewport itself has focus; Esc
301
+ * leaves a maximized map. A seat's own handler runs first, so an Esc that
302
+ * cleared the selection is already `defaultPrevented` and stays at that.
303
+ */
304
+ protected onRootKeydown(event: KeyboardEvent): void;
305
+ /** Maximizes or restores the map. Maximizing needs `maximizable`. */
306
+ toggleMaximized(): void;
307
+ setMaximized(next: boolean): void;
158
308
  private announceSelection;
159
309
  private announce;
160
310
  private visibleSectionIds;
161
311
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<KerusiSeatmapComponent, never>;
162
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<KerusiSeatmapComponent, "kerusi-seatmap", never, { "map": { "alias": "map"; "required": true; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "session": { "alias": "session"; "required": false; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "rtl": { "alias": "rtl"; "required": false; "isSignal": true; }; "ariaStrings": { "alias": "ariaStrings"; "required": false; "isSignal": true; }; "colors": { "alias": "colors"; "required": false; "isSignal": true; }; "typeColors": { "alias": "typeColors"; "required": false; "isSignal": true; }; "seatSize": { "alias": "seatSize"; "required": false; "isSignal": true; }; "seatGap": { "alias": "seatGap"; "required": false; "isSignal": true; }; "freeformBasis": { "alias": "freeformBasis"; "required": false; "isSignal": true; }; "unitScale": { "alias": "unitScale"; "required": false; "isSignal": true; }; "showSectionLabels": { "alias": "showSectionLabels"; "required": false; "isSignal": true; }; "showLegend": { "alias": "showLegend"; "required": false; "isSignal": true; }; "showLegendPrices": { "alias": "showLegendPrices"; "required": false; "isSignal": true; }; "sectionIds": { "alias": "sectionIds"; "required": false; "isSignal": true; }; "sectionOverrides": { "alias": "sectionOverrides"; "required": false; "isSignal": true; }; "selectableStatuses": { "alias": "selectableStatuses"; "required": false; "isSignal": true; }; "companionMode": { "alias": "companionMode"; "required": false; "isSignal": true; }; "maxSelection": { "alias": "maxSelection"; "required": false; "isSignal": true; }; "seatSelectable": { "alias": "seatSelectable"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "expireHolds": { "alias": "expireHolds"; "required": false; "isSignal": true; }; "expiryIntervalMs": { "alias": "expiryIntervalMs"; "required": false; "isSignal": true; }; "validate": { "alias": "validate"; "required": false; "isSignal": true; }; }, { "selection": "selectionChange"; "seatSelect": "seatSelect"; "seatDeselect": "seatDeselect"; "seatDisallowed": "seatDisallowed"; "seatFocus": "seatFocus"; "validationIssues": "validationIssues"; }, never, never, true, never>;
312
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<KerusiSeatmapComponent, "kerusi-seatmap", never, { "map": { "alias": "map"; "required": true; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "session": { "alias": "session"; "required": false; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "rtl": { "alias": "rtl"; "required": false; "isSignal": true; }; "ariaStrings": { "alias": "ariaStrings"; "required": false; "isSignal": true; }; "colors": { "alias": "colors"; "required": false; "isSignal": true; }; "typeColors": { "alias": "typeColors"; "required": false; "isSignal": true; }; "seatTypeStyles": { "alias": "seatTypeStyles"; "required": false; "isSignal": true; }; "seatStyle": { "alias": "seatStyle"; "required": false; "isSignal": true; }; "seatSize": { "alias": "seatSize"; "required": false; "isSignal": true; }; "seatGap": { "alias": "seatGap"; "required": false; "isSignal": true; }; "freeformBasis": { "alias": "freeformBasis"; "required": false; "isSignal": true; }; "unitScale": { "alias": "unitScale"; "required": false; "isSignal": true; }; "showSectionLabels": { "alias": "showSectionLabels"; "required": false; "isSignal": true; }; "showLegend": { "alias": "showLegend"; "required": false; "isSignal": true; }; "showLegendPrices": { "alias": "showLegendPrices"; "required": false; "isSignal": true; }; "sectionIds": { "alias": "sectionIds"; "required": false; "isSignal": true; }; "sectionOverrides": { "alias": "sectionOverrides"; "required": false; "isSignal": true; }; "selectableStatuses": { "alias": "selectableStatuses"; "required": false; "isSignal": true; }; "companionMode": { "alias": "companionMode"; "required": false; "isSignal": true; }; "maxSelection": { "alias": "maxSelection"; "required": false; "isSignal": true; }; "seatSelectable": { "alias": "seatSelectable"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "expireHolds": { "alias": "expireHolds"; "required": false; "isSignal": true; }; "expiryIntervalMs": { "alias": "expiryIntervalMs"; "required": false; "isSignal": true; }; "zoomable": { "alias": "zoomable"; "required": false; "isSignal": true; }; "maxZoom": { "alias": "maxZoom"; "required": false; "isSignal": true; }; "showZoomControls": { "alias": "showZoomControls"; "required": false; "isSignal": true; }; "maximizable": { "alias": "maximizable"; "required": false; "isSignal": true; }; "controlStrings": { "alias": "controlStrings"; "required": false; "isSignal": true; }; "validate": { "alias": "validate"; "required": false; "isSignal": true; }; }, { "selection": "selectionChange"; "seatSelect": "seatSelect"; "seatDeselect": "seatDeselect"; "seatDisallowed": "seatDisallowed"; "seatFocus": "seatFocus"; "validationIssues": "validationIssues"; "viewportChange": "viewportChange"; "maximizedChange": "maximizedChange"; }, ["seatTemplate"], never, true, never>;
163
313
  }
164
314
 
165
315
  /**
@@ -185,6 +335,12 @@ declare class KerusiSectionComponent {
185
335
  readonly colors: _angular_core.InputSignal<Required<KerusiSeatmapColors>>;
186
336
  /** Whether an available seat may take its `SeatType.color` (§4.7). */
187
337
  readonly typeColors: _angular_core.InputSignal<boolean>;
338
+ /** Styles for every seat of a type, keyed by `SeatType.id`. */
339
+ readonly seatTypeStyles: _angular_core.InputSignal<Readonly<Record<string, _kerusiweb_core.KerusiSeatStyle>> | undefined>;
340
+ /** A style for one seat, laid over its type's. */
341
+ readonly seatStyle: _angular_core.InputSignal<SeatStyleFn | undefined>;
342
+ /** Draws the inside of each seat in place of the stock glyph. */
343
+ readonly seatTemplate: _angular_core.InputSignal<TemplateRef<SeatTemplateOutletContext> | undefined>;
188
344
  /** When false, seats render but do not respond to pointer or keyboard. */
189
345
  readonly interactive: _angular_core.InputSignal<boolean>;
190
346
  /**
@@ -203,62 +359,19 @@ declare class KerusiSectionComponent {
203
359
  protected readonly viewBox: _angular_core.Signal<string>;
204
360
  /** The section's natural width in CSS pixels; it never draws wider. */
205
361
  protected readonly naturalWidth: _angular_core.Signal<number>;
206
- protected isSelected(placed: PlacedSeat): boolean;
207
- protected fill(placed: PlacedSeat): string;
208
- protected textFill(placed: PlacedSeat): string;
209
- protected backdrop(): string;
210
- /** The seat's outline: square-ish front, tapered back — shape carries facing. */
211
- protected bodyPath(placed: PlacedSeat): string;
212
- /** `null` for an available or blocked seat — nothing to mark. */
213
- protected occupantVariant(placed: PlacedSeat): SeatOccupantVariant | null;
214
- /**
215
- * The box a seat's decorative marks are drawn to: the seat's own, or — when
216
- * it is selected — the core plate's, which is inset by the frame width.
217
- *
218
- * Everything over a selected seat sits on the core, so it all has to be
219
- * measured from the core. Drawing to the seat's box instead leaves the
220
- * occupant's hips crossing the frame, where a figure tinted `selectedBg`
221
- * lands on a frame filled `selectedBg` and reads as a hard nub. Since the
222
- * core is an exact scaled copy of the body (see {@link seatSelectedFrame}),
223
- * routing through here reproduces every clearance the mark had against the
224
- * body, scaled — so the frame width stays free to change.
225
- */
226
- private markBox;
227
- /** The bright plate inside a selected seat's frame. */
228
- protected corePath(placed: PlacedSeat): string;
229
- protected coreFill(): string;
230
- /** The dimming wash over a held or booked seat's type colour. */
231
- protected washFill(placed: PlacedSeat): string;
232
- /** The occupant figure for a marked seat, sized to whatever it sits on. */
233
- protected occupantPath(placed: PlacedSeat): string;
234
- protected occupantStroke(placed: PlacedSeat): number;
235
- protected wheelchairCx(placed: PlacedSeat): number;
236
- protected wheelchairCy(placed: PlacedSeat): number;
237
- protected wheelchairRadius(placed: PlacedSeat): number;
238
362
  /**
239
- * A core-coloured outline behind a selected seat's number, painted under the
240
- * glyphs via `paint-order`.
241
- *
242
- * The label is centred on the occupant's shoulder line, so without this it is
243
- * `selectedBg` over a `selectedBg` silhouette — the figure, not the plate, is
244
- * what it actually has to contrast against. The halo also rescues a label too
245
- * long for the core, which would otherwise run onto the frame and vanish into
246
- * it.
247
- *
248
- * Bound here rather than in the stylesheet on purpose: a CSS
249
- * `var(--kerusi-selected-fg, …)` rule would carry the library default as its
250
- * fallback and silently skip the `[colors]` input tier, which only the
251
- * TypeScript side can resolve.
363
+ * Every seat with its glyph resolved, once per change of layout, selection or
364
+ * theme rather than once per binding per change detection.
252
365
  */
253
- protected labelHalo(placed: PlacedSeat): string;
254
- protected labelHaloWidth(placed: PlacedSeat): number;
255
- /** Solid for selected/booked; `none` for held, which draws hollow instead. */
256
- protected occupantFill(variant: SeatOccupantVariant): string;
257
- protected occupantStrokeColor(variant: SeatOccupantVariant): string;
366
+ protected readonly seats: _angular_core.Signal<{
367
+ placed: PlacedSeat;
368
+ selected: boolean;
369
+ glyph: _kerusiweb_core.SeatGlyph;
370
+ }[]>;
371
+ protected backdrop(): string;
258
372
  /** The section's single tab stop; every other seat is reachable by arrow key. */
259
373
  protected tabIndex(placed: PlacedSeat): number;
260
374
  protected ariaLabel(placed: PlacedSeat): string;
261
- protected seatClasses(placed: PlacedSeat): string;
262
375
  /** SVG rotate() about the item's center; null when it does not rotate. */
263
376
  protected transform(item: PlacedSeat | PlacedElement): string | null;
264
377
  /** Cancels the parent <g>'s rotation so the label stays upright and stays put. */
@@ -277,7 +390,7 @@ declare class KerusiSectionComponent {
277
390
  protected onSeatFocus(placed: PlacedSeat): void;
278
391
  protected onKeydown(event: KeyboardEvent): void;
279
392
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<KerusiSectionComponent, never>;
280
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<KerusiSectionComponent, "kerusi-section", never, { "section": { "alias": "section"; "required": true; "isSignal": true; }; "layout": { "alias": "layout"; "required": true; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "focusedSeatId": { "alias": "focusedSeatId"; "required": false; "isSignal": true; }; "ariaLabels": { "alias": "ariaLabels"; "required": false; "isSignal": true; }; "colors": { "alias": "colors"; "required": false; "isSignal": true; }; "typeColors": { "alias": "typeColors"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "unitScale": { "alias": "unitScale"; "required": false; "isSignal": true; }; }, { "seatActivate": "seatActivate"; "seatFocused": "seatFocused"; "seatKeydown": "seatKeydown"; }, never, never, true, never>;
393
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<KerusiSectionComponent, "kerusi-section", never, { "section": { "alias": "section"; "required": true; "isSignal": true; }; "layout": { "alias": "layout"; "required": true; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "focusedSeatId": { "alias": "focusedSeatId"; "required": false; "isSignal": true; }; "ariaLabels": { "alias": "ariaLabels"; "required": false; "isSignal": true; }; "colors": { "alias": "colors"; "required": false; "isSignal": true; }; "typeColors": { "alias": "typeColors"; "required": false; "isSignal": true; }; "seatTypeStyles": { "alias": "seatTypeStyles"; "required": false; "isSignal": true; }; "seatStyle": { "alias": "seatStyle"; "required": false; "isSignal": true; }; "seatTemplate": { "alias": "seatTemplate"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "unitScale": { "alias": "unitScale"; "required": false; "isSignal": true; }; }, { "seatActivate": "seatActivate"; "seatFocused": "seatFocused"; "seatKeydown": "seatKeydown"; }, never, never, true, never>;
281
394
  }
282
395
 
283
396
  /** Which marks, beyond the body fill, a status swatch draws. */
@@ -310,6 +423,8 @@ declare class KerusiLegendComponent {
310
423
  readonly colors: _angular_core.InputSignal<Required<KerusiSeatmapColors>>;
311
424
  /** Whether an available seat takes its type color — mirrors the seatmap input. */
312
425
  readonly typeColors: _angular_core.InputSignal<boolean>;
426
+ /** Mirrors the seatmap input: a type's style is what its swatch shows. */
427
+ readonly seatTypeStyles: _angular_core.InputSignal<Readonly<Record<string, _kerusiweb_core.KerusiSeatStyle>> | undefined>;
313
428
  readonly showPrices: _angular_core.InputSignal<boolean>;
314
429
  /** Hide types no seat in the rendered map actually uses. */
315
430
  readonly hideUnusedTypes: _angular_core.InputSignal<boolean>;
@@ -318,6 +433,8 @@ declare class KerusiLegendComponent {
318
433
  protected readonly types: _angular_core.Signal<RenderLegendEntry[]>;
319
434
  protected readonly statuses: _angular_core.Signal<StatusEntry[]>;
320
435
  protected swatch(entry: RenderLegendEntry): string;
436
+ /** The swatch outline for a type styled with its own shape; `undefined` keeps the square. */
437
+ protected typeShape(entry: RenderLegendEntry): string | undefined;
321
438
  protected swatchText(entry: RenderLegendEntry): string;
322
439
  /** The swatch's viewBox edge; the seat glyph is drawn to fill it. */
323
440
  protected readonly glyphSize = 16;
@@ -341,7 +458,7 @@ declare class KerusiLegendComponent {
341
458
  protected coreFill(): string;
342
459
  protected price(entry: RenderLegendEntry): string;
343
460
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<KerusiLegendComponent, never>;
344
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<KerusiLegendComponent, "kerusi-legend", never, { "legend": { "alias": "legend"; "required": true; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "colors": { "alias": "colors"; "required": false; "isSignal": true; }; "typeColors": { "alias": "typeColors"; "required": false; "isSignal": true; }; "showPrices": { "alias": "showPrices"; "required": false; "isSignal": true; }; "hideUnusedTypes": { "alias": "hideUnusedTypes"; "required": false; "isSignal": true; }; "headingSeatTypes": { "alias": "headingSeatTypes"; "required": false; "isSignal": true; }; "headingAvailability": { "alias": "headingAvailability"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
461
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<KerusiLegendComponent, "kerusi-legend", never, { "legend": { "alias": "legend"; "required": true; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "colors": { "alias": "colors"; "required": false; "isSignal": true; }; "typeColors": { "alias": "typeColors"; "required": false; "isSignal": true; }; "seatTypeStyles": { "alias": "seatTypeStyles"; "required": false; "isSignal": true; }; "showPrices": { "alias": "showPrices"; "required": false; "isSignal": true; }; "hideUnusedTypes": { "alias": "hideUnusedTypes"; "required": false; "isSignal": true; }; "headingSeatTypes": { "alias": "headingSeatTypes"; "required": false; "isSignal": true; }; "headingAvailability": { "alias": "headingAvailability"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
345
462
  }
346
463
 
347
464
  /**
@@ -390,5 +507,5 @@ declare class KerusiStateStore {
390
507
  startExpiryTicker(intervalMs?: number): () => void;
391
508
  }
392
509
 
393
- export { KerusiLegendComponent, KerusiSeatmapComponent, KerusiSectionComponent, KerusiStateStore };
394
- export type { SeatDisallowed, SeatInteraction, SectionRenderOptions, ValidationMode };
510
+ export { KerusiLegendComponent, KerusiSeatTemplateDirective, KerusiSeatmapComponent, KerusiSectionComponent, KerusiStateStore };
511
+ export type { SeatDisallowed, SeatInteraction, SeatTemplateContext, SeatTemplateOutletContext, SectionRenderOptions, ValidationMode };