@getmicdrop/venue-calendar 4.2.34 → 4.2.36

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 (104) hide show
  1. package/README.md +809 -809
  2. package/dist/{ArrowLeftIcon-D2dcPIYB.js → ArrowLeftIcon-DS-NEije.js} +1 -1
  3. package/dist/{Badge-Bk1dnG89.js → Badge-DIHnRZlw.js} +1 -1
  4. package/dist/{BlockPlaceholder-DJLtnBDw.js → BlockPlaceholder-Dy9fStDr.js} +1 -1
  5. package/dist/{Calendar-CzvJjOKf.js → Calendar-CXwhGAmC.js} +6 -6
  6. package/dist/{CalendarFoundryView-DWjYFSEw.js → CalendarFoundryView-BbVin_52.js} +32 -31
  7. package/dist/{CalendarIcon-CkJFNFxp.js → CalendarIcon-CvZ-GUH_.js} +1 -1
  8. package/dist/{CartView-DidUQzO-.js → CartView-DT8DlMqQ.js} +19 -19
  9. package/dist/{Checkout-eIj6xGgL.js → Checkout-D3VqG09o.js} +15 -15
  10. package/dist/{Checkout-1NyqTUI8.js → Checkout-UU9C7CXV.js} +18 -18
  11. package/dist/{CheckoutTimer-DFIHUIZl.js → CheckoutTimer-2WEgppW1.js} +3 -3
  12. package/dist/{ChevronDownIcon-rsVUoX0r.js → ChevronDownIcon-DXbr-7H6.js} +1 -1
  13. package/dist/{ClockIcon-CIwbKXZX.js → ClockIcon-Dyx1LMo7.js} +1 -1
  14. package/dist/{CloseIcon-B7HoEcON.js → CloseIcon-NMHzwT75.js} +1 -1
  15. package/dist/{CollectionView-Dt3NmdBh.js → CollectionView-DyMB_bPY.js} +7 -7
  16. package/dist/{Event-B0hDsxnc.js → Event-CXCOnkmi.js} +22 -22
  17. package/dist/EventPage-HlhaVqRG.js +981 -0
  18. package/dist/{Heading-CQlZrwry.js → Heading-DcZtRBSB.js} +1 -1
  19. package/dist/{Image-T6dKIfNx.js → Image-BZGbFJLw.js} +1 -1
  20. package/dist/{Input-8KhoOdg-.js → Input-Cgdxdj6y.js} +1 -1
  21. package/dist/{MailIcon-CSR5vhKU.js → MailIcon-D34sBBQX.js} +1 -1
  22. package/dist/{Modal-D3Otve45.js → Modal-BvT64ak1.js} +2 -2
  23. package/dist/{ModalFooter-fv8mC75I.js → ModalFooter-DM1dyl4S.js} +2 -2
  24. package/dist/{PasswordInput-B_6cNrlw.js → PasswordInput-BNKrzXNm.js} +1 -1
  25. package/dist/{ScarcityBadge-CtHuWSRt.js → ScarcityBadge-DmbmsFlY.js} +5 -5
  26. package/dist/{SeriesPage-BFYmtgoi.js → SeriesPage-BXKz7PJV.js} +4 -4
  27. package/dist/{Spinner-fFAXvj_B.js → Spinner-gpYLD9h6.js} +1 -1
  28. package/dist/{Success-BIVQOUqu.js → Success-DieJd6sx.js} +18 -18
  29. package/dist/{TagIcon-Do5sN8xR.js → TagIcon-Q7BC2QeQ.js} +1 -1
  30. package/dist/{ViewTicketsEmbed-CbbE-8EX.js → ViewTicketsEmbed-DqwHARj0.js} +7 -7
  31. package/dist/{WarningCircleIcon-DuqytsVW.js → WarningCircleIcon-Dxi6_NlN.js} +1 -1
  32. package/dist/{WarningTriIcon-CW-j3Vwq.js → WarningTriIcon-BVNO82pk.js} +1 -1
  33. package/dist/{__SKIP_PATHS__-Chv7DW7h.js → __SKIP_PATHS__-BZ2V4IU4.js} +1 -1
  34. package/dist/api/api.cjs +1 -1
  35. package/dist/api/api.mjs +28 -34
  36. package/dist/api/types.d.ts +554 -554
  37. package/dist/api/waitlist.d.ts +6 -1
  38. package/dist/{colors-BND7shPA.js → colors-B3NG0RHG.js} +55 -61
  39. package/dist/{constants-BYtFptYp.js → constants-DqKf8bUn.js} +1 -1
  40. package/dist/{copyToClipboard-CcwaUGGt.js → copyToClipboard-CLo_2bfp.js} +1 -1
  41. package/dist/{de-CgHrjezX.js → de-tOW1Ez0s.js} +6 -0
  42. package/dist/{defaultLabels-CGMGBy-u.js → defaultLabels-D_9CneL4.js} +6 -0
  43. package/dist/{es-D6ThNLUc.js → es-DrYewQpd.js} +6 -0
  44. package/dist/{event-CbAGNxgd.js → event-CFDG8adb.js} +1 -1
  45. package/dist/{event-transform-DnvOiDVn.js → event-transform-jJPAKgYX.js} +4 -4
  46. package/dist/{eventStatusStream-BIn6Dzkw.js → eventStatusStream-wmEyQSgV.js} +1 -1
  47. package/dist/{fr-W0OOohju.js → fr-Bs9xnPu6.js} +6 -0
  48. package/dist/{href-D2-tmkMF.js → href-BfMcUXhM.js} +2 -2
  49. package/dist/{i18n-4k6uF1EX.js → i18n-BgkKrB4o.js} +5 -5
  50. package/dist/i18n-DK3npqzN.js +28 -0
  51. package/dist/{id-xgAFN1EH.js → id-BLwvLobz.js} +6 -0
  52. package/dist/{it-BhuT9BcE.js → it-CQ_Y8YK3.js} +6 -0
  53. package/dist/{ja-yca07ASz.js → ja-CeudRJqE.js} +6 -0
  54. package/dist/{ko-Pfftd2u0.js → ko-DFwVxd-1.js} +6 -0
  55. package/dist/{labels-Cs7xp4ug.js → labels-BHqxbbw9.js} +1 -1
  56. package/dist/locales/{4.2.34 → 4.2.36}/main/de.js +1 -1
  57. package/dist/locales/{4.2.34 → 4.2.36}/main/es.js +1 -1
  58. package/dist/locales/{4.2.34 → 4.2.36}/main/fr.js +1 -1
  59. package/dist/locales/{4.2.34 → 4.2.36}/main/id.js +1 -1
  60. package/dist/locales/{4.2.34 → 4.2.36}/main/it.js +1 -1
  61. package/dist/locales/{4.2.34 → 4.2.36}/main/ja.js +1 -1
  62. package/dist/locales/{4.2.34 → 4.2.36}/main/ko.js +1 -1
  63. package/dist/locales/{4.2.34 → 4.2.36}/main/nl.js +1 -1
  64. package/dist/locales/{4.2.34 → 4.2.36}/main/pl.js +1 -1
  65. package/dist/locales/{4.2.34 → 4.2.36}/main/pt-br.js +1 -1
  66. package/dist/locales/{4.2.34 → 4.2.36}/main/tr.js +1 -1
  67. package/dist/locales/{4.2.34 → 4.2.36}/main/zh.js +1 -1
  68. package/dist/{modalManager.svelte-vILq6Thx.js → modalManager.svelte-CrrDshW7.js} +2 -2
  69. package/dist/{nl-CQEhSA2l.js → nl-CYc0p85Y.js} +6 -0
  70. package/dist/{order-totals-CSDXY65z.js → order-totals-D0JKhd70.js} +3 -3
  71. package/dist/{orders-CH2nnS3m.js → orders-CCWPQsWb.js} +1 -1
  72. package/dist/{phoneUtils-BtJ3FhMK.js → phoneUtils-aQHGh8C_.js} +1 -1
  73. package/dist/{pixels-BK7PGnme.js → pixels-M2q8HRk_.js} +2 -2
  74. package/dist/{pl-CyWqKYE8.js → pl-BSqAasUa.js} +6 -0
  75. package/dist/{promo-Cy4LPBAC.js → promo-CKvS9lia.js} +1 -1
  76. package/dist/{pt-br-BC2I7f8-.js → pt-br-CZs8_gFN.js} +6 -0
  77. package/dist/seo/types.d.ts +156 -156
  78. package/dist/{serverTotals-C3VhWitQ.js → serverTotals-DGd3p1s5.js} +1 -1
  79. package/dist/{shareCopyFeedback.svelte-DTl5jJ2n.js → shareCopyFeedback.svelte-DTOJruVM.js} +4 -4
  80. package/dist/{showcase-state-options-CeCdn8_C.js → showcase-state-options-Da3jPUQb.js} +1 -1
  81. package/dist/{to-event-card-props-BmsDr8T3.js → to-event-card-props-B5bsYKP4.js} +3 -3
  82. package/dist/{tr-CNjaJc_1.js → tr-O80C2r5C.js} +6 -0
  83. package/dist/types/index.d.ts +509 -509
  84. package/dist/{utils-pF15CnBe.js → utils-RE2aEa_D.js} +2 -2
  85. package/dist/venue-calendar.es.js +30 -30
  86. package/dist/venue-calendar.iife.js +28 -28
  87. package/dist/venue-calendar.umd.js +49 -49
  88. package/dist/{zh-DOeb3_A1.js → zh-ydknixkJ.js} +6 -0
  89. package/package.json +3 -3
  90. package/src/lib/theme.js +222 -222
  91. package/dist/EventPage-De-7chl3.js +0 -970
  92. package/dist/i18n-FnFHQ5QU.js +0 -28
  93. /package/dist/locales/{4.2.34 → 4.2.36}/flow/de.js +0 -0
  94. /package/dist/locales/{4.2.34 → 4.2.36}/flow/es.js +0 -0
  95. /package/dist/locales/{4.2.34 → 4.2.36}/flow/fr.js +0 -0
  96. /package/dist/locales/{4.2.34 → 4.2.36}/flow/id.js +0 -0
  97. /package/dist/locales/{4.2.34 → 4.2.36}/flow/it.js +0 -0
  98. /package/dist/locales/{4.2.34 → 4.2.36}/flow/ja.js +0 -0
  99. /package/dist/locales/{4.2.34 → 4.2.36}/flow/ko.js +0 -0
  100. /package/dist/locales/{4.2.34 → 4.2.36}/flow/nl.js +0 -0
  101. /package/dist/locales/{4.2.34 → 4.2.36}/flow/pl.js +0 -0
  102. /package/dist/locales/{4.2.34 → 4.2.36}/flow/pt-br.js +0 -0
  103. /package/dist/locales/{4.2.34 → 4.2.36}/flow/tr.js +0 -0
  104. /package/dist/locales/{4.2.34 → 4.2.36}/flow/zh.js +0 -0
package/README.md CHANGED
@@ -1,809 +1,809 @@
1
- # @getmicdrop/venue-calendar
2
-
3
- A beautiful, customizable calendar component built with Svelte for displaying comedy events. Perfect for comedy clubs, venues, and event organizers who want to showcase their upcoming shows.
4
-
5
- ## Features
6
-
7
- ✨ **Three View Modes**: List, Gallery, and Calendar views
8
- 🎨 **Beautiful UI**: Modern, responsive design built with Tailwind CSS
9
- 📱 **Mobile-Friendly**: Swipe gestures, touch-optimized, responsive design
10
- 🔌 **Easy Integration**: Works with React, Vue, vanilla JS, and more
11
- 🌐 **One script tag**: Self-hosted bundle — paste one `<script>`, no build step
12
- 🎨 **Styles included**: The bundle injects its own CSS — no separate stylesheet to link
13
- ⚡ **Auto-Mount**: Automatically finds and mounts to designated containers
14
- 🎯 **Customizable**: Configure views, navigation, and more
15
- 🌙 **Dark Mode**: Built-in light, dark, and high-contrast themes
16
- ♿ **Accessible**: ARIA labels, keyboard navigation, screen reader support
17
- 🎫 **Event Status**: Visual badges for "On Sale", "Selling Fast", "Sold Out"
18
-
19
- ## Installation
20
-
21
- Add one `<script>` tag pointing at the Micdrop-hosted bundle. The package is
22
- private (not on npm/jsDelivr) — it is delivered from `get-micdrop.com`:
23
-
24
- ```html
25
- <script
26
- defer
27
- src="https://get-micdrop.com/embed/venue-calendar.iife.js"
28
- ></script>
29
- ```
30
-
31
- - **No stylesheet to link.** The bundle injects its own CSS at runtime, so a
32
- plain page with just this `<script>` renders fully styled.
33
- - **Use `defer`** (or `async`) so the bundle never blocks page parse. It is
34
- ~310 KB gzipped.
35
- - **Serve your page over HTTPS.** Checkout stores a `Secure` cart cookie, which
36
- Safari drops on non-secure (`http://`) pages.
37
-
38
- > **Heads-up:** the exact hosted URL is being finalized (Micdrop ticket
39
- > MIC-1130). Confirm the address with Micdrop before going live.
40
-
41
- > The `import { ... } from '@getmicdrop/venue-calendar'` examples further down
42
- > are for **Micdrop-internal apps** that build with a bundler and have registry
43
- > access to the private package. Public sites (comedy clubs, etc.) use the
44
- > `<script>` tag above — not `npm install`.
45
-
46
- ## Quick Start
47
-
48
- ### Method 1: Auto-Mount (Easiest)
49
-
50
- Simply add a div with the class `micdrop-calendar-container` and the calendar will automatically mount:
51
-
52
- ```html
53
- <!DOCTYPE html>
54
- <html>
55
- <head>
56
- <title>My Comedy Club</title>
57
- <!-- Preload the bundle while the page parses. defer keeps the
58
- <script> non-blocking; preload starts the fetch earlier. -->
59
- <link
60
- rel="preload"
61
- as="script"
62
- href="https://get-micdrop.com/embed/venue-calendar.iife.js"
63
- />
64
- <script
65
- defer
66
- src="https://get-micdrop.com/embed/venue-calendar.iife.js"
67
- ></script>
68
- </head>
69
- <body>
70
- <!-- Calendar auto-mounts here. Use data-organization-id to show all of an
71
- organization's shows, or data-venue-id for a single venue. -->
72
- <div
73
- class="micdrop-calendar-container"
74
- data-organization-id="your-organization-id"
75
- data-view="calendar"
76
- data-show-view-options="true"
77
- data-show-month-switcher="true"
78
- data-locale="en-US"
79
- ></div>
80
- </body>
81
- </html>
82
- ```
83
-
84
- ### Method 2: Web Component
85
-
86
- Use the custom `<micdrop-calendar>` element:
87
-
88
- ```html
89
- <!DOCTYPE html>
90
- <html>
91
- <head>
92
- <title>My Comedy Club</title>
93
- <script
94
- defer
95
- src="https://get-micdrop.com/embed/venue-calendar.iife.js"
96
- ></script>
97
- </head>
98
- <body>
99
- <!-- Web Component -->
100
- <micdrop-calendar
101
- venue-id="your-venue-id"
102
- view="calendar"
103
- show-view-options="true"
104
- show-month-switcher="true"
105
- locale="en-US"
106
- >
107
- </micdrop-calendar>
108
- </body>
109
- </html>
110
- ```
111
-
112
- ### Method 3: JavaScript API
113
-
114
- For more control, use the JavaScript API:
115
-
116
- ```html
117
- <!DOCTYPE html>
118
- <html>
119
- <head>
120
- <title>My Comedy Club</title>
121
- </head>
122
- <body>
123
- <div id="my-calendar"></div>
124
-
125
- <!-- Load the bundle, then call the global it exposes. -->
126
- <script
127
- defer
128
- src="https://get-micdrop.com/embed/venue-calendar.iife.js"
129
- ></script>
130
- <script>
131
- window.addEventListener('load', function () {
132
- window.VenueCalendar.initVenueCalendar({
133
- target: '#my-calendar',
134
- organizationId: 'your-organization-id',
135
- view: 'calendar',
136
- showViewOptions: true,
137
- showMonthSwitcher: true,
138
- });
139
- });
140
- </script>
141
- </body>
142
- </html>
143
- ```
144
-
145
- ## Framework Integration
146
-
147
- ### React
148
-
149
- ```jsx
150
- import React, { useEffect, useRef } from 'react';
151
- import { initVenueCalendar, unmount } from '@getmicdrop/venue-calendar';
152
-
153
- function VenueCalendarComponent({ venueId, view = 'calendar' }) {
154
- const calendarRef = useRef(null);
155
- const instanceRef = useRef(null);
156
-
157
- useEffect(() => {
158
- if (!calendarRef.current) return;
159
- instanceRef.current = initVenueCalendar({
160
- target: calendarRef.current,
161
- venueId,
162
- view,
163
- events: [],
164
- showViewOptions: true,
165
- showMonthSwitcher: true,
166
- });
167
-
168
- return () => {
169
- // Svelte 5 — components mounted via `mount()` are destroyed via
170
- // the `unmount` helper, not `.$destroy()` (that was Svelte 4).
171
- if (instanceRef.current) {
172
- try {
173
- unmount(instanceRef.current);
174
- } catch {}
175
- instanceRef.current = null;
176
- }
177
- };
178
- }, [venueId, view]);
179
-
180
- return <div ref={calendarRef}></div>;
181
- }
182
-
183
- export default VenueCalendarComponent;
184
- ```
185
-
186
- ### Error reporting and runtime config
187
-
188
- Wire any errors caught by the widget into your existing monitoring:
189
-
190
- ```js
191
- import { configureVenueCalendar } from '@getmicdrop/venue-calendar';
192
-
193
- configureVenueCalendar({
194
- onError: (err, { source }) => {
195
- Sentry.captureException(err, { tags: { source, micdrop: true } });
196
- },
197
- // Optional overrides for self-hosted backends or regional failover.
198
- // apiBaseUrl: 'https://api.eu.micdrop.com',
199
- // apiTimeout: 15000,
200
- // apiRetries: 2,
201
- });
202
- ```
203
-
204
- For ad-hoc support diagnostics, the widget exposes its version on
205
- `window`:
206
-
207
- ```js
208
- window.__MICDROP_CALENDAR__.version; // e.g. "3.6.23"
209
- ```
210
-
211
- **Usage in React App:**
212
-
213
- ```jsx
214
- import React, { useState } from 'react';
215
- import VenueCalendarComponent from './VenueCalendarComponent';
216
-
217
- function App() {
218
- const [venueId, setVenueId] = useState('comedy-club-123');
219
- const [view, setView] = useState('calendar');
220
-
221
- return (
222
- <div style={{ padding: '20px' }}>
223
- <h1>Event Viewer</h1>
224
-
225
- <div style={{ marginBottom: '20px' }}>
226
- <label>Venue ID:</label>
227
- <input
228
- type="text"
229
- value={venueId}
230
- onChange={e => setVenueId(e.target.value)}
231
- />
232
- </div>
233
-
234
- <div style={{ marginBottom: '20px' }}>
235
- <label>Select View:</label>
236
- <label>
237
- <input
238
- type="radio"
239
- value="list"
240
- checked={view === 'list'}
241
- onChange={e => setView(e.target.value)}
242
- />
243
- List
244
- </label>
245
- <label>
246
- <input
247
- type="radio"
248
- value="gallery"
249
- checked={view === 'gallery'}
250
- onChange={e => setView(e.target.value)}
251
- />
252
- Gallery
253
- </label>
254
- <label>
255
- <input
256
- type="radio"
257
- value="calendar"
258
- checked={view === 'calendar'}
259
- onChange={e => setView(e.target.value)}
260
- />
261
- Calendar
262
- </label>
263
- </div>
264
-
265
- <VenueCalendarComponent venueId={venueId} view={view} />
266
- </div>
267
- );
268
- }
269
-
270
- export default App;
271
- ```
272
-
273
- ### Vue 3
274
-
275
- ```vue
276
- <template>
277
- <div ref="calendarContainer"></div>
278
- </template>
279
-
280
- <script setup>
281
- import { ref, onMounted, onUnmounted, watch } from 'vue';
282
- import { initVenueCalendar } from '@getmicdrop/venue-calendar';
283
-
284
- const props = defineProps({
285
- venueId: String,
286
- view: {
287
- type: String,
288
- default: 'calendar',
289
- },
290
- });
291
-
292
- const calendarContainer = ref(null);
293
- let calendarInstance = null;
294
-
295
- onMounted(() => {
296
- calendarInstance = initVenueCalendar({
297
- target: calendarContainer.value,
298
- venueId: props.venueId,
299
- view: props.view,
300
- events: [],
301
- showViewOptions: true,
302
- showMonthSwitcher: true,
303
- });
304
- });
305
-
306
- onUnmounted(() => {
307
- if (calendarInstance && calendarInstance.$destroy) {
308
- calendarInstance.$destroy();
309
- }
310
- });
311
-
312
- watch(
313
- () => props.venueId,
314
- newId => {
315
- if (calendarInstance) {
316
- calendarInstance.$destroy();
317
- calendarInstance = initVenueCalendar({
318
- target: calendarContainer.value,
319
- venueId: newId,
320
- view: props.view,
321
- events: [],
322
- showViewOptions: true,
323
- showMonthSwitcher: true,
324
- });
325
- }
326
- }
327
- );
328
- </script>
329
- ```
330
-
331
- ### Svelte
332
-
333
- ```svelte
334
- <script>
335
- import { VenueCalendar } from '@getmicdrop/venue-calendar';
336
- import { Calendar, Grid, List } from 'carbon-icons-svelte';
337
- import { writable } from 'svelte/store';
338
-
339
- let venueId = 'your-venue-id';
340
- let currentMonth = writable(new Date().getUTCMonth());
341
- let currentYear = writable(new Date().getUTCFullYear());
342
-
343
- function handleNext() {
344
- currentMonth.update(m => m + 1);
345
- }
346
-
347
- function handlePrev() {
348
- currentMonth.update(m => m - 1);
349
- }
350
- </script>
351
-
352
- <VenueCalendar
353
- showViewOptions={[
354
- { id: 0, text: 'List view', icon: List },
355
- { id: 1, text: 'Gallery view', icon: Grid },
356
- { id: 2, text: 'Calendar view', icon: Calendar },
357
- ]}
358
- showMonthSwitcher={true}
359
- events={[]}
360
- {currentMonth}
361
- {currentYear}
362
- {handleNext}
363
- {handlePrev}
364
- on:eventClick={e => console.log('Event clicked:', e.detail)}
365
- />
366
- ```
367
-
368
- ### Angular
369
-
370
- ```typescript
371
- import {
372
- Component,
373
- OnInit,
374
- OnDestroy,
375
- ElementRef,
376
- ViewChild,
377
- } from '@angular/core';
378
- import { initVenueCalendar } from '@getmicdrop/venue-calendar';
379
-
380
- @Component({
381
- selector: 'app-venue-calendar',
382
- template: '<div #calendarContainer></div>',
383
- })
384
- export class VenueCalendarComponent implements OnInit, OnDestroy {
385
- @ViewChild('calendarContainer', { static: true })
386
- calendarContainer!: ElementRef;
387
- private calendarInstance: any;
388
-
389
- ngOnInit() {
390
- this.calendarInstance = initVenueCalendar({
391
- target: this.calendarContainer.nativeElement,
392
- venueId: 'your-venue-id',
393
- view: 'calendar',
394
- events: [],
395
- showViewOptions: true,
396
- showMonthSwitcher: true,
397
- });
398
- }
399
-
400
- ngOnDestroy() {
401
- if (this.calendarInstance && this.calendarInstance.$destroy) {
402
- this.calendarInstance.$destroy();
403
- }
404
- }
405
- }
406
- ```
407
-
408
- ## Configuration Options
409
-
410
- ### Data Attributes (for auto-mount)
411
-
412
- | Attribute | Type | Default | Description |
413
- | -------------------------- | ------- | ------------ | ---------------------------------------------------- |
414
- | `data-venue-id` | string | `''` | The venue ID to fetch events for |
415
- | `data-view` | string | `'calendar'` | Initial view: `'list'`, `'gallery'`, or `'calendar'` |
416
- | `data-show-view-options` | boolean | `true` | Show view switcher buttons |
417
- | `data-show-month-switcher` | boolean | `true` | Show month navigation controls |
418
-
419
- ### JavaScript API Options
420
-
421
- ```javascript
422
- initVenueCalendar({
423
- target: '.my-calendar', // CSS selector or HTMLElement (required)
424
- venueId: 'venue-123', // Venue ID (optional)
425
- view: 'calendar', // 'list', 'gallery', or 'calendar' (default: 'calendar')
426
- events: [], // Array of event objects (default: [])
427
- showViewOptions: true, // Show view switcher (default: true)
428
- showMonthSwitcher: true, // Show month navigation (default: true)
429
- });
430
- ```
431
-
432
- ### Event Object Structure
433
-
434
- ```javascript
435
- {
436
- id: 'event-123',
437
- name: 'Comedy Night',
438
- date: '2024-10-25T20:00:00Z',
439
- image: 'https://example.com/image.jpg',
440
- status: 'On Sale',
441
- timeline: '8:00 PM - 10:00 PM',
442
- // ... other fields
443
- }
444
- ```
445
-
446
- ## Views
447
-
448
- ### Calendar View
449
-
450
- The default view showing events in a monthly calendar grid. Perfect for venues with regular shows.
451
-
452
- ### List View
453
-
454
- A vertical list layout showing all upcoming events with details. Great for mobile experiences.
455
-
456
- ### Gallery View
457
-
458
- A grid layout displaying event posters in a gallery format. Ideal for showcasing event imagery.
459
-
460
- ## WordPress Integration
461
-
462
- For WordPress sites, you can add this to your page/post HTML:
463
-
464
- ```html
465
- <div
466
- class="micdrop-calendar-container"
467
- data-venue-id="your-venue-id"
468
- data-view="calendar"
469
- ></div>
470
-
471
- <script src="https://get-micdrop.com/embed/venue-calendar.iife.js"></script>
472
- ```
473
-
474
- Or add the script to your theme's footer and use the div anywhere in your content.
475
-
476
- ## Styling
477
-
478
- The calendar comes with built-in styles using Tailwind CSS. If you need to customize the appearance, you can override the CSS classes or add your own styles.
479
-
480
- ```css
481
- /* Example: Custom styling */
482
- .micdrop-calendar-container {
483
- max-width: 1200px;
484
- margin: 0 auto;
485
- padding: 20px;
486
- }
487
- ```
488
-
489
- **Note:** The mount target must not be a shrink-to-fit box (`display: inline-block`, `float`, `position: absolute`, `display: table-cell`, or an auto-width item in a flex row). The calendar sizes its typography to its own container, which requires the mount target to get its width from its parent — a normal block element (as in every example above) is exactly right. If the calendar detects a shrink-to-fit mount target, it falls back to page-width type scaling and logs a console warning.
490
-
491
- ## Theming
492
-
493
- The calendar supports comprehensive theming via CSS custom properties and JavaScript utilities.
494
-
495
- ### Using CSS Custom Properties
496
-
497
- Override the default theme by setting CSS custom properties:
498
-
499
- ```css
500
- /* Custom brand colors */
501
- .micdrop-calendar-container {
502
- --Brand-Primary: 270 76% 60%; /* Purple */
503
- --Text-Primary: 0 0% 10%;
504
- --BG-Primary: 0 0% 100%;
505
- }
506
-
507
- /* Dark mode */
508
- .dark .micdrop-calendar-container,
509
- [data-theme='dark'] .micdrop-calendar-container {
510
- --Brand-Primary: 270 76% 70%;
511
- --Text-Primary: 0 0% 95%;
512
- --BG-Primary: 0 0% 10%;
513
- }
514
- ```
515
-
516
- ### Available CSS Variables
517
-
518
- | Variable | Description | Default (Light) |
519
- | ---------------------- | ----------------------- | -------------------- |
520
- | `--Brand-Primary` | Primary brand color | `217 91% 60%` (Blue) |
521
- | `--Text-Primary` | Main text color | `0 0% 0%` |
522
- | `--Text-Secondary` | Secondary text | `0 0% 40%` |
523
- | `--BG-Primary` | Main background | `0 0% 100%` |
524
- | `--BG-Secondary` | Secondary background | `0 0% 98%` |
525
- | `--Stroke-Primary` | Border colors | `0 0% 80%` |
526
- | `--Status-OnSale` | "On Sale" badge | `217 91% 60%` |
527
- | `--Status-SellingFast` | "Selling Fast" badge | `38 92% 50%` |
528
- | `--Status-SoldOut` | "Sold Out" badge | `0 84% 60%` |
529
- | `--Today-BG` | Today's date background | `217 91% 97%` |
530
- | `--Focus-Ring` | Keyboard focus ring | `217 91% 60%` |
531
-
532
- ### Using JavaScript Theme Utilities
533
-
534
- ```javascript
535
- import {
536
- applyTheme,
537
- themes,
538
- generateThemeCSS,
539
- } from '@getmicdrop/venue-calendar';
540
-
541
- // Apply a preset theme
542
- applyTheme(themes.dark);
543
-
544
- // Apply to a specific container
545
- const container = document.querySelector('.micdrop-calendar-container');
546
- applyTheme(themes.dark, container);
547
-
548
- // Create a custom theme
549
- const myTheme = {
550
- brandPrimary: '270 76% 60%', // Purple
551
- textPrimary: '0 0% 10%',
552
- bgPrimary: '0 0% 100%',
553
- statusOnSale: '142 71% 45%', // Green for on sale
554
- };
555
- applyTheme(myTheme);
556
-
557
- // Generate CSS string for embedding
558
- const cssString = generateThemeCSS(myTheme);
559
- console.log(cssString);
560
- // Output: :root { --Brand-Primary: 270 76% 60%; ... }
561
- ```
562
-
563
- ### Preset Themes
564
-
565
- Three themes are included out of the box:
566
-
567
- ```javascript
568
- import { themes } from '@getmicdrop/venue-calendar';
569
-
570
- // Light theme (default)
571
- applyTheme(themes.light);
572
-
573
- // Dark theme
574
- applyTheme(themes.dark);
575
-
576
- // High contrast (accessibility)
577
- applyTheme(themes.highContrast);
578
- ```
579
-
580
- ### Automatic Dark Mode
581
-
582
- The calendar automatically respects the user's system preference:
583
-
584
- ```css
585
- /* Automatically applied when user prefers dark mode */
586
- @media (prefers-color-scheme: dark) {
587
- /* Dark theme variables are applied */
588
- }
589
- ```
590
-
591
- You can also manually toggle dark mode:
592
-
593
- ```html
594
- <!-- Add 'dark' class to enable dark theme -->
595
- <div class="dark">
596
- <div class="micdrop-calendar-container" data-venue-id="123"></div>
597
- </div>
598
-
599
- <!-- Or use data-theme attribute -->
600
- <div data-theme="dark">
601
- <div class="micdrop-calendar-container" data-venue-id="123"></div>
602
- </div>
603
- ```
604
-
605
- ## Localization and the CDN bundle
606
-
607
- The calendar and the checkout speak thirteen languages. English is built into the
608
- bundle; the other twelve are **fetched at runtime**, one file, only for the
609
- locale actually in use — so a buyer never downloads twelve languages to read one.
610
- This is what keeps the CDN bundle at ~594 KB gzipped instead of ~686 KB.
611
-
612
- For the npm package nothing is required: your bundler code-splits the catalogues
613
- and addresses them itself.
614
-
615
- For the `<script>` embed, the bundle works out where to fetch from by looking at
616
- the URL it was itself served from, and loads a **version-pinned sibling**:
617
-
618
- ```
619
- https://get-micdrop.com/embed/venue-calendar.iife.js
620
- https://get-micdrop.com/embed/locales/<version>/main/es.js ← calendar copy
621
- https://get-micdrop.com/embed/locales/<version>/flow/es.js ← checkout copy
622
- ```
623
-
624
- If you self-host the bundle, **copy the `dist/locales/` directory alongside it**.
625
- Version-pinned paths mean new releases only ever add files; they never overwrite
626
- the catalogue an older pinned bundle is using.
627
-
628
- ### When the fetch cannot happen
629
-
630
- If the locale file cannot be loaded — a Content-Security-Policy that allowlists
631
- the exact script file rather than the origin, an offline CDN, or a
632
- `<script type="module">` embed where the bundle cannot see its own URL — the
633
- calendar simply stays in English. Nothing errors and nothing breaks; the widget
634
- renders and sells tickets exactly as it does for an English buyer.
635
-
636
- Two ways to point it at the right place when it cannot work it out:
637
-
638
- ```html
639
- <!-- Option A: tell it before the bundle loads -->
640
- <script>
641
- window.__MICDROP_CALENDAR_ASSET_BASE__ = 'https://get-micdrop.com/embed/';
642
- </script>
643
- <script
644
- defer
645
- src="https://get-micdrop.com/embed/venue-calendar.iife.js"
646
- ></script>
647
-
648
- <!-- Option B: put it on the tag -->
649
- <script
650
- defer
651
- src="https://get-micdrop.com/embed/venue-calendar.iife.js"
652
- data-vc-base="https://get-micdrop.com/embed/"
653
- ></script>
654
- ```
655
-
656
- `defer` and `async` need no override — a classic script still knows its own URL.
657
-
658
- ### First paint is always English
659
-
660
- A non-English buyer sees English for a moment on a cold load and the page swaps
661
- to their language when the catalogue arrives. This is deliberate: the calendar
662
- paints immediately rather than waiting on a translation file.
663
-
664
- ## Browser Support
665
-
666
- - Chrome (latest)
667
- - Firefox (latest)
668
- - Safari (latest)
669
- - Edge (latest)
670
- - Mobile browsers (iOS Safari, Chrome Mobile)
671
-
672
- ## Development
673
-
674
- ### Building the Package
675
-
676
- ```bash
677
- # Install dependencies
678
- npm install
679
-
680
- # Build the library
681
- npm run build:lib
682
-
683
- # Development mode (SvelteKit app)
684
- npm run dev
685
-
686
- # Preview production build
687
- npm run preview
688
- ```
689
-
690
- `build:lib` is three passes and the order matters: the IIFE pass runs first and
691
- owns the `dist/` wipe, the ES/UMD pass appends to it, and
692
- `vite.config.locales.js` appends the runtime locale catalogues
693
- (`dist/locales/<version>/{main,flow}/<tag>.js`) last. `npm run size` fails if the
694
- bundle and its catalogues do not both come out of that.
695
-
696
- ### Project Structure
697
-
698
- ```
699
- venue-calendar/
700
- ├── src/
701
- │ ├── components/ # Svelte components
702
- │ │ ├── Calendar/
703
- │ │ ├── CalendarContainer/
704
- │ │ └── Button/
705
- │ ├── lib/ # Library entry points
706
- │ │ ├── VenueCalendar.js
707
- │ │ └── web-component.js
708
- │ └── routes/ # SvelteKit routes (for dev)
709
- ├── dist/ # Built package (generated)
710
- │ └── locales/<version>/ # Runtime locale catalogues for the CDN bundle
711
- ├── package.json
712
- ├── vite.config.lib.js # Library build config (ES/UMD + IIFE passes)
713
- ├── vite.config.locales.js # Emits the runtime locale catalogues
714
- └── README.md
715
- ```
716
-
717
- ### Lockfile policy
718
-
719
- `package-lock.json` is the single canonical lockfile — CI and the publish
720
- workflow install with `npm ci`. Do not commit `yarn.lock` or
721
- `pnpm-lock.yaml` (both gitignored). Local dev machines may use pnpm for the
722
- svelte-components symlink workflow, but dependency changes must land in
723
- `package-lock.json` via npm.
724
-
725
- ## API Reference
726
-
727
- ### `initVenueCalendar(options)`
728
-
729
- Initialize a calendar instance.
730
-
731
- **Parameters:**
732
-
733
- - `options` (Object): Configuration options
734
-
735
- **Returns:** Svelte component instance
736
-
737
- **Example:**
738
-
739
- ```javascript
740
- const calendar = initVenueCalendar({
741
- target: '#calendar',
742
- venueId: 'venue-123',
743
- view: 'calendar',
744
- });
745
- ```
746
-
747
- ### `autoMount()`
748
-
749
- Automatically mount calendars to all elements with class `micdrop-calendar-container`.
750
-
751
- **Example:**
752
-
753
- ```javascript
754
- import { autoMount } from '@getmicdrop/venue-calendar';
755
- autoMount();
756
- ```
757
-
758
- ### Component Events
759
-
760
- The calendar component emits events that you can listen to:
761
-
762
- ```javascript
763
- const calendar = initVenueCalendar({
764
- target: '#calendar',
765
- // ... other options
766
- });
767
-
768
- // Listen to component events (if using Svelte component directly)
769
- calendar.$on('eventClick', event => {
770
- console.log('Event clicked:', event.detail);
771
- });
772
- ```
773
-
774
- ## Troubleshooting
775
-
776
- ### Calendar not appearing
777
-
778
- 1. **Check the script is loaded**: Open browser console and verify no errors
779
- 2. **Verify container exists**: Make sure the target element exists in the DOM
780
- 3. **Check data attributes**: Ensure attributes are correctly formatted with `data-` prefix
781
-
782
- ### Styles not applying
783
-
784
- 1. **CSS not loaded**: The styles are bundled in the JS file and auto-injected — no separate stylesheet needed
785
- 2. **CSS conflicts**: Check if other styles are overriding the calendar styles
786
- 3. **Bundle didn't load**: Confirm the `<script src>` points at the Micdrop-hosted bundle and returns 200 (not 404)
787
-
788
- ### Events not showing
789
-
790
- 1. **Check event data format**: Ensure events match the expected structure
791
- 2. **Date format**: Use ISO 8601 format for dates (`YYYY-MM-DDTHH:mm:ssZ`)
792
- 3. **Venue ID**: Verify the venue ID is correct
793
-
794
- ## Contributing
795
-
796
- Contributions are welcome! Please feel free to submit a Pull Request.
797
-
798
- ## License
799
-
800
- MIT © MicDrop
801
-
802
- ## Support
803
-
804
- For issues, questions, or feature requests, please visit:
805
- https://github.com/get-micdrop/venue-calendar/issues
806
-
807
- ---
808
-
809
- Made with ❤️ by the MicDrop team
1
+ # @getmicdrop/venue-calendar
2
+
3
+ A beautiful, customizable calendar component built with Svelte for displaying comedy events. Perfect for comedy clubs, venues, and event organizers who want to showcase their upcoming shows.
4
+
5
+ ## Features
6
+
7
+ ✨ **Three View Modes**: List, Gallery, and Calendar views
8
+ 🎨 **Beautiful UI**: Modern, responsive design built with Tailwind CSS
9
+ 📱 **Mobile-Friendly**: Swipe gestures, touch-optimized, responsive design
10
+ 🔌 **Easy Integration**: Works with React, Vue, vanilla JS, and more
11
+ 🌐 **One script tag**: Self-hosted bundle — paste one `<script>`, no build step
12
+ 🎨 **Styles included**: The bundle injects its own CSS — no separate stylesheet to link
13
+ ⚡ **Auto-Mount**: Automatically finds and mounts to designated containers
14
+ 🎯 **Customizable**: Configure views, navigation, and more
15
+ 🌙 **Dark Mode**: Built-in light, dark, and high-contrast themes
16
+ ♿ **Accessible**: ARIA labels, keyboard navigation, screen reader support
17
+ 🎫 **Event Status**: Visual badges for "On Sale", "Selling Fast", "Sold Out"
18
+
19
+ ## Installation
20
+
21
+ Add one `<script>` tag pointing at the Micdrop-hosted bundle. The package is
22
+ private (not on npm/jsDelivr) — it is delivered from `get-micdrop.com`:
23
+
24
+ ```html
25
+ <script
26
+ defer
27
+ src="https://get-micdrop.com/embed/venue-calendar.iife.js"
28
+ ></script>
29
+ ```
30
+
31
+ - **No stylesheet to link.** The bundle injects its own CSS at runtime, so a
32
+ plain page with just this `<script>` renders fully styled.
33
+ - **Use `defer`** (or `async`) so the bundle never blocks page parse. It is
34
+ ~310 KB gzipped.
35
+ - **Serve your page over HTTPS.** Checkout stores a `Secure` cart cookie, which
36
+ Safari drops on non-secure (`http://`) pages.
37
+
38
+ > **Heads-up:** the exact hosted URL is being finalized (Micdrop ticket
39
+ > MIC-1130). Confirm the address with Micdrop before going live.
40
+
41
+ > The `import { ... } from '@getmicdrop/venue-calendar'` examples further down
42
+ > are for **Micdrop-internal apps** that build with a bundler and have registry
43
+ > access to the private package. Public sites (comedy clubs, etc.) use the
44
+ > `<script>` tag above — not `npm install`.
45
+
46
+ ## Quick Start
47
+
48
+ ### Method 1: Auto-Mount (Easiest)
49
+
50
+ Simply add a div with the class `micdrop-calendar-container` and the calendar will automatically mount:
51
+
52
+ ```html
53
+ <!DOCTYPE html>
54
+ <html>
55
+ <head>
56
+ <title>My Comedy Club</title>
57
+ <!-- Preload the bundle while the page parses. defer keeps the
58
+ <script> non-blocking; preload starts the fetch earlier. -->
59
+ <link
60
+ rel="preload"
61
+ as="script"
62
+ href="https://get-micdrop.com/embed/venue-calendar.iife.js"
63
+ />
64
+ <script
65
+ defer
66
+ src="https://get-micdrop.com/embed/venue-calendar.iife.js"
67
+ ></script>
68
+ </head>
69
+ <body>
70
+ <!-- Calendar auto-mounts here. Use data-organization-id to show all of an
71
+ organization's shows, or data-venue-id for a single venue. -->
72
+ <div
73
+ class="micdrop-calendar-container"
74
+ data-organization-id="your-organization-id"
75
+ data-view="calendar"
76
+ data-show-view-options="true"
77
+ data-show-month-switcher="true"
78
+ data-locale="en-US"
79
+ ></div>
80
+ </body>
81
+ </html>
82
+ ```
83
+
84
+ ### Method 2: Web Component
85
+
86
+ Use the custom `<micdrop-calendar>` element:
87
+
88
+ ```html
89
+ <!DOCTYPE html>
90
+ <html>
91
+ <head>
92
+ <title>My Comedy Club</title>
93
+ <script
94
+ defer
95
+ src="https://get-micdrop.com/embed/venue-calendar.iife.js"
96
+ ></script>
97
+ </head>
98
+ <body>
99
+ <!-- Web Component -->
100
+ <micdrop-calendar
101
+ venue-id="your-venue-id"
102
+ view="calendar"
103
+ show-view-options="true"
104
+ show-month-switcher="true"
105
+ locale="en-US"
106
+ >
107
+ </micdrop-calendar>
108
+ </body>
109
+ </html>
110
+ ```
111
+
112
+ ### Method 3: JavaScript API
113
+
114
+ For more control, use the JavaScript API:
115
+
116
+ ```html
117
+ <!DOCTYPE html>
118
+ <html>
119
+ <head>
120
+ <title>My Comedy Club</title>
121
+ </head>
122
+ <body>
123
+ <div id="my-calendar"></div>
124
+
125
+ <!-- Load the bundle, then call the global it exposes. -->
126
+ <script
127
+ defer
128
+ src="https://get-micdrop.com/embed/venue-calendar.iife.js"
129
+ ></script>
130
+ <script>
131
+ window.addEventListener('load', function () {
132
+ window.VenueCalendar.initVenueCalendar({
133
+ target: '#my-calendar',
134
+ organizationId: 'your-organization-id',
135
+ view: 'calendar',
136
+ showViewOptions: true,
137
+ showMonthSwitcher: true,
138
+ });
139
+ });
140
+ </script>
141
+ </body>
142
+ </html>
143
+ ```
144
+
145
+ ## Framework Integration
146
+
147
+ ### React
148
+
149
+ ```jsx
150
+ import React, { useEffect, useRef } from 'react';
151
+ import { initVenueCalendar, unmount } from '@getmicdrop/venue-calendar';
152
+
153
+ function VenueCalendarComponent({ venueId, view = 'calendar' }) {
154
+ const calendarRef = useRef(null);
155
+ const instanceRef = useRef(null);
156
+
157
+ useEffect(() => {
158
+ if (!calendarRef.current) return;
159
+ instanceRef.current = initVenueCalendar({
160
+ target: calendarRef.current,
161
+ venueId,
162
+ view,
163
+ events: [],
164
+ showViewOptions: true,
165
+ showMonthSwitcher: true,
166
+ });
167
+
168
+ return () => {
169
+ // Svelte 5 — components mounted via `mount()` are destroyed via
170
+ // the `unmount` helper, not `.$destroy()` (that was Svelte 4).
171
+ if (instanceRef.current) {
172
+ try {
173
+ unmount(instanceRef.current);
174
+ } catch {}
175
+ instanceRef.current = null;
176
+ }
177
+ };
178
+ }, [venueId, view]);
179
+
180
+ return <div ref={calendarRef}></div>;
181
+ }
182
+
183
+ export default VenueCalendarComponent;
184
+ ```
185
+
186
+ ### Error reporting and runtime config
187
+
188
+ Wire any errors caught by the widget into your existing monitoring:
189
+
190
+ ```js
191
+ import { configureVenueCalendar } from '@getmicdrop/venue-calendar';
192
+
193
+ configureVenueCalendar({
194
+ onError: (err, { source }) => {
195
+ Sentry.captureException(err, { tags: { source, micdrop: true } });
196
+ },
197
+ // Optional overrides for self-hosted backends or regional failover.
198
+ // apiBaseUrl: 'https://api.eu.micdrop.com',
199
+ // apiTimeout: 15000,
200
+ // apiRetries: 2,
201
+ });
202
+ ```
203
+
204
+ For ad-hoc support diagnostics, the widget exposes its version on
205
+ `window`:
206
+
207
+ ```js
208
+ window.__MICDROP_CALENDAR__.version; // e.g. "3.6.23"
209
+ ```
210
+
211
+ **Usage in React App:**
212
+
213
+ ```jsx
214
+ import React, { useState } from 'react';
215
+ import VenueCalendarComponent from './VenueCalendarComponent';
216
+
217
+ function App() {
218
+ const [venueId, setVenueId] = useState('comedy-club-123');
219
+ const [view, setView] = useState('calendar');
220
+
221
+ return (
222
+ <div style={{ padding: '20px' }}>
223
+ <h1>Event Viewer</h1>
224
+
225
+ <div style={{ marginBottom: '20px' }}>
226
+ <label>Venue ID:</label>
227
+ <input
228
+ type="text"
229
+ value={venueId}
230
+ onChange={e => setVenueId(e.target.value)}
231
+ />
232
+ </div>
233
+
234
+ <div style={{ marginBottom: '20px' }}>
235
+ <label>Select View:</label>
236
+ <label>
237
+ <input
238
+ type="radio"
239
+ value="list"
240
+ checked={view === 'list'}
241
+ onChange={e => setView(e.target.value)}
242
+ />
243
+ List
244
+ </label>
245
+ <label>
246
+ <input
247
+ type="radio"
248
+ value="gallery"
249
+ checked={view === 'gallery'}
250
+ onChange={e => setView(e.target.value)}
251
+ />
252
+ Gallery
253
+ </label>
254
+ <label>
255
+ <input
256
+ type="radio"
257
+ value="calendar"
258
+ checked={view === 'calendar'}
259
+ onChange={e => setView(e.target.value)}
260
+ />
261
+ Calendar
262
+ </label>
263
+ </div>
264
+
265
+ <VenueCalendarComponent venueId={venueId} view={view} />
266
+ </div>
267
+ );
268
+ }
269
+
270
+ export default App;
271
+ ```
272
+
273
+ ### Vue 3
274
+
275
+ ```vue
276
+ <template>
277
+ <div ref="calendarContainer"></div>
278
+ </template>
279
+
280
+ <script setup>
281
+ import { ref, onMounted, onUnmounted, watch } from 'vue';
282
+ import { initVenueCalendar } from '@getmicdrop/venue-calendar';
283
+
284
+ const props = defineProps({
285
+ venueId: String,
286
+ view: {
287
+ type: String,
288
+ default: 'calendar',
289
+ },
290
+ });
291
+
292
+ const calendarContainer = ref(null);
293
+ let calendarInstance = null;
294
+
295
+ onMounted(() => {
296
+ calendarInstance = initVenueCalendar({
297
+ target: calendarContainer.value,
298
+ venueId: props.venueId,
299
+ view: props.view,
300
+ events: [],
301
+ showViewOptions: true,
302
+ showMonthSwitcher: true,
303
+ });
304
+ });
305
+
306
+ onUnmounted(() => {
307
+ if (calendarInstance && calendarInstance.$destroy) {
308
+ calendarInstance.$destroy();
309
+ }
310
+ });
311
+
312
+ watch(
313
+ () => props.venueId,
314
+ newId => {
315
+ if (calendarInstance) {
316
+ calendarInstance.$destroy();
317
+ calendarInstance = initVenueCalendar({
318
+ target: calendarContainer.value,
319
+ venueId: newId,
320
+ view: props.view,
321
+ events: [],
322
+ showViewOptions: true,
323
+ showMonthSwitcher: true,
324
+ });
325
+ }
326
+ }
327
+ );
328
+ </script>
329
+ ```
330
+
331
+ ### Svelte
332
+
333
+ ```svelte
334
+ <script>
335
+ import { VenueCalendar } from '@getmicdrop/venue-calendar';
336
+ import { Calendar, Grid, List } from 'carbon-icons-svelte';
337
+ import { writable } from 'svelte/store';
338
+
339
+ let venueId = 'your-venue-id';
340
+ let currentMonth = writable(new Date().getUTCMonth());
341
+ let currentYear = writable(new Date().getUTCFullYear());
342
+
343
+ function handleNext() {
344
+ currentMonth.update(m => m + 1);
345
+ }
346
+
347
+ function handlePrev() {
348
+ currentMonth.update(m => m - 1);
349
+ }
350
+ </script>
351
+
352
+ <VenueCalendar
353
+ showViewOptions={[
354
+ { id: 0, text: 'List view', icon: List },
355
+ { id: 1, text: 'Gallery view', icon: Grid },
356
+ { id: 2, text: 'Calendar view', icon: Calendar },
357
+ ]}
358
+ showMonthSwitcher={true}
359
+ events={[]}
360
+ {currentMonth}
361
+ {currentYear}
362
+ {handleNext}
363
+ {handlePrev}
364
+ on:eventClick={e => console.log('Event clicked:', e.detail)}
365
+ />
366
+ ```
367
+
368
+ ### Angular
369
+
370
+ ```typescript
371
+ import {
372
+ Component,
373
+ OnInit,
374
+ OnDestroy,
375
+ ElementRef,
376
+ ViewChild,
377
+ } from '@angular/core';
378
+ import { initVenueCalendar } from '@getmicdrop/venue-calendar';
379
+
380
+ @Component({
381
+ selector: 'app-venue-calendar',
382
+ template: '<div #calendarContainer></div>',
383
+ })
384
+ export class VenueCalendarComponent implements OnInit, OnDestroy {
385
+ @ViewChild('calendarContainer', { static: true })
386
+ calendarContainer!: ElementRef;
387
+ private calendarInstance: any;
388
+
389
+ ngOnInit() {
390
+ this.calendarInstance = initVenueCalendar({
391
+ target: this.calendarContainer.nativeElement,
392
+ venueId: 'your-venue-id',
393
+ view: 'calendar',
394
+ events: [],
395
+ showViewOptions: true,
396
+ showMonthSwitcher: true,
397
+ });
398
+ }
399
+
400
+ ngOnDestroy() {
401
+ if (this.calendarInstance && this.calendarInstance.$destroy) {
402
+ this.calendarInstance.$destroy();
403
+ }
404
+ }
405
+ }
406
+ ```
407
+
408
+ ## Configuration Options
409
+
410
+ ### Data Attributes (for auto-mount)
411
+
412
+ | Attribute | Type | Default | Description |
413
+ | -------------------------- | ------- | ------------ | ---------------------------------------------------- |
414
+ | `data-venue-id` | string | `''` | The venue ID to fetch events for |
415
+ | `data-view` | string | `'calendar'` | Initial view: `'list'`, `'gallery'`, or `'calendar'` |
416
+ | `data-show-view-options` | boolean | `true` | Show view switcher buttons |
417
+ | `data-show-month-switcher` | boolean | `true` | Show month navigation controls |
418
+
419
+ ### JavaScript API Options
420
+
421
+ ```javascript
422
+ initVenueCalendar({
423
+ target: '.my-calendar', // CSS selector or HTMLElement (required)
424
+ venueId: 'venue-123', // Venue ID (optional)
425
+ view: 'calendar', // 'list', 'gallery', or 'calendar' (default: 'calendar')
426
+ events: [], // Array of event objects (default: [])
427
+ showViewOptions: true, // Show view switcher (default: true)
428
+ showMonthSwitcher: true, // Show month navigation (default: true)
429
+ });
430
+ ```
431
+
432
+ ### Event Object Structure
433
+
434
+ ```javascript
435
+ {
436
+ id: 'event-123',
437
+ name: 'Comedy Night',
438
+ date: '2024-10-25T20:00:00Z',
439
+ image: 'https://example.com/image.jpg',
440
+ status: 'On Sale',
441
+ timeline: '8:00 PM - 10:00 PM',
442
+ // ... other fields
443
+ }
444
+ ```
445
+
446
+ ## Views
447
+
448
+ ### Calendar View
449
+
450
+ The default view showing events in a monthly calendar grid. Perfect for venues with regular shows.
451
+
452
+ ### List View
453
+
454
+ A vertical list layout showing all upcoming events with details. Great for mobile experiences.
455
+
456
+ ### Gallery View
457
+
458
+ A grid layout displaying event posters in a gallery format. Ideal for showcasing event imagery.
459
+
460
+ ## WordPress Integration
461
+
462
+ For WordPress sites, you can add this to your page/post HTML:
463
+
464
+ ```html
465
+ <div
466
+ class="micdrop-calendar-container"
467
+ data-venue-id="your-venue-id"
468
+ data-view="calendar"
469
+ ></div>
470
+
471
+ <script src="https://get-micdrop.com/embed/venue-calendar.iife.js"></script>
472
+ ```
473
+
474
+ Or add the script to your theme's footer and use the div anywhere in your content.
475
+
476
+ ## Styling
477
+
478
+ The calendar comes with built-in styles using Tailwind CSS. If you need to customize the appearance, you can override the CSS classes or add your own styles.
479
+
480
+ ```css
481
+ /* Example: Custom styling */
482
+ .micdrop-calendar-container {
483
+ max-width: 1200px;
484
+ margin: 0 auto;
485
+ padding: 20px;
486
+ }
487
+ ```
488
+
489
+ **Note:** The mount target must not be a shrink-to-fit box (`display: inline-block`, `float`, `position: absolute`, `display: table-cell`, or an auto-width item in a flex row). The calendar sizes its typography to its own container, which requires the mount target to get its width from its parent — a normal block element (as in every example above) is exactly right. If the calendar detects a shrink-to-fit mount target, it falls back to page-width type scaling and logs a console warning.
490
+
491
+ ## Theming
492
+
493
+ The calendar supports comprehensive theming via CSS custom properties and JavaScript utilities.
494
+
495
+ ### Using CSS Custom Properties
496
+
497
+ Override the default theme by setting CSS custom properties:
498
+
499
+ ```css
500
+ /* Custom brand colors */
501
+ .micdrop-calendar-container {
502
+ --Brand-Primary: 270 76% 60%; /* Purple */
503
+ --Text-Primary: 0 0% 10%;
504
+ --BG-Primary: 0 0% 100%;
505
+ }
506
+
507
+ /* Dark mode */
508
+ .dark .micdrop-calendar-container,
509
+ [data-theme='dark'] .micdrop-calendar-container {
510
+ --Brand-Primary: 270 76% 70%;
511
+ --Text-Primary: 0 0% 95%;
512
+ --BG-Primary: 0 0% 10%;
513
+ }
514
+ ```
515
+
516
+ ### Available CSS Variables
517
+
518
+ | Variable | Description | Default (Light) |
519
+ | ---------------------- | ----------------------- | -------------------- |
520
+ | `--Brand-Primary` | Primary brand color | `217 91% 60%` (Blue) |
521
+ | `--Text-Primary` | Main text color | `0 0% 0%` |
522
+ | `--Text-Secondary` | Secondary text | `0 0% 40%` |
523
+ | `--BG-Primary` | Main background | `0 0% 100%` |
524
+ | `--BG-Secondary` | Secondary background | `0 0% 98%` |
525
+ | `--Stroke-Primary` | Border colors | `0 0% 80%` |
526
+ | `--Status-OnSale` | "On Sale" badge | `217 91% 60%` |
527
+ | `--Status-SellingFast` | "Selling Fast" badge | `38 92% 50%` |
528
+ | `--Status-SoldOut` | "Sold Out" badge | `0 84% 60%` |
529
+ | `--Today-BG` | Today's date background | `217 91% 97%` |
530
+ | `--Focus-Ring` | Keyboard focus ring | `217 91% 60%` |
531
+
532
+ ### Using JavaScript Theme Utilities
533
+
534
+ ```javascript
535
+ import {
536
+ applyTheme,
537
+ themes,
538
+ generateThemeCSS,
539
+ } from '@getmicdrop/venue-calendar';
540
+
541
+ // Apply a preset theme
542
+ applyTheme(themes.dark);
543
+
544
+ // Apply to a specific container
545
+ const container = document.querySelector('.micdrop-calendar-container');
546
+ applyTheme(themes.dark, container);
547
+
548
+ // Create a custom theme
549
+ const myTheme = {
550
+ brandPrimary: '270 76% 60%', // Purple
551
+ textPrimary: '0 0% 10%',
552
+ bgPrimary: '0 0% 100%',
553
+ statusOnSale: '142 71% 45%', // Green for on sale
554
+ };
555
+ applyTheme(myTheme);
556
+
557
+ // Generate CSS string for embedding
558
+ const cssString = generateThemeCSS(myTheme);
559
+ console.log(cssString);
560
+ // Output: :root { --Brand-Primary: 270 76% 60%; ... }
561
+ ```
562
+
563
+ ### Preset Themes
564
+
565
+ Three themes are included out of the box:
566
+
567
+ ```javascript
568
+ import { themes } from '@getmicdrop/venue-calendar';
569
+
570
+ // Light theme (default)
571
+ applyTheme(themes.light);
572
+
573
+ // Dark theme
574
+ applyTheme(themes.dark);
575
+
576
+ // High contrast (accessibility)
577
+ applyTheme(themes.highContrast);
578
+ ```
579
+
580
+ ### Automatic Dark Mode
581
+
582
+ The calendar automatically respects the user's system preference:
583
+
584
+ ```css
585
+ /* Automatically applied when user prefers dark mode */
586
+ @media (prefers-color-scheme: dark) {
587
+ /* Dark theme variables are applied */
588
+ }
589
+ ```
590
+
591
+ You can also manually toggle dark mode:
592
+
593
+ ```html
594
+ <!-- Add 'dark' class to enable dark theme -->
595
+ <div class="dark">
596
+ <div class="micdrop-calendar-container" data-venue-id="123"></div>
597
+ </div>
598
+
599
+ <!-- Or use data-theme attribute -->
600
+ <div data-theme="dark">
601
+ <div class="micdrop-calendar-container" data-venue-id="123"></div>
602
+ </div>
603
+ ```
604
+
605
+ ## Localization and the CDN bundle
606
+
607
+ The calendar and the checkout speak thirteen languages. English is built into the
608
+ bundle; the other twelve are **fetched at runtime**, one file, only for the
609
+ locale actually in use — so a buyer never downloads twelve languages to read one.
610
+ This is what keeps the CDN bundle at ~594 KB gzipped instead of ~686 KB.
611
+
612
+ For the npm package nothing is required: your bundler code-splits the catalogues
613
+ and addresses them itself.
614
+
615
+ For the `<script>` embed, the bundle works out where to fetch from by looking at
616
+ the URL it was itself served from, and loads a **version-pinned sibling**:
617
+
618
+ ```
619
+ https://get-micdrop.com/embed/venue-calendar.iife.js
620
+ https://get-micdrop.com/embed/locales/<version>/main/es.js ← calendar copy
621
+ https://get-micdrop.com/embed/locales/<version>/flow/es.js ← checkout copy
622
+ ```
623
+
624
+ If you self-host the bundle, **copy the `dist/locales/` directory alongside it**.
625
+ Version-pinned paths mean new releases only ever add files; they never overwrite
626
+ the catalogue an older pinned bundle is using.
627
+
628
+ ### When the fetch cannot happen
629
+
630
+ If the locale file cannot be loaded — a Content-Security-Policy that allowlists
631
+ the exact script file rather than the origin, an offline CDN, or a
632
+ `<script type="module">` embed where the bundle cannot see its own URL — the
633
+ calendar simply stays in English. Nothing errors and nothing breaks; the widget
634
+ renders and sells tickets exactly as it does for an English buyer.
635
+
636
+ Two ways to point it at the right place when it cannot work it out:
637
+
638
+ ```html
639
+ <!-- Option A: tell it before the bundle loads -->
640
+ <script>
641
+ window.__MICDROP_CALENDAR_ASSET_BASE__ = 'https://get-micdrop.com/embed/';
642
+ </script>
643
+ <script
644
+ defer
645
+ src="https://get-micdrop.com/embed/venue-calendar.iife.js"
646
+ ></script>
647
+
648
+ <!-- Option B: put it on the tag -->
649
+ <script
650
+ defer
651
+ src="https://get-micdrop.com/embed/venue-calendar.iife.js"
652
+ data-vc-base="https://get-micdrop.com/embed/"
653
+ ></script>
654
+ ```
655
+
656
+ `defer` and `async` need no override — a classic script still knows its own URL.
657
+
658
+ ### First paint is always English
659
+
660
+ A non-English buyer sees English for a moment on a cold load and the page swaps
661
+ to their language when the catalogue arrives. This is deliberate: the calendar
662
+ paints immediately rather than waiting on a translation file.
663
+
664
+ ## Browser Support
665
+
666
+ - Chrome (latest)
667
+ - Firefox (latest)
668
+ - Safari (latest)
669
+ - Edge (latest)
670
+ - Mobile browsers (iOS Safari, Chrome Mobile)
671
+
672
+ ## Development
673
+
674
+ ### Building the Package
675
+
676
+ ```bash
677
+ # Install dependencies
678
+ npm install
679
+
680
+ # Build the library
681
+ npm run build:lib
682
+
683
+ # Development mode (SvelteKit app)
684
+ npm run dev
685
+
686
+ # Preview production build
687
+ npm run preview
688
+ ```
689
+
690
+ `build:lib` is three passes and the order matters: the IIFE pass runs first and
691
+ owns the `dist/` wipe, the ES/UMD pass appends to it, and
692
+ `vite.config.locales.js` appends the runtime locale catalogues
693
+ (`dist/locales/<version>/{main,flow}/<tag>.js`) last. `npm run size` fails if the
694
+ bundle and its catalogues do not both come out of that.
695
+
696
+ ### Project Structure
697
+
698
+ ```
699
+ venue-calendar/
700
+ ├── src/
701
+ │ ├── components/ # Svelte components
702
+ │ │ ├── Calendar/
703
+ │ │ ├── CalendarContainer/
704
+ │ │ └── Button/
705
+ │ ├── lib/ # Library entry points
706
+ │ │ ├── VenueCalendar.js
707
+ │ │ └── web-component.js
708
+ │ └── routes/ # SvelteKit routes (for dev)
709
+ ├── dist/ # Built package (generated)
710
+ │ └── locales/<version>/ # Runtime locale catalogues for the CDN bundle
711
+ ├── package.json
712
+ ├── vite.config.lib.js # Library build config (ES/UMD + IIFE passes)
713
+ ├── vite.config.locales.js # Emits the runtime locale catalogues
714
+ └── README.md
715
+ ```
716
+
717
+ ### Lockfile policy
718
+
719
+ `package-lock.json` is the single canonical lockfile — CI and the publish
720
+ workflow install with `npm ci`. Do not commit `yarn.lock` or
721
+ `pnpm-lock.yaml` (both gitignored). Local dev machines may use pnpm for the
722
+ svelte-components symlink workflow, but dependency changes must land in
723
+ `package-lock.json` via npm.
724
+
725
+ ## API Reference
726
+
727
+ ### `initVenueCalendar(options)`
728
+
729
+ Initialize a calendar instance.
730
+
731
+ **Parameters:**
732
+
733
+ - `options` (Object): Configuration options
734
+
735
+ **Returns:** Svelte component instance
736
+
737
+ **Example:**
738
+
739
+ ```javascript
740
+ const calendar = initVenueCalendar({
741
+ target: '#calendar',
742
+ venueId: 'venue-123',
743
+ view: 'calendar',
744
+ });
745
+ ```
746
+
747
+ ### `autoMount()`
748
+
749
+ Automatically mount calendars to all elements with class `micdrop-calendar-container`.
750
+
751
+ **Example:**
752
+
753
+ ```javascript
754
+ import { autoMount } from '@getmicdrop/venue-calendar';
755
+ autoMount();
756
+ ```
757
+
758
+ ### Component Events
759
+
760
+ The calendar component emits events that you can listen to:
761
+
762
+ ```javascript
763
+ const calendar = initVenueCalendar({
764
+ target: '#calendar',
765
+ // ... other options
766
+ });
767
+
768
+ // Listen to component events (if using Svelte component directly)
769
+ calendar.$on('eventClick', event => {
770
+ console.log('Event clicked:', event.detail);
771
+ });
772
+ ```
773
+
774
+ ## Troubleshooting
775
+
776
+ ### Calendar not appearing
777
+
778
+ 1. **Check the script is loaded**: Open browser console and verify no errors
779
+ 2. **Verify container exists**: Make sure the target element exists in the DOM
780
+ 3. **Check data attributes**: Ensure attributes are correctly formatted with `data-` prefix
781
+
782
+ ### Styles not applying
783
+
784
+ 1. **CSS not loaded**: The styles are bundled in the JS file and auto-injected — no separate stylesheet needed
785
+ 2. **CSS conflicts**: Check if other styles are overriding the calendar styles
786
+ 3. **Bundle didn't load**: Confirm the `<script src>` points at the Micdrop-hosted bundle and returns 200 (not 404)
787
+
788
+ ### Events not showing
789
+
790
+ 1. **Check event data format**: Ensure events match the expected structure
791
+ 2. **Date format**: Use ISO 8601 format for dates (`YYYY-MM-DDTHH:mm:ssZ`)
792
+ 3. **Venue ID**: Verify the venue ID is correct
793
+
794
+ ## Contributing
795
+
796
+ Contributions are welcome! Please feel free to submit a Pull Request.
797
+
798
+ ## License
799
+
800
+ MIT © MicDrop
801
+
802
+ ## Support
803
+
804
+ For issues, questions, or feature requests, please visit:
805
+ https://github.com/get-micdrop/venue-calendar/issues
806
+
807
+ ---
808
+
809
+ Made with ❤️ by the MicDrop team