@ticketlayer/elements 0.4.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/cjs/{index-I9CoYv3k.js → index-DanPUX0s.js} +1 -1
  2. package/dist/cjs/index.cjs.js +2 -1
  3. package/dist/cjs/loader.cjs.js +2 -2
  4. package/dist/cjs/ticket-selector-BYVL13k_.js +3623 -0
  5. package/dist/cjs/ticketlayer-elements.cjs.js +2 -2
  6. package/dist/cjs/tl-buy-modal.cjs.entry.js +3 -3
  7. package/dist/cjs/tl-buy-tickets-button.cjs.entry.js +89 -74
  8. package/dist/cjs/tl-buy-tickets-wrapper.cjs.entry.js +61 -30
  9. package/dist/cjs/tl-cart-badge.cjs.entry.js +74 -61
  10. package/dist/cjs/tl-cart-drawer.cjs.entry.js +51 -23
  11. package/dist/cjs/tl-cart.cjs.entry.js +71 -103
  12. package/dist/cjs/tl-checkout-wrapper.cjs.entry.js +51 -29
  13. package/dist/cjs/tl-event-card.cjs.entry.js +43 -105
  14. package/dist/cjs/tl-event-list.cjs.entry.js +72 -35
  15. package/dist/cjs/tl-login.cjs.entry.js +74 -141
  16. package/dist/cjs/tl-my-orders.cjs.entry.js +52 -92
  17. package/dist/cjs/tl-occurrence-selector_2.cjs.entry.js +153 -921
  18. package/dist/cjs/tl-order-confirmation.cjs.entry.js +71 -107
  19. package/dist/cjs/tl-order-tickets_2.cjs.entry.js +48 -74
  20. package/dist/cjs/tl-promo-code.cjs.entry.js +50 -47
  21. package/dist/cjs/tl-seat-map.cjs.entry.js +3 -3
  22. package/dist/cjs/web-query-Ce73jB4k.js +16 -0
  23. package/dist/collection/components/tl-buy-modal/tl-buy-modal.js +2 -2
  24. package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.css +37 -0
  25. package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.js +105 -79
  26. package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.js +73 -30
  27. package/dist/collection/components/tl-cart/tl-cart.js +82 -107
  28. package/dist/collection/components/tl-cart-badge/tl-cart-badge.js +62 -62
  29. package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.js +56 -24
  30. package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.js +61 -29
  31. package/dist/collection/components/tl-event-card/event-card-adapter.js +11 -23
  32. package/dist/collection/components/tl-event-card/tl-event-card.js +51 -84
  33. package/dist/collection/components/tl-event-list/tl-event-list.js +82 -38
  34. package/dist/collection/components/tl-login/tl-login.js +83 -147
  35. package/dist/collection/components/tl-my-orders/tl-my-orders.js +58 -96
  36. package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.js +137 -516
  37. package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.js +81 -115
  38. package/dist/collection/components/tl-order-tickets/tl-order-tickets.js +57 -92
  39. package/dist/collection/components/tl-promo-code/tl-promo-code.js +55 -50
  40. package/dist/collection/components/tl-seat-map/tl-seat-map.js +2 -2
  41. package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.js +34 -20
  42. package/dist/collection/utils/order-summary.js +15 -135
  43. package/dist/collection/utils/testing/deep-text.js +29 -0
  44. package/dist/collection/utils/testing/fixtures.js +290 -0
  45. package/dist/collection/utils/testing/mock-sdk.js +13 -2
  46. package/dist/collection/utils/web-query.js +12 -0
  47. package/dist/collection/utils/web-storage.js +22 -0
  48. package/dist/components/ticket-selector.js +1 -0
  49. package/dist/components/tl-buy-modal.js +1 -1
  50. package/dist/components/tl-buy-tickets-button.js +1 -1
  51. package/dist/components/tl-buy-tickets-wrapper2.js +1 -1
  52. package/dist/components/tl-cart-badge.js +1 -1
  53. package/dist/components/tl-cart-drawer.js +1 -1
  54. package/dist/components/tl-cart2.js +1 -1
  55. package/dist/components/tl-checkout-wrapper2.js +1 -1
  56. package/dist/components/tl-event-card2.js +1 -1
  57. package/dist/components/tl-event-list.js +1 -1
  58. package/dist/components/tl-login2.js +1 -1
  59. package/dist/components/tl-my-orders.js +1 -1
  60. package/dist/components/tl-occurrence-selector2.js +1 -1
  61. package/dist/components/tl-order-confirmation.js +1 -1
  62. package/dist/components/tl-order-tickets2.js +1 -1
  63. package/dist/components/tl-promo-code.js +1 -1
  64. package/dist/components/tl-seat-map.js +1 -1
  65. package/dist/components/tl-ticket-selector2.js +1 -1
  66. package/dist/components/web-query.js +1 -0
  67. package/dist/elements.docs.json +34 -34
  68. package/dist/esm/{index-OoYV1faY.js → index-imfxp765.js} +1 -1
  69. package/dist/esm/index.js +2 -1
  70. package/dist/esm/loader.js +3 -3
  71. package/dist/esm/ticket-selector-BwChnHVC.js +3606 -0
  72. package/dist/esm/ticketlayer-elements.js +3 -3
  73. package/dist/esm/tl-buy-modal.entry.js +3 -3
  74. package/dist/esm/tl-buy-tickets-button.entry.js +90 -75
  75. package/dist/esm/tl-buy-tickets-wrapper.entry.js +62 -31
  76. package/dist/esm/tl-cart-badge.entry.js +75 -62
  77. package/dist/esm/tl-cart-drawer.entry.js +52 -24
  78. package/dist/esm/tl-cart.entry.js +72 -104
  79. package/dist/esm/tl-checkout-wrapper.entry.js +52 -30
  80. package/dist/esm/tl-event-card.entry.js +44 -106
  81. package/dist/esm/tl-event-list.entry.js +73 -36
  82. package/dist/esm/tl-login.entry.js +75 -142
  83. package/dist/esm/tl-my-orders.entry.js +53 -93
  84. package/dist/esm/tl-occurrence-selector_2.entry.js +152 -920
  85. package/dist/esm/tl-order-confirmation.entry.js +72 -108
  86. package/dist/esm/tl-order-tickets_2.entry.js +49 -75
  87. package/dist/esm/tl-promo-code.entry.js +51 -48
  88. package/dist/esm/tl-seat-map.entry.js +3 -3
  89. package/dist/esm/web-query-Bdbb2lk5.js +14 -0
  90. package/dist/ticketlayer-elements/index.esm.js +1 -1
  91. package/dist/ticketlayer-elements/p-45923d2c.entry.js +1 -0
  92. package/dist/ticketlayer-elements/p-492adc77.entry.js +1 -0
  93. package/dist/ticketlayer-elements/p-4cfc9e53.entry.js +1 -0
  94. package/dist/ticketlayer-elements/{p-ac264adc.entry.js → p-5135630e.entry.js} +1 -1
  95. package/dist/ticketlayer-elements/p-5bbce3b5.entry.js +1 -0
  96. package/dist/ticketlayer-elements/p-7d547721.entry.js +1 -0
  97. package/dist/ticketlayer-elements/p-7fe7b6ba.entry.js +1 -0
  98. package/dist/ticketlayer-elements/p-8122f0c1.entry.js +1 -0
  99. package/dist/ticketlayer-elements/p-98d40869.entry.js +1 -0
  100. package/dist/ticketlayer-elements/p-Bdbb2lk5.js +1 -0
  101. package/dist/ticketlayer-elements/p-BwChnHVC.js +1 -0
  102. package/dist/ticketlayer-elements/p-abec290a.entry.js +1 -0
  103. package/dist/ticketlayer-elements/p-ac79a3f6.entry.js +1 -0
  104. package/dist/ticketlayer-elements/p-d01f4429.entry.js +1 -0
  105. package/dist/ticketlayer-elements/p-d7ffaf46.entry.js +1 -0
  106. package/dist/ticketlayer-elements/p-e7170f3c.entry.js +1 -0
  107. package/dist/ticketlayer-elements/p-e787e862.entry.js +1 -0
  108. package/dist/ticketlayer-elements/p-f1ff931f.entry.js +1 -0
  109. package/dist/ticketlayer-elements/p-imfxp765.js +3 -0
  110. package/dist/ticketlayer-elements/ticketlayer-elements.css +1 -1
  111. package/dist/ticketlayer-elements/ticketlayer-elements.esm.js +1 -1
  112. package/dist/types/components/tl-buy-tickets-button/tl-buy-tickets-button.d.ts +40 -8
  113. package/dist/types/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.d.ts +20 -1
  114. package/dist/types/components/tl-cart/tl-cart.d.ts +40 -42
  115. package/dist/types/components/tl-cart-badge/tl-cart-badge.d.ts +22 -7
  116. package/dist/types/components/tl-cart-drawer/tl-cart-drawer.d.ts +28 -5
  117. package/dist/types/components/tl-checkout-wrapper/tl-checkout-wrapper.d.ts +18 -1
  118. package/dist/types/components/tl-event-card/event-card-adapter.d.ts +9 -20
  119. package/dist/types/components/tl-event-card/tl-event-card.d.ts +25 -28
  120. package/dist/types/components/tl-event-list/tl-event-list.d.ts +46 -6
  121. package/dist/types/components/tl-login/tl-login.d.ts +68 -22
  122. package/dist/types/components/tl-my-orders/tl-my-orders.d.ts +30 -12
  123. package/dist/types/components/tl-occurrence-selector/tl-occurrence-selector.d.ts +73 -161
  124. package/dist/types/components/tl-order-confirmation/tl-order-confirmation.d.ts +48 -15
  125. package/dist/types/components/tl-order-tickets/tl-order-tickets.d.ts +29 -31
  126. package/dist/types/components/tl-promo-code/tl-promo-code.d.ts +28 -10
  127. package/dist/types/components/tl-ticket/tl-ticket.d.ts +2 -11
  128. package/dist/types/components/tl-ticket-selector/tl-ticket-selector.d.ts +22 -4
  129. package/dist/types/components.d.ts +270 -62
  130. package/dist/types/utils/order-summary.d.ts +14 -97
  131. package/dist/types/utils/testing/deep-text.d.ts +13 -0
  132. package/dist/types/utils/testing/fixtures.d.ts +225 -0
  133. package/dist/types/utils/testing/mock-sdk.d.ts +14 -1
  134. package/dist/types/utils/web-query.d.ts +18 -0
  135. package/dist/types/utils/web-storage.d.ts +16 -0
  136. package/elements.catalogue.json +602 -50
  137. package/package.json +8 -6
  138. package/dist/cjs/money-BtmM2gt-.js +0 -118
  139. package/dist/cjs/order-summary-X3TlZxc2.js +0 -150
  140. package/dist/components/money.js +0 -1
  141. package/dist/components/order-summary.js +0 -1
  142. package/dist/esm/money-4ig0-WB6.js +0 -112
  143. package/dist/esm/order-summary-DiDtvfZM.js +0 -136
  144. package/dist/ticketlayer-elements/p-0814420f.entry.js +0 -1
  145. package/dist/ticketlayer-elements/p-0acabc26.entry.js +0 -1
  146. package/dist/ticketlayer-elements/p-109a3feb.entry.js +0 -1
  147. package/dist/ticketlayer-elements/p-1a4ba71c.entry.js +0 -1
  148. package/dist/ticketlayer-elements/p-318965c1.entry.js +0 -1
  149. package/dist/ticketlayer-elements/p-347c7fd1.entry.js +0 -1
  150. package/dist/ticketlayer-elements/p-3649c26d.entry.js +0 -1
  151. package/dist/ticketlayer-elements/p-40260cc9.entry.js +0 -1
  152. package/dist/ticketlayer-elements/p-4ig0-WB6.js +0 -1
  153. package/dist/ticketlayer-elements/p-55fd2f9f.entry.js +0 -1
  154. package/dist/ticketlayer-elements/p-62ee659b.entry.js +0 -1
  155. package/dist/ticketlayer-elements/p-7d4ecc2b.entry.js +0 -1
  156. package/dist/ticketlayer-elements/p-7f0abf95.entry.js +0 -1
  157. package/dist/ticketlayer-elements/p-BXpVGW51.js +0 -1
  158. package/dist/ticketlayer-elements/p-OoYV1faY.js +0 -3
  159. package/dist/ticketlayer-elements/p-a3c1b26c.entry.js +0 -1
  160. package/dist/ticketlayer-elements/p-aeb4c6fb.entry.js +0 -1
  161. package/dist/ticketlayer-elements/p-ba62541f.entry.js +0 -1
@@ -1,560 +1,178 @@
1
+ /**
2
+ * The web harness for `occurrenceSelectorCore`.
3
+ *
4
+ * It renders and it forwards. Which of the three views to show, where the
5
+ * calendar opens, which days and times are pressable, how far the month
6
+ * navigation may walk and every label are in `@ticketlayer/elements-core`
7
+ * (AGENTS.md, TKT-11, TKT-202). What is left here is the Stencil surface a host
8
+ * writes HTML against, the icons, and the three things that are genuinely web:
9
+ *
10
+ * - finding the client the page published on `window.Ticketlayer`;
11
+ * - measuring the element, with a `ResizeObserver`, and telling the core what
12
+ * it measured - the core decides what a width means;
13
+ * - writing the host's `theme` prop onto the element as `--tl-*` custom
14
+ * properties. Those nine are the same nine the stylesheet reads and the core
15
+ * declares, so nothing is themed here that the catalogue cannot see - a
16
+ * token named only in a `.tsx` would be invisible to
17
+ * `scripts/check-core-tokens.mjs`, which reads the stylesheet.
18
+ */
1
19
  import { h } from "@stencil/core";
20
+ import { createElement, occurrenceSelectorCore, } from "@ticketlayer/elements-core";
2
21
  import { getSDK, whenSDK } from "../../utils/sdk-connector";
22
+ /** Theme field -> the custom property the stylesheet reads it through. */
23
+ const THEME_PROPERTIES = [
24
+ ['primaryColor', '--tl-primary'],
25
+ ['primaryForeground', '--tl-primary-foreground'],
26
+ ['backgroundColor', '--tl-background'],
27
+ ['foregroundColor', '--tl-foreground'],
28
+ ['mutedColor', '--tl-muted'],
29
+ ['mutedForeground', '--tl-muted-foreground'],
30
+ ['borderColor', '--tl-border'],
31
+ ['borderRadius', '--tl-radius'],
32
+ ['fontFamily', '--tl-font-family'],
33
+ ];
3
34
  export class TlOccurrenceSelector {
4
35
  constructor() {
5
36
  /**
6
37
  * Layout orientation (auto = horizontal on desktop, vertical on mobile)
7
38
  */
8
39
  this.orientation = 'auto';
9
- /**
10
- * Loading state
11
- */
12
- this.loading = true;
13
- /**
14
- * Error message
15
- */
16
- this.error = null;
17
- /**
18
- * Fetched occurrences
19
- */
20
- this.occurrences = [];
21
- /**
22
- * Current month being displayed in calendar view
23
- */
24
- this.currentMonth = new Date();
25
- /**
26
- * Currently selected date in calendar view
27
- */
28
- this.selectedDate = null;
29
- /**
30
- * Computed display mode based on occurrences
31
- */
32
- this.computedDisplayMode = 'list';
33
- /**
34
- * Container width for responsive layout
35
- */
36
- this.containerWidth = 0;
37
- }
38
- async componentWillLoad() {
39
- await this.waitForSDKAndFetch();
40
+ this.element = createElement(occurrenceSelectorCore, {
41
+ client: () => getSDK(),
42
+ // What "the client is up" means on the web. The core waits on it inside its
43
+ // own mount and reads there; the harness decides none of it (TKT-197).
44
+ clientReady: () => whenSDK(),
45
+ emit: (event, detail) => this.forward(event, detail),
46
+ });
47
+ /** The core's whole view. Assigning it is what makes Stencil re-render. */
48
+ this.view = this.element.view;
40
49
  }
41
50
  /**
42
- * Wait for SDK to be ready, then fetch occurrences
51
+ * A new event is a new subject: the element is torn down and mounted again,
52
+ * so whatever the core set up for the old one is undone before anything is
53
+ * set up for the new one.
43
54
  */
44
- async waitForSDKAndFetch() {
45
- if (!this.eventId) {
46
- this.error = 'Event ID is required';
47
- this.loading = false;
48
- return;
49
- }
50
- try {
51
- await whenSDK();
52
- await this.fetchOccurrences();
53
- }
54
- catch (err) {
55
- this.error = err instanceof Error ? err.message : 'SDK not initialized.';
56
- this.loading = false;
57
- }
55
+ watchSubject() {
56
+ this.syncProps();
57
+ this.teardown?.();
58
+ this.teardown = this.element.mount();
59
+ }
60
+ syncProps() {
61
+ this.element.setProps({
62
+ eventId: this.eventId,
63
+ displayMode: this.displayMode,
64
+ orientation: this.orientation,
65
+ theme: this.theme,
66
+ });
58
67
  }
68
+ /**
69
+ * On screen: subscribe to the view, then put the core to work.
70
+ *
71
+ * `connectedCallback` and not `componentWillLoad` because this runs again
72
+ * when the element is moved in the DOM, and that is exactly when a mount and
73
+ * its teardown have to pair up.
74
+ */
75
+ connectedCallback() {
76
+ this.syncProps();
77
+ this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
78
+ this.view = view;
79
+ }));
80
+ this.view = this.element.view;
81
+ this.teardown = this.element.mount();
82
+ }
83
+ /** Measuring the element is the runtime's job; what a width means is not. */
59
84
  componentDidLoad() {
60
- // Set up resize observer for responsive layout
61
85
  if (this.containerRef && typeof ResizeObserver !== 'undefined') {
62
86
  this.resizeObserver = new ResizeObserver((entries) => {
63
- for (const entry of entries) {
64
- this.containerWidth = entry.contentRect.width;
65
- }
87
+ for (const entry of entries)
88
+ this.element.actions.resized(entry.contentRect.width);
66
89
  });
67
90
  this.resizeObserver.observe(this.containerRef);
68
91
  }
69
92
  }
70
- disconnectedCallback() {
71
- if (this.resizeObserver) {
72
- this.resizeObserver.disconnect();
73
- }
74
- }
75
- async onEventIdChange() {
76
- await this.fetchOccurrences();
77
- }
78
- /**
79
- * Fetch occurrences from the SDK
80
- */
81
- async fetchOccurrences() {
82
- this.loading = true;
83
- this.error = null;
84
- try {
85
- const sdk = getSDK();
86
- const eventsManager = sdk.getEventsManager();
87
- const event = await eventsManager.get(this.eventId);
88
- if (!event) {
89
- throw new Error('Event not found');
90
- }
91
- // Map EventOccurrence to TlOccurrence
92
- this.occurrences = (event.occurrences || []).map((occ) => ({
93
- id: occ.id,
94
- startsAt: occ.startsAt,
95
- endsAt: occ.endsAt,
96
- status: occ.availabilityStatus,
97
- availableCapacity: occ.availableCapacity,
98
- soldOut: occ.availabilityStatus === 'sold_out',
99
- }));
100
- this.computeDisplayMode();
101
- this.computeInitialSelection();
102
- }
103
- catch (err) {
104
- console.error('[tl-occurrence-selector] Failed to fetch occurrences:', err);
105
- this.error = err instanceof Error ? err.message : 'Failed to load occurrences';
106
- this.occurrences = [];
107
- }
108
- finally {
109
- this.loading = false;
110
- }
111
- }
112
- /**
113
- * Compute the display mode based on occurrence count and date distribution
114
- */
115
- computeDisplayMode() {
116
- if (this.displayMode) {
117
- this.computedDisplayMode = this.displayMode;
118
- return;
119
- }
120
- const count = this.occurrences.length;
121
- if (count <= 5) {
122
- this.computedDisplayMode = 'list';
123
- return;
124
- }
125
- // Check if all occurrences are on the same calendar date
126
- const uniqueDates = new Set();
127
- for (const occ of this.occurrences) {
128
- const date = new Date(occ.startsAt);
129
- uniqueDates.add(date.toDateString());
130
- }
131
- if (uniqueDates.size === 1) {
132
- this.computedDisplayMode = 'slots';
133
- }
134
- else {
135
- this.computedDisplayMode = 'calendar';
136
- }
137
- }
138
- /**
139
- * Compute the initial date selection for calendar/slots views
140
- */
141
- computeInitialSelection() {
142
- if (this.occurrences.length === 0)
143
- return;
144
- const today = new Date();
145
- today.setHours(0, 0, 0, 0);
146
- // Get all dates with occurrences
147
- const datesWithOccurrences = this.getDatesWithOccurrences();
148
- // Find the first date that is today or in the future
149
- let selectedDate = null;
150
- for (const dateStr of Array.from(datesWithOccurrences.keys()).sort()) {
151
- const date = new Date(dateStr);
152
- if (date >= today) {
153
- selectedDate = date;
154
- break;
155
- }
156
- }
157
- // If no future dates, select the last available date
158
- if (!selectedDate && datesWithOccurrences.size > 0) {
159
- const sortedDates = Array.from(datesWithOccurrences.keys()).sort();
160
- selectedDate = new Date(sortedDates[sortedDates.length - 1]);
161
- }
162
- if (selectedDate) {
163
- this.selectedDate = selectedDate;
164
- this.currentMonth = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), 1);
165
- }
166
- }
167
93
  /**
168
- * Get a map of dates to occurrences
94
+ * Off screen: run the mount's teardown, and nothing more. Not `destroy()`:
95
+ * Stencil calls this for a DOM move as well as a removal, and a destroyed
96
+ * core never mounts again.
169
97
  */
170
- getDatesWithOccurrences() {
171
- const map = new Map();
172
- for (const occ of this.occurrences) {
173
- const date = new Date(occ.startsAt);
174
- date.setHours(0, 0, 0, 0);
175
- const dateStr = date.toDateString();
176
- if (!map.has(dateStr)) {
177
- map.set(dateStr, []);
178
- }
179
- map.get(dateStr).push(occ);
180
- }
181
- return map;
182
- }
183
- /**
184
- * Get the first and last occurrence dates
185
- */
186
- getDateRange() {
187
- if (this.occurrences.length === 0)
188
- return null;
189
- let first = new Date(this.occurrences[0].startsAt);
190
- let last = new Date(this.occurrences[0].startsAt);
191
- for (const occ of this.occurrences) {
192
- const date = new Date(occ.startsAt);
193
- if (date < first)
194
- first = date;
195
- if (date > last)
196
- last = date;
197
- }
198
- return { first, last };
199
- }
200
- /**
201
- * Check if all occurrences fit in one week (Monday-Sunday)
202
- */
203
- isSingleWeek() {
204
- const dateRange = this.getDateRange();
205
- if (!dateRange)
206
- return false;
207
- // Get Monday of the week containing the first date
208
- const firstMonday = new Date(dateRange.first);
209
- const dayOfWeek = firstMonday.getDay();
210
- const daysToSubtract = dayOfWeek === 0 ? 6 : dayOfWeek - 1; // Monday = 0 days
211
- firstMonday.setDate(firstMonday.getDate() - daysToSubtract);
212
- firstMonday.setHours(0, 0, 0, 0);
213
- // Get Sunday of that week
214
- const sunday = new Date(firstMonday);
215
- sunday.setDate(sunday.getDate() + 6);
216
- sunday.setHours(23, 59, 59, 999);
217
- // Check if last date is within this week
218
- return dateRange.last <= sunday;
219
- }
220
- /**
221
- * Check if all occurrences are in the same month
222
- */
223
- isSingleMonth() {
224
- const dateRange = this.getDateRange();
225
- if (!dateRange)
226
- return false;
227
- return (dateRange.first.getMonth() === dateRange.last.getMonth() &&
228
- dateRange.first.getFullYear() === dateRange.last.getFullYear());
229
- }
230
- /**
231
- * Check if we should use horizontal layout
232
- */
233
- isHorizontalLayout() {
234
- if (this.orientation === 'horizontal')
235
- return true;
236
- if (this.orientation === 'vertical')
237
- return false;
238
- // Auto: use horizontal if container is wide enough (768px)
239
- return this.containerWidth >= 768;
240
- }
241
- /**
242
- * Format a date for display
243
- */
244
- formatDate(date) {
245
- return date.toLocaleDateString('en-GB', {
246
- weekday: 'long',
247
- day: 'numeric',
248
- month: 'long',
249
- year: 'numeric',
250
- });
251
- }
252
- /**
253
- * Format a time for display
254
- */
255
- formatTime(dateString) {
256
- const date = new Date(dateString);
257
- return date.toLocaleTimeString('en-GB', {
258
- hour: '2-digit',
259
- minute: '2-digit',
260
- });
261
- }
262
- /**
263
- * Format a full date and time for list view
264
- */
265
- formatDateTime(dateString) {
266
- const date = new Date(dateString);
267
- return {
268
- date: date.toLocaleDateString('en-GB', {
269
- weekday: 'long',
270
- day: 'numeric',
271
- month: 'long',
272
- year: 'numeric',
273
- }),
274
- time: date.toLocaleTimeString('en-GB', {
275
- hour: '2-digit',
276
- minute: '2-digit',
277
- }),
278
- };
279
- }
280
- /**
281
- * Handle occurrence selection
282
- */
283
- handleSelect(occurrence) {
284
- if (occurrence.soldOut || occurrence.availableCapacity === 0)
285
- return;
286
- console.log('[tl-occurrence-selector] Selected:', occurrence.id);
287
- this.tlSelect.emit({ occurrence });
288
- }
289
- /**
290
- * Handle date selection in calendar view
291
- */
292
- handleDateSelect(date) {
293
- this.selectedDate = date;
294
- // If date is in a different month, navigate to that month
295
- if (date.getMonth() !== this.currentMonth.getMonth() ||
296
- date.getFullYear() !== this.currentMonth.getFullYear()) {
297
- this.currentMonth = new Date(date.getFullYear(), date.getMonth(), 1);
298
- }
299
- }
300
- /**
301
- * Navigate to previous month
302
- */
303
- handlePrevMonth() {
304
- const dateRange = this.getDateRange();
305
- if (!dateRange)
306
- return;
307
- const newMonth = new Date(this.currentMonth);
308
- newMonth.setMonth(newMonth.getMonth() - 1);
309
- // Don't go before the first occurrence month
310
- const firstMonth = new Date(dateRange.first.getFullYear(), dateRange.first.getMonth(), 1);
311
- if (newMonth >= firstMonth) {
312
- this.currentMonth = newMonth;
313
- }
314
- }
315
- /**
316
- * Navigate to next month
317
- */
318
- handleNextMonth() {
319
- const dateRange = this.getDateRange();
320
- if (!dateRange)
321
- return;
322
- const newMonth = new Date(this.currentMonth);
323
- newMonth.setMonth(newMonth.getMonth() + 1);
324
- // Don't go past the last occurrence month
325
- const lastMonth = new Date(dateRange.last.getFullYear(), dateRange.last.getMonth(), 1);
326
- if (newMonth <= lastMonth) {
327
- this.currentMonth = newMonth;
328
- }
329
- }
330
- /**
331
- * Check if prev month button should be disabled
332
- */
333
- isPrevMonthDisabled() {
334
- const dateRange = this.getDateRange();
335
- if (!dateRange)
336
- return true;
337
- const firstMonth = new Date(dateRange.first.getFullYear(), dateRange.first.getMonth(), 1);
338
- return this.currentMonth <= firstMonth;
339
- }
340
- /**
341
- * Check if next month button should be disabled
342
- */
343
- isNextMonthDisabled() {
344
- const dateRange = this.getDateRange();
345
- if (!dateRange)
346
- return true;
347
- const lastMonth = new Date(dateRange.last.getFullYear(), dateRange.last.getMonth(), 1);
348
- return this.currentMonth >= lastMonth;
349
- }
350
- /**
351
- * Get calendar grid days for the current month
352
- */
353
- getCalendarDays() {
354
- // If all occurrences fit in one week, only show that week
355
- if (this.isSingleWeek()) {
356
- const dateRange = this.getDateRange();
357
- if (!dateRange)
358
- return [];
359
- // Get Monday of the week containing the first date
360
- const firstMonday = new Date(dateRange.first);
361
- const dayOfWeek = firstMonday.getDay();
362
- const daysToSubtract = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
363
- firstMonday.setDate(firstMonday.getDate() - daysToSubtract);
364
- firstMonday.setHours(0, 0, 0, 0);
365
- // Generate 7 days (Monday to Sunday)
366
- const days = [];
367
- const current = new Date(firstMonday);
368
- for (let i = 0; i < 7; i++) {
369
- days.push(new Date(current));
370
- current.setDate(current.getDate() + 1);
371
- }
372
- return days;
373
- }
374
- // Otherwise show full month grid
375
- const year = this.currentMonth.getFullYear();
376
- const month = this.currentMonth.getMonth();
377
- // Start of month
378
- const monthStart = new Date(year, month, 1);
379
- // End of month
380
- const monthEnd = new Date(year, month + 1, 0);
381
- // Get the start of the week containing the first day (Monday = 1)
382
- const startDay = monthStart.getDay();
383
- const daysToSubtract = startDay === 0 ? 6 : startDay - 1; // Monday = 0 days to subtract
384
- const calendarStart = new Date(monthStart);
385
- calendarStart.setDate(calendarStart.getDate() - daysToSubtract);
386
- // Get the end of the week containing the last day
387
- const endDay = monthEnd.getDay();
388
- const daysToAdd = endDay === 0 ? 0 : 7 - endDay;
389
- const calendarEnd = new Date(monthEnd);
390
- calendarEnd.setDate(calendarEnd.getDate() + daysToAdd);
391
- // Generate all days
392
- const days = [];
393
- const current = new Date(calendarStart);
394
- while (current <= calendarEnd) {
395
- days.push(new Date(current));
396
- current.setDate(current.getDate() + 1);
397
- }
398
- return days;
399
- }
400
- /**
401
- * Check if a date has occurrences
402
- */
403
- hasOccurrencesOnDate(date) {
404
- const dateStr = date.toDateString();
405
- return this.getDatesWithOccurrences().has(dateStr);
406
- }
407
- /**
408
- * Check if all occurrences on a date are sold out
409
- */
410
- isDateSoldOut(date) {
411
- const dateStr = date.toDateString();
412
- const occurrences = this.getDatesWithOccurrences().get(dateStr);
413
- if (!occurrences)
414
- return false;
415
- return occurrences.every(occ => occ.soldOut || occ.availableCapacity === 0);
98
+ disconnectedCallback() {
99
+ this.resizeObserver?.disconnect();
100
+ this.resizeObserver = undefined;
101
+ this.teardown?.();
102
+ this.teardown = undefined;
103
+ this.unsubscribe?.();
104
+ this.unsubscribe = undefined;
416
105
  }
417
- /**
418
- * Get occurrences for the selected date
419
- */
420
- getSelectedDateOccurrences() {
421
- if (!this.selectedDate)
422
- return [];
423
- const dateStr = this.selectedDate.toDateString();
424
- const occurrences = this.getDatesWithOccurrences().get(dateStr) || [];
425
- // Sort by start time
426
- return occurrences.sort((a, b) => new Date(a.startsAt).getTime() - new Date(b.startsAt).getTime());
106
+ forward(event, detail) {
107
+ if (event === 'tlSelect')
108
+ this.tlSelect.emit(detail);
427
109
  }
428
110
  /**
429
- * Get CSS custom properties from theme
111
+ * The host's theme, as custom properties over the ones the theme package
112
+ * ships. Web only: a custom property is not a fact every runtime has, so the
113
+ * core declares the prop and the harness is what acts on it.
430
114
  */
431
- getThemeStyles() {
432
- if (!this.theme)
433
- return {};
115
+ themeStyles() {
434
116
  const styles = {};
435
- if (this.theme.primaryColor) {
436
- styles['--tl-primary'] = this.theme.primaryColor;
437
- }
438
- if (this.theme.primaryForeground) {
439
- styles['--tl-primary-foreground'] = this.theme.primaryForeground;
440
- }
441
- if (this.theme.backgroundColor) {
442
- styles['--tl-background'] = this.theme.backgroundColor;
443
- }
444
- if (this.theme.foregroundColor) {
445
- styles['--tl-foreground'] = this.theme.foregroundColor;
446
- }
447
- if (this.theme.mutedColor) {
448
- styles['--tl-muted'] = this.theme.mutedColor;
449
- }
450
- if (this.theme.mutedForeground) {
451
- styles['--tl-muted-foreground'] = this.theme.mutedForeground;
452
- }
453
- if (this.theme.borderColor) {
454
- styles['--tl-border'] = this.theme.borderColor;
455
- }
456
- if (this.theme.borderRadius) {
457
- styles['--tl-radius'] = this.theme.borderRadius;
458
- }
459
- if (this.theme.fontFamily) {
460
- styles['--tl-font-family'] = this.theme.fontFamily;
117
+ if (!this.theme)
118
+ return styles;
119
+ for (const [field, property] of THEME_PROPERTIES) {
120
+ const value = this.theme[field];
121
+ if (value)
122
+ styles[property] = value;
461
123
  }
462
124
  return styles;
463
125
  }
464
126
  // =========================================================================
465
127
  // RENDER METHODS
466
128
  // =========================================================================
467
- /**
468
- * Render the list view (≤5 occurrences)
469
- */
129
+ /** The availability mark a row carries, in the list and the slots panel. */
130
+ renderRowStatus(row) {
131
+ const { strings } = this.view;
132
+ if (row.statusKind === 'soldOut')
133
+ return h("span", { class: "status-sold-out" }, strings.soldOut);
134
+ if (row.statusKind === 'available')
135
+ return h("span", { class: "status-available" }, row.capacityLabel);
136
+ return h("span", { class: "status-arrow" }, "\u2192");
137
+ }
138
+ /** The list view (five occurrences or fewer). */
470
139
  renderListView() {
471
- return (h("div", { class: "list-view" }, this.occurrences.map((occurrence) => {
472
- const isSoldOut = occurrence.soldOut || occurrence.availableCapacity === 0;
473
- const { date, time } = this.formatDateTime(occurrence.startsAt);
474
- return (h("button", { key: occurrence.id, class: `occurrence-item ${isSoldOut ? 'sold-out' : ''}`, onClick: () => this.handleSelect(occurrence), disabled: isSoldOut }, h("div", { class: "occurrence-info" }, h("div", { class: "occurrence-date" }, date), h("div", { class: "occurrence-time" }, time, occurrence.endsAt && ` - ${this.formatTime(occurrence.endsAt)}`)), h("div", { class: "occurrence-status" }, isSoldOut ? (h("span", { class: "status-sold-out" }, "Sold Out")) : occurrence.availableCapacity ? (h("span", { class: "status-available" }, occurrence.availableCapacity, " available")) : (h("span", { class: "status-arrow" }, "\u2192")))));
475
- })));
140
+ return (h("div", { class: "list-view" }, this.view.derived.rows.map((row) => (h("button", { key: row.id, class: `occurrence-item ${row.soldOut ? 'sold-out' : ''}`, onClick: () => this.element.actions.select(row.id), disabled: row.soldOut }, h("div", { class: "occurrence-info" }, h("div", { class: "occurrence-date" }, row.dateLabel), h("div", { class: "occurrence-time" }, row.timeLabel)), h("div", { class: "occurrence-status" }, this.renderRowStatus(row)))))));
476
141
  }
477
- /**
478
- * Render the time slots view (>5 occurrences, same day)
479
- */
142
+ /** The time slots view (more than five, all on one date). */
480
143
  renderSlotsView() {
481
- // Sort occurrences by time
482
- const sortedOccurrences = [...this.occurrences].sort((a, b) => new Date(a.startsAt).getTime() - new Date(b.startsAt).getTime());
483
- // Get the date from the first occurrence
484
- const dateLabel = sortedOccurrences.length > 0
485
- ? this.formatDate(new Date(sortedOccurrences[0].startsAt))
486
- : '';
487
- return (h("div", { class: "slots-view" }, dateLabel && h("div", { class: "slots-date-header" }, dateLabel), h("div", { class: "time-slots-grid" }, sortedOccurrences.map((occurrence) => {
488
- const isSoldOut = occurrence.soldOut || occurrence.availableCapacity === 0;
489
- return (h("button", { key: occurrence.id, class: `time-slot ${isSoldOut ? 'sold-out' : ''}`, onClick: () => this.handleSelect(occurrence), disabled: isSoldOut }, h("span", { class: "slot-time" }, this.formatTime(occurrence.startsAt)), isSoldOut && h("span", { class: "slot-sold-out" }, "Sold Out")));
490
- }))));
144
+ const { slots, slotsDateLabel } = this.view.derived;
145
+ return (h("div", { class: "slots-view" }, slotsDateLabel && h("div", { class: "slots-date-header" }, slotsDateLabel), h("div", { class: "time-slots-grid" }, slots.map((row) => (h("button", { key: row.id, class: `time-slot ${row.soldOut ? 'sold-out' : ''}`, onClick: () => this.element.actions.select(row.id), disabled: row.soldOut }, h("span", { class: "slot-time" }, row.startLabel), row.soldOut && h("span", { class: "slot-sold-out" }, this.view.strings.soldOut)))))));
491
146
  }
492
- /**
493
- * Render the calendar header
494
- */
495
147
  renderCalendarHeader() {
496
- const monthLabel = this.currentMonth.toLocaleDateString('en-GB', {
497
- month: 'long',
498
- year: 'numeric',
499
- });
500
- const showNavigation = !this.isSingleMonth();
501
- return (h("div", { class: "calendar-header" }, h("span", { class: "month-label" }, monthLabel), showNavigation && (h("div", { class: "calendar-nav-buttons" }, h("button", { class: "nav-button", onClick: () => this.handlePrevMonth(), disabled: this.isPrevMonthDisabled(), "aria-label": "Previous month" }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("path", { d: "M15 18l-6-6 6-6" }))), h("button", { class: "nav-button", onClick: () => this.handleNextMonth(), disabled: this.isNextMonthDisabled(), "aria-label": "Next month" }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("path", { d: "M9 18l6-6-6-6" })))))));
148
+ const { strings, derived } = this.view;
149
+ return (h("div", { class: "calendar-header" }, h("span", { class: "month-label" }, derived.monthLabel), derived.showMonthNav && (h("div", { class: "calendar-nav-buttons" }, h("button", { class: "nav-button", onClick: () => this.element.actions.previousMonth(), disabled: !derived.canGoBack, "aria-label": strings.previousMonth }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("path", { d: "M15 18l-6-6 6-6" }))), h("button", { class: "nav-button", onClick: () => this.element.actions.nextMonth(), disabled: !derived.canGoForward, "aria-label": strings.nextMonth }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("path", { d: "M9 18l6-6-6-6" })))))));
150
+ }
151
+ renderCalendarDay(day) {
152
+ return (h("button", { key: day.key, class: day.className, onClick: () => this.element.actions.selectDate(day.key), disabled: !day.selectable }, h("span", { class: "day-number" }, day.dayNumber), day.hasOccurrences && h("span", { class: "occurrence-dot" })));
502
153
  }
503
- /**
504
- * Render the calendar grid
505
- */
506
154
  renderCalendarGrid() {
507
- const days = this.getCalendarDays();
508
- const weekDays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
509
- const today = new Date();
510
- today.setHours(0, 0, 0, 0);
511
- return (h("div", { class: "calendar-grid" }, weekDays.map((day) => (h("div", { class: "weekday-header", key: day }, day))), days.map((date) => {
512
- const isCurrentMonth = date.getMonth() === this.currentMonth.getMonth();
513
- const hasOccurrences = this.hasOccurrencesOnDate(date);
514
- const isSoldOut = this.isDateSoldOut(date);
515
- const isSelected = this.selectedDate && date.toDateString() === this.selectedDate.toDateString();
516
- const isToday = date.toDateString() === today.toDateString();
517
- const isPast = date < today;
518
- return (h("button", { key: date.toISOString(), class: `calendar-day
519
- ${!isCurrentMonth ? 'other-month' : ''}
520
- ${hasOccurrences ? 'has-occurrences' : ''}
521
- ${isSoldOut ? 'sold-out' : ''}
522
- ${isSelected ? 'selected' : ''}
523
- ${isToday ? 'today' : ''}
524
- ${isPast ? 'past' : ''}`, onClick: () => hasOccurrences && this.handleDateSelect(date), disabled: !hasOccurrences }, h("span", { class: "day-number" }, date.getDate()), hasOccurrences && h("span", { class: "occurrence-dot" })));
525
- })));
155
+ const { derived } = this.view;
156
+ return (h("div", { class: "calendar-grid" }, derived.weekdayLabels.map((day) => (h("div", { class: "weekday-header", key: day }, day))), derived.days.map((day) => this.renderCalendarDay(day))));
526
157
  }
527
- /**
528
- * Render the selected date's time slots
529
- */
158
+ /** The picked date's times, beside or below the calendar. */
530
159
  renderSelectedDateSlots() {
531
- const occurrences = this.getSelectedDateOccurrences();
532
- if (!this.selectedDate || occurrences.length === 0) {
533
- return (h("div", { class: "selected-date-slots empty" }, h("p", null, "Select a date to see available times")));
160
+ const { strings, derived } = this.view;
161
+ if (!derived.selectedDateLabel) {
162
+ return (h("div", { class: "selected-date-slots empty" }, h("p", null, strings.selectADate)));
534
163
  }
535
- const dateLabel = this.formatDate(this.selectedDate);
536
- return (h("div", { class: "selected-date-slots" }, h("div", { class: "selected-date-header" }, dateLabel), h("div", { class: "time-slots-list" }, occurrences.map((occurrence) => {
537
- const isSoldOut = occurrence.soldOut || occurrence.availableCapacity === 0;
538
- return (h("button", { key: occurrence.id, class: `time-slot-item ${isSoldOut ? 'sold-out' : ''}`, onClick: () => this.handleSelect(occurrence), disabled: isSoldOut }, h("span", { class: "slot-time" }, this.formatTime(occurrence.startsAt)), h("div", { class: "slot-status" }, isSoldOut ? (h("span", { class: "status-sold-out" }, "Sold Out")) : occurrence.availableCapacity ? (h("span", { class: "status-available" }, occurrence.availableCapacity, " available")) : (h("span", { class: "status-arrow" }, "\u2192")))));
539
- }))));
164
+ return (h("div", { class: "selected-date-slots" }, h("div", { class: "selected-date-header" }, derived.selectedDateLabel), h("div", { class: "time-slots-list" }, derived.selectedSlots.map((row) => (h("button", { key: row.id, class: `time-slot-item ${row.soldOut ? 'sold-out' : ''}`, onClick: () => this.element.actions.select(row.id), disabled: row.soldOut }, h("span", { class: "slot-time" }, row.startLabel), h("div", { class: "slot-status" }, this.renderRowStatus(row))))))));
540
165
  }
541
- /**
542
- * Render the calendar view with slots panel
543
- */
544
166
  renderCalendarView() {
545
- const isHorizontal = this.isHorizontalLayout();
546
- return (h("div", { class: `calendar-view ${isHorizontal ? 'horizontal' : 'vertical'}` }, h("div", { class: "calendar-panel" }, this.renderCalendarHeader(), this.renderCalendarGrid()), h("div", { class: "slots-panel" }, this.renderSelectedDateSlots())));
167
+ return (h("div", { class: this.view.derived.viewClass }, h("div", { class: "calendar-panel" }, this.renderCalendarHeader(), this.renderCalendarGrid()), h("div", { class: "slots-panel" }, this.renderSelectedDateSlots())));
547
168
  }
548
- /**
549
- * Render skeleton loading state
550
- */
169
+ /** The skeleton, which is a calendar because that is the common shape. */
551
170
  renderSkeleton() {
552
- // Default to calendar skeleton as it's the most common
553
171
  return (h("div", { class: "loading-container" }, h("div", { class: "skeleton-calendar" }, h("div", { class: "skeleton skeleton-calendar-header" }), h("div", { class: "skeleton-calendar-grid" }, Array.from({ length: 35 }, (_, i) => (h("div", { key: i, class: "skeleton skeleton-calendar-day" })))))));
554
172
  }
555
173
  render() {
556
- const themeStyles = this.getThemeStyles();
557
- return (h("div", { key: '40758aaa06e5f062de92675766e4531995f74bc7', class: "occurrence-selector", ref: (el) => (this.containerRef = el), style: themeStyles }, this.loading && this.renderSkeleton(), !this.loading && this.error && (h("div", { key: '6d2f5b9c07f65d4941bfb4870729fe3248cb277e', class: "error-container" }, h("svg", { key: 'daaf1f48dcff80ae7cf94ae0da1d66d9c9b7fb0f', class: "error-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("circle", { key: '0a9f494a5cf7f79146e56fc44469a3ca9e5238ae', cx: "12", cy: "12", r: "10" }), h("path", { key: '67d7b453508c847593136bd60e34ea0c38d30cda', d: "M12 8v4m0 4h.01" })), h("p", { key: 'c1cc92585ed4451b686350b6d718c3ba06b72dd3', class: "error-message" }, this.error), h("button", { key: '4ec9d048af704af12a800385b9868501973ba477', class: "retry-button", onClick: () => this.fetchOccurrences() }, "Retry"))), !this.loading && !this.error && (this.occurrences.length === 0 ? (h("div", { class: "no-occurrences" }, h("p", null, "No dates available for this event."))) : (h("div", { class: "selector-content" }, this.computedDisplayMode === 'list' && this.renderListView(), this.computedDisplayMode === 'slots' && this.renderSlotsView(), this.computedDisplayMode === 'calendar' && this.renderCalendarView())))));
174
+ const { status, strings, state, derived } = this.view;
175
+ return (h("div", { key: '84951fb5171ed6c79604861a171f49f43c8b809c', class: "occurrence-selector", ref: (el) => (this.containerRef = el), style: this.themeStyles() }, status === 'loading' && this.renderSkeleton(), status === 'error' && (h("div", { key: '2850712eae277f32f3538426c8a5d36b73ca918e', class: "error-container" }, h("svg", { key: '89192cd7cce29781b87c83c8dee71d59f6a685ed', class: "error-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("circle", { key: '27a52ecb1228c541202752be84323abe687c1655', cx: "12", cy: "12", r: "10" }), h("path", { key: 'f51c2b49de89b9c66cde01e11a2de4c56d4dbad9', d: "M12 8v4m0 4h.01" })), h("p", { key: '3c85d762de430f477d2c9ad4341b23765c93e2e3', class: "error-message" }, state.error), h("button", { key: '22fa271a8f0a15e18141cf6d66a25a2bf9299c72', class: "retry-button", onClick: () => void this.element.actions.load() }, strings.retry))), status === 'empty' && (h("div", { key: '285403135ccff6cd6c8de6a9c62c9186c67c102c', class: "no-occurrences" }, h("p", { key: '98373f0c5793cb1aba1328558fac3210e7c6aeeb' }, strings.empty))), status === 'ready' && (h("div", { key: 'a61d2c02538f3339e54770e53a4f807e72182357', class: "selector-content" }, derived.mode === 'list' && this.renderListView(), derived.mode === 'slots' && this.renderSlotsView(), derived.mode === 'calendar' && this.renderCalendarView()))));
558
176
  }
559
177
  static get is() { return "tl-occurrence-selector"; }
560
178
  static get encapsulation() { return "shadow"; }
@@ -665,13 +283,7 @@ export class TlOccurrenceSelector {
665
283
  }
666
284
  static get states() {
667
285
  return {
668
- "loading": {},
669
- "error": {},
670
- "occurrences": {},
671
- "currentMonth": {},
672
- "selectedDate": {},
673
- "computedDisplayMode": {},
674
- "containerWidth": {}
286
+ "view": {}
675
287
  };
676
288
  }
677
289
  static get events() {
@@ -701,7 +313,16 @@ export class TlOccurrenceSelector {
701
313
  static get watchers() {
702
314
  return [{
703
315
  "propName": "eventId",
704
- "methodName": "onEventIdChange"
316
+ "methodName": "watchSubject"
317
+ }, {
318
+ "propName": "displayMode",
319
+ "methodName": "syncProps"
320
+ }, {
321
+ "propName": "orientation",
322
+ "methodName": "syncProps"
323
+ }, {
324
+ "propName": "theme",
325
+ "methodName": "syncProps"
705
326
  }];
706
327
  }
707
328
  }