@getmicdrop/venue-calendar 4.3.60 → 4.3.61

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 (76) hide show
  1. package/README.md +818 -818
  2. package/dist/{AddOnCard-BzTz2vbx.js → AddOnCard-DAk2utke.js} +2 -2
  3. package/dist/{CalendarFoundryView-CU34kx17.js → CalendarFoundryView-DQpSevsn.js} +3236 -3535
  4. package/dist/{CartView-BU-h3sJN.js → CartView-wqbSdon7.js} +2 -2
  5. package/dist/{Checkout-DYCcC-0N.js → Checkout-1kTSmfH0.js} +6 -5
  6. package/dist/Checkout-sc0A2N8V.js +1974 -0
  7. package/dist/{CheckoutTimer-BuYZ9k4Q.js → CheckoutTimer-Btjo7PEg.js} +1 -1
  8. package/dist/{CloseIcon-DtlW0vQT.js → CloseIcon-DKrZf_7a.js} +2 -2
  9. package/dist/{CollectionView-CXMxxdWQ.js → CollectionView-BSyl9I7o.js} +2 -2
  10. package/dist/{Event-Cqx8TF2r.js → Event-BtaN2kmZ.js} +4 -4
  11. package/dist/{EventPage-BptjljMB.js → EventPage-u_eHSHuz.js} +2 -2
  12. package/dist/{RecommendationsRail-Bd_HV1Ou.js → RecommendationsRail-D9S_5rVW.js} +1 -1
  13. package/dist/Select-CjELEaSF.js +376 -0
  14. package/dist/{SeriesPage-cRi8ifRa.js → SeriesPage-DQhp3Q6I.js} +2 -2
  15. package/dist/{Success-CjjUwjXy.js → Success-3ivVwkoj.js} +3 -3
  16. package/dist/api/transformers/venue.d.ts +26 -0
  17. package/dist/{constants-DWS8C09a.js → constants-CBPzJAKF.js} +2 -0
  18. package/dist/{de-BEfuIgIj.js → de-B7O-dJsW.js} +2 -0
  19. package/dist/{es-Bd4g_HxR.js → es-CAjDq_xB.js} +2 -0
  20. package/dist/{fr-Blc79wvV.js → fr-CcNvBKpw.js} +2 -0
  21. package/dist/{i18n-CfXVv1y8.js → i18n-DLFy3vxe.js} +14 -14
  22. package/dist/{id-DzzsiF4d.js → id-CSzRaEmh.js} +2 -0
  23. package/dist/{it-B4zXRsxu.js → it-C1y4ckE2.js} +2 -0
  24. package/dist/{ja-eT4_QPKd.js → ja-BkffKruj.js} +2 -0
  25. package/dist/{ko-B8Fmrldj.js → ko-rUezp1-T.js} +2 -0
  26. package/dist/locales/4.3.61/flow/de.js +1 -0
  27. package/dist/locales/4.3.61/flow/es.js +1 -0
  28. package/dist/locales/4.3.61/flow/fr.js +1 -0
  29. package/dist/locales/4.3.61/flow/id.js +1 -0
  30. package/dist/locales/4.3.61/flow/it.js +1 -0
  31. package/dist/locales/4.3.61/flow/ja.js +1 -0
  32. package/dist/locales/4.3.61/flow/ko.js +1 -0
  33. package/dist/locales/4.3.61/flow/nl.js +1 -0
  34. package/dist/locales/4.3.61/flow/pl.js +1 -0
  35. package/dist/locales/4.3.61/flow/pt-br.js +1 -0
  36. package/dist/locales/4.3.61/flow/tr.js +1 -0
  37. package/dist/locales/4.3.61/flow/zh.js +1 -0
  38. package/dist/{nl-Dnug8LYt.js → nl-B92Oj0Kq.js} +2 -0
  39. package/dist/{pl-C3XxoYWF.js → pl-Q8SsShQJ.js} +2 -0
  40. package/dist/{pt-br-Y40SBe4b.js → pt-br-HJoQU6-m.js} +2 -0
  41. package/dist/{tr-2zIJ-vq2.js → tr-Cztm2s2I.js} +2 -0
  42. package/dist/types/index.d.ts +597 -597
  43. package/dist/venue-Dv8MYjz_.js +17 -0
  44. package/dist/venue-calendar.es.js +12 -12
  45. package/dist/venue-calendar.iife.js +34 -34
  46. package/dist/venue-calendar.umd.js +30 -30
  47. package/dist/{zh-vNYvCXO7.js → zh-CInFBbgV.js} +2 -0
  48. package/package.json +2 -2
  49. package/src/lib/theme.js +222 -222
  50. package/dist/Checkbox-BtJwg_CF.js +0 -61
  51. package/dist/Checkout-CpbaR-uS.js +0 -1933
  52. package/dist/locales/4.3.60/flow/de.js +0 -1
  53. package/dist/locales/4.3.60/flow/es.js +0 -1
  54. package/dist/locales/4.3.60/flow/fr.js +0 -1
  55. package/dist/locales/4.3.60/flow/id.js +0 -1
  56. package/dist/locales/4.3.60/flow/it.js +0 -1
  57. package/dist/locales/4.3.60/flow/ja.js +0 -1
  58. package/dist/locales/4.3.60/flow/ko.js +0 -1
  59. package/dist/locales/4.3.60/flow/nl.js +0 -1
  60. package/dist/locales/4.3.60/flow/pl.js +0 -1
  61. package/dist/locales/4.3.60/flow/pt-br.js +0 -1
  62. package/dist/locales/4.3.60/flow/tr.js +0 -1
  63. package/dist/locales/4.3.60/flow/zh.js +0 -1
  64. package/dist/venue-BBoZBuhu.js +0 -13
  65. /package/dist/locales/{4.3.60 → 4.3.61}/main/de.js +0 -0
  66. /package/dist/locales/{4.3.60 → 4.3.61}/main/es.js +0 -0
  67. /package/dist/locales/{4.3.60 → 4.3.61}/main/fr.js +0 -0
  68. /package/dist/locales/{4.3.60 → 4.3.61}/main/id.js +0 -0
  69. /package/dist/locales/{4.3.60 → 4.3.61}/main/it.js +0 -0
  70. /package/dist/locales/{4.3.60 → 4.3.61}/main/ja.js +0 -0
  71. /package/dist/locales/{4.3.60 → 4.3.61}/main/ko.js +0 -0
  72. /package/dist/locales/{4.3.60 → 4.3.61}/main/nl.js +0 -0
  73. /package/dist/locales/{4.3.60 → 4.3.61}/main/pl.js +0 -0
  74. /package/dist/locales/{4.3.60 → 4.3.61}/main/pt-br.js +0 -0
  75. /package/dist/locales/{4.3.60 → 4.3.61}/main/tr.js +0 -0
  76. /package/dist/locales/{4.3.60 → 4.3.61}/main/zh.js +0 -0
package/README.md CHANGED
@@ -1,818 +1,818 @@
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
- Host-page props — for a page that already owns part of what the widget draws.
369
- Every one defaults to on, so embeds are unchanged:
370
-
371
- | Prop | Type | Default | Description |
372
- | ------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
373
- | `checkoutProbe` | boolean | `true` | Ask whether this page can sell a ticket and overlay the "Online ticket sales are unavailable here" alert when it cannot. `false` skips the probe and never draws the alert — for a host where no ticket is bought, such as a site builder's design canvas. |
374
- | `showGiftCardsLink` | boolean | `true` | The footer's "Gift cards" link. `false` for a host page whose own footer already carries it. |
375
- | `showPoweredBy` | boolean | `true` | The footer's "Powered by Micdrop" mark. `false` for a host page whose own footer already carries it. |
376
-
377
- ### Angular
378
-
379
- ```typescript
380
- import {
381
- Component,
382
- OnInit,
383
- OnDestroy,
384
- ElementRef,
385
- ViewChild,
386
- } from '@angular/core';
387
- import { initVenueCalendar } from '@getmicdrop/venue-calendar';
388
-
389
- @Component({
390
- selector: 'app-venue-calendar',
391
- template: '<div #calendarContainer></div>',
392
- })
393
- export class VenueCalendarComponent implements OnInit, OnDestroy {
394
- @ViewChild('calendarContainer', { static: true })
395
- calendarContainer!: ElementRef;
396
- private calendarInstance: any;
397
-
398
- ngOnInit() {
399
- this.calendarInstance = initVenueCalendar({
400
- target: this.calendarContainer.nativeElement,
401
- venueId: 'your-venue-id',
402
- view: 'calendar',
403
- events: [],
404
- showViewOptions: true,
405
- showMonthSwitcher: true,
406
- });
407
- }
408
-
409
- ngOnDestroy() {
410
- if (this.calendarInstance && this.calendarInstance.$destroy) {
411
- this.calendarInstance.$destroy();
412
- }
413
- }
414
- }
415
- ```
416
-
417
- ## Configuration Options
418
-
419
- ### Data Attributes (for auto-mount)
420
-
421
- | Attribute | Type | Default | Description |
422
- | -------------------------- | ------- | ------------ | ---------------------------------------------------- |
423
- | `data-venue-id` | string | `''` | The venue ID to fetch events for |
424
- | `data-view` | string | `'calendar'` | Initial view: `'list'`, `'gallery'`, or `'calendar'` |
425
- | `data-show-view-options` | boolean | `true` | Show view switcher buttons |
426
- | `data-show-month-switcher` | boolean | `true` | Show month navigation controls |
427
-
428
- ### JavaScript API Options
429
-
430
- ```javascript
431
- initVenueCalendar({
432
- target: '.my-calendar', // CSS selector or HTMLElement (required)
433
- venueId: 'venue-123', // Venue ID (optional)
434
- view: 'calendar', // 'list', 'gallery', or 'calendar' (default: 'calendar')
435
- events: [], // Array of event objects (default: [])
436
- showViewOptions: true, // Show view switcher (default: true)
437
- showMonthSwitcher: true, // Show month navigation (default: true)
438
- });
439
- ```
440
-
441
- ### Event Object Structure
442
-
443
- ```javascript
444
- {
445
- id: 'event-123',
446
- name: 'Comedy Night',
447
- date: '2024-10-25T20:00:00Z',
448
- image: 'https://example.com/image.jpg',
449
- status: 'On Sale',
450
- timeline: '8:00 PM - 10:00 PM',
451
- // ... other fields
452
- }
453
- ```
454
-
455
- ## Views
456
-
457
- ### Calendar View
458
-
459
- The default view showing events in a monthly calendar grid. Perfect for venues with regular shows.
460
-
461
- ### List View
462
-
463
- A vertical list layout showing all upcoming events with details. Great for mobile experiences.
464
-
465
- ### Gallery View
466
-
467
- A grid layout displaying event posters in a gallery format. Ideal for showcasing event imagery.
468
-
469
- ## WordPress Integration
470
-
471
- For WordPress sites, you can add this to your page/post HTML:
472
-
473
- ```html
474
- <div
475
- class="micdrop-calendar-container"
476
- data-venue-id="your-venue-id"
477
- data-view="calendar"
478
- ></div>
479
-
480
- <script src="https://get-micdrop.com/embed/venue-calendar.iife.js"></script>
481
- ```
482
-
483
- Or add the script to your theme's footer and use the div anywhere in your content.
484
-
485
- ## Styling
486
-
487
- 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.
488
-
489
- ```css
490
- /* Example: Custom styling */
491
- .micdrop-calendar-container {
492
- max-width: 1200px;
493
- margin: 0 auto;
494
- padding: 20px;
495
- }
496
- ```
497
-
498
- **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.
499
-
500
- ## Theming
501
-
502
- The calendar supports comprehensive theming via CSS custom properties and JavaScript utilities.
503
-
504
- ### Using CSS Custom Properties
505
-
506
- Override the default theme by setting CSS custom properties:
507
-
508
- ```css
509
- /* Custom brand colors */
510
- .micdrop-calendar-container {
511
- --Brand-Primary: 270 76% 60%; /* Purple */
512
- --Text-Primary: 0 0% 10%;
513
- --BG-Primary: 0 0% 100%;
514
- }
515
-
516
- /* Dark mode */
517
- .dark .micdrop-calendar-container,
518
- [data-theme='dark'] .micdrop-calendar-container {
519
- --Brand-Primary: 270 76% 70%;
520
- --Text-Primary: 0 0% 95%;
521
- --BG-Primary: 0 0% 10%;
522
- }
523
- ```
524
-
525
- ### Available CSS Variables
526
-
527
- | Variable | Description | Default (Light) |
528
- | ---------------------- | ----------------------- | -------------------- |
529
- | `--Brand-Primary` | Primary brand color | `217 91% 60%` (Blue) |
530
- | `--Text-Primary` | Main text color | `0 0% 0%` |
531
- | `--Text-Secondary` | Secondary text | `0 0% 40%` |
532
- | `--BG-Primary` | Main background | `0 0% 100%` |
533
- | `--BG-Secondary` | Secondary background | `0 0% 98%` |
534
- | `--Stroke-Primary` | Border colors | `0 0% 80%` |
535
- | `--Status-OnSale` | "On Sale" badge | `217 91% 60%` |
536
- | `--Status-SellingFast` | "Selling Fast" badge | `38 92% 50%` |
537
- | `--Status-SoldOut` | "Sold Out" badge | `0 84% 60%` |
538
- | `--Today-BG` | Today's date background | `217 91% 97%` |
539
- | `--Focus-Ring` | Keyboard focus ring | `217 91% 60%` |
540
-
541
- ### Using JavaScript Theme Utilities
542
-
543
- ```javascript
544
- import {
545
- applyTheme,
546
- themes,
547
- generateThemeCSS,
548
- } from '@getmicdrop/venue-calendar';
549
-
550
- // Apply a preset theme
551
- applyTheme(themes.dark);
552
-
553
- // Apply to a specific container
554
- const container = document.querySelector('.micdrop-calendar-container');
555
- applyTheme(themes.dark, container);
556
-
557
- // Create a custom theme
558
- const myTheme = {
559
- brandPrimary: '270 76% 60%', // Purple
560
- textPrimary: '0 0% 10%',
561
- bgPrimary: '0 0% 100%',
562
- statusOnSale: '142 71% 45%', // Green for on sale
563
- };
564
- applyTheme(myTheme);
565
-
566
- // Generate CSS string for embedding
567
- const cssString = generateThemeCSS(myTheme);
568
- console.log(cssString);
569
- // Output: :root { --Brand-Primary: 270 76% 60%; ... }
570
- ```
571
-
572
- ### Preset Themes
573
-
574
- Three themes are included out of the box:
575
-
576
- ```javascript
577
- import { themes } from '@getmicdrop/venue-calendar';
578
-
579
- // Light theme (default)
580
- applyTheme(themes.light);
581
-
582
- // Dark theme
583
- applyTheme(themes.dark);
584
-
585
- // High contrast (accessibility)
586
- applyTheme(themes.highContrast);
587
- ```
588
-
589
- ### Automatic Dark Mode
590
-
591
- The calendar automatically respects the user's system preference:
592
-
593
- ```css
594
- /* Automatically applied when user prefers dark mode */
595
- @media (prefers-color-scheme: dark) {
596
- /* Dark theme variables are applied */
597
- }
598
- ```
599
-
600
- You can also manually toggle dark mode:
601
-
602
- ```html
603
- <!-- Add 'dark' class to enable dark theme -->
604
- <div class="dark">
605
- <div class="micdrop-calendar-container" data-venue-id="123"></div>
606
- </div>
607
-
608
- <!-- Or use data-theme attribute -->
609
- <div data-theme="dark">
610
- <div class="micdrop-calendar-container" data-venue-id="123"></div>
611
- </div>
612
- ```
613
-
614
- ## Localization and the CDN bundle
615
-
616
- The calendar and the checkout speak thirteen languages. English is built into the
617
- bundle; the other twelve are **fetched at runtime**, one file, only for the
618
- locale actually in use — so a buyer never downloads twelve languages to read one.
619
- This is what keeps the CDN bundle at ~594 KB gzipped instead of ~686 KB.
620
-
621
- For the npm package nothing is required: your bundler code-splits the catalogues
622
- and addresses them itself.
623
-
624
- For the `<script>` embed, the bundle works out where to fetch from by looking at
625
- the URL it was itself served from, and loads a **version-pinned sibling**:
626
-
627
- ```
628
- https://get-micdrop.com/embed/venue-calendar.iife.js
629
- https://get-micdrop.com/embed/locales/<version>/main/es.js ← calendar copy
630
- https://get-micdrop.com/embed/locales/<version>/flow/es.js ← checkout copy
631
- ```
632
-
633
- If you self-host the bundle, **copy the `dist/locales/` directory alongside it**.
634
- Version-pinned paths mean new releases only ever add files; they never overwrite
635
- the catalogue an older pinned bundle is using.
636
-
637
- ### When the fetch cannot happen
638
-
639
- If the locale file cannot be loaded — a Content-Security-Policy that allowlists
640
- the exact script file rather than the origin, an offline CDN, or a
641
- `<script type="module">` embed where the bundle cannot see its own URL — the
642
- calendar simply stays in English. Nothing errors and nothing breaks; the widget
643
- renders and sells tickets exactly as it does for an English buyer.
644
-
645
- Two ways to point it at the right place when it cannot work it out:
646
-
647
- ```html
648
- <!-- Option A: tell it before the bundle loads -->
649
- <script>
650
- window.__MICDROP_CALENDAR_ASSET_BASE__ = 'https://get-micdrop.com/embed/';
651
- </script>
652
- <script
653
- defer
654
- src="https://get-micdrop.com/embed/venue-calendar.iife.js"
655
- ></script>
656
-
657
- <!-- Option B: put it on the tag -->
658
- <script
659
- defer
660
- src="https://get-micdrop.com/embed/venue-calendar.iife.js"
661
- data-vc-base="https://get-micdrop.com/embed/"
662
- ></script>
663
- ```
664
-
665
- `defer` and `async` need no override — a classic script still knows its own URL.
666
-
667
- ### First paint is always English
668
-
669
- A non-English buyer sees English for a moment on a cold load and the page swaps
670
- to their language when the catalogue arrives. This is deliberate: the calendar
671
- paints immediately rather than waiting on a translation file.
672
-
673
- ## Browser Support
674
-
675
- - Chrome (latest)
676
- - Firefox (latest)
677
- - Safari (latest)
678
- - Edge (latest)
679
- - Mobile browsers (iOS Safari, Chrome Mobile)
680
-
681
- ## Development
682
-
683
- ### Building the Package
684
-
685
- ```bash
686
- # Install dependencies
687
- npm install
688
-
689
- # Build the library
690
- npm run build:lib
691
-
692
- # Development mode (SvelteKit app)
693
- npm run dev
694
-
695
- # Preview production build
696
- npm run preview
697
- ```
698
-
699
- `build:lib` is three passes and the order matters: the IIFE pass runs first and
700
- owns the `dist/` wipe, the ES/UMD pass appends to it, and
701
- `vite.config.locales.js` appends the runtime locale catalogues
702
- (`dist/locales/<version>/{main,flow}/<tag>.js`) last. `npm run size` fails if the
703
- bundle and its catalogues do not both come out of that.
704
-
705
- ### Project Structure
706
-
707
- ```
708
- venue-calendar/
709
- ├── src/
710
- │ ├── components/ # Svelte components
711
- │ │ ├── Calendar/
712
- │ │ ├── CalendarContainer/
713
- │ │ └── Button/
714
- │ ├── lib/ # Library entry points
715
- │ │ ├── VenueCalendar.js
716
- │ │ └── web-component.js
717
- │ └── routes/ # SvelteKit routes (for dev)
718
- ├── dist/ # Built package (generated)
719
- │ └── locales/<version>/ # Runtime locale catalogues for the CDN bundle
720
- ├── package.json
721
- ├── vite.config.lib.js # Library build config (ES/UMD + IIFE passes)
722
- ├── vite.config.locales.js # Emits the runtime locale catalogues
723
- └── README.md
724
- ```
725
-
726
- ### Lockfile policy
727
-
728
- `package-lock.json` is the single canonical lockfile — CI and the publish
729
- workflow install with `npm ci`. Do not commit `yarn.lock` or
730
- `pnpm-lock.yaml` (both gitignored). Local dev machines may use pnpm for the
731
- svelte-components symlink workflow, but dependency changes must land in
732
- `package-lock.json` via npm.
733
-
734
- ## API Reference
735
-
736
- ### `initVenueCalendar(options)`
737
-
738
- Initialize a calendar instance.
739
-
740
- **Parameters:**
741
-
742
- - `options` (Object): Configuration options
743
-
744
- **Returns:** Svelte component instance
745
-
746
- **Example:**
747
-
748
- ```javascript
749
- const calendar = initVenueCalendar({
750
- target: '#calendar',
751
- venueId: 'venue-123',
752
- view: 'calendar',
753
- });
754
- ```
755
-
756
- ### `autoMount()`
757
-
758
- Automatically mount calendars to all elements with class `micdrop-calendar-container`.
759
-
760
- **Example:**
761
-
762
- ```javascript
763
- import { autoMount } from '@getmicdrop/venue-calendar';
764
- autoMount();
765
- ```
766
-
767
- ### Component Events
768
-
769
- The calendar component emits events that you can listen to:
770
-
771
- ```javascript
772
- const calendar = initVenueCalendar({
773
- target: '#calendar',
774
- // ... other options
775
- });
776
-
777
- // Listen to component events (if using Svelte component directly)
778
- calendar.$on('eventClick', event => {
779
- console.log('Event clicked:', event.detail);
780
- });
781
- ```
782
-
783
- ## Troubleshooting
784
-
785
- ### Calendar not appearing
786
-
787
- 1. **Check the script is loaded**: Open browser console and verify no errors
788
- 2. **Verify container exists**: Make sure the target element exists in the DOM
789
- 3. **Check data attributes**: Ensure attributes are correctly formatted with `data-` prefix
790
-
791
- ### Styles not applying
792
-
793
- 1. **CSS not loaded**: The styles are bundled in the JS file and auto-injected — no separate stylesheet needed
794
- 2. **CSS conflicts**: Check if other styles are overriding the calendar styles
795
- 3. **Bundle didn't load**: Confirm the `<script src>` points at the Micdrop-hosted bundle and returns 200 (not 404)
796
-
797
- ### Events not showing
798
-
799
- 1. **Check event data format**: Ensure events match the expected structure
800
- 2. **Date format**: Use ISO 8601 format for dates (`YYYY-MM-DDTHH:mm:ssZ`)
801
- 3. **Venue ID**: Verify the venue ID is correct
802
-
803
- ## Contributing
804
-
805
- Contributions are welcome! Please feel free to submit a Pull Request.
806
-
807
- ## License
808
-
809
- MIT © MicDrop
810
-
811
- ## Support
812
-
813
- For issues, questions, or feature requests, please visit:
814
- https://github.com/get-micdrop/venue-calendar/issues
815
-
816
- ---
817
-
818
- 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
+ Host-page props — for a page that already owns part of what the widget draws.
369
+ Every one defaults to on, so embeds are unchanged:
370
+
371
+ | Prop | Type | Default | Description |
372
+ | ------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
373
+ | `checkoutProbe` | boolean | `true` | Ask whether this page can sell a ticket and overlay the "Online ticket sales are unavailable here" alert when it cannot. `false` skips the probe and never draws the alert — for a host where no ticket is bought, such as a site builder's design canvas. |
374
+ | `showGiftCardsLink` | boolean | `true` | The footer's "Gift cards" link. `false` for a host page whose own footer already carries it. |
375
+ | `showPoweredBy` | boolean | `true` | The footer's "Powered by Micdrop" mark. `false` for a host page whose own footer already carries it. |
376
+
377
+ ### Angular
378
+
379
+ ```typescript
380
+ import {
381
+ Component,
382
+ OnInit,
383
+ OnDestroy,
384
+ ElementRef,
385
+ ViewChild,
386
+ } from '@angular/core';
387
+ import { initVenueCalendar } from '@getmicdrop/venue-calendar';
388
+
389
+ @Component({
390
+ selector: 'app-venue-calendar',
391
+ template: '<div #calendarContainer></div>',
392
+ })
393
+ export class VenueCalendarComponent implements OnInit, OnDestroy {
394
+ @ViewChild('calendarContainer', { static: true })
395
+ calendarContainer!: ElementRef;
396
+ private calendarInstance: any;
397
+
398
+ ngOnInit() {
399
+ this.calendarInstance = initVenueCalendar({
400
+ target: this.calendarContainer.nativeElement,
401
+ venueId: 'your-venue-id',
402
+ view: 'calendar',
403
+ events: [],
404
+ showViewOptions: true,
405
+ showMonthSwitcher: true,
406
+ });
407
+ }
408
+
409
+ ngOnDestroy() {
410
+ if (this.calendarInstance && this.calendarInstance.$destroy) {
411
+ this.calendarInstance.$destroy();
412
+ }
413
+ }
414
+ }
415
+ ```
416
+
417
+ ## Configuration Options
418
+
419
+ ### Data Attributes (for auto-mount)
420
+
421
+ | Attribute | Type | Default | Description |
422
+ | -------------------------- | ------- | ------------ | ---------------------------------------------------- |
423
+ | `data-venue-id` | string | `''` | The venue ID to fetch events for |
424
+ | `data-view` | string | `'calendar'` | Initial view: `'list'`, `'gallery'`, or `'calendar'` |
425
+ | `data-show-view-options` | boolean | `true` | Show view switcher buttons |
426
+ | `data-show-month-switcher` | boolean | `true` | Show month navigation controls |
427
+
428
+ ### JavaScript API Options
429
+
430
+ ```javascript
431
+ initVenueCalendar({
432
+ target: '.my-calendar', // CSS selector or HTMLElement (required)
433
+ venueId: 'venue-123', // Venue ID (optional)
434
+ view: 'calendar', // 'list', 'gallery', or 'calendar' (default: 'calendar')
435
+ events: [], // Array of event objects (default: [])
436
+ showViewOptions: true, // Show view switcher (default: true)
437
+ showMonthSwitcher: true, // Show month navigation (default: true)
438
+ });
439
+ ```
440
+
441
+ ### Event Object Structure
442
+
443
+ ```javascript
444
+ {
445
+ id: 'event-123',
446
+ name: 'Comedy Night',
447
+ date: '2024-10-25T20:00:00Z',
448
+ image: 'https://example.com/image.jpg',
449
+ status: 'On Sale',
450
+ timeline: '8:00 PM - 10:00 PM',
451
+ // ... other fields
452
+ }
453
+ ```
454
+
455
+ ## Views
456
+
457
+ ### Calendar View
458
+
459
+ The default view showing events in a monthly calendar grid. Perfect for venues with regular shows.
460
+
461
+ ### List View
462
+
463
+ A vertical list layout showing all upcoming events with details. Great for mobile experiences.
464
+
465
+ ### Gallery View
466
+
467
+ A grid layout displaying event posters in a gallery format. Ideal for showcasing event imagery.
468
+
469
+ ## WordPress Integration
470
+
471
+ For WordPress sites, you can add this to your page/post HTML:
472
+
473
+ ```html
474
+ <div
475
+ class="micdrop-calendar-container"
476
+ data-venue-id="your-venue-id"
477
+ data-view="calendar"
478
+ ></div>
479
+
480
+ <script src="https://get-micdrop.com/embed/venue-calendar.iife.js"></script>
481
+ ```
482
+
483
+ Or add the script to your theme's footer and use the div anywhere in your content.
484
+
485
+ ## Styling
486
+
487
+ 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.
488
+
489
+ ```css
490
+ /* Example: Custom styling */
491
+ .micdrop-calendar-container {
492
+ max-width: 1200px;
493
+ margin: 0 auto;
494
+ padding: 20px;
495
+ }
496
+ ```
497
+
498
+ **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.
499
+
500
+ ## Theming
501
+
502
+ The calendar supports comprehensive theming via CSS custom properties and JavaScript utilities.
503
+
504
+ ### Using CSS Custom Properties
505
+
506
+ Override the default theme by setting CSS custom properties:
507
+
508
+ ```css
509
+ /* Custom brand colors */
510
+ .micdrop-calendar-container {
511
+ --Brand-Primary: 270 76% 60%; /* Purple */
512
+ --Text-Primary: 0 0% 10%;
513
+ --BG-Primary: 0 0% 100%;
514
+ }
515
+
516
+ /* Dark mode */
517
+ .dark .micdrop-calendar-container,
518
+ [data-theme='dark'] .micdrop-calendar-container {
519
+ --Brand-Primary: 270 76% 70%;
520
+ --Text-Primary: 0 0% 95%;
521
+ --BG-Primary: 0 0% 10%;
522
+ }
523
+ ```
524
+
525
+ ### Available CSS Variables
526
+
527
+ | Variable | Description | Default (Light) |
528
+ | ---------------------- | ----------------------- | -------------------- |
529
+ | `--Brand-Primary` | Primary brand color | `217 91% 60%` (Blue) |
530
+ | `--Text-Primary` | Main text color | `0 0% 0%` |
531
+ | `--Text-Secondary` | Secondary text | `0 0% 40%` |
532
+ | `--BG-Primary` | Main background | `0 0% 100%` |
533
+ | `--BG-Secondary` | Secondary background | `0 0% 98%` |
534
+ | `--Stroke-Primary` | Border colors | `0 0% 80%` |
535
+ | `--Status-OnSale` | "On Sale" badge | `217 91% 60%` |
536
+ | `--Status-SellingFast` | "Selling Fast" badge | `38 92% 50%` |
537
+ | `--Status-SoldOut` | "Sold Out" badge | `0 84% 60%` |
538
+ | `--Today-BG` | Today's date background | `217 91% 97%` |
539
+ | `--Focus-Ring` | Keyboard focus ring | `217 91% 60%` |
540
+
541
+ ### Using JavaScript Theme Utilities
542
+
543
+ ```javascript
544
+ import {
545
+ applyTheme,
546
+ themes,
547
+ generateThemeCSS,
548
+ } from '@getmicdrop/venue-calendar';
549
+
550
+ // Apply a preset theme
551
+ applyTheme(themes.dark);
552
+
553
+ // Apply to a specific container
554
+ const container = document.querySelector('.micdrop-calendar-container');
555
+ applyTheme(themes.dark, container);
556
+
557
+ // Create a custom theme
558
+ const myTheme = {
559
+ brandPrimary: '270 76% 60%', // Purple
560
+ textPrimary: '0 0% 10%',
561
+ bgPrimary: '0 0% 100%',
562
+ statusOnSale: '142 71% 45%', // Green for on sale
563
+ };
564
+ applyTheme(myTheme);
565
+
566
+ // Generate CSS string for embedding
567
+ const cssString = generateThemeCSS(myTheme);
568
+ console.log(cssString);
569
+ // Output: :root { --Brand-Primary: 270 76% 60%; ... }
570
+ ```
571
+
572
+ ### Preset Themes
573
+
574
+ Three themes are included out of the box:
575
+
576
+ ```javascript
577
+ import { themes } from '@getmicdrop/venue-calendar';
578
+
579
+ // Light theme (default)
580
+ applyTheme(themes.light);
581
+
582
+ // Dark theme
583
+ applyTheme(themes.dark);
584
+
585
+ // High contrast (accessibility)
586
+ applyTheme(themes.highContrast);
587
+ ```
588
+
589
+ ### Automatic Dark Mode
590
+
591
+ The calendar automatically respects the user's system preference:
592
+
593
+ ```css
594
+ /* Automatically applied when user prefers dark mode */
595
+ @media (prefers-color-scheme: dark) {
596
+ /* Dark theme variables are applied */
597
+ }
598
+ ```
599
+
600
+ You can also manually toggle dark mode:
601
+
602
+ ```html
603
+ <!-- Add 'dark' class to enable dark theme -->
604
+ <div class="dark">
605
+ <div class="micdrop-calendar-container" data-venue-id="123"></div>
606
+ </div>
607
+
608
+ <!-- Or use data-theme attribute -->
609
+ <div data-theme="dark">
610
+ <div class="micdrop-calendar-container" data-venue-id="123"></div>
611
+ </div>
612
+ ```
613
+
614
+ ## Localization and the CDN bundle
615
+
616
+ The calendar and the checkout speak thirteen languages. English is built into the
617
+ bundle; the other twelve are **fetched at runtime**, one file, only for the
618
+ locale actually in use — so a buyer never downloads twelve languages to read one.
619
+ This is what keeps the CDN bundle at ~594 KB gzipped instead of ~686 KB.
620
+
621
+ For the npm package nothing is required: your bundler code-splits the catalogues
622
+ and addresses them itself.
623
+
624
+ For the `<script>` embed, the bundle works out where to fetch from by looking at
625
+ the URL it was itself served from, and loads a **version-pinned sibling**:
626
+
627
+ ```
628
+ https://get-micdrop.com/embed/venue-calendar.iife.js
629
+ https://get-micdrop.com/embed/locales/<version>/main/es.js ← calendar copy
630
+ https://get-micdrop.com/embed/locales/<version>/flow/es.js ← checkout copy
631
+ ```
632
+
633
+ If you self-host the bundle, **copy the `dist/locales/` directory alongside it**.
634
+ Version-pinned paths mean new releases only ever add files; they never overwrite
635
+ the catalogue an older pinned bundle is using.
636
+
637
+ ### When the fetch cannot happen
638
+
639
+ If the locale file cannot be loaded — a Content-Security-Policy that allowlists
640
+ the exact script file rather than the origin, an offline CDN, or a
641
+ `<script type="module">` embed where the bundle cannot see its own URL — the
642
+ calendar simply stays in English. Nothing errors and nothing breaks; the widget
643
+ renders and sells tickets exactly as it does for an English buyer.
644
+
645
+ Two ways to point it at the right place when it cannot work it out:
646
+
647
+ ```html
648
+ <!-- Option A: tell it before the bundle loads -->
649
+ <script>
650
+ window.__MICDROP_CALENDAR_ASSET_BASE__ = 'https://get-micdrop.com/embed/';
651
+ </script>
652
+ <script
653
+ defer
654
+ src="https://get-micdrop.com/embed/venue-calendar.iife.js"
655
+ ></script>
656
+
657
+ <!-- Option B: put it on the tag -->
658
+ <script
659
+ defer
660
+ src="https://get-micdrop.com/embed/venue-calendar.iife.js"
661
+ data-vc-base="https://get-micdrop.com/embed/"
662
+ ></script>
663
+ ```
664
+
665
+ `defer` and `async` need no override — a classic script still knows its own URL.
666
+
667
+ ### First paint is always English
668
+
669
+ A non-English buyer sees English for a moment on a cold load and the page swaps
670
+ to their language when the catalogue arrives. This is deliberate: the calendar
671
+ paints immediately rather than waiting on a translation file.
672
+
673
+ ## Browser Support
674
+
675
+ - Chrome (latest)
676
+ - Firefox (latest)
677
+ - Safari (latest)
678
+ - Edge (latest)
679
+ - Mobile browsers (iOS Safari, Chrome Mobile)
680
+
681
+ ## Development
682
+
683
+ ### Building the Package
684
+
685
+ ```bash
686
+ # Install dependencies
687
+ npm install
688
+
689
+ # Build the library
690
+ npm run build:lib
691
+
692
+ # Development mode (SvelteKit app)
693
+ npm run dev
694
+
695
+ # Preview production build
696
+ npm run preview
697
+ ```
698
+
699
+ `build:lib` is three passes and the order matters: the IIFE pass runs first and
700
+ owns the `dist/` wipe, the ES/UMD pass appends to it, and
701
+ `vite.config.locales.js` appends the runtime locale catalogues
702
+ (`dist/locales/<version>/{main,flow}/<tag>.js`) last. `npm run size` fails if the
703
+ bundle and its catalogues do not both come out of that.
704
+
705
+ ### Project Structure
706
+
707
+ ```
708
+ venue-calendar/
709
+ ├── src/
710
+ │ ├── components/ # Svelte components
711
+ │ │ ├── Calendar/
712
+ │ │ ├── CalendarContainer/
713
+ │ │ └── Button/
714
+ │ ├── lib/ # Library entry points
715
+ │ │ ├── VenueCalendar.js
716
+ │ │ └── web-component.js
717
+ │ └── routes/ # SvelteKit routes (for dev)
718
+ ├── dist/ # Built package (generated)
719
+ │ └── locales/<version>/ # Runtime locale catalogues for the CDN bundle
720
+ ├── package.json
721
+ ├── vite.config.lib.js # Library build config (ES/UMD + IIFE passes)
722
+ ├── vite.config.locales.js # Emits the runtime locale catalogues
723
+ └── README.md
724
+ ```
725
+
726
+ ### Lockfile policy
727
+
728
+ `package-lock.json` is the single canonical lockfile — CI and the publish
729
+ workflow install with `npm ci`. Do not commit `yarn.lock` or
730
+ `pnpm-lock.yaml` (both gitignored). Local dev machines may use pnpm for the
731
+ svelte-components symlink workflow, but dependency changes must land in
732
+ `package-lock.json` via npm.
733
+
734
+ ## API Reference
735
+
736
+ ### `initVenueCalendar(options)`
737
+
738
+ Initialize a calendar instance.
739
+
740
+ **Parameters:**
741
+
742
+ - `options` (Object): Configuration options
743
+
744
+ **Returns:** Svelte component instance
745
+
746
+ **Example:**
747
+
748
+ ```javascript
749
+ const calendar = initVenueCalendar({
750
+ target: '#calendar',
751
+ venueId: 'venue-123',
752
+ view: 'calendar',
753
+ });
754
+ ```
755
+
756
+ ### `autoMount()`
757
+
758
+ Automatically mount calendars to all elements with class `micdrop-calendar-container`.
759
+
760
+ **Example:**
761
+
762
+ ```javascript
763
+ import { autoMount } from '@getmicdrop/venue-calendar';
764
+ autoMount();
765
+ ```
766
+
767
+ ### Component Events
768
+
769
+ The calendar component emits events that you can listen to:
770
+
771
+ ```javascript
772
+ const calendar = initVenueCalendar({
773
+ target: '#calendar',
774
+ // ... other options
775
+ });
776
+
777
+ // Listen to component events (if using Svelte component directly)
778
+ calendar.$on('eventClick', event => {
779
+ console.log('Event clicked:', event.detail);
780
+ });
781
+ ```
782
+
783
+ ## Troubleshooting
784
+
785
+ ### Calendar not appearing
786
+
787
+ 1. **Check the script is loaded**: Open browser console and verify no errors
788
+ 2. **Verify container exists**: Make sure the target element exists in the DOM
789
+ 3. **Check data attributes**: Ensure attributes are correctly formatted with `data-` prefix
790
+
791
+ ### Styles not applying
792
+
793
+ 1. **CSS not loaded**: The styles are bundled in the JS file and auto-injected — no separate stylesheet needed
794
+ 2. **CSS conflicts**: Check if other styles are overriding the calendar styles
795
+ 3. **Bundle didn't load**: Confirm the `<script src>` points at the Micdrop-hosted bundle and returns 200 (not 404)
796
+
797
+ ### Events not showing
798
+
799
+ 1. **Check event data format**: Ensure events match the expected structure
800
+ 2. **Date format**: Use ISO 8601 format for dates (`YYYY-MM-DDTHH:mm:ssZ`)
801
+ 3. **Venue ID**: Verify the venue ID is correct
802
+
803
+ ## Contributing
804
+
805
+ Contributions are welcome! Please feel free to submit a Pull Request.
806
+
807
+ ## License
808
+
809
+ MIT © MicDrop
810
+
811
+ ## Support
812
+
813
+ For issues, questions, or feature requests, please visit:
814
+ https://github.com/get-micdrop/venue-calendar/issues
815
+
816
+ ---
817
+
818
+ Made with ❤️ by the MicDrop team