@forgebuild/hotal-ui 0.23.15 → 0.23.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +660 -659
  2. package/dist/hotal-ui.js +6444 -5727
  3. package/dist/index.d.ts +1191 -1099
  4. package/package.json +45 -45
package/dist/index.d.ts CHANGED
@@ -1,1099 +1,1191 @@
1
- export interface ReservationBarStrings {
2
- dateLabel: string;
3
- guestsLabel: string;
4
- timeLabel: string;
5
- guestSingular: string;
6
- guestPlural: string;
7
- reserveLabel: string;
8
- previousMonth: string;
9
- nextMonth: string;
10
- selectGuestsAriaLabel: string;
11
- selectTimeAriaLabel: string;
12
- }
13
-
14
- export interface ReservationBarElement extends HTMLElement {
15
- minGuests?: number;
16
- maxGuests?: number;
17
- defaultGuests?: number;
18
- timeSlots?: string[];
19
- defaultTime?: string;
20
- showReserveButton?: boolean;
21
- reserveHref?: string;
22
- syncWithUrl?: boolean;
23
- closedWeekdays?: number[];
24
- locale?: string;
25
- strings?: Partial<ReservationBarStrings>;
26
- fetchAvailableDates?: (partySize: number) => Promise<string[]>;
27
- }
28
-
29
- export interface BookingSlot {
30
- time: string;
31
- availableTables: number;
32
- }
33
-
34
- export interface PaymentIntentResult {
35
- clientSecret: string;
36
- paymentIntentId: string;
37
- amountCents: number;
38
- }
39
-
40
- export interface BookingDetails {
41
- date: string;
42
- startTime: string;
43
- timeDisplay: string;
44
- partySize: number;
45
- contactName: string;
46
- contactEmail?: string;
47
- contactPhone?: string;
48
- notes?: string;
49
- customFieldValues?: Record<string, string>;
50
- stripePaymentIntentId?: string;
51
- clerkUserId?: string;
52
- }
53
-
54
- export type BookResult = { ok: true } | { ok: false; status: number; message: string };
55
-
56
- export interface BookingFlowCustomField {
57
- key: string;
58
- label: string;
59
- options: string[];
60
- }
61
-
62
- export interface BookingFlowStrings {
63
- pickDateEmpty: string;
64
- errorEmpty: string;
65
- noSlotsEmpty: string;
66
- previousMonth: string;
67
- nextMonth: string;
68
- availableTimes: string;
69
- nameLabel: string;
70
- emailLabel: string;
71
- phoneLabel: string;
72
- selectOption: string;
73
- paymentMethod: string;
74
- holdSuffix: string;
75
- paymentHint: string;
76
- paymentUnavailable: string;
77
- notesLabel: string;
78
- cancellationPolicy: string;
79
- confirmButton: string;
80
- processingButton: string;
81
- paymentNotReady: string;
82
- cardHoldFailed: string;
83
- confirmedTitle: string;
84
- dateLabel: string;
85
- timeLabel: string;
86
- partySizeLabel: string;
87
- guestSingular: string;
88
- guestPlural: string;
89
- }
90
-
91
- export interface BookingFlowElement extends HTMLElement {
92
- date?: string;
93
- guests?: number;
94
- preferredTime?: string;
95
- closedWeekdays?: number[];
96
- customFields?: BookingFlowCustomField[];
97
- /** BCP 47 locale (e.g. "fr-FR") for date/month/weekday formatting and the
98
- * payment hold amount; defaults to the browser's own locale. */
99
- locale?: string;
100
- /** ISO 4217 currency code (e.g. "EUR") for the payment hold amount label. */
101
- currency?: string;
102
- /** UI copy overrides; unset keys fall back to English defaults. */
103
- strings?: Partial<BookingFlowStrings>;
104
- fetchAvailableDates?: (partySize: number) => Promise<string[]>;
105
- searchSlots?: (date: string, guests: number) => Promise<BookingSlot[]>;
106
- createPaymentIntent?: (date: string, guests: number) => Promise<PaymentIntentResult>;
107
- book?: (details: BookingDetails) => Promise<BookResult>;
108
- stripePublishableKey?: string;
109
- stripeAppearance?: Record<string, unknown>;
110
- }
111
-
112
- export interface MenuItem {
113
- name: string;
114
- description?: string;
115
- price?: string;
116
- tagline?: string;
117
- group?: string;
118
- country?: string;
119
- allergens?: string[];
120
- }
121
-
122
- export interface MenuCategory {
123
- label: string;
124
- items: MenuItem[];
125
- }
126
-
127
- export interface MenuInfoItem {
128
- label: string;
129
- value: string;
130
- }
131
-
132
- export interface Allergen {
133
- id: string;
134
- label: string;
135
- icon: string;
136
- }
137
-
138
- export interface MenuSectionElement extends HTMLElement {
139
- title?: string;
140
- eyebrow?: string;
141
- categories?: MenuCategory[];
142
- info?: MenuInfoItem[];
143
- allergens?: Allergen[];
144
- pdfUrl?: string;
145
- pdfLabel?: string;
146
- topFlourishSrc?: string;
147
- tailFlourishSrc?: string;
148
- headingCircleColor?: string;
149
- headingAlign?: 'center' | 'left';
150
- layout?: 'stacked' | 'split';
151
- variant?: 'default' | 'panel';
152
- openInNewTabLabel?: string;
153
- closeAriaLabel?: string;
154
- groupFilter?: boolean;
155
- countryFilter?: boolean;
156
- groupFilterAllLabel?: string;
157
- countryFilterAllLabel?: string;
158
- }
159
-
160
- export interface EventInquiry {
161
- id: string;
162
- name: string;
163
- email: string;
164
- phone?: string | null;
165
- restaurant_id?: string | null;
166
- event_date: string;
167
- guests?: number | null;
168
- event_type?: string | null;
169
- format?: string | null;
170
- message?: string | null;
171
- status: string;
172
- created_at: string;
173
- /** Avatar of whoever sent the most recent reply, if any -- shown on
174
- * the list item in place of the "contacted" status badge. */
175
- lastReplyAvatarUrl?: string | null;
176
- /** Name used for the avatar's alt text / tooltip. */
177
- lastReplyByName?: string | null;
178
- }
179
-
180
- export interface EventInquiryRestaurantRef {
181
- id: string;
182
- name: string;
183
- }
184
-
185
- export interface EventInquiriesFeedElement extends HTMLElement {
186
- heading?: string;
187
- channelName?: string;
188
- ablyAuthUrl?: string;
189
- /** Fetches one page of inquiry history, newest-first; Ably only delivers
190
- * events published after the subscription starts, so history has to
191
- * come from a REST call. Omit `cursor` for the first page; each
192
- * subsequent call passes the `created_at` of the oldest loaded inquiry
193
- * so only the next page is fetched. Return fewer than `limit` items to
194
- * signal there's nothing older left. */
195
- fetchInquiries?: (params: { cursor?: string; limit: number }) => Promise<EventInquiry[]>;
196
- /** Resolves restaurant_id -> display name; the inquiry payload only
197
- * carries the id, since the source event is property-wide across
198
- * restaurants. */
199
- restaurants?: EventInquiryRestaurantRef[];
200
- maxItems?: number;
201
- /** Set false to stop fetching further pages once the first one loads --
202
- * disables the scroll-to-bottom sentinel entirely rather than swapping
203
- * in a manual button. Default true. */
204
- autoLoadMore?: boolean;
205
- emptyLabel?: string;
206
- /** Set false to hide the guest email address on list items. Must be set
207
- * as a JS property to get false -- Boolean-typed custom-element
208
- * attributes are presence-based. Default true. */
209
- showEmail?: boolean;
210
- }
211
-
212
- export interface EventInquiryPayload {
213
- name: string;
214
- email: string;
215
- phone: string;
216
- /** Null when requireDate is false and no date was picked. */
217
- date: string | null;
218
- guests: number;
219
- eventType: string;
220
- format: string;
221
- message: string;
222
- }
223
-
224
- export type SubmitInquiryResult = { ok: true } | { ok: false; error?: string };
225
-
226
- export interface EventContactFormStrings {
227
- nameLabel: string;
228
- namePlaceholder: string;
229
- emailLabel: string;
230
- emailPlaceholder: string;
231
- emailInvalidTitle: string;
232
- phoneLabel: string;
233
- phonePlaceholder: string;
234
- eventTypeLabel: string;
235
- selectOption: string;
236
- guestsLabel: string;
237
- guestsMinimumHint: string;
238
- decreaseGuestsAriaLabel: string;
239
- increaseGuestsAriaLabel: string;
240
- eventFormatLabel: string;
241
- seatedDinner: string;
242
- cocktailReception: string;
243
- selectDateLabel: string;
244
- previousMonth: string;
245
- nextMonth: string;
246
- selectDateError: string;
247
- messageLabel: string;
248
- messagePlaceholder: string;
249
- sendingLabel: string;
250
- sendLabel: string;
251
- notWiredError: string;
252
- genericSubmitError: string;
253
- successMessage: string;
254
- }
255
-
256
- export interface EventContactFormElement extends HTMLElement {
257
- heading?: string;
258
- tagline?: string;
259
- description?: string;
260
- eventTypes?: string[];
261
- minGuests?: number;
262
- /** Whether a date must be picked before the form can submit. Default true,
263
- * matching this component's original wedding/event-venue behavior. Set
264
- * false for venues that also take undated general enquiries (e.g. "do you
265
- * do X?") -- the calendar stays visible so a preferred date can still be
266
- * given, but payload.date is null when none is picked. Must be set as a
267
- * JS property to get false -- Boolean-typed custom-element attributes are
268
- * presence-based, so `require-date="false"` as a plain HTML attribute is
269
- * still truthy. */
270
- requireDate?: boolean;
271
- /** Whether the Event Format (Seated Dinner / Cocktail Reception) toggle is
272
- * shown. Default true. Set false for venues this distinction doesn't apply
273
- * to (e.g. a barbershop's group booking) -- payload.format is then always
274
- * ''. The two format values themselves are not configurable: they're a
275
- * fixed pair, not a general-purpose field. Must be set as a JS property to
276
- * get false, same as requireDate above. */
277
- showFormat?: boolean;
278
- /** Whether the Event Type / Occasion field is shown at all. Default true.
279
- * Unlike showFormat, there is no way to make this field optional-but-visible
280
- * -- it's always been unvalidated (never blocked submit), so the only
281
- * meaningful states are "shown" and "not shown". When false, payload.eventType
282
- * is always ''. Must be set as a JS property to get false, same as above. */
283
- showEventType?: boolean;
284
- /** Whether the Guests stepper (and, if shown, the Event Format toggle nested
285
- * inside it) is shown at all. Default true. When false, payload.guests is
286
- * always minGuests (the stepper never gets a chance to change it) and
287
- * showFormat has no effect, since its toggle lives inside this block. Must
288
- * be set as a JS property to get false, same as above. */
289
- showGuests?: boolean;
290
- /** Whether the date calendar is shown at all. Default true. Distinct from
291
- * requireDate, which only controls whether a visible calendar is mandatory
292
- * -- this removes the field entirely. Forces requireDate's effect off
293
- * regardless of its own value (a hidden field can't be required), so
294
- * payload.date is always null when this is false. Must be set as a JS
295
- * property to get false, same as above. */
296
- showDate?: boolean;
297
- submitNote?: string;
298
- /** BCP 47 locale (e.g. "fr-FR") used to format month/weekday names in the
299
- * calendar; defaults to the browser's own locale. */
300
- locale?: string;
301
- strings?: Partial<EventContactFormStrings>;
302
- submitInquiry?: (payload: EventInquiryPayload) => Promise<SubmitInquiryResult>;
303
- }
304
-
305
- export interface ReservationCancelPayload {
306
- reservationId: string;
307
- restaurantSlug?: string;
308
- }
309
- export type CancelActionResult = { ok: true } | { ok: false; status?: number };
310
-
311
- export interface ReservationCancelStrings {
312
- cancelReservation: string;
313
- requestSuccess: string;
314
- noEmailOnFile: string;
315
- requestError: string;
316
- dialogEyebrow: string;
317
- dialogBody: string;
318
- nevermind: string;
319
- continueLabel: string;
320
- confirmSuccess: string;
321
- expiresIn: string;
322
- confirmButton: string;
323
- confirmError: string;
324
- resendSuccess: string;
325
- linkExpired: string;
326
- resendButton: string;
327
- resendError: string;
328
- }
329
-
330
- export interface ReservationCancelElement extends HTMLElement {
331
- mode?: 'request' | 'confirm';
332
- reservationId?: string;
333
- restaurantSlug?: string;
334
- token?: string;
335
- expiresAt?: number;
336
- initiallyExpired?: boolean;
337
- strings?: Partial<ReservationCancelStrings>;
338
- requestCancel?: (payload: ReservationCancelPayload) => Promise<CancelActionResult>;
339
- confirmCancel?: (payload: { token: string }) => Promise<CancelActionResult>;
340
- }
341
-
342
- export interface LiveReservation {
343
- id: string;
344
- restaurant_id?: string | null;
345
- reservation_date: string;
346
- start_time: string;
347
- party_size: number;
348
- status: string;
349
- contact_name: string;
350
- contact_email?: string | null;
351
- contact_phone?: string | null;
352
- table_number?: string | null;
353
- location?: string | null;
354
- created_at: string;
355
- }
356
-
357
- export interface LiveReservationRestaurantRef {
358
- id: string;
359
- name: string;
360
- }
361
-
362
- export interface LiveReservationsFeedDateFilterEventDetail {
363
- filterDateFrom?: string;
364
- filterDateTo?: string;
365
- }
366
-
367
- export interface LiveReservationsFeedElement extends HTMLElement {
368
- heading?: string;
369
- channelName?: string;
370
- ablyAuthUrl?: string;
371
- /** Fetches one page of reservation history, newest-first; Ably only
372
- * delivers events published after the subscription starts, so history
373
- * has to come from a REST call. Omit `cursor` for the first page; each
374
- * subsequent call passes the `created_at` of the oldest loaded
375
- * reservation so only the next page is fetched. Return fewer than
376
- * `limit` items to signal there's nothing older left. */
377
- fetchReservations?: (params: {
378
- cursor?: string;
379
- limit: number;
380
- filterDateFrom?: string;
381
- filterDateTo?: string;
382
- }) => Promise<LiveReservation[]>;
383
- restaurants?: LiveReservationRestaurantRef[];
384
- maxItems?: number;
385
- /** Set false to stop fetching further pages once the first one loads --
386
- * disables the scroll-to-bottom sentinel entirely rather than swapping
387
- * in a manual button. Default true. */
388
- autoLoadMore?: boolean;
389
- emptyLabel?: string;
390
- /** Set false to hide the guest email address on list items. Must be set
391
- * as a JS property to get false -- Boolean-typed custom-element
392
- * attributes are presence-based. Default true. */
393
- showEmail?: boolean;
394
- /** Restricts the feed to reservations booked for this date range
395
- * (inclusive, 'YYYY-MM-DD'), filtering by reservation_date -- not
396
- * created_at, which stays reserved for pagination. Either bound can be
397
- * set alone for an open-ended range. Passed through to
398
- * fetchReservations so the host can scope the query server-side;
399
- * changing either value triggers a full refetch. Also guards live
400
- * new-reservation / reservation-status-changed events. */
401
- filterDateFrom?: string;
402
- filterDateTo?: string;
403
- /** Set true to render a built-in from/to date picker in the header,
404
- * seeded from filterDateFrom/filterDateTo and driving the same
405
- * filtering -- for host pages that don't want to build their own
406
- * filter UI. Leave false (default) to filter purely via the props.
407
- * Each input's min/max is cross-constrained by the other's current
408
- * value so an inverted range can't be picked. Picking a date emits
409
- * `date-filter-change` with the new range. */
410
- showDateFilter?: boolean;
411
- }
412
-
413
- export interface LiveRoomBooking {
414
- id: string;
415
- first_name: string;
416
- last_name: string;
417
- email?: string | null;
418
- phone?: string | null;
419
- room_number?: string | null;
420
- room_type_name?: string | null;
421
- check_in: string;
422
- check_out: string;
423
- guests: number;
424
- total_price?: string | number | null;
425
- status: string;
426
- created_at: string;
427
- }
428
-
429
- export interface LiveRoomBookingsFeedElement extends HTMLElement {
430
- heading?: string;
431
- channelName?: string;
432
- ablyAuthUrl?: string;
433
- /** Fetches one page of booking history, newest-first; Ably only
434
- * delivers events published after the subscription starts, so history
435
- * has to come from a REST call. Omit `cursor` for the first page; each
436
- * subsequent call passes the `created_at` of the oldest loaded
437
- * booking so only the next page is fetched. Return fewer than
438
- * `limit` items to signal there's nothing older left. */
439
- fetchBookings?: (params: { cursor?: string; limit: number }) => Promise<LiveRoomBooking[]>;
440
- maxItems?: number;
441
- /** Set false to stop fetching further pages once the first one loads --
442
- * disables the scroll-to-bottom sentinel entirely rather than swapping
443
- * in a manual button. Default true. */
444
- autoLoadMore?: boolean;
445
- emptyLabel?: string;
446
- /** Set false to hide the guest email address on list items. Must be set
447
- * as a JS property to get false -- Boolean-typed custom-element
448
- * attributes are presence-based. Default true. */
449
- showEmail?: boolean;
450
- /** Symbol prefixed to total_price ('€', '$', ...); the PMS payload
451
- * carries no currency of its own. Default empty (bare amount). */
452
- currency?: string;
453
- }
454
-
455
- export interface LiveSpaBooking {
456
- id: string;
457
- first_name: string;
458
- last_name: string;
459
- email?: string | null;
460
- phone?: string | null;
461
- treatment_name?: string | null;
462
- therapist_name?: string | null;
463
- start_time: string;
464
- duration_minutes: number;
465
- price?: string | number | null;
466
- status: string;
467
- created_at: string;
468
- }
469
-
470
- export interface LiveSpaBookingsFeedElement extends HTMLElement {
471
- heading?: string;
472
- channelName?: string;
473
- ablyAuthUrl?: string;
474
- /** Fetches one page of booking history, newest-first; Ably only
475
- * delivers events published after the subscription starts, so history
476
- * has to come from a REST call. Omit `cursor` for the first page; each
477
- * subsequent call passes the `created_at` of the oldest loaded
478
- * booking so only the next page is fetched. Return fewer than
479
- * `limit` items to signal there's nothing older left. */
480
- fetchBookings?: (params: { cursor?: string; limit: number }) => Promise<LiveSpaBooking[]>;
481
- maxItems?: number;
482
- /** Set false to stop fetching further pages once the first one loads --
483
- * disables the scroll-to-bottom sentinel entirely rather than swapping
484
- * in a manual button. Default true. */
485
- autoLoadMore?: boolean;
486
- emptyLabel?: string;
487
- /** Set false to hide the guest email address on list items. Must be set
488
- * as a JS property to get false -- Boolean-typed custom-element
489
- * attributes are presence-based. Default true. */
490
- showEmail?: boolean;
491
- /** Symbol prefixed to price ('€', '$', ...); the payload carries no
492
- * currency of its own. Default empty (bare amount). */
493
- currency?: string;
494
- }
495
-
496
- export interface GolfShopItem {
497
- id?: string;
498
- item_id?: string;
499
- item_name: string;
500
- quantity: number;
501
- unit_price?: string | number;
502
- total?: string | number;
503
- variant?: string | null;
504
- }
505
-
506
- export interface GolfBooking {
507
- id: string;
508
- first_name: string;
509
- last_name: string;
510
- email?: string | null;
511
- phone?: string | null;
512
- course_name?: string | null;
513
- tee_time: string;
514
- players: number;
515
- holes?: number | null;
516
- price?: string | number | null;
517
- /** Pro-shop purchases attached to the booking; omit or leave empty for a
518
- * plain tee-time booking with nothing purchased alongside it. */
519
- items?: GolfShopItem[];
520
- status: string;
521
- created_at: string;
522
- }
523
-
524
- export interface LiveGolfBookingsFeedElement extends HTMLElement {
525
- heading?: string;
526
- channelName?: string;
527
- ablyAuthUrl?: string;
528
- /** Fetches one page of booking history, newest-first; Ably only
529
- * delivers events published after the subscription starts, so history
530
- * has to come from a REST call. Omit `cursor` for the first page; each
531
- * subsequent call passes the `created_at` of the oldest loaded
532
- * booking so only the next page is fetched. Return fewer than
533
- * `limit` items to signal there's nothing older left. */
534
- fetchBookings?: (params: { cursor?: string; limit: number }) => Promise<GolfBooking[]>;
535
- maxItems?: number;
536
- /** Set false to stop fetching further pages once the first one loads --
537
- * disables the scroll-to-bottom sentinel entirely rather than swapping
538
- * in a manual button. Default true. */
539
- autoLoadMore?: boolean;
540
- emptyLabel?: string;
541
- /** Set false to hide the guest email address on list items. Must be set
542
- * as a JS property to get false -- Boolean-typed custom-element
543
- * attributes are presence-based. Default true. */
544
- showEmail?: boolean;
545
- /** Symbol prefixed to price ('€', '$', ...); the payload carries no
546
- * currency of its own. Default empty (bare amount). */
547
- currency?: string;
548
- }
549
-
550
- export interface DiningOrderItem {
551
- id?: string;
552
- item_id?: string;
553
- item_name: string;
554
- quantity: number;
555
- unit_price?: string | number;
556
- total?: string | number;
557
- variant?: string | null;
558
- }
559
-
560
- export interface DiningOrder {
561
- id: string;
562
- restaurant_id: string;
563
- booking_id?: string | null;
564
- table_id?: string | null;
565
- /** Joined from restaurant_table by OTA; absent on older payloads. */
566
- table_number?: string | null;
567
- guest_id?: string | null;
568
- status: string;
569
- notes?: string | null;
570
- scheduled_for?: string | null;
571
- total_price: string | number;
572
- created_at: string;
573
- items: DiningOrderItem[];
574
- }
575
-
576
- export interface DiningOrderBookingRef {
577
- id: string;
578
- room_number?: string | null;
579
- guest_name?: string | null;
580
- }
581
-
582
- export interface LiveDiningOrdersFeedElement extends HTMLElement {
583
- heading?: string;
584
- channelName?: string;
585
- ablyAuthUrl?: string;
586
- /** Fetches one page of order history, newest-first; Ably only delivers
587
- * events published after the subscription starts, so history has to
588
- * come from a REST call. Omit `cursor` for the first page; each
589
- * subsequent call passes the `created_at` of the oldest loaded order so
590
- * only the next page is fetched. Return fewer than `limit` items to
591
- * signal there's nothing older left. */
592
- fetchOrders?: (params: { cursor?: string; limit: number }) => Promise<DiningOrder[]>;
593
- /** Resolves booking_id -> room/guest, since the order payload carries
594
- * only the id. */
595
- bookings?: DiningOrderBookingRef[];
596
- maxItems?: number;
597
- /** Set false to stop fetching further pages once the first one loads --
598
- * disables the scroll-to-bottom sentinel entirely rather than swapping
599
- * in a manual button. Default true. */
600
- autoLoadMore?: boolean;
601
- emptyLabel?: string;
602
- /** Symbol prefixed to money amounts ('€', '$', ...); the payload carries
603
- * no currency of its own. Default empty (bare amount). */
604
- currency?: string;
605
- /** Restricts the feed to one booking's orders -- `fetchOrders` should
606
- * already be scoped server-side; this only filters the live `new-order`
607
- * events, which otherwise arrive unfiltered off the channel. */
608
- filterBookingId?: string;
609
- }
610
-
611
- export interface VenueCarouselSlide {
612
- key: string;
613
- src: string;
614
- label?: string;
615
- tag?: string;
616
- description?: string;
617
- }
618
-
619
- export interface VenueCarouselSlideEventDetail {
620
- index: number;
621
- key: string;
622
- }
623
-
624
- export interface VenueCarouselElement extends HTMLElement {
625
- slides?: VenueCarouselSlide[];
626
- activeIndex?: number;
627
- reserveLabel?: string;
628
- previousLabel?: string;
629
- nextLabel?: string;
630
- }
631
-
632
- export interface RoomGridRoom {
633
- slug: string;
634
- name: string;
635
- description?: string;
636
- /** Nightly from-price as a number; formatted internally. */
637
- price?: number;
638
- /** Resolved image URL — the component is backend-agnostic. */
639
- image?: string;
640
- /** Filter-tab grouping, e.g. "Beach Front". */
641
- tag?: string;
642
- /** Card CTA destination. */
643
- href?: string;
644
- }
645
-
646
- export interface RoomGridDatesEventDetail {
647
- checkIn: string;
648
- checkOut: string;
649
- }
650
-
651
- export interface RoomGridRoomEventDetail {
652
- slug: string;
653
- }
654
-
655
- export interface RoomGridFilterTab {
656
- label: string;
657
- slug: string;
658
- }
659
-
660
- export interface RoomGridElement extends HTMLElement {
661
- rooms?: RoomGridRoom[];
662
- filterTabs?: RoomGridFilterTab[];
663
- checkIn?: string;
664
- checkOut?: string;
665
- currency?: string;
666
- allLabel?: string;
667
- variant?: 'grid' | 'rows';
668
- }
669
-
670
- export interface AvailabilityDay {
671
- date: string;
672
- available_rooms?: string | number;
673
- min_rate?: string | number;
674
- }
675
-
676
- export interface AvailabilityCalendarElement extends HTMLElement {
677
- checkIn?: string;
678
- checkOut?: string;
679
- horizonMonths?: number;
680
- fetchAvailability?: (fromISO: string, toISO: string) => Promise<AvailabilityDay[]>;
681
- }
682
-
683
- export interface BookingCardReserveDetail {
684
- checkIn: string;
685
- checkOut: string;
686
- guests: number;
687
- }
688
-
689
- export interface BookingCardElement extends HTMLElement {
690
- price?: number;
691
- maxGuests?: number;
692
- currency?: string;
693
- resortFeeRate?: number;
694
- ratingText?: string;
695
- finePrint?: string;
696
- conciergeTitle?: string;
697
- conciergeSub?: string;
698
- conciergeCtaLabel?: string;
699
- conciergeCtaHref?: string;
700
- checkIn?: string;
701
- checkOut?: string;
702
- guests?: number;
703
- horizonMonths?: number;
704
- fetchAvailability?: (fromISO: string, toISO: string) => Promise<AvailabilityDay[]>;
705
- }
706
-
707
- export interface IslandMapPin {
708
- label: string;
709
- sub?: string;
710
- lat: number;
711
- lng: number;
712
- /** Key for highlightPin()/unhighlightPin() — e.g. a room slug. */
713
- slug?: string;
714
- }
715
-
716
- export interface IslandMapElement extends HTMLElement {
717
- pins?: IslandMapPin[];
718
- /** "lat,lng" */
719
- center?: string;
720
- zoom?: number;
721
- mapId?: string;
722
- /** Used only when no Google Maps loader is already present on the page. */
723
- apiKey?: string;
724
- colorScheme?: 'DARK' | 'LIGHT' | 'FOLLOW_SYSTEM';
725
- fitPins?: boolean;
726
- fitPadding?: number;
727
- panY?: number;
728
- interactionLock?: boolean;
729
- pinsVisible?: boolean;
730
- highlightPin?: (slug: string) => void;
731
- unhighlightPin?: (slug: string) => void;
732
- }
733
-
734
- export interface FloorPlanRoom {
735
- id: string;
736
- type?: string;
737
- label?: string;
738
- x: number;
739
- y: number;
740
- w: number;
741
- h: number;
742
- sqm?: string | number;
743
- imgUrl?: string;
744
- }
745
-
746
- export interface FloorPlanItem {
747
- id: string;
748
- asset: string;
749
- x: number;
750
- y: number;
751
- w: number;
752
- h: number;
753
- }
754
-
755
- export interface FloorPlan {
756
- rooms?: FloorPlanRoom[];
757
- items?: FloorPlanItem[];
758
- w?: number;
759
- h?: number;
760
- }
761
-
762
- export interface FloorPlanZoneEventDetail {
763
- img?: string;
764
- label?: string;
765
- type?: string;
766
- }
767
-
768
- export interface FloorPlanHeroElement extends HTMLElement {
769
- plan?: FloorPlan;
770
- bgUrl?: string;
771
- assetBase?: string;
772
- label?: string;
773
- closeLabel?: string;
774
- open?: () => void;
775
- close?: () => void;
776
- toggle?: () => void;
777
- }
778
-
779
- export interface StaySearchRoom {
780
- roomTypeId: string;
781
- name: string;
782
- description?: string;
783
- image?: string;
784
- href?: string;
785
- }
786
-
787
- export interface StaySearchResult {
788
- room_type_id: string;
789
- name?: string;
790
- description?: string;
791
- from_rate?: string | number;
792
- min_available?: string | number;
793
- }
794
-
795
- export interface StaySearchElement extends HTMLElement {
796
- rooms?: StaySearchRoom[];
797
- checkIn?: string;
798
- checkOut?: string;
799
- guests?: number;
800
- currency?: string;
801
- appendDatesToHref?: boolean;
802
- searchStays?: (checkIn: string, checkOut: string, guests: number) => Promise<StaySearchResult[]>;
803
- }
804
-
805
- export interface DiningTeaserVenue {
806
- key: string;
807
- name: string;
808
- tag?: string;
809
- image: string;
810
- }
811
-
812
- export interface DiningTeaserVenueEventDetail {
813
- index: number;
814
- key: string;
815
- }
816
-
817
- export interface DiningTeaserElement extends HTMLElement {
818
- venues?: DiningTeaserVenue[];
819
- heading?: string;
820
- headingAccent?: string;
821
- body?: string;
822
- ctaLabel?: string;
823
- ctaHref?: string;
824
- imageLabel?: string;
825
- activeIndex?: number;
826
- }
827
-
828
- export interface BookingCheckoutLocation {
829
- iso2: string;
830
- countryName: string;
831
- dialCode: string;
832
- }
833
-
834
- export interface BookingCheckoutPrefill {
835
- firstName?: string;
836
- lastName?: string;
837
- email?: string;
838
- }
839
-
840
- export interface BookingCheckoutResolvedLocation {
841
- countryName: string;
842
- iso2?: string;
843
- dialCode?: string;
844
- }
845
-
846
- export interface BookingCheckoutPayload {
847
- paymentIntentId: string;
848
- bookingFor: 'myself' | 'other';
849
- contactName: string;
850
- contactEmail: string;
851
- contactPhone: string;
852
- country: string;
853
- countryIso2: string;
854
- requests?: string;
855
- booker?: { name: string; email: string; phone: string };
856
- }
857
-
858
- export type BookingCheckoutResult = { ok: true; ref?: string } | { ok: false; message: string };
859
-
860
- export interface BookingCheckoutElement extends HTMLElement {
861
- total?: number;
862
- currency?: string;
863
- clientSecret?: string;
864
- stripePublishableKey?: string;
865
- stripeAppearance?: Record<string, unknown>;
866
- cancellationPolicy?: string;
867
- locations?: BookingCheckoutLocation[];
868
- defaultPhoneCode?: string;
869
- allowBookingForOther?: boolean;
870
- termsHref?: string;
871
- prefill?: BookingCheckoutPrefill;
872
- resolveInitialLocation?: () => Promise<BookingCheckoutResolvedLocation | null>;
873
- beforeSubmit?: () => Promise<void>;
874
- submitBooking?: (payload: BookingCheckoutPayload) => Promise<BookingCheckoutResult>;
875
- strings?: Record<string, string>;
876
- }
877
-
878
- export interface ConfirmationCardElement extends HTMLElement {
879
- eyebrow?: string;
880
- heading?: string;
881
- body?: string;
882
- reference?: string;
883
- referenceLabel?: string;
884
- primaryLabel?: string;
885
- primaryHref?: string;
886
- secondaryLabel?: string;
887
- secondaryHref?: string;
888
- notePrefix?: string;
889
- noteLinkLabel?: string;
890
- noteLinkHref?: string;
891
- noteSuffix?: string;
892
- }
893
-
894
- export interface ExtraItem {
895
- id: string;
896
- name: string;
897
- slug?: string;
898
- description?: string;
899
- category?: string;
900
- image?: string;
901
- imageEmail?: string;
902
- price: number | null;
903
- }
904
-
905
- export interface ExtrasCatalogueSelection {
906
- id: string;
907
- name: string;
908
- price: number | null;
909
- quantity: number;
910
- imageEmail?: string;
911
- }
912
-
913
- export interface ExtrasCatalogueChangeEventDetail {
914
- items: ExtrasCatalogueSelection[];
915
- total: number;
916
- }
917
-
918
- export interface ExtrasCatalogueElement extends HTMLElement {
919
- extras?: ExtraItem[];
920
- currency?: string;
921
- categories?: string[];
922
- fallbackCategory?: string;
923
- initial?: { id: string; quantity: number }[];
924
- }
925
-
926
- export interface BookingProgressStep {
927
- label: string;
928
- href?: string;
929
- }
930
-
931
- export interface BookingProgressBarElement extends HTMLElement {
932
- steps?: BookingProgressStep[];
933
- activeStep?: number;
934
- secureLabel?: string;
935
- logoHref?: string;
936
- }
937
-
938
- export interface BookingSummaryExtraItem {
939
- name: string;
940
- price: number;
941
- quantity: number;
942
- }
943
-
944
- export interface BookingSummaryElement extends HTMLElement {
945
- resortName?: string;
946
- logoSrc?: string;
947
- checkIn?: string;
948
- checkOut?: string;
949
- roomImage?: string | null;
950
- roomName?: string;
951
- roomEmptyLabel?: string;
952
- guests?: number;
953
- nightlyRate?: number;
954
- currency?: string;
955
- totalLabel?: string;
956
- /** String pseudo-boolean, not a real Boolean-typed attribute: pass the
957
- * literal attribute value "false" to hide the rate line -- any other
958
- * value, including bare attribute presence, renders it. */
959
- showRate?: string;
960
- extrasLabel?: string;
961
- extrasItems?: BookingSummaryExtraItem[];
962
- continueHref?: string;
963
- continueLabelSelected?: string;
964
- continueLabelEmpty?: string;
965
- needHelpHref?: string;
966
- }
967
-
968
- export interface SearchableSelectOption {
969
- value: string;
970
- label: string;
971
- /** Passed through untouched on the `change` event -- e.g. an ISO2 code alongside a country name. */
972
- meta?: unknown;
973
- }
974
-
975
- export interface SearchableSelectChangeEventDetail {
976
- value: string;
977
- label: string;
978
- meta?: unknown;
979
- }
980
-
981
- export interface SearchableSelectElement extends HTMLElement {
982
- options?: SearchableSelectOption[];
983
- value?: string;
984
- placeholder?: string;
985
- variant?: 'phone' | 'country' | '';
986
- searchPlaceholder?: string;
987
- }
988
-
989
- export interface BookingConfirmedElement extends HTMLElement {
990
- heading?: string;
991
- detail?: string;
992
- note?: string;
993
- doneLabel?: string;
994
- secondaryLabel?: string;
995
- secondaryHref?: string;
996
- }
997
-
998
- export interface RestaurantOrderMenuItem {
999
- id: string;
1000
- name: string;
1001
- description?: string;
1002
- category: string;
1003
- price: number | string;
1004
- allergens?: string[];
1005
- variants?: string[];
1006
- }
1007
-
1008
- export interface RestaurantOrderCartItem {
1009
- item_id: string;
1010
- quantity: number;
1011
- variant?: string;
1012
- }
1013
-
1014
- export interface RestaurantOrderPlaceParams {
1015
- items: RestaurantOrderCartItem[];
1016
- scheduledFor: string | null;
1017
- }
1018
-
1019
- export interface RestaurantOrderPlaceResult {
1020
- ok: boolean;
1021
- error?: string;
1022
- order?: unknown;
1023
- }
1024
-
1025
- export interface RestaurantOrderPlacedEventDetail {
1026
- items: RestaurantOrderCartItem[];
1027
- total: number;
1028
- scheduledFor: string | null;
1029
- order: unknown;
1030
- }
1031
-
1032
- export interface RestaurantOrderFormElement extends HTMLElement {
1033
- items?: RestaurantOrderMenuItem[];
1034
- categoryOrder?: string[];
1035
- categoryLabels?: Record<string, string>;
1036
- currency?: string;
1037
- iconFor?: (tag: string) => string | undefined;
1038
- checkIn?: string;
1039
- checkOut?: string;
1040
- placeOrder?: (params: RestaurantOrderPlaceParams) => Promise<RestaurantOrderPlaceResult>;
1041
- }
1042
-
1043
- declare global {
1044
- interface HTMLElementTagNameMap {
1045
- 'reservation-bar': ReservationBarElement;
1046
- 'booking-flow': BookingFlowElement;
1047
- 'menu-section': MenuSectionElement;
1048
- 'event-inquiries-feed': EventInquiriesFeedElement;
1049
- 'event-contact-form': EventContactFormElement;
1050
- 'reservation-cancel': ReservationCancelElement;
1051
- 'live-reservations-feed': LiveReservationsFeedElement;
1052
- 'live-room-bookings-feed': LiveRoomBookingsFeedElement;
1053
- 'live-spa-bookings-feed': LiveSpaBookingsFeedElement;
1054
- 'live-golf-bookings-feed': LiveGolfBookingsFeedElement;
1055
- 'live-dining-orders-feed': LiveDiningOrdersFeedElement;
1056
- 'venue-carousel': VenueCarouselElement;
1057
- 'room-grid': RoomGridElement;
1058
- 'island-map': IslandMapElement;
1059
- 'floor-plan-hero': FloorPlanHeroElement;
1060
- 'stay-search': StaySearchElement;
1061
- 'availability-calendar': AvailabilityCalendarElement;
1062
- 'booking-card': BookingCardElement;
1063
- 'dining-teaser': DiningTeaserElement;
1064
- 'booking-checkout': BookingCheckoutElement;
1065
- 'confirmation-card': ConfirmationCardElement;
1066
- 'extras-catalogue': ExtrasCatalogueElement;
1067
- 'booking-progress-bar': BookingProgressBarElement;
1068
- 'booking-summary': BookingSummaryElement;
1069
- 'searchable-select': SearchableSelectElement;
1070
- 'booking-confirmed': BookingConfirmedElement;
1071
- 'restaurant-order-form': RestaurantOrderFormElement;
1072
- }
1073
-
1074
- interface HTMLElementEventMap {
1075
- reservationchange: CustomEvent<{ date: string; guests: number; time: string }>;
1076
- 'booking-confirmed': CustomEvent<{ date: string; time: string; guests: number }>;
1077
- 'inquiry-submitted': CustomEvent<EventInquiryPayload>;
1078
- 'cancel-requested': CustomEvent<ReservationCancelPayload>;
1079
- 'cancel-confirmed': CustomEvent<{ token: string }>;
1080
- 'slide-change': CustomEvent<VenueCarouselSlideEventDetail>;
1081
- 'slide-reserve': CustomEvent<VenueCarouselSlideEventDetail>;
1082
- 'dates-change': CustomEvent<RoomGridDatesEventDetail>;
1083
- 'room-hover': CustomEvent<RoomGridRoomEventDetail>;
1084
- 'room-leave': CustomEvent<RoomGridRoomEventDetail>;
1085
- 'zone-hover': CustomEvent<FloorPlanZoneEventDetail>;
1086
- 'zone-leave': CustomEvent<FloorPlanZoneEventDetail>;
1087
- 'venue-change': CustomEvent<DiningTeaserVenueEventDetail>;
1088
- 'checkout-confirmed': CustomEvent<{ ref?: string }>;
1089
- ready: CustomEvent<undefined>;
1090
- 'extras-change': CustomEvent<ExtrasCatalogueChangeEventDetail>;
1091
- 'order-placed': CustomEvent<RestaurantOrderPlacedEventDetail>;
1092
- 'date-filter-change': CustomEvent<LiveReservationsFeedDateFilterEventDetail>;
1093
- // Not declared here: `searchable-select` dispatches a CustomEvent named
1094
- // literally "change" (SearchableSelectChangeEventDetail, above). Adding
1095
- // it to this global map would override the native `change: Event` type
1096
- // for every element in any consuming codebase (<input>, <select>, ...).
1097
- // Cast at the listener instead: `(e as CustomEvent<SearchableSelectChangeEventDetail>).detail`.
1098
- }
1099
- }
1
+ export interface ReservationBarStrings {
2
+ dateLabel: string;
3
+ guestsLabel: string;
4
+ timeLabel: string;
5
+ guestSingular: string;
6
+ guestPlural: string;
7
+ reserveLabel: string;
8
+ previousMonth: string;
9
+ nextMonth: string;
10
+ selectGuestsAriaLabel: string;
11
+ selectTimeAriaLabel: string;
12
+ }
13
+
14
+ export interface ReservationBarElement extends HTMLElement {
15
+ minGuests?: number;
16
+ maxGuests?: number;
17
+ defaultGuests?: number;
18
+ timeSlots?: string[];
19
+ defaultTime?: string;
20
+ showReserveButton?: boolean;
21
+ reserveHref?: string;
22
+ applyOnSelect?: boolean;
23
+ syncWithUrl?: boolean;
24
+ closedWeekdays?: number[];
25
+ locale?: string;
26
+ strings?: Partial<ReservationBarStrings>;
27
+ fetchAvailableDates?: (partySize: number) => Promise<string[]>;
28
+ }
29
+
30
+ export interface BookingSlot {
31
+ time: string;
32
+ availableTables: number;
33
+ }
34
+
35
+ export interface PaymentIntentResult {
36
+ clientSecret: string;
37
+ paymentIntentId: string;
38
+ amountCents: number;
39
+ }
40
+
41
+ export interface BookingDetails {
42
+ date: string;
43
+ startTime: string;
44
+ timeDisplay: string;
45
+ partySize: number;
46
+ contactName: string;
47
+ contactEmail?: string;
48
+ contactPhone?: string;
49
+ notes?: string;
50
+ customFieldValues?: Record<string, string>;
51
+ stripePaymentIntentId?: string;
52
+ clerkUserId?: string;
53
+ }
54
+
55
+ export type BookResult = { ok: true } | { ok: false; status: number; message: string };
56
+
57
+ export interface BookingFlowCustomField {
58
+ key: string;
59
+ label: string;
60
+ options: string[];
61
+ }
62
+
63
+ export interface BookingFlowStrings {
64
+ pickDateEmpty: string;
65
+ errorEmpty: string;
66
+ noSlotsEmpty: string;
67
+ previousMonth: string;
68
+ nextMonth: string;
69
+ availableTimes: string;
70
+ nameLabel: string;
71
+ emailLabel: string;
72
+ phoneLabel: string;
73
+ selectOption: string;
74
+ paymentMethod: string;
75
+ holdSuffix: string;
76
+ paymentHint: string;
77
+ paymentUnavailable: string;
78
+ notesLabel: string;
79
+ cancellationPolicy: string;
80
+ confirmButton: string;
81
+ processingButton: string;
82
+ paymentNotReady: string;
83
+ cardHoldFailed: string;
84
+ confirmedTitle: string;
85
+ dateLabel: string;
86
+ timeLabel: string;
87
+ partySizeLabel: string;
88
+ guestSingular: string;
89
+ guestPlural: string;
90
+ }
91
+
92
+ export interface BookingFlowElement extends HTMLElement {
93
+ date?: string;
94
+ guests?: number;
95
+ preferredTime?: string;
96
+ closedWeekdays?: number[];
97
+ customFields?: BookingFlowCustomField[];
98
+ /** BCP 47 locale (e.g. "fr-FR") for date/month/weekday formatting and the
99
+ * payment hold amount; defaults to the browser's own locale. */
100
+ locale?: string;
101
+ /** ISO 4217 currency code (e.g. "EUR") for the payment hold amount label. */
102
+ currency?: string;
103
+ /** UI copy overrides; unset keys fall back to English defaults. */
104
+ strings?: Partial<BookingFlowStrings>;
105
+ fetchAvailableDates?: (partySize: number) => Promise<string[]>;
106
+ searchSlots?: (date: string, guests: number) => Promise<BookingSlot[]>;
107
+ createPaymentIntent?: (date: string, guests: number) => Promise<PaymentIntentResult>;
108
+ book?: (details: BookingDetails) => Promise<BookResult>;
109
+ stripePublishableKey?: string;
110
+ stripeAppearance?: Record<string, unknown>;
111
+ }
112
+
113
+ export interface MenuItem {
114
+ name: string;
115
+ description?: string;
116
+ price?: string;
117
+ tagline?: string;
118
+ group?: string;
119
+ country?: string;
120
+ allergens?: string[];
121
+ }
122
+
123
+ export interface MenuCategory {
124
+ label: string;
125
+ items: MenuItem[];
126
+ }
127
+
128
+ export interface MenuInfoItem {
129
+ label: string;
130
+ value: string;
131
+ }
132
+
133
+ export interface Allergen {
134
+ id: string;
135
+ label: string;
136
+ icon: string;
137
+ }
138
+
139
+ export interface MenuSectionElement extends HTMLElement {
140
+ title?: string;
141
+ eyebrow?: string;
142
+ categories?: MenuCategory[];
143
+ info?: MenuInfoItem[];
144
+ allergens?: Allergen[];
145
+ pdfUrl?: string;
146
+ pdfLabel?: string;
147
+ topFlourishSrc?: string;
148
+ tailFlourishSrc?: string;
149
+ headingCircleColor?: string;
150
+ headingAlign?: 'center' | 'left';
151
+ layout?: 'stacked' | 'split';
152
+ variant?: 'default' | 'panel';
153
+ openInNewTabLabel?: string;
154
+ closeAriaLabel?: string;
155
+ groupFilter?: boolean;
156
+ countryFilter?: boolean;
157
+ groupFilterAllLabel?: string;
158
+ countryFilterAllLabel?: string;
159
+ }
160
+
161
+ export interface EventInquiry {
162
+ id: string;
163
+ name: string;
164
+ email: string;
165
+ phone?: string | null;
166
+ restaurant_id?: string | null;
167
+ event_date: string;
168
+ guests?: number | null;
169
+ event_type?: string | null;
170
+ format?: string | null;
171
+ message?: string | null;
172
+ status: string;
173
+ created_at: string;
174
+ /** Avatar of whoever sent the most recent reply, if any -- shown on
175
+ * the list item in place of the "contacted" status badge. */
176
+ lastReplyAvatarUrl?: string | null;
177
+ /** Name used for the avatar's alt text / tooltip. */
178
+ lastReplyByName?: string | null;
179
+ }
180
+
181
+ export interface EventInquiryRestaurantRef {
182
+ id: string;
183
+ name: string;
184
+ }
185
+
186
+ export interface EventInquiriesFeedElement extends HTMLElement {
187
+ heading?: string;
188
+ channelName?: string;
189
+ ablyAuthUrl?: string;
190
+ /** Fetches one page of inquiry history, newest-first; Ably only delivers
191
+ * events published after the subscription starts, so history has to
192
+ * come from a REST call. Omit `cursor` for the first page; each
193
+ * subsequent call passes the `created_at` of the oldest loaded inquiry
194
+ * so only the next page is fetched. Return fewer than `limit` items to
195
+ * signal there's nothing older left. */
196
+ fetchInquiries?: (params: { cursor?: string; limit: number }) => Promise<EventInquiry[]>;
197
+ /** Resolves restaurant_id -> display name; the inquiry payload only
198
+ * carries the id, since the source event is property-wide across
199
+ * restaurants. */
200
+ restaurants?: EventInquiryRestaurantRef[];
201
+ maxItems?: number;
202
+ /** Set false to stop fetching further pages once the first one loads --
203
+ * disables the scroll-to-bottom sentinel entirely rather than swapping
204
+ * in a manual button. Default true. */
205
+ autoLoadMore?: boolean;
206
+ emptyLabel?: string;
207
+ /** Set false to hide the guest email address on list items. Must be set
208
+ * as a JS property to get false -- Boolean-typed custom-element
209
+ * attributes are presence-based. Default true. */
210
+ showEmail?: boolean;
211
+ }
212
+
213
+ export interface EventInquiryPayload {
214
+ name: string;
215
+ email: string;
216
+ phone: string;
217
+ /** Null when requireDate is false and no date was picked. */
218
+ date: string | null;
219
+ guests: number;
220
+ eventType: string;
221
+ format: string;
222
+ message: string;
223
+ }
224
+
225
+ export type SubmitInquiryResult = { ok: true } | { ok: false; error?: string };
226
+
227
+ export interface EventContactFormStrings {
228
+ nameLabel: string;
229
+ namePlaceholder: string;
230
+ emailLabel: string;
231
+ emailPlaceholder: string;
232
+ emailInvalidTitle: string;
233
+ phoneLabel: string;
234
+ phonePlaceholder: string;
235
+ eventTypeLabel: string;
236
+ selectOption: string;
237
+ guestsLabel: string;
238
+ guestsMinimumHint: string;
239
+ decreaseGuestsAriaLabel: string;
240
+ increaseGuestsAriaLabel: string;
241
+ eventFormatLabel: string;
242
+ seatedDinner: string;
243
+ cocktailReception: string;
244
+ selectDateLabel: string;
245
+ previousMonth: string;
246
+ nextMonth: string;
247
+ selectDateError: string;
248
+ messageLabel: string;
249
+ messagePlaceholder: string;
250
+ sendingLabel: string;
251
+ sendLabel: string;
252
+ notWiredError: string;
253
+ genericSubmitError: string;
254
+ successMessage: string;
255
+ }
256
+
257
+ export interface EventContactFormElement extends HTMLElement {
258
+ heading?: string;
259
+ tagline?: string;
260
+ description?: string;
261
+ eventTypes?: string[];
262
+ minGuests?: number;
263
+ /** Whether a date must be picked before the form can submit. Default true,
264
+ * matching this component's original wedding/event-venue behavior. Set
265
+ * false for venues that also take undated general enquiries (e.g. "do you
266
+ * do X?") -- the calendar stays visible so a preferred date can still be
267
+ * given, but payload.date is null when none is picked. Must be set as a
268
+ * JS property to get false -- Boolean-typed custom-element attributes are
269
+ * presence-based, so `require-date="false"` as a plain HTML attribute is
270
+ * still truthy. */
271
+ requireDate?: boolean;
272
+ /** Whether the Event Format (Seated Dinner / Cocktail Reception) toggle is
273
+ * shown. Default true. Set false for venues this distinction doesn't apply
274
+ * to (e.g. a barbershop's group booking) -- payload.format is then always
275
+ * ''. The two format values themselves are not configurable: they're a
276
+ * fixed pair, not a general-purpose field. Must be set as a JS property to
277
+ * get false, same as requireDate above. */
278
+ showFormat?: boolean;
279
+ /** Whether the Event Type / Occasion field is shown at all. Default true.
280
+ * Unlike showFormat, there is no way to make this field optional-but-visible
281
+ * -- it's always been unvalidated (never blocked submit), so the only
282
+ * meaningful states are "shown" and "not shown". When false, payload.eventType
283
+ * is always ''. Must be set as a JS property to get false, same as above. */
284
+ showEventType?: boolean;
285
+ /** Whether the Guests stepper (and, if shown, the Event Format toggle nested
286
+ * inside it) is shown at all. Default true. When false, payload.guests is
287
+ * always minGuests (the stepper never gets a chance to change it) and
288
+ * showFormat has no effect, since its toggle lives inside this block. Must
289
+ * be set as a JS property to get false, same as above. */
290
+ showGuests?: boolean;
291
+ /** Whether the date calendar is shown at all. Default true. Distinct from
292
+ * requireDate, which only controls whether a visible calendar is mandatory
293
+ * -- this removes the field entirely. Forces requireDate's effect off
294
+ * regardless of its own value (a hidden field can't be required), so
295
+ * payload.date is always null when this is false. Must be set as a JS
296
+ * property to get false, same as above. */
297
+ showDate?: boolean;
298
+ submitNote?: string;
299
+ /** BCP 47 locale (e.g. "fr-FR") used to format month/weekday names in the
300
+ * calendar; defaults to the browser's own locale. */
301
+ locale?: string;
302
+ strings?: Partial<EventContactFormStrings>;
303
+ submitInquiry?: (payload: EventInquiryPayload) => Promise<SubmitInquiryResult>;
304
+ }
305
+
306
+ export interface SpaTreatment {
307
+ id: string;
308
+ name: string;
309
+ durationMins: number;
310
+ price: number;
311
+ }
312
+
313
+ export interface SpaAvailabilityDay {
314
+ date: string;
315
+ slots: { time: string }[];
316
+ }
317
+
318
+ export interface SpaBookingPayload {
319
+ treatmentId: string;
320
+ date: string;
321
+ time: string;
322
+ name: string;
323
+ email: string;
324
+ phone: string;
325
+ notes: string;
326
+ }
327
+
328
+ export type SpaBookingResult = { ok: true } | { ok: false; error?: string };
329
+
330
+ export interface SpaBookingWidgetStrings {
331
+ chooseServiceLabel: string;
332
+ loadingServices: string;
333
+ loadServicesError: string;
334
+ previousMonth: string;
335
+ nextMonth: string;
336
+ morningLabel: string;
337
+ afternoonLabel: string;
338
+ eveningLabel: string;
339
+ pickDayHint: string;
340
+ noSlotsForDay: string;
341
+ staffRowLabel: string;
342
+ totalLabel: string;
343
+ continueLabel: string;
344
+ changeServiceLabel: string;
345
+ backLabel: string;
346
+ nameLabel: string;
347
+ namePlaceholder: string;
348
+ emailLabel: string;
349
+ emailPlaceholder: string;
350
+ emailInvalidTitle: string;
351
+ phoneLabel: string;
352
+ phonePlaceholder: string;
353
+ notesLabel: string;
354
+ notesPlaceholder: string;
355
+ confirmingLabel: string;
356
+ confirmLabel: string;
357
+ notWiredError: string;
358
+ genericSubmitError: string;
359
+ successMessage: string;
360
+ }
361
+
362
+ export interface SpaBookingWidgetElement extends HTMLElement {
363
+ heading?: string;
364
+ tagline?: string;
365
+ description?: string;
366
+ /** Shown as a "Staff" row in the summary panel once a service is picked.
367
+ * Purely presentational -- there is no staff picker. Omit to hide the row. */
368
+ staffLabel?: string;
369
+ /** JS `Date#getDay()` values (0 = Sunday) to gray out on the calendar,
370
+ * e.g. `[2]` for a Tuesday closure. Visual hint only -- fetchAvailability
371
+ * is the real source of truth for what's bookable. */
372
+ closedWeekdays?: number[];
373
+ /** BCP 47 locale (e.g. "fr-FR") used to format month/weekday names;
374
+ * defaults to the browser's own locale. */
375
+ locale?: string;
376
+ /** Jumps straight to the calendar step, pre-selected to whichever
377
+ * treatment's name matches this, once treatments have loaded. Re-fires on
378
+ * every change, even mid-flow on a different service or step. */
379
+ initialTreatmentTitle?: string;
380
+ /** Pre-fills the matching contact-details field once a (truthy) value is
381
+ * available, but only while that field is still empty -- never overwrites
382
+ * something the visitor already typed. Applied once per field, no re-fire
383
+ * on later changes (unlike initialTreatmentTitle). */
384
+ initialName?: string;
385
+ initialEmail?: string;
386
+ initialPhone?: string;
387
+ strings?: Partial<SpaBookingWidgetStrings>;
388
+ fetchTreatments?: () => Promise<SpaTreatment[]>;
389
+ /** Host-provided loader for one calendar month at a time:
390
+ * (fromISO, toISO, treatmentId) => per-day bookable start times. */
391
+ fetchAvailability?: (fromISO: string, toISO: string, treatmentId: string) => Promise<SpaAvailabilityDay[]>;
392
+ submitBooking?: (payload: SpaBookingPayload) => Promise<SpaBookingResult>;
393
+ }
394
+
395
+ export interface ReservationCancelPayload {
396
+ reservationId: string;
397
+ restaurantSlug?: string;
398
+ }
399
+ export type CancelActionResult = { ok: true } | { ok: false; status?: number };
400
+
401
+ export interface ReservationCancelStrings {
402
+ cancelReservation: string;
403
+ requestSuccess: string;
404
+ noEmailOnFile: string;
405
+ requestError: string;
406
+ dialogEyebrow: string;
407
+ dialogBody: string;
408
+ nevermind: string;
409
+ continueLabel: string;
410
+ confirmSuccess: string;
411
+ expiresIn: string;
412
+ confirmButton: string;
413
+ confirmError: string;
414
+ resendSuccess: string;
415
+ linkExpired: string;
416
+ resendButton: string;
417
+ resendError: string;
418
+ }
419
+
420
+ export interface ReservationCancelElement extends HTMLElement {
421
+ mode?: 'request' | 'confirm';
422
+ reservationId?: string;
423
+ restaurantSlug?: string;
424
+ token?: string;
425
+ expiresAt?: number;
426
+ initiallyExpired?: boolean;
427
+ strings?: Partial<ReservationCancelStrings>;
428
+ requestCancel?: (payload: ReservationCancelPayload) => Promise<CancelActionResult>;
429
+ confirmCancel?: (payload: { token: string }) => Promise<CancelActionResult>;
430
+ }
431
+
432
+ export interface LiveReservation {
433
+ id: string;
434
+ restaurant_id?: string | null;
435
+ reservation_date: string;
436
+ start_time: string;
437
+ party_size: number;
438
+ status: string;
439
+ contact_name: string;
440
+ contact_email?: string | null;
441
+ contact_phone?: string | null;
442
+ table_number?: string | null;
443
+ location?: string | null;
444
+ created_at: string;
445
+ }
446
+
447
+ export interface LiveReservationRestaurantRef {
448
+ id: string;
449
+ name: string;
450
+ }
451
+
452
+ export interface LiveReservationsFeedDateFilterEventDetail {
453
+ filterDateFrom?: string;
454
+ filterDateTo?: string;
455
+ }
456
+
457
+ export interface LiveReservationsFeedElement extends HTMLElement {
458
+ heading?: string;
459
+ channelName?: string;
460
+ ablyAuthUrl?: string;
461
+ /** Fetches one page of reservation history, newest-first; Ably only
462
+ * delivers events published after the subscription starts, so history
463
+ * has to come from a REST call. Omit `cursor` for the first page; each
464
+ * subsequent call passes the `created_at` of the oldest loaded
465
+ * reservation so only the next page is fetched. Return fewer than
466
+ * `limit` items to signal there's nothing older left. */
467
+ fetchReservations?: (params: {
468
+ cursor?: string;
469
+ limit: number;
470
+ filterDateFrom?: string;
471
+ filterDateTo?: string;
472
+ }) => Promise<LiveReservation[]>;
473
+ restaurants?: LiveReservationRestaurantRef[];
474
+ maxItems?: number;
475
+ /** Set false to stop fetching further pages once the first one loads --
476
+ * disables the scroll-to-bottom sentinel entirely rather than swapping
477
+ * in a manual button. Default true. */
478
+ autoLoadMore?: boolean;
479
+ emptyLabel?: string;
480
+ /** Set false to hide the guest email address on list items. Must be set
481
+ * as a JS property to get false -- Boolean-typed custom-element
482
+ * attributes are presence-based. Default true. */
483
+ showEmail?: boolean;
484
+ /** Restricts the feed to reservations booked for this date range
485
+ * (inclusive, 'YYYY-MM-DD'), filtering by reservation_date -- not
486
+ * created_at, which stays reserved for pagination. Either bound can be
487
+ * set alone for an open-ended range. Passed through to
488
+ * fetchReservations so the host can scope the query server-side;
489
+ * changing either value triggers a full refetch. Also guards live
490
+ * new-reservation / reservation-status-changed events. */
491
+ filterDateFrom?: string;
492
+ filterDateTo?: string;
493
+ /** Set true to render a built-in from/to date picker in the header,
494
+ * seeded from filterDateFrom/filterDateTo and driving the same
495
+ * filtering -- for host pages that don't want to build their own
496
+ * filter UI. Leave false (default) to filter purely via the props.
497
+ * Each input's min/max is cross-constrained by the other's current
498
+ * value so an inverted range can't be picked. Picking a date emits
499
+ * `date-filter-change` with the new range. */
500
+ showDateFilter?: boolean;
501
+ }
502
+
503
+ export interface LiveRoomBooking {
504
+ id: string;
505
+ first_name: string;
506
+ last_name: string;
507
+ email?: string | null;
508
+ phone?: string | null;
509
+ room_number?: string | null;
510
+ room_type_name?: string | null;
511
+ check_in: string;
512
+ check_out: string;
513
+ guests: number;
514
+ total_price?: string | number | null;
515
+ status: string;
516
+ created_at: string;
517
+ }
518
+
519
+ export interface LiveRoomBookingsFeedElement extends HTMLElement {
520
+ heading?: string;
521
+ channelName?: string;
522
+ ablyAuthUrl?: string;
523
+ /** Fetches one page of booking history, newest-first; Ably only
524
+ * delivers events published after the subscription starts, so history
525
+ * has to come from a REST call. Omit `cursor` for the first page; each
526
+ * subsequent call passes the `created_at` of the oldest loaded
527
+ * booking so only the next page is fetched. Return fewer than
528
+ * `limit` items to signal there's nothing older left. */
529
+ fetchBookings?: (params: { cursor?: string; limit: number }) => Promise<LiveRoomBooking[]>;
530
+ maxItems?: number;
531
+ /** Set false to stop fetching further pages once the first one loads --
532
+ * disables the scroll-to-bottom sentinel entirely rather than swapping
533
+ * in a manual button. Default true. */
534
+ autoLoadMore?: boolean;
535
+ emptyLabel?: string;
536
+ /** Set false to hide the guest email address on list items. Must be set
537
+ * as a JS property to get false -- Boolean-typed custom-element
538
+ * attributes are presence-based. Default true. */
539
+ showEmail?: boolean;
540
+ /** Symbol prefixed to total_price ('€', '$', ...); the PMS payload
541
+ * carries no currency of its own. Default empty (bare amount). */
542
+ currency?: string;
543
+ }
544
+
545
+ export interface LiveSpaBooking {
546
+ id: string;
547
+ first_name: string;
548
+ last_name: string;
549
+ email?: string | null;
550
+ phone?: string | null;
551
+ treatment_name?: string | null;
552
+ therapist_name?: string | null;
553
+ start_time: string;
554
+ duration_minutes: number;
555
+ price?: string | number | null;
556
+ status: string;
557
+ created_at: string;
558
+ }
559
+
560
+ export interface LiveSpaBookingsFeedElement extends HTMLElement {
561
+ heading?: string;
562
+ channelName?: string;
563
+ ablyAuthUrl?: string;
564
+ /** Fetches one page of booking history, newest-first; Ably only
565
+ * delivers events published after the subscription starts, so history
566
+ * has to come from a REST call. Omit `cursor` for the first page; each
567
+ * subsequent call passes the `created_at` of the oldest loaded
568
+ * booking so only the next page is fetched. Return fewer than
569
+ * `limit` items to signal there's nothing older left. */
570
+ fetchBookings?: (params: { cursor?: string; limit: number }) => Promise<LiveSpaBooking[]>;
571
+ maxItems?: number;
572
+ /** Set false to stop fetching further pages once the first one loads --
573
+ * disables the scroll-to-bottom sentinel entirely rather than swapping
574
+ * in a manual button. Default true. */
575
+ autoLoadMore?: boolean;
576
+ emptyLabel?: string;
577
+ /** Set false to hide the guest email address on list items. Must be set
578
+ * as a JS property to get false -- Boolean-typed custom-element
579
+ * attributes are presence-based. Default true. */
580
+ showEmail?: boolean;
581
+ /** Symbol prefixed to price ('€', '$', ...); the payload carries no
582
+ * currency of its own. Default empty (bare amount). */
583
+ currency?: string;
584
+ }
585
+
586
+ export interface GolfShopItem {
587
+ id?: string;
588
+ item_id?: string;
589
+ item_name: string;
590
+ quantity: number;
591
+ unit_price?: string | number;
592
+ total?: string | number;
593
+ variant?: string | null;
594
+ }
595
+
596
+ export interface GolfBooking {
597
+ id: string;
598
+ first_name: string;
599
+ last_name: string;
600
+ email?: string | null;
601
+ phone?: string | null;
602
+ course_name?: string | null;
603
+ tee_time: string;
604
+ players: number;
605
+ holes?: number | null;
606
+ price?: string | number | null;
607
+ /** Pro-shop purchases attached to the booking; omit or leave empty for a
608
+ * plain tee-time booking with nothing purchased alongside it. */
609
+ items?: GolfShopItem[];
610
+ status: string;
611
+ created_at: string;
612
+ }
613
+
614
+ export interface LiveGolfBookingsFeedElement extends HTMLElement {
615
+ heading?: string;
616
+ channelName?: string;
617
+ ablyAuthUrl?: string;
618
+ /** Fetches one page of booking history, newest-first; Ably only
619
+ * delivers events published after the subscription starts, so history
620
+ * has to come from a REST call. Omit `cursor` for the first page; each
621
+ * subsequent call passes the `created_at` of the oldest loaded
622
+ * booking so only the next page is fetched. Return fewer than
623
+ * `limit` items to signal there's nothing older left. */
624
+ fetchBookings?: (params: { cursor?: string; limit: number }) => Promise<GolfBooking[]>;
625
+ maxItems?: number;
626
+ /** Set false to stop fetching further pages once the first one loads --
627
+ * disables the scroll-to-bottom sentinel entirely rather than swapping
628
+ * in a manual button. Default true. */
629
+ autoLoadMore?: boolean;
630
+ emptyLabel?: string;
631
+ /** Set false to hide the guest email address on list items. Must be set
632
+ * as a JS property to get false -- Boolean-typed custom-element
633
+ * attributes are presence-based. Default true. */
634
+ showEmail?: boolean;
635
+ /** Symbol prefixed to price ('€', '$', ...); the payload carries no
636
+ * currency of its own. Default empty (bare amount). */
637
+ currency?: string;
638
+ }
639
+
640
+ export interface DiningOrderItem {
641
+ id?: string;
642
+ item_id?: string;
643
+ item_name: string;
644
+ quantity: number;
645
+ unit_price?: string | number;
646
+ total?: string | number;
647
+ variant?: string | null;
648
+ }
649
+
650
+ export interface DiningOrder {
651
+ id: string;
652
+ restaurant_id: string;
653
+ booking_id?: string | null;
654
+ table_id?: string | null;
655
+ /** Joined from restaurant_table by OTA; absent on older payloads. */
656
+ table_number?: string | null;
657
+ guest_id?: string | null;
658
+ status: string;
659
+ notes?: string | null;
660
+ scheduled_for?: string | null;
661
+ total_price: string | number;
662
+ created_at: string;
663
+ items: DiningOrderItem[];
664
+ }
665
+
666
+ export interface DiningOrderBookingRef {
667
+ id: string;
668
+ room_number?: string | null;
669
+ guest_name?: string | null;
670
+ }
671
+
672
+ export interface LiveDiningOrdersFeedElement extends HTMLElement {
673
+ heading?: string;
674
+ channelName?: string;
675
+ ablyAuthUrl?: string;
676
+ /** Fetches one page of order history, newest-first; Ably only delivers
677
+ * events published after the subscription starts, so history has to
678
+ * come from a REST call. Omit `cursor` for the first page; each
679
+ * subsequent call passes the `created_at` of the oldest loaded order so
680
+ * only the next page is fetched. Return fewer than `limit` items to
681
+ * signal there's nothing older left. */
682
+ fetchOrders?: (params: { cursor?: string; limit: number }) => Promise<DiningOrder[]>;
683
+ /** Resolves booking_id -> room/guest, since the order payload carries
684
+ * only the id. */
685
+ bookings?: DiningOrderBookingRef[];
686
+ maxItems?: number;
687
+ /** Set false to stop fetching further pages once the first one loads --
688
+ * disables the scroll-to-bottom sentinel entirely rather than swapping
689
+ * in a manual button. Default true. */
690
+ autoLoadMore?: boolean;
691
+ emptyLabel?: string;
692
+ /** Symbol prefixed to money amounts ('€', '$', ...); the payload carries
693
+ * no currency of its own. Default empty (bare amount). */
694
+ currency?: string;
695
+ /** Restricts the feed to one booking's orders -- `fetchOrders` should
696
+ * already be scoped server-side; this only filters the live `new-order`
697
+ * events, which otherwise arrive unfiltered off the channel. */
698
+ filterBookingId?: string;
699
+ }
700
+
701
+ export interface VenueCarouselSlide {
702
+ key: string;
703
+ src: string;
704
+ label?: string;
705
+ tag?: string;
706
+ description?: string;
707
+ }
708
+
709
+ export interface VenueCarouselSlideEventDetail {
710
+ index: number;
711
+ key: string;
712
+ }
713
+
714
+ export interface VenueCarouselElement extends HTMLElement {
715
+ slides?: VenueCarouselSlide[];
716
+ activeIndex?: number;
717
+ reserveLabel?: string;
718
+ previousLabel?: string;
719
+ nextLabel?: string;
720
+ }
721
+
722
+ export interface RoomGridRoom {
723
+ slug: string;
724
+ name: string;
725
+ description?: string;
726
+ /** Nightly from-price as a number; formatted internally. */
727
+ price?: number;
728
+ /** Resolved image URL — the component is backend-agnostic. */
729
+ image?: string;
730
+ /** Filter-tab grouping, e.g. "Beach Front". */
731
+ tag?: string;
732
+ /** Card CTA destination. */
733
+ href?: string;
734
+ }
735
+
736
+ export interface RoomGridDatesEventDetail {
737
+ checkIn: string;
738
+ checkOut: string;
739
+ }
740
+
741
+ export interface RoomGridRoomEventDetail {
742
+ slug: string;
743
+ }
744
+
745
+ export interface RoomGridFilterTab {
746
+ label: string;
747
+ slug: string;
748
+ }
749
+
750
+ export interface RoomGridElement extends HTMLElement {
751
+ rooms?: RoomGridRoom[];
752
+ filterTabs?: RoomGridFilterTab[];
753
+ checkIn?: string;
754
+ checkOut?: string;
755
+ currency?: string;
756
+ allLabel?: string;
757
+ variant?: 'grid' | 'rows';
758
+ }
759
+
760
+ export interface AvailabilityDay {
761
+ date: string;
762
+ available_rooms?: string | number;
763
+ min_rate?: string | number;
764
+ }
765
+
766
+ export interface AvailabilityCalendarElement extends HTMLElement {
767
+ checkIn?: string;
768
+ checkOut?: string;
769
+ horizonMonths?: number;
770
+ fetchAvailability?: (fromISO: string, toISO: string) => Promise<AvailabilityDay[]>;
771
+ }
772
+
773
+ export interface BookingCardReserveDetail {
774
+ checkIn: string;
775
+ checkOut: string;
776
+ guests: number;
777
+ }
778
+
779
+ export interface BookingCardElement extends HTMLElement {
780
+ price?: number;
781
+ maxGuests?: number;
782
+ currency?: string;
783
+ resortFeeRate?: number;
784
+ ratingText?: string;
785
+ finePrint?: string;
786
+ conciergeTitle?: string;
787
+ conciergeSub?: string;
788
+ conciergeCtaLabel?: string;
789
+ conciergeCtaHref?: string;
790
+ checkIn?: string;
791
+ checkOut?: string;
792
+ guests?: number;
793
+ horizonMonths?: number;
794
+ fetchAvailability?: (fromISO: string, toISO: string) => Promise<AvailabilityDay[]>;
795
+ }
796
+
797
+ export interface IslandMapPin {
798
+ label: string;
799
+ sub?: string;
800
+ lat: number;
801
+ lng: number;
802
+ /** Key for highlightPin()/unhighlightPin() — e.g. a room slug. */
803
+ slug?: string;
804
+ }
805
+
806
+ export interface IslandMapElement extends HTMLElement {
807
+ pins?: IslandMapPin[];
808
+ /** "lat,lng" */
809
+ center?: string;
810
+ zoom?: number;
811
+ mapId?: string;
812
+ /** Used only when no Google Maps loader is already present on the page. */
813
+ apiKey?: string;
814
+ colorScheme?: 'DARK' | 'LIGHT' | 'FOLLOW_SYSTEM';
815
+ fitPins?: boolean;
816
+ fitPadding?: number;
817
+ panY?: number;
818
+ interactionLock?: boolean;
819
+ pinsVisible?: boolean;
820
+ highlightPin?: (slug: string) => void;
821
+ unhighlightPin?: (slug: string) => void;
822
+ }
823
+
824
+ export interface FloorPlanRoom {
825
+ id: string;
826
+ type?: string;
827
+ label?: string;
828
+ x: number;
829
+ y: number;
830
+ w: number;
831
+ h: number;
832
+ sqm?: string | number;
833
+ imgUrl?: string;
834
+ }
835
+
836
+ export interface FloorPlanItem {
837
+ id: string;
838
+ asset: string;
839
+ x: number;
840
+ y: number;
841
+ w: number;
842
+ h: number;
843
+ }
844
+
845
+ export interface FloorPlan {
846
+ rooms?: FloorPlanRoom[];
847
+ items?: FloorPlanItem[];
848
+ w?: number;
849
+ h?: number;
850
+ }
851
+
852
+ export interface FloorPlanZoneEventDetail {
853
+ img?: string;
854
+ label?: string;
855
+ type?: string;
856
+ }
857
+
858
+ export interface FloorPlanHeroElement extends HTMLElement {
859
+ plan?: FloorPlan;
860
+ bgUrl?: string;
861
+ assetBase?: string;
862
+ label?: string;
863
+ closeLabel?: string;
864
+ open?: () => void;
865
+ close?: () => void;
866
+ toggle?: () => void;
867
+ }
868
+
869
+ export interface StaySearchRoom {
870
+ roomTypeId: string;
871
+ name: string;
872
+ description?: string;
873
+ image?: string;
874
+ href?: string;
875
+ }
876
+
877
+ export interface StaySearchResult {
878
+ room_type_id: string;
879
+ name?: string;
880
+ description?: string;
881
+ from_rate?: string | number;
882
+ min_available?: string | number;
883
+ }
884
+
885
+ export interface StaySearchElement extends HTMLElement {
886
+ rooms?: StaySearchRoom[];
887
+ checkIn?: string;
888
+ checkOut?: string;
889
+ guests?: number;
890
+ currency?: string;
891
+ appendDatesToHref?: boolean;
892
+ searchStays?: (checkIn: string, checkOut: string, guests: number) => Promise<StaySearchResult[]>;
893
+ }
894
+
895
+ export interface DiningTeaserVenue {
896
+ key: string;
897
+ name: string;
898
+ tag?: string;
899
+ image: string;
900
+ }
901
+
902
+ export interface DiningTeaserVenueEventDetail {
903
+ index: number;
904
+ key: string;
905
+ }
906
+
907
+ export interface DiningTeaserElement extends HTMLElement {
908
+ venues?: DiningTeaserVenue[];
909
+ heading?: string;
910
+ headingAccent?: string;
911
+ body?: string;
912
+ ctaLabel?: string;
913
+ ctaHref?: string;
914
+ imageLabel?: string;
915
+ activeIndex?: number;
916
+ }
917
+
918
+ export interface BookingCheckoutLocation {
919
+ iso2: string;
920
+ countryName: string;
921
+ dialCode: string;
922
+ }
923
+
924
+ export interface BookingCheckoutPrefill {
925
+ firstName?: string;
926
+ lastName?: string;
927
+ email?: string;
928
+ }
929
+
930
+ export interface BookingCheckoutResolvedLocation {
931
+ countryName: string;
932
+ iso2?: string;
933
+ dialCode?: string;
934
+ }
935
+
936
+ export interface BookingCheckoutPayload {
937
+ paymentIntentId: string;
938
+ bookingFor: 'myself' | 'other';
939
+ contactName: string;
940
+ contactEmail: string;
941
+ contactPhone: string;
942
+ country: string;
943
+ countryIso2: string;
944
+ requests?: string;
945
+ booker?: { name: string; email: string; phone: string };
946
+ }
947
+
948
+ export type BookingCheckoutResult = { ok: true; ref?: string } | { ok: false; message: string };
949
+
950
+ export interface BookingCheckoutElement extends HTMLElement {
951
+ total?: number;
952
+ currency?: string;
953
+ clientSecret?: string;
954
+ stripePublishableKey?: string;
955
+ stripeAppearance?: Record<string, unknown>;
956
+ cancellationPolicy?: string;
957
+ locations?: BookingCheckoutLocation[];
958
+ defaultPhoneCode?: string;
959
+ allowBookingForOther?: boolean;
960
+ termsHref?: string;
961
+ prefill?: BookingCheckoutPrefill;
962
+ resolveInitialLocation?: () => Promise<BookingCheckoutResolvedLocation | null>;
963
+ beforeSubmit?: () => Promise<void>;
964
+ submitBooking?: (payload: BookingCheckoutPayload) => Promise<BookingCheckoutResult>;
965
+ strings?: Record<string, string>;
966
+ }
967
+
968
+ export interface ConfirmationCardElement extends HTMLElement {
969
+ eyebrow?: string;
970
+ heading?: string;
971
+ body?: string;
972
+ reference?: string;
973
+ referenceLabel?: string;
974
+ primaryLabel?: string;
975
+ primaryHref?: string;
976
+ secondaryLabel?: string;
977
+ secondaryHref?: string;
978
+ notePrefix?: string;
979
+ noteLinkLabel?: string;
980
+ noteLinkHref?: string;
981
+ noteSuffix?: string;
982
+ }
983
+
984
+ export interface ExtraItem {
985
+ id: string;
986
+ name: string;
987
+ slug?: string;
988
+ description?: string;
989
+ category?: string;
990
+ image?: string;
991
+ imageEmail?: string;
992
+ price: number | null;
993
+ }
994
+
995
+ export interface ExtrasCatalogueSelection {
996
+ id: string;
997
+ name: string;
998
+ price: number | null;
999
+ quantity: number;
1000
+ imageEmail?: string;
1001
+ }
1002
+
1003
+ export interface ExtrasCatalogueChangeEventDetail {
1004
+ items: ExtrasCatalogueSelection[];
1005
+ total: number;
1006
+ }
1007
+
1008
+ export interface ExtrasCatalogueElement extends HTMLElement {
1009
+ extras?: ExtraItem[];
1010
+ currency?: string;
1011
+ categories?: string[];
1012
+ fallbackCategory?: string;
1013
+ initial?: { id: string; quantity: number }[];
1014
+ }
1015
+
1016
+ export interface BookingProgressStep {
1017
+ label: string;
1018
+ href?: string;
1019
+ }
1020
+
1021
+ export interface BookingProgressBarElement extends HTMLElement {
1022
+ steps?: BookingProgressStep[];
1023
+ activeStep?: number;
1024
+ secureLabel?: string;
1025
+ logoHref?: string;
1026
+ }
1027
+
1028
+ export interface BookingSummaryExtraItem {
1029
+ name: string;
1030
+ price: number;
1031
+ quantity: number;
1032
+ }
1033
+
1034
+ export interface BookingSummaryElement extends HTMLElement {
1035
+ resortName?: string;
1036
+ logoSrc?: string;
1037
+ checkIn?: string;
1038
+ checkOut?: string;
1039
+ roomImage?: string | null;
1040
+ roomName?: string;
1041
+ roomEmptyLabel?: string;
1042
+ guests?: number;
1043
+ nightlyRate?: number;
1044
+ currency?: string;
1045
+ totalLabel?: string;
1046
+ /** String pseudo-boolean, not a real Boolean-typed attribute: pass the
1047
+ * literal attribute value "false" to hide the rate line -- any other
1048
+ * value, including bare attribute presence, renders it. */
1049
+ showRate?: string;
1050
+ extrasLabel?: string;
1051
+ extrasItems?: BookingSummaryExtraItem[];
1052
+ continueHref?: string;
1053
+ continueLabelSelected?: string;
1054
+ continueLabelEmpty?: string;
1055
+ needHelpHref?: string;
1056
+ }
1057
+
1058
+ export interface SearchableSelectOption {
1059
+ value: string;
1060
+ label: string;
1061
+ /** Passed through untouched on the `change` event -- e.g. an ISO2 code alongside a country name. */
1062
+ meta?: unknown;
1063
+ }
1064
+
1065
+ export interface SearchableSelectChangeEventDetail {
1066
+ value: string;
1067
+ label: string;
1068
+ meta?: unknown;
1069
+ }
1070
+
1071
+ export interface SearchableSelectElement extends HTMLElement {
1072
+ options?: SearchableSelectOption[];
1073
+ value?: string;
1074
+ placeholder?: string;
1075
+ variant?: 'phone' | 'country' | '';
1076
+ searchPlaceholder?: string;
1077
+ }
1078
+
1079
+ export interface BookingConfirmedElement extends HTMLElement {
1080
+ heading?: string;
1081
+ detail?: string;
1082
+ note?: string;
1083
+ doneLabel?: string;
1084
+ secondaryLabel?: string;
1085
+ secondaryHref?: string;
1086
+ }
1087
+
1088
+ export interface RestaurantOrderMenuItem {
1089
+ id: string;
1090
+ name: string;
1091
+ description?: string;
1092
+ category: string;
1093
+ price: number | string;
1094
+ allergens?: string[];
1095
+ variants?: string[];
1096
+ }
1097
+
1098
+ export interface RestaurantOrderCartItem {
1099
+ item_id: string;
1100
+ quantity: number;
1101
+ variant?: string;
1102
+ }
1103
+
1104
+ export interface RestaurantOrderPlaceParams {
1105
+ items: RestaurantOrderCartItem[];
1106
+ scheduledFor: string | null;
1107
+ }
1108
+
1109
+ export interface RestaurantOrderPlaceResult {
1110
+ ok: boolean;
1111
+ error?: string;
1112
+ order?: unknown;
1113
+ }
1114
+
1115
+ export interface RestaurantOrderPlacedEventDetail {
1116
+ items: RestaurantOrderCartItem[];
1117
+ total: number;
1118
+ scheduledFor: string | null;
1119
+ order: unknown;
1120
+ }
1121
+
1122
+ export interface RestaurantOrderFormElement extends HTMLElement {
1123
+ items?: RestaurantOrderMenuItem[];
1124
+ categoryOrder?: string[];
1125
+ categoryLabels?: Record<string, string>;
1126
+ currency?: string;
1127
+ iconFor?: (tag: string) => string | undefined;
1128
+ checkIn?: string;
1129
+ checkOut?: string;
1130
+ placeOrder?: (params: RestaurantOrderPlaceParams) => Promise<RestaurantOrderPlaceResult>;
1131
+ }
1132
+
1133
+ declare global {
1134
+ interface HTMLElementTagNameMap {
1135
+ 'reservation-bar': ReservationBarElement;
1136
+ 'booking-flow': BookingFlowElement;
1137
+ 'menu-section': MenuSectionElement;
1138
+ 'event-inquiries-feed': EventInquiriesFeedElement;
1139
+ 'event-contact-form': EventContactFormElement;
1140
+ 'spa-booking-widget': SpaBookingWidgetElement;
1141
+ 'reservation-cancel': ReservationCancelElement;
1142
+ 'live-reservations-feed': LiveReservationsFeedElement;
1143
+ 'live-room-bookings-feed': LiveRoomBookingsFeedElement;
1144
+ 'live-spa-bookings-feed': LiveSpaBookingsFeedElement;
1145
+ 'live-golf-bookings-feed': LiveGolfBookingsFeedElement;
1146
+ 'live-dining-orders-feed': LiveDiningOrdersFeedElement;
1147
+ 'venue-carousel': VenueCarouselElement;
1148
+ 'room-grid': RoomGridElement;
1149
+ 'island-map': IslandMapElement;
1150
+ 'floor-plan-hero': FloorPlanHeroElement;
1151
+ 'stay-search': StaySearchElement;
1152
+ 'availability-calendar': AvailabilityCalendarElement;
1153
+ 'booking-card': BookingCardElement;
1154
+ 'dining-teaser': DiningTeaserElement;
1155
+ 'booking-checkout': BookingCheckoutElement;
1156
+ 'confirmation-card': ConfirmationCardElement;
1157
+ 'extras-catalogue': ExtrasCatalogueElement;
1158
+ 'booking-progress-bar': BookingProgressBarElement;
1159
+ 'booking-summary': BookingSummaryElement;
1160
+ 'searchable-select': SearchableSelectElement;
1161
+ 'booking-confirmed': BookingConfirmedElement;
1162
+ 'restaurant-order-form': RestaurantOrderFormElement;
1163
+ }
1164
+
1165
+ interface HTMLElementEventMap {
1166
+ reservationchange: CustomEvent<{ date: string; guests: number; time: string }>;
1167
+ 'booking-confirmed': CustomEvent<{ date: string; time: string; guests: number }>;
1168
+ 'inquiry-submitted': CustomEvent<EventInquiryPayload>;
1169
+ 'booking-submitted': CustomEvent<SpaBookingPayload>;
1170
+ 'cancel-requested': CustomEvent<ReservationCancelPayload>;
1171
+ 'cancel-confirmed': CustomEvent<{ token: string }>;
1172
+ 'slide-change': CustomEvent<VenueCarouselSlideEventDetail>;
1173
+ 'slide-reserve': CustomEvent<VenueCarouselSlideEventDetail>;
1174
+ 'dates-change': CustomEvent<RoomGridDatesEventDetail>;
1175
+ 'room-hover': CustomEvent<RoomGridRoomEventDetail>;
1176
+ 'room-leave': CustomEvent<RoomGridRoomEventDetail>;
1177
+ 'zone-hover': CustomEvent<FloorPlanZoneEventDetail>;
1178
+ 'zone-leave': CustomEvent<FloorPlanZoneEventDetail>;
1179
+ 'venue-change': CustomEvent<DiningTeaserVenueEventDetail>;
1180
+ 'checkout-confirmed': CustomEvent<{ ref?: string }>;
1181
+ ready: CustomEvent<undefined>;
1182
+ 'extras-change': CustomEvent<ExtrasCatalogueChangeEventDetail>;
1183
+ 'order-placed': CustomEvent<RestaurantOrderPlacedEventDetail>;
1184
+ 'date-filter-change': CustomEvent<LiveReservationsFeedDateFilterEventDetail>;
1185
+ // Not declared here: `searchable-select` dispatches a CustomEvent named
1186
+ // literally "change" (SearchableSelectChangeEventDetail, above). Adding
1187
+ // it to this global map would override the native `change: Event` type
1188
+ // for every element in any consuming codebase (<input>, <select>, ...).
1189
+ // Cast at the listener instead: `(e as CustomEvent<SearchableSelectChangeEventDetail>).detail`.
1190
+ }
1191
+ }