@kerusiweb/angular 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as _kerusiweb_core from '@kerusiweb/core';
2
- import { KerusiMap, KerusiState, KerusiSession, SeatAriaStrings, KerusiSeatmapColors, RenderSeat, DisallowedReason, KerusiViolation, RenderMap, SectionLayout, RenderSection, PlacedSeat, SeatOccupantVariant, PlacedElement, RenderLegendEntry, SequenceReader, HeldSeat, KerusiStateDelta, DeltaApplication } from '@kerusiweb/core';
2
+ import { KerusiMap, KerusiState, KerusiSession, SeatAriaStrings, KerusiSeatmapColors, RenderSeat, KerusiControlStrings, DisallowedReason, KerusiViolation, KerusiViewport, RenderMap, SectionLayout, RenderSection, PlacedSeat, SeatOccupantVariant, PlacedElement, RenderLegendEntry, SequenceReader, HeldSeat, KerusiStateDelta, DeltaApplication } from '@kerusiweb/core';
3
3
  import * as _angular_core from '@angular/core';
4
4
  import { Signal } from '@angular/core';
5
5
 
@@ -46,6 +46,11 @@ interface SeatDisallowed {
46
46
  * ```html
47
47
  * <kerusi-seatmap [map]="map" [state]="state" [(selection)]="picked" />
48
48
  * ```
49
+ *
50
+ * Two opt-in features sit on top, both off by default so an existing map is
51
+ * unchanged: `[zoomable]` (pinch, wheel and drag, keys `+` `-` `0`, buttons)
52
+ * and `[maximizable]` (a full-screen button). Their arithmetic is in
53
+ * `@kerusiweb/core`; this class supplies the listeners and the signals.
49
54
  */
50
55
  declare class KerusiSeatmapComponent {
51
56
  private readonly host;
@@ -96,6 +101,22 @@ declare class KerusiSeatmapComponent {
96
101
  */
97
102
  readonly expireHolds: _angular_core.InputSignal<boolean>;
98
103
  readonly expiryIntervalMs: _angular_core.InputSignal<number>;
104
+ /**
105
+ * Pinch, Ctrl/Cmd + wheel and drag to zoom and pan, `+` `-` `0` on the keyboard,
106
+ * and on-screen buttons. A tap still selects at every zoom level.
107
+ */
108
+ readonly zoomable: _angular_core.InputSignal<boolean>;
109
+ /** The zoom ceiling, relative to fit-to-container. The floor is always fit. */
110
+ readonly maxZoom: _angular_core.InputSignal<number>;
111
+ /**
112
+ * The built-in zoom buttons. Turn this off to supply your own, anywhere on the
113
+ * page, and call `zoomIn()`, `zoomOut()` and `resetZoom()` on the component.
114
+ */
115
+ readonly showZoomControls: _angular_core.InputSignal<boolean>;
116
+ /** A button that maximizes the map to fill the screen. */
117
+ readonly maximizable: _angular_core.InputSignal<boolean>;
118
+ /** Overrides the built-in English names of the zoom and maximize controls. */
119
+ readonly controlStrings: _angular_core.InputSignal<Partial<KerusiControlStrings>>;
99
120
  readonly validate: _angular_core.InputSignal<ValidationMode>;
100
121
  readonly seatSelect: _angular_core.OutputEmitterRef<SeatInteraction>;
101
122
  readonly seatDeselect: _angular_core.OutputEmitterRef<SeatInteraction>;
@@ -103,6 +124,9 @@ declare class KerusiSeatmapComponent {
103
124
  readonly seatFocus: _angular_core.OutputEmitterRef<RenderSeat>;
104
125
  /** Emitted whenever the documents change, in `collect` mode. */
105
126
  readonly validationIssues: _angular_core.OutputEmitterRef<readonly KerusiViolation[]>;
127
+ /** The zoom and pan, whenever they change. For a host that persists or shows them. */
128
+ readonly viewportChange: _angular_core.OutputEmitterRef<KerusiViewport>;
129
+ readonly maximizedChange: _angular_core.OutputEmitterRef<boolean>;
106
130
  /**
107
131
  * The seat holding each section's tab stop, keyed by section id.
108
132
  *
@@ -114,6 +138,20 @@ declare class KerusiSeatmapComponent {
114
138
  private readonly announcement;
115
139
  /** Bumped by the hold ticker to force the state to be re-derived. */
116
140
  private readonly expiryTick;
141
+ private readonly viewportState;
142
+ private readonly maximizedState;
143
+ /** True for a discrete change (button, key, reveal), false while a finger or wheel drives. */
144
+ protected readonly animated: _angular_core.WritableSignal<boolean>;
145
+ private readonly viewportEl;
146
+ private readonly contentEl;
147
+ private gesture;
148
+ private suppressClick;
149
+ private gestureBase;
150
+ private nativeFullscreen;
151
+ /** The current zoom and pan. Read-only: drive it with the methods below. */
152
+ readonly viewport: _angular_core.Signal<KerusiViewport>;
153
+ /** Whether the map is currently maximized. */
154
+ readonly maximized: _angular_core.Signal<boolean>;
117
155
  constructor();
118
156
  protected readonly issues: _angular_core.Signal<readonly KerusiViolation[]>;
119
157
  /** The state actually rendered — with lapsed holds reverted when asked. */
@@ -123,6 +161,14 @@ declare class KerusiSeatmapComponent {
123
161
  protected readonly isRtl: _angular_core.Signal<boolean>;
124
162
  protected readonly resolvedColors: _angular_core.Signal<Required<KerusiSeatmapColors>>;
125
163
  protected readonly selectionSet: _angular_core.Signal<Set<string>>;
164
+ protected readonly limits: _angular_core.Signal<_kerusiweb_core.ViewportLimits>;
165
+ protected readonly strings: _angular_core.Signal<KerusiControlStrings>;
166
+ protected readonly controlColors: _angular_core.Signal<_kerusiweb_core.KerusiControlStyle>;
167
+ protected readonly transform: _angular_core.Signal<string>;
168
+ protected readonly touchAction: _angular_core.Signal<string>;
169
+ protected readonly showToolbar: _angular_core.Signal<boolean>;
170
+ protected readonly atMaxZoom: _angular_core.Signal<boolean>;
171
+ protected readonly atFit: _angular_core.Signal<boolean>;
126
172
  /** Placed geometry per section, keyed by section id. */
127
173
  protected readonly layouts: _angular_core.Signal<ReadonlyMap<string, SectionLayout>>;
128
174
  /** Navigation graphs per section — arrows move within, Tab moves between. */
@@ -150,16 +196,58 @@ declare class KerusiSeatmapComponent {
150
196
  /**
151
197
  * Moves the roving tab stop, then the DOM focus that follows it.
152
198
  *
199
+ * When zoomable, the browser's own scroll-into-view is suppressed: the map
200
+ * moves by transform, so `onSeatFocused` pans it into view instead.
201
+ */
202
+ private moveFocusTo;
203
+ /**
153
204
  * Seat ids come from the document, so they cannot be interpolated into a
154
205
  * selector unescaped — and `CSS.escape` is absent in jsdom and under SSR.
155
206
  * Scanning the rendered nodes avoids both problems.
156
207
  */
157
- private moveFocusTo;
208
+ private seatNode;
209
+ /** Zooms in one step about the centre. No-op unless `zoomable`. */
210
+ zoomIn(): void;
211
+ /** Zooms out one step about the centre. No-op unless `zoomable`. */
212
+ zoomOut(): void;
213
+ /** Back to fit-to-container: the map's original view. No-op unless `zoomable`. */
214
+ resetZoom(): void;
215
+ private stepZoom;
216
+ /** Sizes in CSS pixels, read when needed: `offset*` ignores the zoom transform. */
217
+ private measure;
218
+ private commit;
219
+ private reclamp;
220
+ /** A point in the viewport's own coordinates, which is what the core functions take. */
221
+ private containerPoint;
222
+ /**
223
+ * Pans a seat that took keyboard focus into view. A click or tap also
224
+ * focuses the seat, but should not move the map under the finger, so only a
225
+ * `:focus-visible` focus counts.
226
+ */
227
+ private revealSeat;
228
+ private onPointerDown;
229
+ private onPointerMove;
230
+ private onPointerEnd;
231
+ private feedGesture;
232
+ private onClickCapture;
233
+ private onWheel;
234
+ private onGestureStart;
235
+ private onGestureChange;
236
+ private onGestureEnd;
237
+ /**
238
+ * `+` `-` `0` zoom, and arrows pan when the viewport itself has focus; Esc
239
+ * leaves a maximized map. A seat's own handler runs first, so an Esc that
240
+ * cleared the selection is already `defaultPrevented` and stays at that.
241
+ */
242
+ protected onRootKeydown(event: KeyboardEvent): void;
243
+ /** Maximizes or restores the map. Maximizing needs `maximizable`. */
244
+ toggleMaximized(): void;
245
+ setMaximized(next: boolean): void;
158
246
  private announceSelection;
159
247
  private announce;
160
248
  private visibleSectionIds;
161
249
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<KerusiSeatmapComponent, never>;
162
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<KerusiSeatmapComponent, "kerusi-seatmap", never, { "map": { "alias": "map"; "required": true; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "session": { "alias": "session"; "required": false; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "rtl": { "alias": "rtl"; "required": false; "isSignal": true; }; "ariaStrings": { "alias": "ariaStrings"; "required": false; "isSignal": true; }; "colors": { "alias": "colors"; "required": false; "isSignal": true; }; "typeColors": { "alias": "typeColors"; "required": false; "isSignal": true; }; "seatSize": { "alias": "seatSize"; "required": false; "isSignal": true; }; "seatGap": { "alias": "seatGap"; "required": false; "isSignal": true; }; "freeformBasis": { "alias": "freeformBasis"; "required": false; "isSignal": true; }; "unitScale": { "alias": "unitScale"; "required": false; "isSignal": true; }; "showSectionLabels": { "alias": "showSectionLabels"; "required": false; "isSignal": true; }; "showLegend": { "alias": "showLegend"; "required": false; "isSignal": true; }; "showLegendPrices": { "alias": "showLegendPrices"; "required": false; "isSignal": true; }; "sectionIds": { "alias": "sectionIds"; "required": false; "isSignal": true; }; "sectionOverrides": { "alias": "sectionOverrides"; "required": false; "isSignal": true; }; "selectableStatuses": { "alias": "selectableStatuses"; "required": false; "isSignal": true; }; "companionMode": { "alias": "companionMode"; "required": false; "isSignal": true; }; "maxSelection": { "alias": "maxSelection"; "required": false; "isSignal": true; }; "seatSelectable": { "alias": "seatSelectable"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "expireHolds": { "alias": "expireHolds"; "required": false; "isSignal": true; }; "expiryIntervalMs": { "alias": "expiryIntervalMs"; "required": false; "isSignal": true; }; "validate": { "alias": "validate"; "required": false; "isSignal": true; }; }, { "selection": "selectionChange"; "seatSelect": "seatSelect"; "seatDeselect": "seatDeselect"; "seatDisallowed": "seatDisallowed"; "seatFocus": "seatFocus"; "validationIssues": "validationIssues"; }, never, never, true, never>;
250
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<KerusiSeatmapComponent, "kerusi-seatmap", never, { "map": { "alias": "map"; "required": true; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "session": { "alias": "session"; "required": false; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "rtl": { "alias": "rtl"; "required": false; "isSignal": true; }; "ariaStrings": { "alias": "ariaStrings"; "required": false; "isSignal": true; }; "colors": { "alias": "colors"; "required": false; "isSignal": true; }; "typeColors": { "alias": "typeColors"; "required": false; "isSignal": true; }; "seatSize": { "alias": "seatSize"; "required": false; "isSignal": true; }; "seatGap": { "alias": "seatGap"; "required": false; "isSignal": true; }; "freeformBasis": { "alias": "freeformBasis"; "required": false; "isSignal": true; }; "unitScale": { "alias": "unitScale"; "required": false; "isSignal": true; }; "showSectionLabels": { "alias": "showSectionLabels"; "required": false; "isSignal": true; }; "showLegend": { "alias": "showLegend"; "required": false; "isSignal": true; }; "showLegendPrices": { "alias": "showLegendPrices"; "required": false; "isSignal": true; }; "sectionIds": { "alias": "sectionIds"; "required": false; "isSignal": true; }; "sectionOverrides": { "alias": "sectionOverrides"; "required": false; "isSignal": true; }; "selectableStatuses": { "alias": "selectableStatuses"; "required": false; "isSignal": true; }; "companionMode": { "alias": "companionMode"; "required": false; "isSignal": true; }; "maxSelection": { "alias": "maxSelection"; "required": false; "isSignal": true; }; "seatSelectable": { "alias": "seatSelectable"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "expireHolds": { "alias": "expireHolds"; "required": false; "isSignal": true; }; "expiryIntervalMs": { "alias": "expiryIntervalMs"; "required": false; "isSignal": true; }; "zoomable": { "alias": "zoomable"; "required": false; "isSignal": true; }; "maxZoom": { "alias": "maxZoom"; "required": false; "isSignal": true; }; "showZoomControls": { "alias": "showZoomControls"; "required": false; "isSignal": true; }; "maximizable": { "alias": "maximizable"; "required": false; "isSignal": true; }; "controlStrings": { "alias": "controlStrings"; "required": false; "isSignal": true; }; "validate": { "alias": "validate"; "required": false; "isSignal": true; }; }, { "selection": "selectionChange"; "seatSelect": "seatSelect"; "seatDeselect": "seatDeselect"; "seatDisallowed": "seatDisallowed"; "seatFocus": "seatFocus"; "validationIssues": "validationIssues"; "viewportChange": "viewportChange"; "maximizedChange": "maximizedChange"; }, never, never, true, never>;
163
251
  }
164
252
 
165
253
  /**