@kerusiweb/angular 2.2.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -22
- package/README.md +673 -421
- package/fesm2022/kerusiweb-angular.mjs +187 -201
- package/fesm2022/kerusiweb-angular.mjs.map +1 -1
- package/package.json +3 -3
- package/types/kerusiweb-angular.d.ts +140 -102
|
@@ -1,24 +1,34 @@
|
|
|
1
1
|
import { NgTemplateOutlet } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { input, output, computed, ChangeDetectionStrategy, Component, inject, ElementRef, model, signal, viewChild, effect, untracked } from '@angular/core';
|
|
4
|
-
import { DEFAULT_KERUSI_COLORS,
|
|
3
|
+
import { input, output, computed, ChangeDetectionStrategy, Component, inject, TemplateRef, Directive, ElementRef, model, contentChild, signal, viewChild, effect, untracked } from '@angular/core';
|
|
4
|
+
import { DEFAULT_KERUSI_COLORS, elementTextFill, elementStyle, resolveSeatStyle, resolveSeatGlyph, elementFill, screenPath, cssVar, legendSwatchFill, legendSwatchPath, legendSwatchText, seatBodyPath, seatSelectedFrame, seatOccupantPath, seatOccupantStroke, seatWashFill, occupantFillFor, occupantStrokeFor, seatCoreFill, formatMoney, DEFAULT_SEAT_ARIA_STRINGS, DEFAULT_MAX_ZOOM, FIT_VIEWPORT, IDLE_GESTURE, errorsOf, KerusiValidationError, viewportsEqual, validateDocumentSet, expireHolds, buildRenderModel, resolveMapLocale, isRtlLocale, resolveColors, zoomLimits, resolveControlStrings, controlStyle, viewportTransform, viewportTouchAction, computeMapLayout, buildNavigationGraph, seatAriaLabel, summarizeSelection, toggleSeatSelection, disallowedAnnouncement, fitViewport, viewportForRect, canvasRectToContent, stepViewport, clampViewport, revealRect, reduceGesture, wheelZoomFactor, zoomViewportBy, zoomViewportTo, isTextEntryTarget, keyboardViewport, DEFAULT_SEQUENCE_READER, heldSeats, applyStateDeltaOrdered } from '@kerusiweb/core';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Draws one Kerusi `Section` as a
|
|
7
|
+
* Draws one Kerusi `Section` as a `<g>` on the map's shared canvas (D1).
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* Sections are zones on one canvas in rev 15 (§4.1), and two may sit side by
|
|
10
|
+
* side, so a section is a group inside the map's single `<svg>` rather than an
|
|
11
|
+
* SVG of its own. Use it on a `<g>` inside an `<svg>` whose `viewBox` is the
|
|
12
|
+
* canvas: `<g kerusi-section [layout]="placed"></g>`. Its template prefixes
|
|
13
|
+
* every element with `svg:`, because Angular cannot tell from a component's
|
|
14
|
+
* own template that it will be stamped into SVG.
|
|
14
15
|
*
|
|
15
16
|
* This component holds no state. It takes a placed layout and emits intent.
|
|
16
17
|
*/
|
|
17
18
|
class KerusiSectionComponent {
|
|
18
|
-
section
|
|
19
|
-
...(ngDevMode ? [{ debugName: "section" }] : /* istanbul ignore next */ []));
|
|
19
|
+
/** The section with its seats and elements placed on the map's canvas. */
|
|
20
20
|
layout = input.required(/* @ts-ignore */
|
|
21
21
|
...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
|
|
22
|
+
/** The section's name: its accessible name, and its drawn label when one is shown. */
|
|
23
|
+
label = input(undefined, /* @ts-ignore */
|
|
24
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
25
|
+
/**
|
|
26
|
+
* Draw `label` inside the SVG, at the top-left of the section's bounds. Only
|
|
27
|
+
* a section that declares `bounds` gets one (D5); otherwise the label is the
|
|
28
|
+
* group's accessible name alone.
|
|
29
|
+
*/
|
|
30
|
+
showLabel = input(true, /* @ts-ignore */
|
|
31
|
+
...(ngDevMode ? [{ debugName: "showLabel" }] : /* istanbul ignore next */ []));
|
|
22
32
|
/** Currently selected seat ids. */
|
|
23
33
|
selection = input(new Set(), /* @ts-ignore */
|
|
24
34
|
...(ngDevMode ? [{ debugName: "selection" }] : /* istanbul ignore next */ []));
|
|
@@ -33,137 +43,47 @@ class KerusiSectionComponent {
|
|
|
33
43
|
/** Whether an available seat may take its `SeatType.color` (§4.7). */
|
|
34
44
|
typeColors = input(true, /* @ts-ignore */
|
|
35
45
|
...(ngDevMode ? [{ debugName: "typeColors" }] : /* istanbul ignore next */ []));
|
|
46
|
+
/** Styles for every seat of a type, keyed by `SeatType.id`. */
|
|
47
|
+
seatTypeStyles = input(undefined, /* @ts-ignore */
|
|
48
|
+
...(ngDevMode ? [{ debugName: "seatTypeStyles" }] : /* istanbul ignore next */ []));
|
|
49
|
+
/** A style for one seat, laid over its type's. */
|
|
50
|
+
seatStyle = input(undefined, /* @ts-ignore */
|
|
51
|
+
...(ngDevMode ? [{ debugName: "seatStyle" }] : /* istanbul ignore next */ []));
|
|
52
|
+
/** Draws the inside of each seat in place of the stock glyph. */
|
|
53
|
+
seatTemplate = input(undefined, /* @ts-ignore */
|
|
54
|
+
...(ngDevMode ? [{ debugName: "seatTemplate" }] : /* istanbul ignore next */ []));
|
|
36
55
|
/** When false, seats render but do not respond to pointer or keyboard. */
|
|
37
56
|
interactive = input(true, /* @ts-ignore */
|
|
38
57
|
...(ngDevMode ? [{ debugName: "interactive" }] : /* istanbul ignore next */ []));
|
|
39
|
-
/**
|
|
40
|
-
* CSS pixels per viewBox unit, at the section's natural size.
|
|
41
|
-
*
|
|
42
|
-
* Without a cap every section stretches to the full container width, so in a
|
|
43
|
-
* four-tier theatre the narrow box row would draw seats several times the
|
|
44
|
-
* size of the orchestra's. Capping each section at its own intrinsic width
|
|
45
|
-
* keeps a seat the same size everywhere; a section too wide to fit still
|
|
46
|
-
* scales down to the container.
|
|
47
|
-
*/
|
|
48
|
-
unitScale = input(1, /* @ts-ignore */
|
|
49
|
-
...(ngDevMode ? [{ debugName: "unitScale" }] : /* istanbul ignore next */ []));
|
|
50
58
|
seatActivate = output();
|
|
51
59
|
seatFocused = output();
|
|
52
60
|
seatKeydown = output();
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
...(ngDevMode ? [{ debugName: "
|
|
58
|
-
isSelected(placed) {
|
|
59
|
-
return this.selection().has(placed.seat.id);
|
|
60
|
-
}
|
|
61
|
-
fill(placed) {
|
|
62
|
-
return seatFill(placed.seat, this.isSelected(placed), this.colors(), this.typeColors());
|
|
63
|
-
}
|
|
64
|
-
textFill(placed) {
|
|
65
|
-
return seatTextFill(placed.seat, this.isSelected(placed), this.colors(), this.typeColors());
|
|
66
|
-
}
|
|
67
|
-
backdrop() {
|
|
68
|
-
return cssVar('backdrop', this.colors().backdrop);
|
|
69
|
-
}
|
|
70
|
-
// --- the seat glyph -------------------------------------------------------
|
|
71
|
-
/** The seat's outline: square-ish front, tapered back — shape carries facing. */
|
|
72
|
-
bodyPath(placed) {
|
|
73
|
-
return seatBodyPath(placed.x, placed.y, placed.width, placed.height);
|
|
74
|
-
}
|
|
75
|
-
/** `null` for an available or blocked seat — nothing to mark. */
|
|
76
|
-
occupantVariant(placed) {
|
|
77
|
-
return seatOccupantVariant(placed.seat, this.isSelected(placed));
|
|
78
|
-
}
|
|
61
|
+
/** Whether the label is drawn: asked for, present, and with declared bounds to sit in. */
|
|
62
|
+
drawsLabel = computed(() => this.showLabel() && !!this.label() && this.layout().section.boundsDeclared, /* @ts-ignore */
|
|
63
|
+
...(ngDevMode ? [{ debugName: "drawsLabel" }] : /* istanbul ignore next */ []));
|
|
64
|
+
labelFill = computed(() => elementTextFill(elementStyle('label').tone, this.colors()), /* @ts-ignore */
|
|
65
|
+
...(ngDevMode ? [{ debugName: "labelFill" }] : /* istanbul ignore next */ []));
|
|
79
66
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* Everything over a selected seat sits on the core, so it all has to be
|
|
84
|
-
* measured from the core. Drawing to the seat's box instead leaves the
|
|
85
|
-
* occupant's hips crossing the frame, where a figure tinted `selectedBg`
|
|
86
|
-
* lands on a frame filled `selectedBg` and reads as a hard nub. Since the
|
|
87
|
-
* core is an exact scaled copy of the body (see {@link seatSelectedFrame}),
|
|
88
|
-
* routing through here reproduces every clearance the mark had against the
|
|
89
|
-
* body, scaled — so the frame width stays free to change.
|
|
67
|
+
* Every seat with its glyph resolved, once per change of layout, selection or
|
|
68
|
+
* theme rather than once per binding per change detection.
|
|
90
69
|
*/
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
coreFill() {
|
|
109
|
-
return seatCoreFill(this.colors());
|
|
110
|
-
}
|
|
111
|
-
/** The dimming wash over a held or booked seat's type colour. */
|
|
112
|
-
washFill(placed) {
|
|
113
|
-
// Only called for held/booked seats — see the template's @if.
|
|
114
|
-
return seatWashFill(placed.seat.status, this.colors());
|
|
115
|
-
}
|
|
116
|
-
/** The occupant figure for a marked seat, sized to whatever it sits on. */
|
|
117
|
-
occupantPath(placed) {
|
|
118
|
-
const box = this.markBox(placed);
|
|
119
|
-
return seatOccupantPath(box.x, box.y, box.width, box.height);
|
|
120
|
-
}
|
|
121
|
-
occupantStroke(placed) {
|
|
122
|
-
const box = this.markBox(placed);
|
|
123
|
-
return seatOccupantStroke(box.width, box.height);
|
|
124
|
-
}
|
|
125
|
-
// The wheelchair marker keeps the same relative corner it always had; only
|
|
126
|
-
// the box it is relative to changes, so a selected seat's dot stays whole
|
|
127
|
-
// instead of being bitten off by the frame.
|
|
128
|
-
wheelchairCx(placed) {
|
|
129
|
-
const box = this.markBox(placed);
|
|
130
|
-
return box.x + box.width * 0.82;
|
|
131
|
-
}
|
|
132
|
-
wheelchairCy(placed) {
|
|
133
|
-
const box = this.markBox(placed);
|
|
134
|
-
return box.y + box.height * 0.18;
|
|
135
|
-
}
|
|
136
|
-
wheelchairRadius(placed) {
|
|
137
|
-
return this.markBox(placed).width * 0.13;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* A core-coloured outline behind a selected seat's number, painted under the
|
|
141
|
-
* glyphs via `paint-order`.
|
|
142
|
-
*
|
|
143
|
-
* The label is centred on the occupant's shoulder line, so without this it is
|
|
144
|
-
* `selectedBg` over a `selectedBg` silhouette — the figure, not the plate, is
|
|
145
|
-
* what it actually has to contrast against. The halo also rescues a label too
|
|
146
|
-
* long for the core, which would otherwise run onto the frame and vanish into
|
|
147
|
-
* it.
|
|
148
|
-
*
|
|
149
|
-
* Bound here rather than in the stylesheet on purpose: a CSS
|
|
150
|
-
* `var(--kerusi-selected-fg, …)` rule would carry the library default as its
|
|
151
|
-
* fallback and silently skip the `[colors]` input tier, which only the
|
|
152
|
-
* TypeScript side can resolve.
|
|
153
|
-
*/
|
|
154
|
-
labelHalo(placed) {
|
|
155
|
-
return this.isSelected(placed) ? seatCoreFill(this.colors()) : 'none';
|
|
156
|
-
}
|
|
157
|
-
labelHaloWidth(placed) {
|
|
158
|
-
return this.isSelected(placed) ? placed.fontSize * 0.22 : 0;
|
|
159
|
-
}
|
|
160
|
-
/** Solid for selected/booked; `none` for held, which draws hollow instead. */
|
|
161
|
-
occupantFill(variant) {
|
|
162
|
-
return occupantFillFor(variant, this.colors());
|
|
163
|
-
}
|
|
164
|
-
occupantStrokeColor(variant) {
|
|
165
|
-
return occupantStrokeFor(variant, this.colors());
|
|
166
|
-
}
|
|
70
|
+
seats = computed(() => {
|
|
71
|
+
const selection = this.selection();
|
|
72
|
+
const colors = this.colors();
|
|
73
|
+
const typeColors = this.typeColors();
|
|
74
|
+
const typeStyles = this.seatTypeStyles();
|
|
75
|
+
const seatStyle = this.seatStyle();
|
|
76
|
+
return this.layout().seats.map((placed) => {
|
|
77
|
+
const selected = selection.has(placed.seat.id);
|
|
78
|
+
const style = resolveSeatStyle(placed.seat, selected, typeStyles, seatStyle);
|
|
79
|
+
return {
|
|
80
|
+
placed,
|
|
81
|
+
selected,
|
|
82
|
+
glyph: resolveSeatGlyph(placed, selected, colors, typeColors, style),
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
}, /* @ts-ignore */
|
|
86
|
+
...(ngDevMode ? [{ debugName: "seats" }] : /* istanbul ignore next */ []));
|
|
167
87
|
/** The section's single tab stop; every other seat is reachable by arrow key. */
|
|
168
88
|
tabIndex(placed) {
|
|
169
89
|
if (!this.interactive()) {
|
|
@@ -174,19 +94,6 @@ class KerusiSectionComponent {
|
|
|
174
94
|
ariaLabel(placed) {
|
|
175
95
|
return this.ariaLabels().get(placed.seat.id) ?? placed.seat.label;
|
|
176
96
|
}
|
|
177
|
-
seatClasses(placed) {
|
|
178
|
-
const seat = placed.seat;
|
|
179
|
-
return [
|
|
180
|
-
'kerusi-seat',
|
|
181
|
-
`kerusi-seat--${seat.status}`,
|
|
182
|
-
this.isSelected(placed) ? 'kerusi-seat--selected' : '',
|
|
183
|
-
seat.selectable ? '' : 'kerusi-seat--unselectable',
|
|
184
|
-
seat.accessibility?.wheelchairAccessible ? 'kerusi-seat--wheelchair' : '',
|
|
185
|
-
seat.companions.length > 0 ? 'kerusi-seat--companion' : '',
|
|
186
|
-
]
|
|
187
|
-
.filter(Boolean)
|
|
188
|
-
.join(' ');
|
|
189
|
-
}
|
|
190
97
|
/** SVG rotate() about the item's center; null when it does not rotate. */
|
|
191
98
|
transform(item) {
|
|
192
99
|
return item.rotation ? `rotate(${item.rotation} ${item.centerX} ${item.centerY})` : null;
|
|
@@ -238,12 +145,17 @@ class KerusiSectionComponent {
|
|
|
238
145
|
}
|
|
239
146
|
}
|
|
240
147
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: KerusiSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
241
|
-
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 });
|
|
148
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: KerusiSectionComponent, isStandalone: true, selector: "g[kerusi-section]", inputs: { layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, showLabel: { classPropertyName: "showLabel", publicName: "showLabel", isSignal: true, isRequired: false, 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 }, seatTypeStyles: { classPropertyName: "seatTypeStyles", publicName: "seatTypeStyles", isSignal: true, isRequired: false, transformFunction: null }, seatStyle: { classPropertyName: "seatStyle", publicName: "seatStyle", isSignal: true, isRequired: false, transformFunction: null }, seatTemplate: { classPropertyName: "seatTemplate", publicName: "seatTemplate", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { seatActivate: "seatActivate", seatFocused: "seatFocused", seatKeydown: "seatKeydown" }, host: { attributes: { "role": "group" }, properties: { "attr.aria-label": "label() ?? layout().section.id", "attr.data-section-id": "layout().section.id" }, classAttribute: "kerusi-section" }, ngImport: i0, template: "<!-- Hidden from assistive technology: the group already carries the same text\n as its accessible name. -->\n@if (drawsLabel()) {\n <svg:text\n class=\"kerusi-section-label\"\n aria-hidden=\"true\"\n text-anchor=\"start\"\n dominant-baseline=\"hanging\"\n [attr.x]=\"layout().labelAnchor.x\"\n [attr.y]=\"layout().labelAnchor.y\"\n [attr.font-size]=\"layout().labelAnchor.fontSize\"\n [style.fill]=\"labelFill()\"\n >\n {{ label() }}\n </svg:text>\n}\n\n<!-- Elements first, so a screen or stage sits behind the seats it faces. -->\n@for (placed of layout().elements; track placed.element.id) {\n @if (drawsElement(placed)) {\n <svg:g\n class=\"kerusi-element kerusi-element--{{ placed.element.kind }}\"\n [attr.transform]=\"transform(placed)\"\n >\n @switch (style(placed).shape) {\n @case ('screen') {\n <svg:path [attr.d]=\"screenPathOf(placed)\" [style.fill]=\"elementFillOf(placed)\" />\n }\n @case ('text') {}\n @case ('void') {\n <svg:rect\n class=\"kerusi-element__void\"\n [attr.x]=\"placed.x\"\n [attr.y]=\"placed.y\"\n [attr.width]=\"placed.width\"\n [attr.height]=\"placed.height\"\n [style.stroke]=\"elementTextFillOf(placed)\"\n fill=\"none\"\n />\n }\n @default {\n <svg:rect\n [attr.x]=\"placed.x\"\n [attr.y]=\"placed.y\"\n [attr.width]=\"placed.width\"\n [attr.height]=\"placed.height\"\n [attr.rx]=\"radiusOf(placed)\"\n [style.fill]=\"elementFillOf(placed)\"\n />\n }\n }\n @if (placed.element.label && style(placed).showLabel) {\n <svg:text\n class=\"kerusi-element__label\"\n text-anchor=\"middle\"\n dominant-baseline=\"central\"\n [attr.x]=\"placed.centerX\"\n [attr.y]=\"placed.centerY\"\n [attr.font-size]=\"placed.fontSize\"\n [style.fill]=\"elementTextFillOf(placed)\"\n [attr.transform]=\"labelTransform(placed)\"\n >\n {{ placed.element.label }}\n </svg:text>\n }\n </svg:g>\n }\n}\n\n<!-- The stock seat drawing. A template rather than inline markup so that a\n custom seat template can render it too, as `defaultContent`. Its `svg:`\n prefixes create its elements in the SVG namespace wherever it is stamped. -->\n<ng-template #stockSeat let-seat>\n @let glyph = seat.glyph;\n <!-- Tapered toward the back, square at the front, so which way the seat\n faces is legible from the outline alone. -->\n <svg:path class=\"kerusi-seat__box\" [attr.d]=\"glyph.body.d\" [style.fill]=\"glyph.body.fill\" />\n @if (glyph.wash; as wash) {\n <!-- Colour stays the seat type's; a taken seat recedes under a wash\n instead, so the type colour still reads on a busy map. -->\n <svg:path class=\"kerusi-seat__wash\" [attr.d]=\"wash.d\" [style.fill]=\"wash.fill\" />\n }\n @if (glyph.core; as core) {\n <!-- Selection is the one state drawn from two tones: the body above is\n the frame, this is the bright core inside it. Whichever way the\n host page is themed, one of the two separates from it \u2014 which is\n why nothing here has to know about the page. -->\n <svg:path class=\"kerusi-seat__core\" [attr.d]=\"core.d\" [style.fill]=\"core.fill\" />\n }\n @if (glyph.occupant; as occupant) {\n <!-- Deliberately not counter-rotated: the occupant leans with the\n seat. Solid = settled (yours, or sold); hollow = a hold still in\n progress. -->\n <svg:path\n class=\"kerusi-seat__occupant kerusi-seat__occupant--{{ occupant.variant }}\"\n [attr.d]=\"occupant.d\"\n [attr.stroke-width]=\"occupant.strokeWidth\"\n [style.fill]=\"occupant.fill\"\n [style.stroke]=\"occupant.stroke\"\n />\n }\n <!-- The halo colour and width are bound here rather than in the\n stylesheet: a CSS `var(--kerusi-selected-fg, \u2026)` rule would carry\n the library default as its fallback and skip the [colors] tier. -->\n <svg:text\n class=\"kerusi-seat__label\"\n text-anchor=\"middle\"\n dominant-baseline=\"central\"\n [attr.x]=\"glyph.label.x\"\n [attr.y]=\"glyph.label.y\"\n [attr.font-size]=\"glyph.label.fontSize\"\n [style.fill]=\"glyph.label.fill\"\n [style.stroke]=\"glyph.label.halo\"\n [attr.stroke-width]=\"glyph.label.haloWidth\"\n [attr.transform]=\"glyph.label.transform\"\n >\n {{ glyph.label.text }}\n </svg:text>\n @if (glyph.wheelchair; as wheelchair) {\n <!-- A corner marker, not a different fill: availability owns the fill. -->\n <svg:circle\n class=\"kerusi-seat__wheelchair\"\n [attr.cx]=\"wheelchair.cx\"\n [attr.cy]=\"wheelchair.cy\"\n [attr.r]=\"wheelchair.r\"\n [style.fill]=\"wheelchair.fill\"\n />\n }\n</ng-template>\n\n@for (entry of seats(); track entry.placed.seat.id) {\n @let placed = entry.placed;\n @let glyph = entry.glyph;\n @let ctx =\n {\n placed: placed,\n seat: placed.seat,\n selected: entry.selected,\n glyph: glyph,\n defaultContent: stockSeat,\n };\n <svg:g\n [class]=\"glyph.className\"\n role=\"button\"\n [attr.tabindex]=\"tabIndex(placed)\"\n [attr.aria-label]=\"ariaLabel(placed)\"\n [attr.aria-pressed]=\"entry.selected\"\n [attr.aria-disabled]=\"!placed.seat.selectable\"\n [attr.data-seat-id]=\"placed.seat.id\"\n [attr.data-seat-type]=\"glyph.data.type\"\n [attr.data-seat-status]=\"glyph.data.status\"\n [attr.data-section-id]=\"glyph.data.section\"\n [attr.data-seat-row]=\"glyph.data.row\"\n [attr.data-seat-attributes]=\"glyph.data.attributes\"\n [attr.transform]=\"transform(placed)\"\n (click)=\"onSeatClick(placed)\"\n (focus)=\"onSeatFocus(placed)\"\n (keydown)=\"onKeydown($event)\"\n >\n @if (seatTemplate(); as custom) {\n <ng-container *ngTemplateOutlet=\"custom; context: { $implicit: ctx }\" />\n <!-- Drawn over the custom content, because that content may have no\n .kerusi-seat__box for the stock focus ring to stroke. Only visible\n on :focus-visible. -->\n <svg:path class=\"kerusi-seat__focus\" [attr.d]=\"glyph.body.d\" />\n } @else {\n <ng-container *ngTemplateOutlet=\"stockSeat; context: { $implicit: ctx }\" />\n }\n </svg:g>\n}\n", styles: [".kerusi-section-label{font-family:inherit;font-weight:600;letter-spacing:.04em;text-transform:uppercase;opacity:.75;pointer-events:none;-webkit-user-select:none;user-select:none}.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-seat__focus{fill:none;stroke:none;pointer-events:none}.kerusi-seat:focus-visible .kerusi-seat__focus{stroke:var(--kerusi-focus-ring, var(--kerusi-focus-ring-input, currentColor));stroke-width:2.5}.kerusi-element__void{stroke-width:1;stroke-dasharray:4 3;opacity:.7}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
242
149
|
}
|
|
243
150
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: KerusiSectionComponent, decorators: [{
|
|
244
151
|
type: Component,
|
|
245
|
-
args: [{ selector: 'kerusi-section', changeDetection: ChangeDetectionStrategy.OnPush,
|
|
246
|
-
|
|
152
|
+
args: [{ selector: 'g[kerusi-section]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], host: {
|
|
153
|
+
class: 'kerusi-section',
|
|
154
|
+
role: 'group',
|
|
155
|
+
'[attr.aria-label]': 'label() ?? layout().section.id',
|
|
156
|
+
'[attr.data-section-id]': 'layout().section.id',
|
|
157
|
+
}, template: "<!-- Hidden from assistive technology: the group already carries the same text\n as its accessible name. -->\n@if (drawsLabel()) {\n <svg:text\n class=\"kerusi-section-label\"\n aria-hidden=\"true\"\n text-anchor=\"start\"\n dominant-baseline=\"hanging\"\n [attr.x]=\"layout().labelAnchor.x\"\n [attr.y]=\"layout().labelAnchor.y\"\n [attr.font-size]=\"layout().labelAnchor.fontSize\"\n [style.fill]=\"labelFill()\"\n >\n {{ label() }}\n </svg:text>\n}\n\n<!-- Elements first, so a screen or stage sits behind the seats it faces. -->\n@for (placed of layout().elements; track placed.element.id) {\n @if (drawsElement(placed)) {\n <svg:g\n class=\"kerusi-element kerusi-element--{{ placed.element.kind }}\"\n [attr.transform]=\"transform(placed)\"\n >\n @switch (style(placed).shape) {\n @case ('screen') {\n <svg:path [attr.d]=\"screenPathOf(placed)\" [style.fill]=\"elementFillOf(placed)\" />\n }\n @case ('text') {}\n @case ('void') {\n <svg:rect\n class=\"kerusi-element__void\"\n [attr.x]=\"placed.x\"\n [attr.y]=\"placed.y\"\n [attr.width]=\"placed.width\"\n [attr.height]=\"placed.height\"\n [style.stroke]=\"elementTextFillOf(placed)\"\n fill=\"none\"\n />\n }\n @default {\n <svg:rect\n [attr.x]=\"placed.x\"\n [attr.y]=\"placed.y\"\n [attr.width]=\"placed.width\"\n [attr.height]=\"placed.height\"\n [attr.rx]=\"radiusOf(placed)\"\n [style.fill]=\"elementFillOf(placed)\"\n />\n }\n }\n @if (placed.element.label && style(placed).showLabel) {\n <svg:text\n class=\"kerusi-element__label\"\n text-anchor=\"middle\"\n dominant-baseline=\"central\"\n [attr.x]=\"placed.centerX\"\n [attr.y]=\"placed.centerY\"\n [attr.font-size]=\"placed.fontSize\"\n [style.fill]=\"elementTextFillOf(placed)\"\n [attr.transform]=\"labelTransform(placed)\"\n >\n {{ placed.element.label }}\n </svg:text>\n }\n </svg:g>\n }\n}\n\n<!-- The stock seat drawing. A template rather than inline markup so that a\n custom seat template can render it too, as `defaultContent`. Its `svg:`\n prefixes create its elements in the SVG namespace wherever it is stamped. -->\n<ng-template #stockSeat let-seat>\n @let glyph = seat.glyph;\n <!-- Tapered toward the back, square at the front, so which way the seat\n faces is legible from the outline alone. -->\n <svg:path class=\"kerusi-seat__box\" [attr.d]=\"glyph.body.d\" [style.fill]=\"glyph.body.fill\" />\n @if (glyph.wash; as wash) {\n <!-- Colour stays the seat type's; a taken seat recedes under a wash\n instead, so the type colour still reads on a busy map. -->\n <svg:path class=\"kerusi-seat__wash\" [attr.d]=\"wash.d\" [style.fill]=\"wash.fill\" />\n }\n @if (glyph.core; as core) {\n <!-- Selection is the one state drawn from two tones: the body above is\n the frame, this is the bright core inside it. Whichever way the\n host page is themed, one of the two separates from it \u2014 which is\n why nothing here has to know about the page. -->\n <svg:path class=\"kerusi-seat__core\" [attr.d]=\"core.d\" [style.fill]=\"core.fill\" />\n }\n @if (glyph.occupant; as occupant) {\n <!-- Deliberately not counter-rotated: the occupant leans with the\n seat. Solid = settled (yours, or sold); hollow = a hold still in\n progress. -->\n <svg:path\n class=\"kerusi-seat__occupant kerusi-seat__occupant--{{ occupant.variant }}\"\n [attr.d]=\"occupant.d\"\n [attr.stroke-width]=\"occupant.strokeWidth\"\n [style.fill]=\"occupant.fill\"\n [style.stroke]=\"occupant.stroke\"\n />\n }\n <!-- The halo colour and width are bound here rather than in the\n stylesheet: a CSS `var(--kerusi-selected-fg, \u2026)` rule would carry\n the library default as its fallback and skip the [colors] tier. -->\n <svg:text\n class=\"kerusi-seat__label\"\n text-anchor=\"middle\"\n dominant-baseline=\"central\"\n [attr.x]=\"glyph.label.x\"\n [attr.y]=\"glyph.label.y\"\n [attr.font-size]=\"glyph.label.fontSize\"\n [style.fill]=\"glyph.label.fill\"\n [style.stroke]=\"glyph.label.halo\"\n [attr.stroke-width]=\"glyph.label.haloWidth\"\n [attr.transform]=\"glyph.label.transform\"\n >\n {{ glyph.label.text }}\n </svg:text>\n @if (glyph.wheelchair; as wheelchair) {\n <!-- A corner marker, not a different fill: availability owns the fill. -->\n <svg:circle\n class=\"kerusi-seat__wheelchair\"\n [attr.cx]=\"wheelchair.cx\"\n [attr.cy]=\"wheelchair.cy\"\n [attr.r]=\"wheelchair.r\"\n [style.fill]=\"wheelchair.fill\"\n />\n }\n</ng-template>\n\n@for (entry of seats(); track entry.placed.seat.id) {\n @let placed = entry.placed;\n @let glyph = entry.glyph;\n @let ctx =\n {\n placed: placed,\n seat: placed.seat,\n selected: entry.selected,\n glyph: glyph,\n defaultContent: stockSeat,\n };\n <svg:g\n [class]=\"glyph.className\"\n role=\"button\"\n [attr.tabindex]=\"tabIndex(placed)\"\n [attr.aria-label]=\"ariaLabel(placed)\"\n [attr.aria-pressed]=\"entry.selected\"\n [attr.aria-disabled]=\"!placed.seat.selectable\"\n [attr.data-seat-id]=\"placed.seat.id\"\n [attr.data-seat-type]=\"glyph.data.type\"\n [attr.data-seat-status]=\"glyph.data.status\"\n [attr.data-section-id]=\"glyph.data.section\"\n [attr.data-seat-row]=\"glyph.data.row\"\n [attr.data-seat-attributes]=\"glyph.data.attributes\"\n [attr.transform]=\"transform(placed)\"\n (click)=\"onSeatClick(placed)\"\n (focus)=\"onSeatFocus(placed)\"\n (keydown)=\"onKeydown($event)\"\n >\n @if (seatTemplate(); as custom) {\n <ng-container *ngTemplateOutlet=\"custom; context: { $implicit: ctx }\" />\n <!-- Drawn over the custom content, because that content may have no\n .kerusi-seat__box for the stock focus ring to stroke. Only visible\n on :focus-visible. -->\n <svg:path class=\"kerusi-seat__focus\" [attr.d]=\"glyph.body.d\" />\n } @else {\n <ng-container *ngTemplateOutlet=\"stockSeat; context: { $implicit: ctx }\" />\n }\n </svg:g>\n}\n", styles: [".kerusi-section-label{font-family:inherit;font-weight:600;letter-spacing:.04em;text-transform:uppercase;opacity:.75;pointer-events:none;-webkit-user-select:none;user-select:none}.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-seat__focus{fill:none;stroke:none;pointer-events:none}.kerusi-seat:focus-visible .kerusi-seat__focus{stroke:var(--kerusi-focus-ring, var(--kerusi-focus-ring-input, currentColor));stroke-width:2.5}.kerusi-element__void{stroke-width:1;stroke-dasharray:4 3;opacity:.7}\n"] }]
|
|
158
|
+
}], propDecorators: { layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], 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 }] }], seatTypeStyles: [{ type: i0.Input, args: [{ isSignal: true, alias: "seatTypeStyles", required: false }] }], seatStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "seatStyle", required: false }] }], seatTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "seatTemplate", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], seatActivate: [{ type: i0.Output, args: ["seatActivate"] }], seatFocused: [{ type: i0.Output, args: ["seatFocused"] }], seatKeydown: [{ type: i0.Output, args: ["seatKeydown"] }] } });
|
|
247
159
|
|
|
248
160
|
/**
|
|
249
161
|
* A key for a seat map: its seat types on one side, its availability states on
|
|
@@ -269,6 +181,9 @@ class KerusiLegendComponent {
|
|
|
269
181
|
/** Whether an available seat takes its type color — mirrors the seatmap input. */
|
|
270
182
|
typeColors = input(true, /* @ts-ignore */
|
|
271
183
|
...(ngDevMode ? [{ debugName: "typeColors" }] : /* istanbul ignore next */ []));
|
|
184
|
+
/** Mirrors the seatmap input: a type's style is what its swatch shows. */
|
|
185
|
+
seatTypeStyles = input(undefined, /* @ts-ignore */
|
|
186
|
+
...(ngDevMode ? [{ debugName: "seatTypeStyles" }] : /* istanbul ignore next */ []));
|
|
272
187
|
showPrices = input(true, /* @ts-ignore */
|
|
273
188
|
...(ngDevMode ? [{ debugName: "showPrices" }] : /* istanbul ignore next */ []));
|
|
274
189
|
/** Hide types no seat in the rendered map actually uses. */
|
|
@@ -302,13 +217,17 @@ class KerusiLegendComponent {
|
|
|
302
217
|
];
|
|
303
218
|
}, /* @ts-ignore */
|
|
304
219
|
...(ngDevMode ? [{ debugName: "statuses" }] : /* istanbul ignore next */ []));
|
|
220
|
+
// Same token and fallback as an available seat of the type, so a rule that
|
|
221
|
+
// sets `--kerusi-seat-fill` on the seats and the legend items keeps them in step.
|
|
305
222
|
swatch(entry) {
|
|
306
|
-
return (this.
|
|
223
|
+
return legendSwatchFill(entry, this.colors(), this.typeColors(), this.seatTypeStyles()?.[entry.id]);
|
|
224
|
+
}
|
|
225
|
+
/** The swatch outline for a type styled with its own shape; `undefined` keeps the square. */
|
|
226
|
+
typeShape(entry) {
|
|
227
|
+
return legendSwatchPath(this.seatTypeStyles()?.[entry.id], this.glyphSize);
|
|
307
228
|
}
|
|
308
229
|
swatchText(entry) {
|
|
309
|
-
|
|
310
|
-
// Mirrors `seatTextFill`: only a document-supplied color needs guessing at.
|
|
311
|
-
return typeColor ? readableOn(typeColor) : cssVar('availableFg', this.colors().availableFg);
|
|
230
|
+
return legendSwatchText(entry, this.colors(), this.typeColors(), this.seatTypeStyles()?.[entry.id]);
|
|
312
231
|
}
|
|
313
232
|
// --- status swatches -------------------------------------------------------
|
|
314
233
|
//
|
|
@@ -356,19 +275,53 @@ class KerusiLegendComponent {
|
|
|
356
275
|
return entry.price ? formatMoney(entry.price, this.locale()) : '';
|
|
357
276
|
}
|
|
358
277
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: KerusiLegendComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
359
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: KerusiLegendComponent, isStandalone: true, selector: "kerusi-legend", inputs: { legend: { classPropertyName: "legend", publicName: "legend", isSignal: true, isRequired: true, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", 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 }, showPrices: { classPropertyName: "showPrices", publicName: "showPrices", isSignal: true, isRequired: false, transformFunction: null }, hideUnusedTypes: { classPropertyName: "hideUnusedTypes", publicName: "hideUnusedTypes", isSignal: true, isRequired: false, transformFunction: null }, headingSeatTypes: { classPropertyName: "headingSeatTypes", publicName: "headingSeatTypes", isSignal: true, isRequired: false, transformFunction: null }, headingAvailability: { classPropertyName: "headingAvailability", publicName: "headingAvailability", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"kerusi-legend\">\
|
|
278
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: KerusiLegendComponent, isStandalone: true, selector: "kerusi-legend", inputs: { legend: { classPropertyName: "legend", publicName: "legend", isSignal: true, isRequired: true, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", 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 }, seatTypeStyles: { classPropertyName: "seatTypeStyles", publicName: "seatTypeStyles", isSignal: true, isRequired: false, transformFunction: null }, showPrices: { classPropertyName: "showPrices", publicName: "showPrices", isSignal: true, isRequired: false, transformFunction: null }, hideUnusedTypes: { classPropertyName: "hideUnusedTypes", publicName: "hideUnusedTypes", isSignal: true, isRequired: false, transformFunction: null }, headingSeatTypes: { classPropertyName: "headingSeatTypes", publicName: "headingSeatTypes", isSignal: true, isRequired: false, transformFunction: null }, headingAvailability: { classPropertyName: "headingAvailability", publicName: "headingAvailability", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"kerusi-legend\">\n @if (types().length) {\n <div class=\"kerusi-legend__block\">\n <h4 class=\"kerusi-legend__heading\">{{ headingSeatTypes() }}</h4>\n <ul class=\"kerusi-legend__list\">\n @for (entry of types(); track entry.id) {\n <li class=\"kerusi-legend__item\" [attr.data-seat-type]=\"entry.id\">\n @if (typeShape(entry); as d) {\n <!-- A type styled with its own shape keys that shape, not a square. -->\n <svg\n class=\"kerusi-legend__swatch kerusi-legend__swatch--glyph\"\n aria-hidden=\"true\"\n focusable=\"false\"\n [attr.viewBox]=\"'0 0 ' + glyphSize + ' ' + glyphSize\"\n >\n <path [attr.d]=\"d\" [style.fill]=\"swatch(entry)\" />\n </svg>\n } @else {\n <span\n class=\"kerusi-legend__swatch\"\n aria-hidden=\"true\"\n [style.background]=\"swatch(entry)\"\n [style.color]=\"swatchText(entry)\"\n ></span>\n }\n <span class=\"kerusi-legend__label\">{{ entry.label }}</span>\n @if (showPrices() && price(entry)) {\n <span class=\"kerusi-legend__price\">{{ price(entry) }}</span>\n }\n </li>\n }\n </ul>\n </div>\n }\n\n <div class=\"kerusi-legend__block\">\n <h4 class=\"kerusi-legend__heading\">{{ headingAvailability() }}</h4>\n <ul class=\"kerusi-legend__list\">\n @for (status of statuses(); track status.key) {\n <li class=\"kerusi-legend__item\">\n <!-- Shape carries status now, so every swatch draws the seat glyph\n rather than a flat colour \u2014 a flat swatch would teach a cue the\n map itself no longer uses. -->\n <svg\n class=\"kerusi-legend__swatch kerusi-legend__swatch--glyph\"\n aria-hidden=\"true\"\n focusable=\"false\"\n [attr.viewBox]=\"'0 0 ' + glyphSize + ' ' + glyphSize\"\n >\n <path [attr.d]=\"glyphBody\" [style.fill]=\"status.fill\" />\n @if (hasWash(status.kind)) {\n <path [attr.d]=\"glyphBody\" [style.fill]=\"washFillOf(status.kind)\" opacity=\"0.5\" />\n }\n @if (status.kind === 'selected') {\n <!-- Two tones, same as the map: the body above is the frame, this\n is the core inside it. -->\n <path [attr.d]=\"glyphCore\" [style.fill]=\"coreFill()\" />\n }\n @if (hasOccupant(status.kind)) {\n <path\n [attr.d]=\"occupantPathOf(status.kind)\"\n class=\"kerusi-legend__occupant kerusi-legend__occupant--{{ status.kind }}\"\n [attr.stroke-width]=\"occupantStrokeWidthOf(status.kind)\"\n [style.fill]=\"occupantFillOf(status.kind)\"\n [style.stroke]=\"occupantStrokeOf(status.kind)\"\n />\n }\n </svg>\n <span class=\"kerusi-legend__label\">{{ status.label }}</span>\n </li>\n }\n </ul>\n </div>\n</div>\n", styles: [":host{display:block}.kerusi-legend{display:flex;flex-wrap:wrap;gap:1.5rem;font-size:.85rem}.kerusi-legend__block{display:flex;flex-direction:column;gap:.4rem;min-width:0}.kerusi-legend__heading{margin:0;font-size:.72rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;opacity:.65}.kerusi-legend__list{display:flex;flex-wrap:wrap;gap:.35rem .9rem;margin:0;padding:0;list-style:none}.kerusi-legend__item{display:inline-flex;align-items:center;gap:.4rem}.kerusi-legend__swatch{display:inline-block;flex:none;width:.85rem;height:.85rem;border-radius:3px}.kerusi-legend__swatch--glyph{border-radius:0;overflow:visible}.kerusi-legend__occupant--selected{opacity:.2}.kerusi-legend__occupant--booked{opacity:.55}.kerusi-legend__occupant--held{opacity:.75;stroke-linecap:round;stroke-linejoin:round}.kerusi-legend__price{opacity:.7;font-variant-numeric:tabular-nums}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
360
279
|
}
|
|
361
280
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: KerusiLegendComponent, decorators: [{
|
|
362
281
|
type: Component,
|
|
363
|
-
args: [{ selector: 'kerusi-legend', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"kerusi-legend\">\
|
|
364
|
-
}], propDecorators: { legend: [{ type: i0.Input, args: [{ isSignal: true, alias: "legend", required: true }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], colors: [{ type: i0.Input, args: [{ isSignal: true, alias: "colors", required: false }] }], typeColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeColors", required: false }] }], showPrices: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPrices", required: false }] }], hideUnusedTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideUnusedTypes", required: false }] }], headingSeatTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "headingSeatTypes", required: false }] }], headingAvailability: [{ type: i0.Input, args: [{ isSignal: true, alias: "headingAvailability", required: false }] }] } });
|
|
282
|
+
args: [{ selector: 'kerusi-legend', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"kerusi-legend\">\n @if (types().length) {\n <div class=\"kerusi-legend__block\">\n <h4 class=\"kerusi-legend__heading\">{{ headingSeatTypes() }}</h4>\n <ul class=\"kerusi-legend__list\">\n @for (entry of types(); track entry.id) {\n <li class=\"kerusi-legend__item\" [attr.data-seat-type]=\"entry.id\">\n @if (typeShape(entry); as d) {\n <!-- A type styled with its own shape keys that shape, not a square. -->\n <svg\n class=\"kerusi-legend__swatch kerusi-legend__swatch--glyph\"\n aria-hidden=\"true\"\n focusable=\"false\"\n [attr.viewBox]=\"'0 0 ' + glyphSize + ' ' + glyphSize\"\n >\n <path [attr.d]=\"d\" [style.fill]=\"swatch(entry)\" />\n </svg>\n } @else {\n <span\n class=\"kerusi-legend__swatch\"\n aria-hidden=\"true\"\n [style.background]=\"swatch(entry)\"\n [style.color]=\"swatchText(entry)\"\n ></span>\n }\n <span class=\"kerusi-legend__label\">{{ entry.label }}</span>\n @if (showPrices() && price(entry)) {\n <span class=\"kerusi-legend__price\">{{ price(entry) }}</span>\n }\n </li>\n }\n </ul>\n </div>\n }\n\n <div class=\"kerusi-legend__block\">\n <h4 class=\"kerusi-legend__heading\">{{ headingAvailability() }}</h4>\n <ul class=\"kerusi-legend__list\">\n @for (status of statuses(); track status.key) {\n <li class=\"kerusi-legend__item\">\n <!-- Shape carries status now, so every swatch draws the seat glyph\n rather than a flat colour \u2014 a flat swatch would teach a cue the\n map itself no longer uses. -->\n <svg\n class=\"kerusi-legend__swatch kerusi-legend__swatch--glyph\"\n aria-hidden=\"true\"\n focusable=\"false\"\n [attr.viewBox]=\"'0 0 ' + glyphSize + ' ' + glyphSize\"\n >\n <path [attr.d]=\"glyphBody\" [style.fill]=\"status.fill\" />\n @if (hasWash(status.kind)) {\n <path [attr.d]=\"glyphBody\" [style.fill]=\"washFillOf(status.kind)\" opacity=\"0.5\" />\n }\n @if (status.kind === 'selected') {\n <!-- Two tones, same as the map: the body above is the frame, this\n is the core inside it. -->\n <path [attr.d]=\"glyphCore\" [style.fill]=\"coreFill()\" />\n }\n @if (hasOccupant(status.kind)) {\n <path\n [attr.d]=\"occupantPathOf(status.kind)\"\n class=\"kerusi-legend__occupant kerusi-legend__occupant--{{ status.kind }}\"\n [attr.stroke-width]=\"occupantStrokeWidthOf(status.kind)\"\n [style.fill]=\"occupantFillOf(status.kind)\"\n [style.stroke]=\"occupantStrokeOf(status.kind)\"\n />\n }\n </svg>\n <span class=\"kerusi-legend__label\">{{ status.label }}</span>\n </li>\n }\n </ul>\n </div>\n</div>\n", styles: [":host{display:block}.kerusi-legend{display:flex;flex-wrap:wrap;gap:1.5rem;font-size:.85rem}.kerusi-legend__block{display:flex;flex-direction:column;gap:.4rem;min-width:0}.kerusi-legend__heading{margin:0;font-size:.72rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;opacity:.65}.kerusi-legend__list{display:flex;flex-wrap:wrap;gap:.35rem .9rem;margin:0;padding:0;list-style:none}.kerusi-legend__item{display:inline-flex;align-items:center;gap:.4rem}.kerusi-legend__swatch{display:inline-block;flex:none;width:.85rem;height:.85rem;border-radius:3px}.kerusi-legend__swatch--glyph{border-radius:0;overflow:visible}.kerusi-legend__occupant--selected{opacity:.2}.kerusi-legend__occupant--booked{opacity:.55}.kerusi-legend__occupant--held{opacity:.75;stroke-linecap:round;stroke-linejoin:round}.kerusi-legend__price{opacity:.7;font-variant-numeric:tabular-nums}\n"] }]
|
|
283
|
+
}], propDecorators: { legend: [{ type: i0.Input, args: [{ isSignal: true, alias: "legend", required: true }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], colors: [{ type: i0.Input, args: [{ isSignal: true, alias: "colors", required: false }] }], typeColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeColors", required: false }] }], seatTypeStyles: [{ type: i0.Input, args: [{ isSignal: true, alias: "seatTypeStyles", required: false }] }], showPrices: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPrices", required: false }] }], hideUnusedTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideUnusedTypes", required: false }] }], headingSeatTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "headingSeatTypes", required: false }] }], headingAvailability: [{ type: i0.Input, args: [{ isSignal: true, alias: "headingAvailability", required: false }] }] } });
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Marks an `<ng-template>` inside `<kerusi-seatmap>` as the drawing for every
|
|
287
|
+
* seat. The library keeps each seat's `<g>`, with its role, ARIA state, tab
|
|
288
|
+
* stop, rotation and event handlers, and stamps this template inside it.
|
|
289
|
+
*
|
|
290
|
+
* The template is declared outside any `<svg>`, so Angular cannot tell its
|
|
291
|
+
* elements are SVG: prefix each one with `svg:` (`<svg:path>`, `<svg:image>`),
|
|
292
|
+
* or it is created as an unknown HTML element and draws nothing.
|
|
293
|
+
*
|
|
294
|
+
* ```html
|
|
295
|
+
* <kerusi-seatmap [map]="map">
|
|
296
|
+
* <ng-template kerusiSeat let-seat>
|
|
297
|
+
* <ng-container *ngTemplateOutlet="seat.defaultContent; context: { $implicit: seat }" />
|
|
298
|
+
* <svg:circle [attr.cx]="seat.glyph.box.x" [attr.cy]="seat.glyph.box.y" r="3" />
|
|
299
|
+
* </ng-template>
|
|
300
|
+
* </kerusi-seatmap>
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
303
|
+
class KerusiSeatTemplateDirective {
|
|
304
|
+
template = inject(TemplateRef);
|
|
305
|
+
/** Types `let-seat` as a {@link SeatTemplateContext}. */
|
|
306
|
+
static ngTemplateContextGuard(_dir, ctx) {
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: KerusiSeatTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
310
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.2", type: KerusiSeatTemplateDirective, isStandalone: true, selector: "ng-template[kerusiSeat]", ngImport: i0 });
|
|
311
|
+
}
|
|
312
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: KerusiSeatTemplateDirective, decorators: [{
|
|
313
|
+
type: Directive,
|
|
314
|
+
args: [{ selector: 'ng-template[kerusiSeat]' }]
|
|
315
|
+
}] });
|
|
365
316
|
|
|
366
317
|
/**
|
|
367
318
|
* Renders a Kerusi seat map.
|
|
368
319
|
*
|
|
369
320
|
* Takes a `KerusiMap` and its `KerusiState` directly — no adapter, no
|
|
370
|
-
* intermediate row model.
|
|
371
|
-
*
|
|
321
|
+
* intermediate row model. The map is one `<svg>` whose `viewBox` is the
|
|
322
|
+
* document's canvas (§4.5), each `Section` is a group on it, every seat is
|
|
323
|
+
* drawn exactly where and as large as the document says, seats take their
|
|
324
|
+
* color from the map's legend,
|
|
372
325
|
* every seat is keyboard-reachable and announced with its type, price, status
|
|
373
326
|
* and §4.3.4 accessibility properties, and selection is an immutable set of
|
|
374
327
|
* seat ids the caller can two-way bind.
|
|
@@ -412,21 +365,38 @@ class KerusiSeatmapComponent {
|
|
|
412
365
|
/** Let an available seat take its `SeatType.color` (§4.7). */
|
|
413
366
|
typeColors = input(true, /* @ts-ignore */
|
|
414
367
|
...(ngDevMode ? [{ debugName: "typeColors" }] : /* istanbul ignore next */ []));
|
|
415
|
-
/** Grid cell edge, in viewBox units. */
|
|
416
|
-
seatSize = input(28, /* @ts-ignore */
|
|
417
|
-
...(ngDevMode ? [{ debugName: "seatSize" }] : /* istanbul ignore next */ []));
|
|
418
|
-
/** Gap between grid cells, in viewBox units. */
|
|
419
|
-
seatGap = input(6, /* @ts-ignore */
|
|
420
|
-
...(ngDevMode ? [{ debugName: "seatGap" }] : /* istanbul ignore next */ []));
|
|
421
|
-
/** Freeform viewBox width; height follows the section's aspect ratio. */
|
|
422
|
-
freeformBasis = input(1000, /* @ts-ignore */
|
|
423
|
-
...(ngDevMode ? [{ debugName: "freeformBasis" }] : /* istanbul ignore next */ []));
|
|
424
368
|
/**
|
|
425
|
-
*
|
|
426
|
-
*
|
|
369
|
+
* Styles for every seat of a type, keyed by `SeatType.id`: fill, label colour
|
|
370
|
+
* and classes. Never changes a selected or blocked seat's fill. Also shown in
|
|
371
|
+
* the legend.
|
|
372
|
+
*/
|
|
373
|
+
seatTypeStyles = input(undefined, /* @ts-ignore */
|
|
374
|
+
...(ngDevMode ? [{ debugName: "seatTypeStyles" }] : /* istanbul ignore next */ []));
|
|
375
|
+
/**
|
|
376
|
+
* A style for one seat, laid over its `seatTypeStyles` entry; return nothing
|
|
377
|
+
* to leave a seat be. Never changes a selected or blocked seat's fill.
|
|
378
|
+
*/
|
|
379
|
+
seatStyle = input(undefined, /* @ts-ignore */
|
|
380
|
+
...(ngDevMode ? [{ debugName: "seatStyle" }] : /* istanbul ignore next */ []));
|
|
381
|
+
/**
|
|
382
|
+
* A `<ng-template kerusiSeat let-seat>` placed inside the map draws the inside
|
|
383
|
+
* of every seat in place of the stock glyph. See {@link KerusiSeatTemplateDirective}.
|
|
384
|
+
*/
|
|
385
|
+
seatTemplate = contentChild(KerusiSeatTemplateDirective, /* @ts-ignore */
|
|
386
|
+
...(ngDevMode ? [{ debugName: "seatTemplate" }] : /* istanbul ignore next */ []));
|
|
387
|
+
/**
|
|
388
|
+
* CSS pixels per canvas unit, at most: the map never draws wider than
|
|
389
|
+
* `canvas.width × unitScale`, and scales down to a narrower container. The
|
|
390
|
+
* document fixes every seat's size and position (§4.5); this is the only
|
|
391
|
+
* sizing control. Default `1`.
|
|
427
392
|
*/
|
|
428
393
|
unitScale = input(1, /* @ts-ignore */
|
|
429
394
|
...(ngDevMode ? [{ debugName: "unitScale" }] : /* istanbul ignore next */ []));
|
|
395
|
+
/**
|
|
396
|
+
* Draws each section's label inside the map, at the top-left of its `bounds`.
|
|
397
|
+
* Only sections that declare `bounds` get one; every section's label is its
|
|
398
|
+
* group's accessible name either way. Default `true`.
|
|
399
|
+
*/
|
|
430
400
|
showSectionLabels = input(true, /* @ts-ignore */
|
|
431
401
|
...(ngDevMode ? [{ debugName: "showSectionLabels" }] : /* istanbul ignore next */ []));
|
|
432
402
|
showLegend = input(false, /* @ts-ignore */
|
|
@@ -708,24 +678,19 @@ class KerusiSeatmapComponent {
|
|
|
708
678
|
...(ngDevMode ? [{ debugName: "atMaxZoom" }] : /* istanbul ignore next */ []));
|
|
709
679
|
atFit = computed(() => this.viewportState().zoom <= 1 + 1e-4, /* @ts-ignore */
|
|
710
680
|
...(ngDevMode ? [{ debugName: "atFit" }] : /* istanbul ignore next */ []));
|
|
711
|
-
/**
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
}),
|
|
725
|
-
];
|
|
726
|
-
}));
|
|
727
|
-
}, /* @ts-ignore */
|
|
728
|
-
...(ngDevMode ? [{ debugName: "layouts" }] : /* istanbul ignore next */ []));
|
|
681
|
+
/** Every seat and element placed on the canvas, mirrored as a whole in RTL (D3). */
|
|
682
|
+
layout = computed(() => computeMapLayout(this.renderMap(), { rtl: this.isRtl() }), /* @ts-ignore */
|
|
683
|
+
...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
|
|
684
|
+
viewBox = computed(() => `0 0 ${this.layout().width} ${this.layout().height}`, /* @ts-ignore */
|
|
685
|
+
...(ngDevMode ? [{ debugName: "viewBox" }] : /* istanbul ignore next */ []));
|
|
686
|
+
/** `unitScale` CSS pixels per canvas unit, at most. */
|
|
687
|
+
naturalWidth = computed(() => this.layout().width * this.unitScale(), /* @ts-ignore */
|
|
688
|
+
...(ngDevMode ? [{ debugName: "naturalWidth" }] : /* istanbul ignore next */ []));
|
|
689
|
+
/** The canvas's width over its height, so a maximized map can fit the whole canvas. */
|
|
690
|
+
canvasAspect = computed(() => String(this.layout().width / this.layout().height), /* @ts-ignore */
|
|
691
|
+
...(ngDevMode ? [{ debugName: "canvasAspect" }] : /* istanbul ignore next */ []));
|
|
692
|
+
backdrop = computed(() => cssVar('backdrop', this.resolvedColors().backdrop), /* @ts-ignore */
|
|
693
|
+
...(ngDevMode ? [{ debugName: "backdrop" }] : /* istanbul ignore next */ []));
|
|
729
694
|
/** Navigation graphs per section — arrows move within, Tab moves between. */
|
|
730
695
|
graphs = computed(() => new Map(this.renderMap().sections.map((section) => [
|
|
731
696
|
section.id,
|
|
@@ -750,15 +715,9 @@ class KerusiSeatmapComponent {
|
|
|
750
715
|
summary = computed(() => summarizeSelection(this.renderMap(), this.selection()), /* @ts-ignore */
|
|
751
716
|
...(ngDevMode ? [{ debugName: "summary" }] : /* istanbul ignore next */ []));
|
|
752
717
|
// --- template helpers -----------------------------------------------------
|
|
753
|
-
layoutFor(section) {
|
|
754
|
-
return this.layouts().get(section.id);
|
|
755
|
-
}
|
|
756
718
|
labelFor(section) {
|
|
757
719
|
return this.sectionOverrides()[section.id]?.label ?? section.label;
|
|
758
720
|
}
|
|
759
|
-
labelId(section) {
|
|
760
|
-
return `kerusi-section-${this.renderMap().id}-${section.id}`;
|
|
761
|
-
}
|
|
762
721
|
/** The seat holding this section's tab stop. */
|
|
763
722
|
focusedSeatIn(section) {
|
|
764
723
|
return this.focusedSeatIds()[section.id] ?? null;
|
|
@@ -801,9 +760,9 @@ class KerusiSeatmapComponent {
|
|
|
801
760
|
}
|
|
802
761
|
}
|
|
803
762
|
/**
|
|
804
|
-
* Arrow keys walk the navigation graph, which
|
|
805
|
-
*
|
|
806
|
-
*
|
|
763
|
+
* Arrow keys walk the navigation graph, which follows §4.3.5 adjacency:
|
|
764
|
+
* along a row by `col`, across an aisle (a skipped `col`) rather than into
|
|
765
|
+
* it, and between rows to the seat nearest on the canvas (D6).
|
|
807
766
|
*/
|
|
808
767
|
onKeydown(section, event) {
|
|
809
768
|
const seatId = event.target?.dataset?.['seatId'];
|
|
@@ -906,13 +865,40 @@ class KerusiSeatmapComponent {
|
|
|
906
865
|
zoomOut() {
|
|
907
866
|
this.stepZoom(-1);
|
|
908
867
|
}
|
|
909
|
-
/** Back to fit-to-container: the
|
|
868
|
+
/** Back to fit-to-container: the whole canvas. No-op unless `zoomable`. */
|
|
910
869
|
resetZoom() {
|
|
911
870
|
const measured = this.measure();
|
|
912
871
|
if (measured && this.zoomable()) {
|
|
913
872
|
this.commit(fitViewport(measured.content, measured.container, this.limits()), true);
|
|
914
873
|
}
|
|
915
874
|
}
|
|
875
|
+
/**
|
|
876
|
+
* Zooms to one section: its declared `bounds`, or the box around its seats
|
|
877
|
+
* and elements when it declares none. No-op unless `zoomable`, or for a
|
|
878
|
+
* section that is not rendered.
|
|
879
|
+
*/
|
|
880
|
+
zoomToSection(sectionId) {
|
|
881
|
+
const layout = this.layout();
|
|
882
|
+
const placed = layout.sections.find((s) => s.section.id === sectionId);
|
|
883
|
+
if (placed) {
|
|
884
|
+
this.zoomToCanvasRect(placed.bounds, layout);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
zoomToCanvasRect(rect, canvas) {
|
|
888
|
+
const measured = this.measure();
|
|
889
|
+
if (!measured || !this.zoomable() || !(canvas.width > 0 && canvas.height > 0)) {
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
// The map's <svg> is the content's only child and sets its height; its
|
|
893
|
+
// width can stop short of the content's (`unitScale` caps it), so the
|
|
894
|
+
// drawn width comes from the height and the canvas's proportions.
|
|
895
|
+
const drawn = {
|
|
896
|
+
width: (measured.content.height * canvas.width) / canvas.height,
|
|
897
|
+
height: measured.content.height,
|
|
898
|
+
};
|
|
899
|
+
const next = viewportForRect(canvasRectToContent(rect, canvas, drawn), measured.content, measured.container, undefined, this.limits());
|
|
900
|
+
this.commit(next, true);
|
|
901
|
+
}
|
|
916
902
|
stepZoom(direction) {
|
|
917
903
|
const measured = this.measure();
|
|
918
904
|
if (measured && this.zoomable()) {
|
|
@@ -1142,12 +1128,12 @@ class KerusiSeatmapComponent {
|
|
|
1142
1128
|
return (this.map().sections ?? []).map((s) => s.id).filter((id) => !hidden.includes(id));
|
|
1143
1129
|
}
|
|
1144
1130
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: KerusiSeatmapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1145
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: KerusiSeatmapComponent, isStandalone: true, selector: "kerusi-seatmap", inputs: { map: { classPropertyName: "map", publicName: "map", isSignal: true, isRequired: true, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, session: { classPropertyName: "session", publicName: "session", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, rtl: { classPropertyName: "rtl", publicName: "rtl", isSignal: true, isRequired: false, transformFunction: null }, ariaStrings: { classPropertyName: "ariaStrings", publicName: "ariaStrings", 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 }, seatSize: { classPropertyName: "seatSize", publicName: "seatSize", isSignal: true, isRequired: false, transformFunction: null }, seatGap: { classPropertyName: "seatGap", publicName: "seatGap", isSignal: true, isRequired: false, transformFunction: null }, freeformBasis: { classPropertyName: "freeformBasis", publicName: "freeformBasis", isSignal: true, isRequired: false, transformFunction: null }, unitScale: { classPropertyName: "unitScale", publicName: "unitScale", isSignal: true, isRequired: false, transformFunction: null }, showSectionLabels: { classPropertyName: "showSectionLabels", publicName: "showSectionLabels", isSignal: true, isRequired: false, transformFunction: null }, showLegend: { classPropertyName: "showLegend", publicName: "showLegend", isSignal: true, isRequired: false, transformFunction: null }, showLegendPrices: { classPropertyName: "showLegendPrices", publicName: "showLegendPrices", isSignal: true, isRequired: false, transformFunction: null }, sectionIds: { classPropertyName: "sectionIds", publicName: "sectionIds", isSignal: true, isRequired: false, transformFunction: null }, sectionOverrides: { classPropertyName: "sectionOverrides", publicName: "sectionOverrides", isSignal: true, isRequired: false, transformFunction: null }, selectableStatuses: { classPropertyName: "selectableStatuses", publicName: "selectableStatuses", isSignal: true, isRequired: false, transformFunction: null }, companionMode: { classPropertyName: "companionMode", publicName: "companionMode", isSignal: true, isRequired: false, transformFunction: null }, maxSelection: { classPropertyName: "maxSelection", publicName: "maxSelection", isSignal: true, isRequired: false, transformFunction: null }, seatSelectable: { classPropertyName: "seatSelectable", publicName: "seatSelectable", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, expireHolds: { classPropertyName: "expireHolds", publicName: "expireHolds", isSignal: true, isRequired: false, transformFunction: null }, expiryIntervalMs: { classPropertyName: "expiryIntervalMs", publicName: "expiryIntervalMs", isSignal: true, isRequired: false, transformFunction: null }, zoomable: { classPropertyName: "zoomable", publicName: "zoomable", isSignal: true, isRequired: false, transformFunction: null }, maxZoom: { classPropertyName: "maxZoom", publicName: "maxZoom", isSignal: true, isRequired: false, transformFunction: null }, showZoomControls: { classPropertyName: "showZoomControls", publicName: "showZoomControls", isSignal: true, isRequired: false, transformFunction: null }, maximizable: { classPropertyName: "maximizable", publicName: "maximizable", isSignal: true, isRequired: false, transformFunction: null }, controlStrings: { classPropertyName: "controlStrings", publicName: "controlStrings", isSignal: true, isRequired: false, transformFunction: null }, validate: { classPropertyName: "validate", publicName: "validate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selection: "selectionChange", seatSelect: "seatSelect", seatDeselect: "seatDeselect", seatDisallowed: "seatDisallowed", seatFocus: "seatFocus", validationIssues: "validationIssues", viewportChange: "viewportChange", maximizedChange: "maximizedChange" }, viewQueries: [{ propertyName: "viewportEl", first: true, predicate: ["viewport"], descendants: true, isSignal: true }, { propertyName: "contentEl", first: true, predicate: ["content"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\r\n class=\"kerusi-seatmap\"\r\n [class.kerusi-seatmap--maximized]=\"maximized()\"\r\n role=\"group\"\r\n [attr.aria-label]=\"mapLabel()\"\r\n [attr.dir]=\"isRtl() ? 'rtl' : null\"\r\n [attr.lang]=\"resolvedLocale()\"\r\n [style.background]=\"maximized() ? controlColors().maximizedBackground : null\"\r\n [style.color]=\"maximized() ? controlColors().maximizedColor : null\"\r\n (keydown)=\"onRootKeydown($event)\"\r\n>\r\n @if (zoomable() || maximizable()) {\r\n <!-- Only when a zoom or maximize feature is on, so a plain map's markup is\r\n exactly what it was before they existed. -->\r\n <div class=\"kerusi-seatmap__stage\">\r\n @if (showToolbar()) {\r\n <div\r\n class=\"kerusi-toolbar\"\r\n role=\"toolbar\"\r\n [attr.aria-label]=\"strings().toolbar\"\r\n [style.--kerusi-focus-ring-input]=\"resolvedColors().focusRing\"\r\n >\r\n @if (zoomable() && showZoomControls()) {\r\n <button\r\n type=\"button\"\r\n class=\"kerusi-control kerusi-control--zoom-in\"\r\n [attr.aria-label]=\"strings().zoomIn\"\r\n [attr.aria-disabled]=\"atMaxZoom()\"\r\n [style.background]=\"controlColors().buttonBackground\"\r\n [style.color]=\"controlColors().buttonColor\"\r\n [style.border-color]=\"controlColors().buttonBorder\"\r\n (click)=\"zoomIn()\"\r\n >\r\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\r\n <path d=\"M12 5v14M5 12h14\" />\r\n </svg>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"kerusi-control kerusi-control--zoom-out\"\r\n [attr.aria-label]=\"strings().zoomOut\"\r\n [attr.aria-disabled]=\"atFit()\"\r\n [style.background]=\"controlColors().buttonBackground\"\r\n [style.color]=\"controlColors().buttonColor\"\r\n [style.border-color]=\"controlColors().buttonBorder\"\r\n (click)=\"zoomOut()\"\r\n >\r\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\r\n <path d=\"M5 12h14\" />\r\n </svg>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"kerusi-control kerusi-control--zoom-reset\"\r\n [attr.aria-label]=\"strings().zoomReset\"\r\n [attr.aria-disabled]=\"atFit()\"\r\n [style.background]=\"controlColors().buttonBackground\"\r\n [style.color]=\"controlColors().buttonColor\"\r\n [style.border-color]=\"controlColors().buttonBorder\"\r\n (click)=\"resetZoom()\"\r\n >\r\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\r\n <path d=\"M3 12a9 9 0 1 0 3-6.7M3 4v5h5\" />\r\n </svg>\r\n </button>\r\n }\r\n @if (maximizable()) {\r\n <button\r\n type=\"button\"\r\n class=\"kerusi-control kerusi-control--maximize\"\r\n [attr.aria-label]=\"maximized() ? strings().restore : strings().maximize\"\r\n [style.background]=\"controlColors().buttonBackground\"\r\n [style.color]=\"controlColors().buttonColor\"\r\n [style.border-color]=\"controlColors().buttonBorder\"\r\n (click)=\"toggleMaximized()\"\r\n >\r\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\r\n @if (maximized()) {\r\n <path d=\"M9 4v5H4M15 4v5h5M15 20v-5h5M9 20v-5H4\" />\r\n } @else {\r\n <path d=\"M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5\" />\r\n }\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n @if (zoomable()) {\r\n <div\r\n #viewport\r\n class=\"kerusi-viewport\"\r\n role=\"group\"\r\n tabindex=\"0\"\r\n [attr.aria-label]=\"strings().viewport\"\r\n [style.touch-action]=\"touchAction()\"\r\n [style.--kerusi-focus-ring-input]=\"resolvedColors().focusRing\"\r\n >\r\n <div\r\n #content\r\n class=\"kerusi-viewport__content\"\r\n [class.kerusi-viewport__content--animated]=\"animated()\"\r\n [style.transform]=\"transform()\"\r\n >\r\n <ng-container [ngTemplateOutlet]=\"sections\" />\r\n </div>\r\n </div>\r\n } @else {\r\n <ng-container [ngTemplateOutlet]=\"sections\" />\r\n }\r\n </div>\r\n } @else {\r\n <ng-container [ngTemplateOutlet]=\"sections\" />\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<ng-template #sections>\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</ng-template>\r\n", styles: [":host{display:block}.kerusi-seatmap{display:flex;flex-direction:column;gap:1.25rem}.kerusi-seatmap__section{display:flex;flex-direction:column;gap:.4rem}.kerusi-section-label{margin:0;font-size:.85rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;opacity:.75}.kerusi-sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}.kerusi-seatmap__stage{display:flex;flex-direction:column;gap:1.25rem}.kerusi-toolbar{display:flex;gap:.35rem;align-self:flex-end;flex:none}.kerusi-control{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;width:2.75rem;height:2.75rem;padding:0;border:1px solid;border-radius:.4rem;font:inherit;cursor:pointer}.kerusi-control[aria-disabled=true]{opacity:.45;cursor:default}.kerusi-control svg{width:1.4rem;height:1.4rem;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}.kerusi-control:focus-visible,.kerusi-viewport:focus-visible{outline:2px solid var(--kerusi-focus-ring, var(--kerusi-focus-ring-input, currentColor));outline-offset:2px}.kerusi-viewport{position:relative;overflow:hidden;overflow:clip;user-select:none;-webkit-user-select:none}.kerusi-viewport__content{display:flex;flex-direction:column;gap:1.25rem;transform-origin:0 0}.kerusi-viewport__content--animated{transition:transform .16s ease-out}@media(prefers-reduced-motion:reduce){.kerusi-viewport__content--animated{transition:none}}.kerusi-seatmap--maximized{position:fixed;inset:0;z-index:var(--kerusi-maximized-z-index, 1000);box-sizing:border-box;padding:.75rem;gap:.5rem;overflow:hidden;overscroll-behavior:contain}.kerusi-seatmap--maximized .kerusi-seatmap__stage{flex:1 1 auto;min-height:0;gap:.5rem;overflow:auto}.kerusi-seatmap--maximized .kerusi-viewport{flex:1 1 auto;min-height:0}\n"], dependencies: [{ kind: "component", type: KerusiSectionComponent, selector: "kerusi-section", inputs: ["section", "layout", "selection", "focusedSeatId", "ariaLabels", "colors", "typeColors", "interactive", "unitScale"], outputs: ["seatActivate", "seatFocused", "seatKeydown"] }, { kind: "component", type: KerusiLegendComponent, selector: "kerusi-legend", inputs: ["legend", "locale", "colors", "typeColors", "showPrices", "hideUnusedTypes", "headingSeatTypes", "headingAvailability"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1131
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: KerusiSeatmapComponent, isStandalone: true, selector: "kerusi-seatmap", inputs: { map: { classPropertyName: "map", publicName: "map", isSignal: true, isRequired: true, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, session: { classPropertyName: "session", publicName: "session", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, rtl: { classPropertyName: "rtl", publicName: "rtl", isSignal: true, isRequired: false, transformFunction: null }, ariaStrings: { classPropertyName: "ariaStrings", publicName: "ariaStrings", 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 }, seatTypeStyles: { classPropertyName: "seatTypeStyles", publicName: "seatTypeStyles", isSignal: true, isRequired: false, transformFunction: null }, seatStyle: { classPropertyName: "seatStyle", publicName: "seatStyle", isSignal: true, isRequired: false, transformFunction: null }, unitScale: { classPropertyName: "unitScale", publicName: "unitScale", isSignal: true, isRequired: false, transformFunction: null }, showSectionLabels: { classPropertyName: "showSectionLabels", publicName: "showSectionLabels", isSignal: true, isRequired: false, transformFunction: null }, showLegend: { classPropertyName: "showLegend", publicName: "showLegend", isSignal: true, isRequired: false, transformFunction: null }, showLegendPrices: { classPropertyName: "showLegendPrices", publicName: "showLegendPrices", isSignal: true, isRequired: false, transformFunction: null }, sectionIds: { classPropertyName: "sectionIds", publicName: "sectionIds", isSignal: true, isRequired: false, transformFunction: null }, sectionOverrides: { classPropertyName: "sectionOverrides", publicName: "sectionOverrides", isSignal: true, isRequired: false, transformFunction: null }, selectableStatuses: { classPropertyName: "selectableStatuses", publicName: "selectableStatuses", isSignal: true, isRequired: false, transformFunction: null }, companionMode: { classPropertyName: "companionMode", publicName: "companionMode", isSignal: true, isRequired: false, transformFunction: null }, maxSelection: { classPropertyName: "maxSelection", publicName: "maxSelection", isSignal: true, isRequired: false, transformFunction: null }, seatSelectable: { classPropertyName: "seatSelectable", publicName: "seatSelectable", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, expireHolds: { classPropertyName: "expireHolds", publicName: "expireHolds", isSignal: true, isRequired: false, transformFunction: null }, expiryIntervalMs: { classPropertyName: "expiryIntervalMs", publicName: "expiryIntervalMs", isSignal: true, isRequired: false, transformFunction: null }, zoomable: { classPropertyName: "zoomable", publicName: "zoomable", isSignal: true, isRequired: false, transformFunction: null }, maxZoom: { classPropertyName: "maxZoom", publicName: "maxZoom", isSignal: true, isRequired: false, transformFunction: null }, showZoomControls: { classPropertyName: "showZoomControls", publicName: "showZoomControls", isSignal: true, isRequired: false, transformFunction: null }, maximizable: { classPropertyName: "maximizable", publicName: "maximizable", isSignal: true, isRequired: false, transformFunction: null }, controlStrings: { classPropertyName: "controlStrings", publicName: "controlStrings", isSignal: true, isRequired: false, transformFunction: null }, validate: { classPropertyName: "validate", publicName: "validate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selection: "selectionChange", seatSelect: "seatSelect", seatDeselect: "seatDeselect", seatDisallowed: "seatDisallowed", seatFocus: "seatFocus", validationIssues: "validationIssues", viewportChange: "viewportChange", maximizedChange: "maximizedChange" }, queries: [{ propertyName: "seatTemplate", first: true, predicate: KerusiSeatTemplateDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "viewportEl", first: true, predicate: ["viewport"], descendants: true, isSignal: true }, { propertyName: "contentEl", first: true, predicate: ["content"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"kerusi-seatmap\"\n [class.kerusi-seatmap--maximized]=\"maximized()\"\n role=\"group\"\n [attr.aria-label]=\"mapLabel()\"\n [attr.dir]=\"isRtl() ? 'rtl' : null\"\n [attr.lang]=\"resolvedLocale()\"\n [style.background]=\"maximized() ? controlColors().maximizedBackground : null\"\n [style.color]=\"maximized() ? controlColors().maximizedColor : null\"\n (keydown)=\"onRootKeydown($event)\"\n>\n @if (zoomable() || maximizable()) {\n <!-- Only when a zoom or maximize feature is on, so a plain map's markup is\n exactly what it was before they existed. -->\n <div class=\"kerusi-seatmap__stage\">\n @if (showToolbar()) {\n <div\n class=\"kerusi-toolbar\"\n role=\"toolbar\"\n [attr.aria-label]=\"strings().toolbar\"\n [style.--kerusi-focus-ring-input]=\"resolvedColors().focusRing\"\n >\n @if (zoomable() && showZoomControls()) {\n <button\n type=\"button\"\n class=\"kerusi-control kerusi-control--zoom-in\"\n [attr.aria-label]=\"strings().zoomIn\"\n [attr.aria-disabled]=\"atMaxZoom()\"\n [style.background]=\"controlColors().buttonBackground\"\n [style.color]=\"controlColors().buttonColor\"\n [style.border-color]=\"controlColors().buttonBorder\"\n (click)=\"zoomIn()\"\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M12 5v14M5 12h14\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"kerusi-control kerusi-control--zoom-out\"\n [attr.aria-label]=\"strings().zoomOut\"\n [attr.aria-disabled]=\"atFit()\"\n [style.background]=\"controlColors().buttonBackground\"\n [style.color]=\"controlColors().buttonColor\"\n [style.border-color]=\"controlColors().buttonBorder\"\n (click)=\"zoomOut()\"\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M5 12h14\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"kerusi-control kerusi-control--zoom-reset\"\n [attr.aria-label]=\"strings().zoomReset\"\n [attr.aria-disabled]=\"atFit()\"\n [style.background]=\"controlColors().buttonBackground\"\n [style.color]=\"controlColors().buttonColor\"\n [style.border-color]=\"controlColors().buttonBorder\"\n (click)=\"resetZoom()\"\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M3 12a9 9 0 1 0 3-6.7M3 4v5h5\" />\n </svg>\n </button>\n }\n @if (maximizable()) {\n <button\n type=\"button\"\n class=\"kerusi-control kerusi-control--maximize\"\n [attr.aria-label]=\"maximized() ? strings().restore : strings().maximize\"\n [style.background]=\"controlColors().buttonBackground\"\n [style.color]=\"controlColors().buttonColor\"\n [style.border-color]=\"controlColors().buttonBorder\"\n (click)=\"toggleMaximized()\"\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n @if (maximized()) {\n <path d=\"M9 4v5H4M15 4v5h5M15 20v-5h5M9 20v-5H4\" />\n } @else {\n <path d=\"M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5\" />\n }\n </svg>\n </button>\n }\n </div>\n }\n\n @if (zoomable()) {\n <div\n #viewport\n class=\"kerusi-viewport\"\n role=\"group\"\n tabindex=\"0\"\n [attr.aria-label]=\"strings().viewport\"\n [style.touch-action]=\"touchAction()\"\n [style.--kerusi-focus-ring-input]=\"resolvedColors().focusRing\"\n [style.--kerusi-canvas-aspect]=\"canvasAspect()\"\n >\n <div\n #content\n class=\"kerusi-viewport__content\"\n [class.kerusi-viewport__content--animated]=\"animated()\"\n [style.transform]=\"transform()\"\n >\n <ng-container [ngTemplateOutlet]=\"drawing\" />\n </div>\n </div>\n } @else {\n <ng-container [ngTemplateOutlet]=\"drawing\" />\n }\n </div>\n } @else {\n <ng-container [ngTemplateOutlet]=\"drawing\" />\n }\n\n @if (showLegend()) {\n <kerusi-legend\n [legend]=\"renderMap().legend\"\n [locale]=\"resolvedLocale()\"\n [colors]=\"resolvedColors()\"\n [typeColors]=\"typeColors()\"\n [seatTypeStyles]=\"seatTypeStyles()\"\n [showPrices]=\"showLegendPrices()\"\n />\n }\n\n <p class=\"kerusi-sr-only\" aria-live=\"polite\" aria-atomic=\"true\">{{ liveMessage() }}</p>\n</div>\n\n<!-- The whole map: one <svg> on the document's canvas, a group per section (D1). -->\n<ng-template #drawing>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"kerusi-seatmap__svg\"\n role=\"presentation\"\n focusable=\"false\"\n [style.--kerusi-focus-ring-input]=\"resolvedColors().focusRing\"\n [attr.viewBox]=\"viewBox()\"\n [style.max-width.px]=\"naturalWidth()\"\n preserveAspectRatio=\"xMidYMid meet\"\n >\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"layout().width\"\n [attr.height]=\"layout().height\"\n [style.fill]=\"backdrop()\"\n />\n <!-- A hidden section's group is simply absent; the canvas keeps its size,\n so its space stays empty (D4). -->\n @for (placed of layout().sections; track placed.section.id) {\n <g\n kerusi-section\n [layout]=\"placed\"\n [label]=\"labelFor(placed.section)\"\n [showLabel]=\"showSectionLabels()\"\n [selection]=\"selectionSet()\"\n [focusedSeatId]=\"focusedSeatIn(placed.section)\"\n [ariaLabels]=\"ariaLabels()\"\n [colors]=\"resolvedColors()\"\n [typeColors]=\"typeColors()\"\n [seatTemplate]=\"seatTemplate()?.template\"\n [seatStyle]=\"seatStyle()\"\n [seatTypeStyles]=\"seatTypeStyles()\"\n [interactive]=\"interactive()\"\n (seatActivate)=\"onSeatActivate($event)\"\n (seatFocused)=\"onSeatFocused($event)\"\n (seatKeydown)=\"onKeydown(placed.section, $event)\"\n ></g>\n }\n </svg>\n</ng-template>\n", styles: [":host{display:block}.kerusi-seatmap{display:flex;flex-direction:column;gap:1.25rem}.kerusi-seatmap__svg{display:block;width:100%;height:auto;max-width:100%}.kerusi-sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}.kerusi-seatmap__stage{display:flex;flex-direction:column;gap:1.25rem}.kerusi-toolbar{display:flex;gap:.35rem;align-self:flex-end;flex:none}.kerusi-control{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;width:2.75rem;height:2.75rem;padding:0;border:1px solid;border-radius:.4rem;font:inherit;cursor:pointer}.kerusi-control[aria-disabled=true]{opacity:.45;cursor:default}.kerusi-control svg{width:1.4rem;height:1.4rem;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}.kerusi-control:focus-visible,.kerusi-viewport:focus-visible{outline:2px solid var(--kerusi-focus-ring, var(--kerusi-focus-ring-input, currentColor));outline-offset:2px}.kerusi-viewport{position:relative;overflow:hidden;overflow:clip;user-select:none;-webkit-user-select:none}.kerusi-viewport__content{display:flex;flex-direction:column;gap:1.25rem;transform-origin:0 0}.kerusi-viewport__content--animated{transition:transform .16s ease-out}@media(prefers-reduced-motion:reduce){.kerusi-viewport__content--animated{transition:none}}.kerusi-seatmap--maximized{position:fixed;inset:0;z-index:var(--kerusi-maximized-z-index, 1000);box-sizing:border-box;padding:.75rem;gap:.5rem;overflow:hidden;overscroll-behavior:contain}.kerusi-seatmap--maximized .kerusi-seatmap__stage{flex:1 1 auto;min-height:0;gap:.5rem;overflow:auto}.kerusi-seatmap--maximized .kerusi-viewport{flex:1 1 auto;min-height:0;container-type:size}.kerusi-seatmap--maximized .kerusi-viewport__content{width:min(100cqw,100cqh * var(--kerusi-canvas-aspect, 1))}\n"], dependencies: [{ kind: "component", type: KerusiSectionComponent, selector: "g[kerusi-section]", inputs: ["layout", "label", "showLabel", "selection", "focusedSeatId", "ariaLabels", "colors", "typeColors", "seatTypeStyles", "seatStyle", "seatTemplate", "interactive"], outputs: ["seatActivate", "seatFocused", "seatKeydown"] }, { kind: "component", type: KerusiLegendComponent, selector: "kerusi-legend", inputs: ["legend", "locale", "colors", "typeColors", "seatTypeStyles", "showPrices", "hideUnusedTypes", "headingSeatTypes", "headingAvailability"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1146
1132
|
}
|
|
1147
1133
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: KerusiSeatmapComponent, decorators: [{
|
|
1148
1134
|
type: Component,
|
|
1149
|
-
args: [{ selector: 'kerusi-seatmap', changeDetection: ChangeDetectionStrategy.OnPush, imports: [KerusiSectionComponent, KerusiLegendComponent, NgTemplateOutlet], template: "<div\
|
|
1150
|
-
}], ctorParameters: () => [], propDecorators: { map: [{ type: i0.Input, args: [{ isSignal: true, alias: "map", required: true }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], session: [{ type: i0.Input, args: [{ isSignal: true, alias: "session", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], rtl: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtl", required: false }] }], ariaStrings: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaStrings", required: false }] }], colors: [{ type: i0.Input, args: [{ isSignal: true, alias: "colors", required: false }] }], typeColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeColors", required: false }] }],
|
|
1135
|
+
args: [{ selector: 'kerusi-seatmap', changeDetection: ChangeDetectionStrategy.OnPush, imports: [KerusiSectionComponent, KerusiLegendComponent, NgTemplateOutlet], template: "<div\n class=\"kerusi-seatmap\"\n [class.kerusi-seatmap--maximized]=\"maximized()\"\n role=\"group\"\n [attr.aria-label]=\"mapLabel()\"\n [attr.dir]=\"isRtl() ? 'rtl' : null\"\n [attr.lang]=\"resolvedLocale()\"\n [style.background]=\"maximized() ? controlColors().maximizedBackground : null\"\n [style.color]=\"maximized() ? controlColors().maximizedColor : null\"\n (keydown)=\"onRootKeydown($event)\"\n>\n @if (zoomable() || maximizable()) {\n <!-- Only when a zoom or maximize feature is on, so a plain map's markup is\n exactly what it was before they existed. -->\n <div class=\"kerusi-seatmap__stage\">\n @if (showToolbar()) {\n <div\n class=\"kerusi-toolbar\"\n role=\"toolbar\"\n [attr.aria-label]=\"strings().toolbar\"\n [style.--kerusi-focus-ring-input]=\"resolvedColors().focusRing\"\n >\n @if (zoomable() && showZoomControls()) {\n <button\n type=\"button\"\n class=\"kerusi-control kerusi-control--zoom-in\"\n [attr.aria-label]=\"strings().zoomIn\"\n [attr.aria-disabled]=\"atMaxZoom()\"\n [style.background]=\"controlColors().buttonBackground\"\n [style.color]=\"controlColors().buttonColor\"\n [style.border-color]=\"controlColors().buttonBorder\"\n (click)=\"zoomIn()\"\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M12 5v14M5 12h14\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"kerusi-control kerusi-control--zoom-out\"\n [attr.aria-label]=\"strings().zoomOut\"\n [attr.aria-disabled]=\"atFit()\"\n [style.background]=\"controlColors().buttonBackground\"\n [style.color]=\"controlColors().buttonColor\"\n [style.border-color]=\"controlColors().buttonBorder\"\n (click)=\"zoomOut()\"\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M5 12h14\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"kerusi-control kerusi-control--zoom-reset\"\n [attr.aria-label]=\"strings().zoomReset\"\n [attr.aria-disabled]=\"atFit()\"\n [style.background]=\"controlColors().buttonBackground\"\n [style.color]=\"controlColors().buttonColor\"\n [style.border-color]=\"controlColors().buttonBorder\"\n (click)=\"resetZoom()\"\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M3 12a9 9 0 1 0 3-6.7M3 4v5h5\" />\n </svg>\n </button>\n }\n @if (maximizable()) {\n <button\n type=\"button\"\n class=\"kerusi-control kerusi-control--maximize\"\n [attr.aria-label]=\"maximized() ? strings().restore : strings().maximize\"\n [style.background]=\"controlColors().buttonBackground\"\n [style.color]=\"controlColors().buttonColor\"\n [style.border-color]=\"controlColors().buttonBorder\"\n (click)=\"toggleMaximized()\"\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n @if (maximized()) {\n <path d=\"M9 4v5H4M15 4v5h5M15 20v-5h5M9 20v-5H4\" />\n } @else {\n <path d=\"M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5\" />\n }\n </svg>\n </button>\n }\n </div>\n }\n\n @if (zoomable()) {\n <div\n #viewport\n class=\"kerusi-viewport\"\n role=\"group\"\n tabindex=\"0\"\n [attr.aria-label]=\"strings().viewport\"\n [style.touch-action]=\"touchAction()\"\n [style.--kerusi-focus-ring-input]=\"resolvedColors().focusRing\"\n [style.--kerusi-canvas-aspect]=\"canvasAspect()\"\n >\n <div\n #content\n class=\"kerusi-viewport__content\"\n [class.kerusi-viewport__content--animated]=\"animated()\"\n [style.transform]=\"transform()\"\n >\n <ng-container [ngTemplateOutlet]=\"drawing\" />\n </div>\n </div>\n } @else {\n <ng-container [ngTemplateOutlet]=\"drawing\" />\n }\n </div>\n } @else {\n <ng-container [ngTemplateOutlet]=\"drawing\" />\n }\n\n @if (showLegend()) {\n <kerusi-legend\n [legend]=\"renderMap().legend\"\n [locale]=\"resolvedLocale()\"\n [colors]=\"resolvedColors()\"\n [typeColors]=\"typeColors()\"\n [seatTypeStyles]=\"seatTypeStyles()\"\n [showPrices]=\"showLegendPrices()\"\n />\n }\n\n <p class=\"kerusi-sr-only\" aria-live=\"polite\" aria-atomic=\"true\">{{ liveMessage() }}</p>\n</div>\n\n<!-- The whole map: one <svg> on the document's canvas, a group per section (D1). -->\n<ng-template #drawing>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"kerusi-seatmap__svg\"\n role=\"presentation\"\n focusable=\"false\"\n [style.--kerusi-focus-ring-input]=\"resolvedColors().focusRing\"\n [attr.viewBox]=\"viewBox()\"\n [style.max-width.px]=\"naturalWidth()\"\n preserveAspectRatio=\"xMidYMid meet\"\n >\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"layout().width\"\n [attr.height]=\"layout().height\"\n [style.fill]=\"backdrop()\"\n />\n <!-- A hidden section's group is simply absent; the canvas keeps its size,\n so its space stays empty (D4). -->\n @for (placed of layout().sections; track placed.section.id) {\n <g\n kerusi-section\n [layout]=\"placed\"\n [label]=\"labelFor(placed.section)\"\n [showLabel]=\"showSectionLabels()\"\n [selection]=\"selectionSet()\"\n [focusedSeatId]=\"focusedSeatIn(placed.section)\"\n [ariaLabels]=\"ariaLabels()\"\n [colors]=\"resolvedColors()\"\n [typeColors]=\"typeColors()\"\n [seatTemplate]=\"seatTemplate()?.template\"\n [seatStyle]=\"seatStyle()\"\n [seatTypeStyles]=\"seatTypeStyles()\"\n [interactive]=\"interactive()\"\n (seatActivate)=\"onSeatActivate($event)\"\n (seatFocused)=\"onSeatFocused($event)\"\n (seatKeydown)=\"onKeydown(placed.section, $event)\"\n ></g>\n }\n </svg>\n</ng-template>\n", styles: [":host{display:block}.kerusi-seatmap{display:flex;flex-direction:column;gap:1.25rem}.kerusi-seatmap__svg{display:block;width:100%;height:auto;max-width:100%}.kerusi-sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}.kerusi-seatmap__stage{display:flex;flex-direction:column;gap:1.25rem}.kerusi-toolbar{display:flex;gap:.35rem;align-self:flex-end;flex:none}.kerusi-control{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;width:2.75rem;height:2.75rem;padding:0;border:1px solid;border-radius:.4rem;font:inherit;cursor:pointer}.kerusi-control[aria-disabled=true]{opacity:.45;cursor:default}.kerusi-control svg{width:1.4rem;height:1.4rem;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}.kerusi-control:focus-visible,.kerusi-viewport:focus-visible{outline:2px solid var(--kerusi-focus-ring, var(--kerusi-focus-ring-input, currentColor));outline-offset:2px}.kerusi-viewport{position:relative;overflow:hidden;overflow:clip;user-select:none;-webkit-user-select:none}.kerusi-viewport__content{display:flex;flex-direction:column;gap:1.25rem;transform-origin:0 0}.kerusi-viewport__content--animated{transition:transform .16s ease-out}@media(prefers-reduced-motion:reduce){.kerusi-viewport__content--animated{transition:none}}.kerusi-seatmap--maximized{position:fixed;inset:0;z-index:var(--kerusi-maximized-z-index, 1000);box-sizing:border-box;padding:.75rem;gap:.5rem;overflow:hidden;overscroll-behavior:contain}.kerusi-seatmap--maximized .kerusi-seatmap__stage{flex:1 1 auto;min-height:0;gap:.5rem;overflow:auto}.kerusi-seatmap--maximized .kerusi-viewport{flex:1 1 auto;min-height:0;container-type:size}.kerusi-seatmap--maximized .kerusi-viewport__content{width:min(100cqw,100cqh * var(--kerusi-canvas-aspect, 1))}\n"] }]
|
|
1136
|
+
}], ctorParameters: () => [], propDecorators: { map: [{ type: i0.Input, args: [{ isSignal: true, alias: "map", required: true }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], session: [{ type: i0.Input, args: [{ isSignal: true, alias: "session", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], rtl: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtl", required: false }] }], ariaStrings: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaStrings", required: false }] }], colors: [{ type: i0.Input, args: [{ isSignal: true, alias: "colors", required: false }] }], typeColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeColors", required: false }] }], seatTypeStyles: [{ type: i0.Input, args: [{ isSignal: true, alias: "seatTypeStyles", required: false }] }], seatStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "seatStyle", required: false }] }], seatTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => KerusiSeatTemplateDirective), { isSignal: true }] }], unitScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "unitScale", required: false }] }], showSectionLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSectionLabels", required: false }] }], showLegend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLegend", required: false }] }], showLegendPrices: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLegendPrices", required: false }] }], sectionIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "sectionIds", required: false }] }], sectionOverrides: [{ type: i0.Input, args: [{ isSignal: true, alias: "sectionOverrides", required: false }] }], selectableStatuses: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectableStatuses", required: false }] }], companionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "companionMode", required: false }] }], maxSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelection", required: false }] }], seatSelectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "seatSelectable", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], expireHolds: [{ type: i0.Input, args: [{ isSignal: true, alias: "expireHolds", required: false }] }], expiryIntervalMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "expiryIntervalMs", required: false }] }], zoomable: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomable", required: false }] }], maxZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxZoom", required: false }] }], showZoomControls: [{ type: i0.Input, args: [{ isSignal: true, alias: "showZoomControls", required: false }] }], maximizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "maximizable", required: false }] }], controlStrings: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlStrings", required: false }] }], validate: [{ type: i0.Input, args: [{ isSignal: true, alias: "validate", required: false }] }], seatSelect: [{ type: i0.Output, args: ["seatSelect"] }], seatDeselect: [{ type: i0.Output, args: ["seatDeselect"] }], seatDisallowed: [{ type: i0.Output, args: ["seatDisallowed"] }], seatFocus: [{ type: i0.Output, args: ["seatFocus"] }], validationIssues: [{ type: i0.Output, args: ["validationIssues"] }], viewportChange: [{ type: i0.Output, args: ["viewportChange"] }], maximizedChange: [{ type: i0.Output, args: ["maximizedChange"] }], viewportEl: [{ type: i0.ViewChild, args: ['viewport', { isSignal: true }] }], contentEl: [{ type: i0.ViewChild, args: ['content', { isSignal: true }] }] } });
|
|
1151
1137
|
function formatTotal(total, locale) {
|
|
1152
1138
|
return total ? formatMoney(total, locale) : '';
|
|
1153
1139
|
}
|
|
@@ -1269,5 +1255,5 @@ class KerusiStateStore {
|
|
|
1269
1255
|
* Generated bundle index. Do not edit.
|
|
1270
1256
|
*/
|
|
1271
1257
|
|
|
1272
|
-
export { KerusiLegendComponent, KerusiSeatmapComponent, KerusiSectionComponent, KerusiStateStore };
|
|
1258
|
+
export { KerusiLegendComponent, KerusiSeatTemplateDirective, KerusiSeatmapComponent, KerusiSectionComponent, KerusiStateStore };
|
|
1273
1259
|
//# sourceMappingURL=kerusiweb-angular.mjs.map
|