@getmicdrop/venue-calendar 4.3.138 → 4.3.139

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 (211) hide show
  1. package/README.md +895 -840
  2. package/dist/AddOnCard-Dlt_4lut.js +475 -0
  3. package/dist/{Button-B_gbGd63.js → Button-Ch5dRGa2.js} +1 -1
  4. package/dist/{Button-B1cBhdtC.js → Button-DpeQSoQ7.js} +58 -45
  5. package/dist/Calendar-CNWS2xiR.js +396 -0
  6. package/dist/CalendarFoundryView-B5puOxH5.js +16431 -0
  7. package/dist/{ChartBlock-DcsCXfX8.js → ChartBlock-vihMKVdW.js} +2 -2
  8. package/dist/Checkout-B2FvX6Af.js +1070 -0
  9. package/dist/Checkout-y0x-v-XH.js +1995 -0
  10. package/dist/{ChevronDownOutline-kFgRY2cg.js → CloseOutline-Bh9UO94v.js} +2 -2
  11. package/dist/{CollectionView-C0vDg5oJ.js → CollectionView-CcLlV-1f.js} +22 -24
  12. package/dist/{CropImage-BWYeQXXA.js → CropImage-DqLAGy5s.js} +6 -6
  13. package/dist/{DatePicker-v-4ijT8N.js → DatePicker-D20Te0JY.js} +222 -215
  14. package/dist/{Event-vlu8IAxh.js → Event-DoIU0o6C.js} +799 -796
  15. package/dist/{EventCard-puoh3xag.js → EventCard-B8MYLyIW.js} +1 -1
  16. package/dist/EventPage-DM4tjCcL.js +1298 -0
  17. package/dist/{FilterChipGroup-DUGvOaRI.js → FilterChipGroup-BCjc7-pE.js} +1 -1
  18. package/dist/{ImageUploader-CKrf5amy.js → ImageUploader-BzxKIyLN.js} +324 -375
  19. package/dist/{Input-BQk4itex.js → Input-Bp5EgI1F.js} +5 -3
  20. package/dist/Modal-C6EXgkuF.js +159 -0
  21. package/dist/{ModalFooter-gFaDKsfj.js → ModalFooter-C4TD3-zP.js} +1 -1
  22. package/dist/{ModalTitle-CS5AGwrU.js → ModalTitle-BP4Bm3sA.js} +1 -1
  23. package/dist/{PasswordInput-C65GqxdF.js → PasswordInput-BlzSrkPs.js} +1 -1
  24. package/dist/{PhoneInput-Dj1I3bEn.js → PhoneInput-_ByjRGQ0.js} +2 -2
  25. package/dist/{RecommendationsRail-DC5qX1ei.js → RecommendationsRail-BDNnbL-p.js} +53 -50
  26. package/dist/{Search-BE64QcmK.js → Search-vhGEpog6.js} +1 -1
  27. package/dist/SeriesPage-YE8czX1M.js +32 -0
  28. package/dist/{SkeletonLoader-DfW0N9R3.js → SkeletonLoader-Beh2izdy.js} +1 -1
  29. package/dist/Spinner-DgubkWKo.js +88 -0
  30. package/dist/{Success-D3CrUeka.js → Success-CN5HB8gN.js} +164 -166
  31. package/dist/{TimePicker-HYzPI_Dc.js → TimePicker-BDuy9Ap8.js} +1 -1
  32. package/dist/{ViewTicketsEmbed-DzUCgtA3.js → ViewTicketsEmbed-DvoMIiKJ.js} +9 -10
  33. package/dist/{address-field-BwGHcMKY.js → address-field-DOalICpt.js} +2 -2
  34. package/dist/{allowlist-Bt1BHhNd.js → allowlist-DinkGF2L.js} +77 -68
  35. package/dist/api/api.cjs +1 -1
  36. package/dist/api/api.mjs +320 -297
  37. package/dist/api/client.d.ts +14 -53
  38. package/dist/api/events.d.ts +15 -1
  39. package/dist/api/result.d.ts +9 -0
  40. package/dist/{api-CRHFSJDI.js → api-CsAx0rmV.js} +150 -145
  41. package/dist/{avatar-picker-field-CSF5okeM.js → avatar-picker-field-CV4vRxTB.js} +14 -14
  42. package/dist/{checkoutState-BDNjJRMG.js → checkoutState-DSx7g3Vy.js} +12 -21
  43. package/dist/{client-BIN_q-of.js → client-SaDt6559.js} +18 -12
  44. package/dist/{constants-DrFV2oht.js → constants-sRRJh0u-.js} +1 -1
  45. package/dist/{date-field-3tgOYbzF.js → date-field-DylJx0y7.js} +9 -9
  46. package/dist/{date-range-field-D0Aq9me9.js → date-range-field-BOV1V2ab.js} +7 -7
  47. package/dist/{de-CUrE_4a4.js → de-CzkX3H3B.js} +1 -0
  48. package/dist/{defaultLabels-CybwJCNn.js → defaultLabels-DLK6rq4q.js} +1 -0
  49. package/dist/{defaults-B5wyre5O.js → defaults-Cbuin1WS.js} +2 -3
  50. package/dist/{es-B2-z2Kft.js → es-BOLzv8eD.js} +1 -0
  51. package/dist/{event-CEg17hts.js → event-ClsLWtR0.js} +1 -1
  52. package/dist/{event-transform-DqFigNQg.js → event-transform-Cs63X0hI.js} +42 -42
  53. package/dist/{eventStatusStream-DK5fWCTB.js → eventStatusStream-BAh5nJP2.js} +1 -1
  54. package/dist/{field-rules-CKFFB16Q.js → field-rules-Yb2JY1qT.js} +1 -1
  55. package/dist/{file-field-2mPz4Da-.js → file-field-Dwo80kDg.js} +4 -4
  56. package/dist/format-CehusdDg.js +1718 -0
  57. package/dist/{formatters-CzKNRUwz.js → formatters-DUWbI_Eg.js} +25 -35
  58. package/dist/{fr-CiePVIlj.js → fr-DYTIBBXo.js} +1 -0
  59. package/dist/{href-DpbLXQ4c.js → href-BXhNawil.js} +1 -1
  60. package/dist/i18n-BNF6a-QA.js +720 -0
  61. package/dist/i18n-F_QOaHVh.js +26 -0
  62. package/dist/{id-Ttw95_7t.js → id-CIyIkGhH.js} +1 -0
  63. package/dist/{image-field-B8x0WQ2j.js → image-field-DeLzsJlz.js} +2 -2
  64. package/dist/{it-CBbXcFY1.js → it-CF4Db_nK.js} +1 -0
  65. package/dist/{ja-B2Tag7J3.js → ja-5-7WNHD1.js} +1 -0
  66. package/dist/{ko-CcTn4lAi.js → ko-huIzHR3W.js} +1 -0
  67. package/dist/locales/4.3.139/main/de.js +1 -0
  68. package/dist/locales/4.3.139/main/es.js +1 -0
  69. package/dist/locales/4.3.139/main/fr.js +1 -0
  70. package/dist/locales/4.3.139/main/id.js +1 -0
  71. package/dist/locales/4.3.139/main/it.js +1 -0
  72. package/dist/locales/4.3.139/main/ja.js +1 -0
  73. package/dist/locales/4.3.139/main/ko.js +1 -0
  74. package/dist/locales/4.3.139/main/nl.js +1 -0
  75. package/dist/locales/4.3.139/main/pl.js +1 -0
  76. package/dist/locales/4.3.139/main/pt-br.js +1 -0
  77. package/dist/locales/4.3.139/main/tr.js +1 -0
  78. package/dist/locales/4.3.139/main/zh.js +1 -0
  79. package/dist/look-island.svelte-D87MvHNy.js +1296 -0
  80. package/dist/{look-names-JI6GE-Zj.js → look-names-B9MywEzX.js} +68 -58
  81. package/dist/looks/4.3.139/AddOnCard-CKdkGcHH.js +1 -0
  82. package/dist/looks/4.3.139/Checkout-D7CaFYTE.js +1 -0
  83. package/dist/looks/4.3.139/Checkout-DuP46PMi.js +3 -0
  84. package/dist/looks/4.3.139/CollectionView-CWm57JCx.js +1 -0
  85. package/dist/looks/4.3.139/Event-ePupb4qY.js +8 -0
  86. package/dist/looks/4.3.139/EventPage-CgexXhEU.js +1 -0
  87. package/dist/looks/4.3.139/RecommendationsRail-Bz-Mj_r4.js +2 -0
  88. package/dist/looks/4.3.139/SeriesPage-DI2uFF0Y.js +1 -0
  89. package/dist/looks/4.3.139/Success-BkMzmgmc.js +3 -0
  90. package/dist/looks/4.3.139/ViewTicketsEmbed-CbEJwRry.js +1 -0
  91. package/dist/looks/4.3.139/_vc-foundry-calendar-stub-BVbAFEK2.js +1 -0
  92. package/dist/looks/4.3.139/buyerEmail-CqCYa9UL.js +1 -0
  93. package/dist/looks/4.3.139/constants-CU_ZgyQu.js +3 -0
  94. package/dist/looks/4.3.139/defaults-B46SFFK4.js +2 -0
  95. package/dist/looks/4.3.139/dist-BWGSqtWN.js +1 -0
  96. package/dist/looks/4.3.139/eventStatusStream-CW5lJwsa.js +10 -0
  97. package/dist/looks/4.3.139/first-core-DrYq9wBy.js +151 -0
  98. package/dist/looks/4.3.139/first-mount.svelte-DFSLaJWm.js +1 -0
  99. package/dist/looks/4.3.139/first-seo-DvHuCXT6.js +1 -0
  100. package/dist/looks/4.3.139/first.js +1 -0
  101. package/dist/looks/4.3.139/full-widget-DDBk3i4U.js +2 -0
  102. package/dist/looks/4.3.139/gate-password-cache-D4Zls6QM.js +1 -0
  103. package/dist/looks/4.3.139/i18n-DRaFNYsQ.js +1 -0
  104. package/dist/looks/4.3.139/listing-BSaRTkxW.js +1 -0
  105. package/dist/looks/4.3.139/loaders.runtime-BNG8O6-A.js +1 -0
  106. package/dist/looks/4.3.139/looks.js +1 -0
  107. package/dist/looks/4.3.139/modalManager.svelte-CqQWYz9Y.js +1 -0
  108. package/dist/looks/4.3.139/rolldown-runtime-4tVxZq34.js +1 -0
  109. package/dist/looks/4.3.139/serverTotals-Bu1fAPLB.js +1 -0
  110. package/dist/looks/4.3.139/showcase-state-options-CpCMAKVD.js +1 -0
  111. package/dist/looks/4.3.139/utils-pGRhH4A-.js +1 -0
  112. package/dist/{media-field-HieD0ye0.js → media-field-PW8ye6EL.js} +2 -2
  113. package/dist/modalManager.svelte-BHE0t9q3.js +79 -0
  114. package/dist/{multiselect-field-CJ4lUJyS.js → multiselect-field-CmocFs5f.js} +2 -2
  115. package/dist/navigation-B8rkS2On.js +348 -0
  116. package/dist/{nl-DeIqgjIs.js → nl-B1dv1lqk.js} +1 -0
  117. package/dist/{number-field-D5GkkGLB.js → number-field-CUD936Ai.js} +3 -3
  118. package/dist/{numeric-filter-field-CRWZFu_P.js → numeric-filter-field-BmgSYhOt.js} +3 -3
  119. package/dist/{password-field-DdOHXEOo.js → password-field-BfvcZUi_.js} +1 -1
  120. package/dist/{phone-field-BaYmp56f.js → phone-field-DO0U_YZ6.js} +17 -18
  121. package/dist/{pixels-BXXzmoCb.js → pixels-ipedGW6i.js} +7 -7
  122. package/dist/{pl-Bm2FcD5y.js → pl-DLlM_IPo.js} +1 -0
  123. package/dist/{pt-br-XS1Lsa6z.js → pt-br-8V3mf7pu.js} +1 -0
  124. package/dist/repeating-field-BwtJ6VS1.js +488 -0
  125. package/dist/{richtext-field-KmUytgBW.js → richtext-field-DvLCxPvq.js} +2 -2
  126. package/dist/{datetime-BlEsN_-8.js → scarcity-DfQvyhJP.js} +74 -85
  127. package/dist/{searchable-select-field-DettZRav.js → searchable-select-field-zuIqfoQM.js} +17 -17
  128. package/dist/{select-field-Wc4gAtou.js → select-field-DNNN-a9q.js} +6 -6
  129. package/dist/seo/HostSeoController.d.ts +13 -0
  130. package/dist/seo/seo.cjs +1 -1
  131. package/dist/seo/seo.mjs +18 -11
  132. package/dist/{serverTotals-COsx7Mc5.js → serverTotals-BprgDwOw.js} +1 -1
  133. package/dist/{showcase-state-options-CHGUaZtl.js → showcase-state-options-bKRf48yz.js} +2 -2
  134. package/dist/{time-field-CnxZcziR.js → time-field-DmPCoppV.js} +2 -2
  135. package/dist/{to-event-card-props-C9v9dspT.js → to-event-card-props-KVcCJ2JF.js} +34 -34
  136. package/dist/{tr-zKAbMfgi.js → tr-DiUEV65j.js} +1 -0
  137. package/dist/{tree-picker-field-aqux58EQ.js → tree-picker-field-BJwSFAqm.js} +8 -8
  138. package/dist/types/index.d.ts +3 -0
  139. package/dist/{utils-AIszIHUb.js → utils-x8YXRKvr.js} +12 -12
  140. package/dist/venue-BjLCg-PW.js +38 -0
  141. package/dist/venue-calendar.css +1 -1
  142. package/dist/venue-calendar.es.js +1655 -1539
  143. package/dist/venue-calendar.iife.js +1 -54
  144. package/dist/venue-calendar.umd.js +69 -63
  145. package/dist/{widget-key-context-BrWW0fyM.js → widget-key-context-dHM9FiuJ.js} +8 -37
  146. package/dist/{zh-DHfgJplH.js → zh-Cej2EPQt.js} +1 -0
  147. package/package.json +2 -2
  148. package/src/lib/constants/scarcity.ts +1 -2
  149. package/dist/AddOnCard-DEAC26HA.js +0 -348
  150. package/dist/BlockPlaceholder-BKfoE795.js +0 -59
  151. package/dist/Calendar-9xskvKUf.js +0 -395
  152. package/dist/CalendarFoundryView-DbxZNmY7.js +0 -16356
  153. package/dist/CartView-DMINN6bY.js +0 -2200
  154. package/dist/Checkbox-sObkYRdj.js +0 -60
  155. package/dist/Checkout-H9RGBU9D.js +0 -1071
  156. package/dist/Checkout-VnochAZE.js +0 -1967
  157. package/dist/CheckoutTimer-mjcYR3XU.js +0 -42
  158. package/dist/EventPage-DycxjX5Z.js +0 -1301
  159. package/dist/Modal-DOpDiAwN.js +0 -102
  160. package/dist/ScarcityBadge-BAaV2CIY.js +0 -141
  161. package/dist/SeriesPage-Cx89s2B6.js +0 -34
  162. package/dist/Skeleton-B6qB_udJ.js +0 -31
  163. package/dist/Spinner-Bj4dr9kO.js +0 -19
  164. package/dist/Textarea-BDcuBI13.js +0 -72
  165. package/dist/__SKIP_NAVIGATION__-Znm_vYcG.js +0 -5
  166. package/dist/__SKIP_PATHS__-Bzzrca_a.js +0 -5
  167. package/dist/calendar-Cdu8fCIt.js +0 -111
  168. package/dist/cta-qbcIt5Gh.js +0 -116
  169. package/dist/format-CypC4vWs.js +0 -1587
  170. package/dist/heic-to-BoLfsr3l.js +0 -70
  171. package/dist/i18n-B_TWnExv.js +0 -495
  172. package/dist/i18n-CSaUFnlD.js +0 -26
  173. package/dist/instant-BkZCSGDm.js +0 -162
  174. package/dist/labels-D8LN8lY7.js +0 -227
  175. package/dist/locales/4.3.138/main/de.js +0 -1
  176. package/dist/locales/4.3.138/main/es.js +0 -1
  177. package/dist/locales/4.3.138/main/fr.js +0 -1
  178. package/dist/locales/4.3.138/main/id.js +0 -1
  179. package/dist/locales/4.3.138/main/it.js +0 -1
  180. package/dist/locales/4.3.138/main/ja.js +0 -1
  181. package/dist/locales/4.3.138/main/ko.js +0 -1
  182. package/dist/locales/4.3.138/main/nl.js +0 -1
  183. package/dist/locales/4.3.138/main/pl.js +0 -1
  184. package/dist/locales/4.3.138/main/pt-br.js +0 -1
  185. package/dist/locales/4.3.138/main/tr.js +0 -1
  186. package/dist/locales/4.3.138/main/zh.js +0 -1
  187. package/dist/look-island.svelte-B7UH_QVv.js +0 -1270
  188. package/dist/looks/4.3.138/looks.js +0 -248
  189. package/dist/modalManager.svelte-BvyrTI6p.js +0 -51
  190. package/dist/navigation-DpqP-ahJ.js +0 -241
  191. package/dist/repeating-field-B4OhsaGB.js +0 -488
  192. package/dist/venue-DLkL7xdj.js +0 -12
  193. /package/dist/checkout-libs/{4.3.138 → 4.3.139}/phone.js +0 -0
  194. /package/dist/checkout-libs/{4.3.138 → 4.3.139}/qrcode.js +0 -0
  195. /package/dist/locales/{4.3.138 → 4.3.139}/flow/de.js +0 -0
  196. /package/dist/locales/{4.3.138 → 4.3.139}/flow/es.js +0 -0
  197. /package/dist/locales/{4.3.138 → 4.3.139}/flow/fr.js +0 -0
  198. /package/dist/locales/{4.3.138 → 4.3.139}/flow/id.js +0 -0
  199. /package/dist/locales/{4.3.138 → 4.3.139}/flow/it.js +0 -0
  200. /package/dist/locales/{4.3.138 → 4.3.139}/flow/ja.js +0 -0
  201. /package/dist/locales/{4.3.138 → 4.3.139}/flow/ko.js +0 -0
  202. /package/dist/locales/{4.3.138 → 4.3.139}/flow/nl.js +0 -0
  203. /package/dist/locales/{4.3.138 → 4.3.139}/flow/pl.js +0 -0
  204. /package/dist/locales/{4.3.138 → 4.3.139}/flow/pt-br.js +0 -0
  205. /package/dist/locales/{4.3.138 → 4.3.139}/flow/tr.js +0 -0
  206. /package/dist/locales/{4.3.138 → 4.3.139}/flow/zh.js +0 -0
  207. /package/dist/looks/{4.3.138 → 4.3.139}/micdrop-wordmark-BYMG6vLR.svg +0 -0
  208. /package/dist/looks/{4.3.138 → 4.3.139}/microphone-Ccv4r-kd.webp +0 -0
  209. /package/dist/looks/{4.3.138 → 4.3.139}/superuser-CZF6a5gO.webp +0 -0
  210. /package/dist/looks/{4.3.138 → 4.3.139}/treasury-DUE7lZzT.webp +0 -0
  211. /package/dist/looks/{4.3.138 → 4.3.139}/user-image-yuS4errP.svg +0 -0
package/README.md CHANGED
@@ -1,840 +1,895 @@
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
- **Venue pixels and visitor consent:**
212
-
213
- If your organization saved a Meta Pixel or Google Analytics 4 ID, the calendar
214
- loads it only after the visitor agrees. With no consent signal on the page it
215
- sends no Meta or Google request at all. Tell it the visitor said yes in any one
216
- of these ways:
217
-
218
- ```html
219
- <!-- your consent tool already said yes before the calendar loads -->
220
- <div data-consent-granted="true" ...></div>
221
- ```
222
-
223
- ```js
224
- // from your cookie banner's "Accept" callback
225
- window.__MICDROP_CALENDAR__.setConsentGranted(true);
226
- ```
227
-
228
- A consent tool that speaks the IAB TCF API (`__tcfapi`) is picked up
229
- automatically. `data-consent-required="false"` tells the calendar your site does
230
- not need consent for these pixels; that is your own legal call, and a visitor
231
- whose browser sends Global Privacy Control still gets no pixels.
232
-
233
- **Usage in React App:**
234
-
235
- ```jsx
236
- import React, { useState } from 'react';
237
- import VenueCalendarComponent from './VenueCalendarComponent';
238
-
239
- function App() {
240
- const [venueId, setVenueId] = useState('comedy-club-123');
241
- const [view, setView] = useState('calendar');
242
-
243
- return (
244
- <div style={{ padding: '20px' }}>
245
- <h1>Event Viewer</h1>
246
-
247
- <div style={{ marginBottom: '20px' }}>
248
- <label>Venue ID:</label>
249
- <input
250
- type="text"
251
- value={venueId}
252
- onChange={e => setVenueId(e.target.value)}
253
- />
254
- </div>
255
-
256
- <div style={{ marginBottom: '20px' }}>
257
- <label>Select View:</label>
258
- <label>
259
- <input
260
- type="radio"
261
- value="list"
262
- checked={view === 'list'}
263
- onChange={e => setView(e.target.value)}
264
- />
265
- List
266
- </label>
267
- <label>
268
- <input
269
- type="radio"
270
- value="gallery"
271
- checked={view === 'gallery'}
272
- onChange={e => setView(e.target.value)}
273
- />
274
- Gallery
275
- </label>
276
- <label>
277
- <input
278
- type="radio"
279
- value="calendar"
280
- checked={view === 'calendar'}
281
- onChange={e => setView(e.target.value)}
282
- />
283
- Calendar
284
- </label>
285
- </div>
286
-
287
- <VenueCalendarComponent venueId={venueId} view={view} />
288
- </div>
289
- );
290
- }
291
-
292
- export default App;
293
- ```
294
-
295
- ### Vue 3
296
-
297
- ```vue
298
- <template>
299
- <div ref="calendarContainer"></div>
300
- </template>
301
-
302
- <script setup>
303
- import { ref, onMounted, onUnmounted, watch } from 'vue';
304
- import { initVenueCalendar } from '@getmicdrop/venue-calendar';
305
-
306
- const props = defineProps({
307
- venueId: String,
308
- view: {
309
- type: String,
310
- default: 'calendar',
311
- },
312
- });
313
-
314
- const calendarContainer = ref(null);
315
- let calendarInstance = null;
316
-
317
- onMounted(() => {
318
- calendarInstance = initVenueCalendar({
319
- target: calendarContainer.value,
320
- venueId: props.venueId,
321
- view: props.view,
322
- events: [],
323
- showViewOptions: true,
324
- showMonthSwitcher: true,
325
- });
326
- });
327
-
328
- onUnmounted(() => {
329
- if (calendarInstance && calendarInstance.$destroy) {
330
- calendarInstance.$destroy();
331
- }
332
- });
333
-
334
- watch(
335
- () => props.venueId,
336
- newId => {
337
- if (calendarInstance) {
338
- calendarInstance.$destroy();
339
- calendarInstance = initVenueCalendar({
340
- target: calendarContainer.value,
341
- venueId: newId,
342
- view: props.view,
343
- events: [],
344
- showViewOptions: true,
345
- showMonthSwitcher: true,
346
- });
347
- }
348
- }
349
- );
350
- </script>
351
- ```
352
-
353
- ### Svelte
354
-
355
- ```svelte
356
- <script>
357
- import { VenueCalendar } from '@getmicdrop/venue-calendar';
358
- import { Calendar, Grid, List } from 'carbon-icons-svelte';
359
- import { writable } from 'svelte/store';
360
-
361
- let venueId = 'your-venue-id';
362
- let currentMonth = writable(new Date().getUTCMonth());
363
- let currentYear = writable(new Date().getUTCFullYear());
364
-
365
- function handleNext() {
366
- currentMonth.update(m => m + 1);
367
- }
368
-
369
- function handlePrev() {
370
- currentMonth.update(m => m - 1);
371
- }
372
- </script>
373
-
374
- <VenueCalendar
375
- showViewOptions={[
376
- { id: 0, text: 'List view', icon: List },
377
- { id: 1, text: 'Gallery view', icon: Grid },
378
- { id: 2, text: 'Calendar view', icon: Calendar },
379
- ]}
380
- showMonthSwitcher={true}
381
- events={[]}
382
- {currentMonth}
383
- {currentYear}
384
- {handleNext}
385
- {handlePrev}
386
- on:eventClick={e => console.log('Event clicked:', e.detail)}
387
- />
388
- ```
389
-
390
- Host-page props — for a page that already owns part of what the widget draws.
391
- Every one defaults to on, so embeds are unchanged:
392
-
393
- | Prop | Type | Default | Description |
394
- | ------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
395
- | `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. |
396
- | `showGiftCardsLink` | boolean | `true` | The footer's "Gift cards" link. `false` for a host page whose own footer already carries it. |
397
- | `showPoweredBy` | boolean | `true` | The footer's "Powered by Micdrop" mark. `false` for a host page whose own footer already carries it. |
398
-
399
- ### Angular
400
-
401
- ```typescript
402
- import {
403
- Component,
404
- OnInit,
405
- OnDestroy,
406
- ElementRef,
407
- ViewChild,
408
- } from '@angular/core';
409
- import { initVenueCalendar } from '@getmicdrop/venue-calendar';
410
-
411
- @Component({
412
- selector: 'app-venue-calendar',
413
- template: '<div #calendarContainer></div>',
414
- })
415
- export class VenueCalendarComponent implements OnInit, OnDestroy {
416
- @ViewChild('calendarContainer', { static: true })
417
- calendarContainer!: ElementRef;
418
- private calendarInstance: any;
419
-
420
- ngOnInit() {
421
- this.calendarInstance = initVenueCalendar({
422
- target: this.calendarContainer.nativeElement,
423
- venueId: 'your-venue-id',
424
- view: 'calendar',
425
- events: [],
426
- showViewOptions: true,
427
- showMonthSwitcher: true,
428
- });
429
- }
430
-
431
- ngOnDestroy() {
432
- if (this.calendarInstance && this.calendarInstance.$destroy) {
433
- this.calendarInstance.$destroy();
434
- }
435
- }
436
- }
437
- ```
438
-
439
- ## Configuration Options
440
-
441
- ### Data Attributes (for auto-mount)
442
-
443
- | Attribute | Type | Default | Description |
444
- | -------------------------- | ------- | ------------ | ---------------------------------------------------- |
445
- | `data-venue-id` | string | `''` | The venue ID to fetch events for |
446
- | `data-view` | string | `'calendar'` | Initial view: `'list'`, `'gallery'`, or `'calendar'` |
447
- | `data-show-view-options` | boolean | `true` | Show view switcher buttons |
448
- | `data-show-month-switcher` | boolean | `true` | Show month navigation controls |
449
-
450
- ### JavaScript API Options
451
-
452
- ```javascript
453
- initVenueCalendar({
454
- target: '.my-calendar', // CSS selector or HTMLElement (required)
455
- venueId: 'venue-123', // Venue ID (optional)
456
- view: 'calendar', // 'list', 'gallery', or 'calendar' (default: 'calendar')
457
- events: [], // Array of event objects (default: [])
458
- showViewOptions: true, // Show view switcher (default: true)
459
- showMonthSwitcher: true, // Show month navigation (default: true)
460
- });
461
- ```
462
-
463
- ### Event Object Structure
464
-
465
- ```javascript
466
- {
467
- id: 'event-123',
468
- name: 'Comedy Night',
469
- date: '2024-10-25T20:00:00Z',
470
- image: 'https://example.com/image.jpg',
471
- status: 'On Sale',
472
- timeline: '8:00 PM - 10:00 PM',
473
- // ... other fields
474
- }
475
- ```
476
-
477
- ## Views
478
-
479
- ### Calendar View
480
-
481
- The default view showing events in a monthly calendar grid. Perfect for venues with regular shows.
482
-
483
- ### List View
484
-
485
- A vertical list layout showing all upcoming events with details. Great for mobile experiences.
486
-
487
- ### Gallery View
488
-
489
- A grid layout displaying event posters in a gallery format. Ideal for showcasing event imagery.
490
-
491
- ## WordPress Integration
492
-
493
- For WordPress sites, you can add this to your page/post HTML:
494
-
495
- ```html
496
- <div
497
- class="micdrop-calendar-container"
498
- data-venue-id="your-venue-id"
499
- data-view="calendar"
500
- ></div>
501
-
502
- <script src="https://get-micdrop.com/embed/venue-calendar.iife.js"></script>
503
- ```
504
-
505
- Or add the script to your theme's footer and use the div anywhere in your content.
506
-
507
- ## Styling
508
-
509
- 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.
510
-
511
- ```css
512
- /* Example: Custom styling */
513
- .micdrop-calendar-container {
514
- max-width: 1200px;
515
- margin: 0 auto;
516
- padding: 20px;
517
- }
518
- ```
519
-
520
- **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.
521
-
522
- ## Theming
523
-
524
- The calendar supports comprehensive theming via CSS custom properties and JavaScript utilities.
525
-
526
- ### Using CSS Custom Properties
527
-
528
- Override the default theme by setting CSS custom properties:
529
-
530
- ```css
531
- /* Custom brand colors */
532
- .micdrop-calendar-container {
533
- --Brand-Primary: 270 76% 60%; /* Purple */
534
- --Text-Primary: 0 0% 10%;
535
- --BG-Primary: 0 0% 100%;
536
- }
537
-
538
- /* Dark mode */
539
- .dark .micdrop-calendar-container,
540
- [data-theme='dark'] .micdrop-calendar-container {
541
- --Brand-Primary: 270 76% 70%;
542
- --Text-Primary: 0 0% 95%;
543
- --BG-Primary: 0 0% 10%;
544
- }
545
- ```
546
-
547
- ### Available CSS Variables
548
-
549
- | Variable | Description | Default (Light) |
550
- | ---------------------- | ----------------------- | -------------------- |
551
- | `--Brand-Primary` | Primary brand color | `217 91% 60%` (Blue) |
552
- | `--Text-Primary` | Main text color | `0 0% 0%` |
553
- | `--Text-Secondary` | Secondary text | `0 0% 40%` |
554
- | `--BG-Primary` | Main background | `0 0% 100%` |
555
- | `--BG-Secondary` | Secondary background | `0 0% 98%` |
556
- | `--Stroke-Primary` | Border colors | `0 0% 80%` |
557
- | `--Status-OnSale` | "On Sale" badge | `217 91% 60%` |
558
- | `--Status-SellingFast` | "Selling Fast" badge | `38 92% 50%` |
559
- | `--Status-SoldOut` | "Sold Out" badge | `0 84% 60%` |
560
- | `--Today-BG` | Today's date background | `217 91% 97%` |
561
- | `--Focus-Ring` | Keyboard focus ring | `217 91% 60%` |
562
-
563
- ### Using JavaScript Theme Utilities
564
-
565
- ```javascript
566
- import {
567
- applyTheme,
568
- themes,
569
- generateThemeCSS,
570
- } from '@getmicdrop/venue-calendar';
571
-
572
- // Apply a preset theme
573
- applyTheme(themes.dark);
574
-
575
- // Apply to a specific container
576
- const container = document.querySelector('.micdrop-calendar-container');
577
- applyTheme(themes.dark, container);
578
-
579
- // Create a custom theme
580
- const myTheme = {
581
- brandPrimary: '270 76% 60%', // Purple
582
- textPrimary: '0 0% 10%',
583
- bgPrimary: '0 0% 100%',
584
- statusOnSale: '142 71% 45%', // Green for on sale
585
- };
586
- applyTheme(myTheme);
587
-
588
- // Generate CSS string for embedding
589
- const cssString = generateThemeCSS(myTheme);
590
- console.log(cssString);
591
- // Output: :root { --Brand-Primary: 270 76% 60%; ... }
592
- ```
593
-
594
- ### Preset Themes
595
-
596
- Three themes are included out of the box:
597
-
598
- ```javascript
599
- import { themes } from '@getmicdrop/venue-calendar';
600
-
601
- // Light theme (default)
602
- applyTheme(themes.light);
603
-
604
- // Dark theme
605
- applyTheme(themes.dark);
606
-
607
- // High contrast (accessibility)
608
- applyTheme(themes.highContrast);
609
- ```
610
-
611
- ### Automatic Dark Mode
612
-
613
- The calendar automatically respects the user's system preference:
614
-
615
- ```css
616
- /* Automatically applied when user prefers dark mode */
617
- @media (prefers-color-scheme: dark) {
618
- /* Dark theme variables are applied */
619
- }
620
- ```
621
-
622
- You can also manually toggle dark mode:
623
-
624
- ```html
625
- <!-- Add 'dark' class to enable dark theme -->
626
- <div class="dark">
627
- <div class="micdrop-calendar-container" data-venue-id="123"></div>
628
- </div>
629
-
630
- <!-- Or use data-theme attribute -->
631
- <div data-theme="dark">
632
- <div class="micdrop-calendar-container" data-venue-id="123"></div>
633
- </div>
634
- ```
635
-
636
- ## Localization and the CDN bundle
637
-
638
- The calendar and the checkout speak thirteen languages. English is built into the
639
- bundle; the other twelve are **fetched at runtime**, one file, only for the
640
- locale actually in use — so a buyer never downloads twelve languages to read one.
641
- This is what keeps the CDN bundle at ~594 KB gzipped instead of ~686 KB.
642
-
643
- For the npm package nothing is required: your bundler code-splits the catalogues
644
- and addresses them itself.
645
-
646
- For the `<script>` embed, the bundle works out where to fetch from by looking at
647
- the URL it was itself served from, and loads a **version-pinned sibling**:
648
-
649
- ```
650
- https://get-micdrop.com/embed/venue-calendar.iife.js
651
- https://get-micdrop.com/embed/locales/<version>/main/es.js ← calendar copy
652
- https://get-micdrop.com/embed/locales/<version>/flow/es.js ← checkout copy
653
- ```
654
-
655
- If you self-host the bundle, **copy the `dist/locales/` directory alongside it**.
656
- Version-pinned paths mean new releases only ever add files; they never overwrite
657
- the catalogue an older pinned bundle is using.
658
-
659
- ### When the fetch cannot happen
660
-
661
- If the locale file cannot be loaded — a Content-Security-Policy that allowlists
662
- the exact script file rather than the origin, an offline CDN, or a
663
- `<script type="module">` embed where the bundle cannot see its own URL — the
664
- calendar simply stays in English. Nothing errors and nothing breaks; the widget
665
- renders and sells tickets exactly as it does for an English buyer.
666
-
667
- Two ways to point it at the right place when it cannot work it out:
668
-
669
- ```html
670
- <!-- Option A: tell it before the bundle loads -->
671
- <script>
672
- window.__MICDROP_CALENDAR_ASSET_BASE__ = 'https://get-micdrop.com/embed/';
673
- </script>
674
- <script
675
- defer
676
- src="https://get-micdrop.com/embed/venue-calendar.iife.js"
677
- ></script>
678
-
679
- <!-- Option B: put it on the tag -->
680
- <script
681
- defer
682
- src="https://get-micdrop.com/embed/venue-calendar.iife.js"
683
- data-vc-base="https://get-micdrop.com/embed/"
684
- ></script>
685
- ```
686
-
687
- `defer` and `async` need no override — a classic script still knows its own URL.
688
-
689
- ### First paint is always English
690
-
691
- A non-English buyer sees English for a moment on a cold load and the page swaps
692
- to their language when the catalogue arrives. This is deliberate: the calendar
693
- paints immediately rather than waiting on a translation file.
694
-
695
- ## Browser Support
696
-
697
- - Chrome (latest)
698
- - Firefox (latest)
699
- - Safari (latest)
700
- - Edge (latest)
701
- - Mobile browsers (iOS Safari, Chrome Mobile)
702
-
703
- ## Development
704
-
705
- ### Building the Package
706
-
707
- ```bash
708
- # Install dependencies
709
- npm install
710
-
711
- # Build the library
712
- npm run build:lib
713
-
714
- # Development mode (SvelteKit app)
715
- npm run dev
716
-
717
- # Preview production build
718
- npm run preview
719
- ```
720
-
721
- `build:lib` is three passes and the order matters: the IIFE pass runs first and
722
- owns the `dist/` wipe, the ES/UMD pass appends to it, and
723
- `vite.config.locales.js` appends the runtime locale catalogues
724
- (`dist/locales/<version>/{main,flow}/<tag>.js`) last. `npm run size` fails if the
725
- bundle and its catalogues do not both come out of that.
726
-
727
- ### Project Structure
728
-
729
- ```
730
- venue-calendar/
731
- ├── src/
732
- │ ├── components/ # Svelte components
733
- │ │ ├── Calendar/
734
- │ │ ├── CalendarContainer/
735
- │ │ └── Button/
736
- │ ├── lib/ # Library entry points
737
- │ │ ├── VenueCalendar.js
738
- │ │ └── web-component.js
739
- │ └── routes/ # SvelteKit routes (for dev)
740
- ├── dist/ # Built package (generated)
741
- │ └── locales/<version>/ # Runtime locale catalogues for the CDN bundle
742
- ├── package.json
743
- ├── vite.config.lib.js # Library build config (ES/UMD + IIFE passes)
744
- ├── vite.config.locales.js # Emits the runtime locale catalogues
745
- └── README.md
746
- ```
747
-
748
- ### Lockfile policy
749
-
750
- `package-lock.json` is the single canonical lockfile — CI and the publish
751
- workflow install with `npm ci`. Do not commit `yarn.lock` or
752
- `pnpm-lock.yaml` (both gitignored). Local dev machines may use pnpm for the
753
- svelte-components symlink workflow, but dependency changes must land in
754
- `package-lock.json` via npm.
755
-
756
- ## API Reference
757
-
758
- ### `initVenueCalendar(options)`
759
-
760
- Initialize a calendar instance.
761
-
762
- **Parameters:**
763
-
764
- - `options` (Object): Configuration options
765
-
766
- **Returns:** Svelte component instance
767
-
768
- **Example:**
769
-
770
- ```javascript
771
- const calendar = initVenueCalendar({
772
- target: '#calendar',
773
- venueId: 'venue-123',
774
- view: 'calendar',
775
- });
776
- ```
777
-
778
- ### `autoMount()`
779
-
780
- Automatically mount calendars to all elements with class `micdrop-calendar-container`.
781
-
782
- **Example:**
783
-
784
- ```javascript
785
- import { autoMount } from '@getmicdrop/venue-calendar';
786
- autoMount();
787
- ```
788
-
789
- ### Component Events
790
-
791
- The calendar component emits events that you can listen to:
792
-
793
- ```javascript
794
- const calendar = initVenueCalendar({
795
- target: '#calendar',
796
- // ... other options
797
- });
798
-
799
- // Listen to component events (if using Svelte component directly)
800
- calendar.$on('eventClick', event => {
801
- console.log('Event clicked:', event.detail);
802
- });
803
- ```
804
-
805
- ## Troubleshooting
806
-
807
- ### Calendar not appearing
808
-
809
- 1. **Check the script is loaded**: Open browser console and verify no errors
810
- 2. **Verify container exists**: Make sure the target element exists in the DOM
811
- 3. **Check data attributes**: Ensure attributes are correctly formatted with `data-` prefix
812
-
813
- ### Styles not applying
814
-
815
- 1. **CSS not loaded**: The styles are bundled in the JS file and auto-injected — no separate stylesheet needed
816
- 2. **CSS conflicts**: Check if other styles are overriding the calendar styles
817
- 3. **Bundle didn't load**: Confirm the `<script src>` points at the Micdrop-hosted bundle and returns 200 (not 404)
818
-
819
- ### Events not showing
820
-
821
- 1. **Check event data format**: Ensure events match the expected structure
822
- 2. **Date format**: Use ISO 8601 format for dates (`YYYY-MM-DDTHH:mm:ssZ`)
823
- 3. **Venue ID**: Verify the venue ID is correct
824
-
825
- ## Contributing
826
-
827
- Contributions are welcome! Please feel free to submit a Pull Request.
828
-
829
- ## License
830
-
831
- MIT © MicDrop
832
-
833
- ## Support
834
-
835
- For issues, questions, or feature requests, please visit:
836
- https://github.com/get-micdrop/venue-calendar/issues
837
-
838
- ---
839
-
840
- 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
+ ✨ **Six Views**: Calendar, List, Gallery, Carousel, Featured and Tonight, plus eight looks
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
+ **Venue pixels and visitor consent:**
212
+
213
+ If your organization saved a Meta Pixel or Google Analytics 4 ID, the calendar
214
+ loads it only after the visitor agrees. With no consent signal on the page it
215
+ sends no Meta or Google request at all. Tell it the visitor said yes in any one
216
+ of these ways:
217
+
218
+ ```html
219
+ <!-- your consent tool already said yes before the calendar loads -->
220
+ <div data-consent-granted="true" ...></div>
221
+ ```
222
+
223
+ ```js
224
+ // from your cookie banner's "Accept" callback
225
+ window.__MICDROP_CALENDAR__.setConsentGranted(true);
226
+ ```
227
+
228
+ A consent tool that speaks the IAB TCF API (`__tcfapi`) is picked up
229
+ automatically. `data-consent-required="false"` tells the calendar your site does
230
+ not need consent for these pixels; that is your own legal call, and a visitor
231
+ whose browser sends Global Privacy Control still gets no pixels.
232
+
233
+ **Usage in React App:**
234
+
235
+ ```jsx
236
+ import React, { useState } from 'react';
237
+ import VenueCalendarComponent from './VenueCalendarComponent';
238
+
239
+ function App() {
240
+ const [venueId, setVenueId] = useState('comedy-club-123');
241
+ const [view, setView] = useState('calendar');
242
+
243
+ return (
244
+ <div style={{ padding: '20px' }}>
245
+ <h1>Event Viewer</h1>
246
+
247
+ <div style={{ marginBottom: '20px' }}>
248
+ <label>Venue ID:</label>
249
+ <input
250
+ type="text"
251
+ value={venueId}
252
+ onChange={e => setVenueId(e.target.value)}
253
+ />
254
+ </div>
255
+
256
+ <div style={{ marginBottom: '20px' }}>
257
+ <label>Select View:</label>
258
+ <label>
259
+ <input
260
+ type="radio"
261
+ value="list"
262
+ checked={view === 'list'}
263
+ onChange={e => setView(e.target.value)}
264
+ />
265
+ List
266
+ </label>
267
+ <label>
268
+ <input
269
+ type="radio"
270
+ value="gallery"
271
+ checked={view === 'gallery'}
272
+ onChange={e => setView(e.target.value)}
273
+ />
274
+ Gallery
275
+ </label>
276
+ <label>
277
+ <input
278
+ type="radio"
279
+ value="calendar"
280
+ checked={view === 'calendar'}
281
+ onChange={e => setView(e.target.value)}
282
+ />
283
+ Calendar
284
+ </label>
285
+ </div>
286
+
287
+ <VenueCalendarComponent venueId={venueId} view={view} />
288
+ </div>
289
+ );
290
+ }
291
+
292
+ export default App;
293
+ ```
294
+
295
+ ### Vue 3
296
+
297
+ ```vue
298
+ <template>
299
+ <div ref="calendarContainer"></div>
300
+ </template>
301
+
302
+ <script setup>
303
+ import { ref, onMounted, onUnmounted, watch } from 'vue';
304
+ import { initVenueCalendar } from '@getmicdrop/venue-calendar';
305
+
306
+ const props = defineProps({
307
+ venueId: String,
308
+ view: {
309
+ type: String,
310
+ default: 'calendar',
311
+ },
312
+ });
313
+
314
+ const calendarContainer = ref(null);
315
+ let calendarInstance = null;
316
+
317
+ onMounted(() => {
318
+ calendarInstance = initVenueCalendar({
319
+ target: calendarContainer.value,
320
+ venueId: props.venueId,
321
+ view: props.view,
322
+ events: [],
323
+ showViewOptions: true,
324
+ showMonthSwitcher: true,
325
+ });
326
+ });
327
+
328
+ onUnmounted(() => {
329
+ if (calendarInstance && calendarInstance.$destroy) {
330
+ calendarInstance.$destroy();
331
+ }
332
+ });
333
+
334
+ watch(
335
+ () => props.venueId,
336
+ newId => {
337
+ if (calendarInstance) {
338
+ calendarInstance.$destroy();
339
+ calendarInstance = initVenueCalendar({
340
+ target: calendarContainer.value,
341
+ venueId: newId,
342
+ view: props.view,
343
+ events: [],
344
+ showViewOptions: true,
345
+ showMonthSwitcher: true,
346
+ });
347
+ }
348
+ }
349
+ );
350
+ </script>
351
+ ```
352
+
353
+ ### Svelte
354
+
355
+ ```svelte
356
+ <script>
357
+ import { VenueCalendar } from '@getmicdrop/venue-calendar';
358
+ import { Calendar, Grid, List } from 'carbon-icons-svelte';
359
+ import { writable } from 'svelte/store';
360
+
361
+ let venueId = 'your-venue-id';
362
+ let currentMonth = writable(new Date().getUTCMonth());
363
+ let currentYear = writable(new Date().getUTCFullYear());
364
+
365
+ function handleNext() {
366
+ currentMonth.update(m => m + 1);
367
+ }
368
+
369
+ function handlePrev() {
370
+ currentMonth.update(m => m - 1);
371
+ }
372
+ </script>
373
+
374
+ <VenueCalendar
375
+ showViewOptions={[
376
+ { id: 0, text: 'List view', icon: List },
377
+ { id: 1, text: 'Gallery view', icon: Grid },
378
+ { id: 2, text: 'Calendar view', icon: Calendar },
379
+ ]}
380
+ showMonthSwitcher={true}
381
+ events={[]}
382
+ {currentMonth}
383
+ {currentYear}
384
+ {handleNext}
385
+ {handlePrev}
386
+ on:eventClick={e => console.log('Event clicked:', e.detail)}
387
+ />
388
+ ```
389
+
390
+ Host-page props — for a page that already owns part of what the widget draws.
391
+ Every one defaults to on, so embeds are unchanged:
392
+
393
+ | Prop | Type | Default | Description |
394
+ | ------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
395
+ | `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. |
396
+ | `showGiftCardsLink` | boolean | `true` | The footer's "Gift cards" link. `false` for a host page whose own footer already carries it. |
397
+ | `showPoweredBy` | boolean | `true` | The footer's "Powered by Micdrop" mark. `false` for a host page whose own footer already carries it. |
398
+
399
+ ### Angular
400
+
401
+ ```typescript
402
+ import {
403
+ Component,
404
+ OnInit,
405
+ OnDestroy,
406
+ ElementRef,
407
+ ViewChild,
408
+ } from '@angular/core';
409
+ import { initVenueCalendar } from '@getmicdrop/venue-calendar';
410
+
411
+ @Component({
412
+ selector: 'app-venue-calendar',
413
+ template: '<div #calendarContainer></div>',
414
+ })
415
+ export class VenueCalendarComponent implements OnInit, OnDestroy {
416
+ @ViewChild('calendarContainer', { static: true })
417
+ calendarContainer!: ElementRef;
418
+ private calendarInstance: any;
419
+
420
+ ngOnInit() {
421
+ this.calendarInstance = initVenueCalendar({
422
+ target: this.calendarContainer.nativeElement,
423
+ venueId: 'your-venue-id',
424
+ view: 'calendar',
425
+ events: [],
426
+ showViewOptions: true,
427
+ showMonthSwitcher: true,
428
+ });
429
+ }
430
+
431
+ ngOnDestroy() {
432
+ if (this.calendarInstance && this.calendarInstance.$destroy) {
433
+ this.calendarInstance.$destroy();
434
+ }
435
+ }
436
+ }
437
+ ```
438
+
439
+ ## Configuration Options
440
+
441
+ ### Tag attributes
442
+
443
+ Every attribute below works on `<micdrop-calendar>` as written, and as its `data-` twin
444
+ (`data-view`, `data-venue-id`, …) on `<micdrop-calendar>` or the auto-mount
445
+ `<div class="micdrop-calendar-container">`. The auto-mount `<div>` reads the ones marked
446
+ "div" in the last column; for the rest, use `<micdrop-calendar>` or a saved widget.
447
+
448
+ | Attribute | Values | Default | What it does | div |
449
+ | --------------------- | ----------------------------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------- | --- |
450
+ | `venue-id` | venue ID | none | The venue whose shows draw | ✓ |
451
+ | `organization-id` | organization ID | none | The organization whose shows draw, for an embed across venues | ✓ |
452
+ | `widget-id` | saved widget ID | none | Loads a widget saved in Micdrop: its view, filters, colors and display options; a div takes only its display options | ✓ |
453
+ | `widget-key` | widget key | none | Lets the widget read its shows from a site that is not Micdrop's own | ✓ |
454
+ | `view` | see [Views](#views) | `calendar` | The view the widget opens on | ✓ |
455
+ | `show-view-options` | `true` / `false` | `true` | Draws the view switcher | ✓ |
456
+ | `show-month-switcher` | `true` / `false` | `true` | Draws the month arrows | ✓ |
457
+ | `show-search` | `true` / `false` | with the switcher | Draws the search box; `true` draws it without the view switcher, `false` hides it | |
458
+ | `show-powered-by` | `true` / `false` | `true` | Draws the "Powered by Micdrop" line under the widget | ✓ |
459
+ | `show-background` | `true` / `false` | `true` | Paints the widget's own canvas and outer insets; `false` sits it straight on the page | |
460
+ | `max-items` | a positive whole number | all | How many shows Gallery, List and Carousel draw | |
461
+ | `skip-items` | a whole number | `0` | How many of the first upcoming shows the List looks (`look-list`, `look-board`) leave out | |
462
+ | `event-details` | JSON, e.g. `{"date":false}` | all on | Which card parts draw (`poster`, `date`, `description`); only `false` hides one | |
463
+ | `size` | `S` / `M` / `L` | `M` | How big each view draws its shows | |
464
+ | `heading-level` | `1` / `2` | `1` | Level of the screen-reader heading; `2` when the page already has its own `h1` | ✓ |
465
+ | `poster-fit` | `fit` / `fill` | `fit` | The whole poster, or cropped to cover its box | ✓ |
466
+ | `poster-backdrop` | `blur` / `color` | `blur` | What fills around a `fit` poster | ✓ |
467
+ | `inject-seo` | `true` / `false` | `true` | Opening a show sets the page's title and share preview to the show's, restored when it closes | ✓ |
468
+ | `url-mode` | `pushState` / `none` | `pushState` | `none` keeps the widget from changing the page address (for iframe and Wix embeds) | ✓ |
469
+ | `locale` | a language tag, e.g. `en-US` | browser setting | The language and date format the widget draws in | ✓ |
470
+ | `api-base` | an origin, e.g. `https://get-micdrop.com` | Micdrop's API | Where the widget reads its shows from | ✓ |
471
+ | `public-site-origin` | an origin | `api-base` | Where share links, event pages and gift-card links point | ✓ |
472
+ | `consent-required` | `true` / `false` | auto-detected | For a non-TCF cookie banner: hold tracking until consent is granted | ✓ |
473
+ | `consent-granted` | `true` / `false` | auto-detected | For a non-TCF cookie banner: the visitor has granted consent | ✓ |
474
+
475
+ A `true`/`false` attribute is on when present (`show-search` or `show-search=""`) and off only
476
+ as `="false"`. A value that does not parse is ignored, and the widget keeps its default.
477
+
478
+ ### JavaScript API Options
479
+
480
+ ```javascript
481
+ initVenueCalendar({
482
+ target: '.my-calendar', // CSS selector or HTMLElement (required)
483
+ venueId: 'venue-123', // Venue ID (optional)
484
+ view: 'calendar', // any view name under Views (default: 'calendar')
485
+ events: [], // Array of event objects (default: [])
486
+ showViewOptions: true, // Show view switcher (default: true)
487
+ showMonthSwitcher: true, // Show month navigation (default: true)
488
+ });
489
+ ```
490
+
491
+ ### Event Object Structure
492
+
493
+ ```javascript
494
+ {
495
+ id: 'event-123',
496
+ name: 'Comedy Night',
497
+ date: '2024-10-25T20:00:00Z',
498
+ image: 'https://example.com/image.jpg',
499
+ status: 'On Sale',
500
+ timeline: '8:00 PM - 10:00 PM',
501
+ // ... other fields
502
+ }
503
+ ```
504
+
505
+ ## Views
506
+
507
+ `view` takes one of six view names, or one of eight looks.
508
+
509
+ | View | What it draws |
510
+ | ---------- | -------------------------------------- |
511
+ | `calendar` | The default: a month grid of shows |
512
+ | `list` | Upcoming shows in a vertical list |
513
+ | `gallery` | A grid of show posters |
514
+ | `carousel` | Shows in a row that scrolls sideways |
515
+ | `featured` | One show, large |
516
+ | `tonight` | Today's shows, drawn as the Board look |
517
+
518
+ ### Looks
519
+
520
+ A look is a view with its card parts and size set, the starting points of Micdrop's widget builder.
521
+
522
+ | View | Look |
523
+ | --------------- | --------------------------------------- |
524
+ | `look-featured` | Featured: one show as a stacked card |
525
+ | `look-ticket` | Ticket card: one show, the date set big |
526
+ | `look-strip` | Strip: one show per slim row |
527
+ | `look-list` | List: shows grouped under their day |
528
+ | `look-board` | Board: a list led by each show's time |
529
+ | `look-grid` | Grid: show cards in a grid |
530
+ | `look-posters` | Poster wall: posters only |
531
+ | `look-calendar` | Calendar: a month grid |
532
+
533
+ ## WordPress Integration
534
+
535
+ For WordPress sites, you can add this to your page/post HTML:
536
+
537
+ ```html
538
+ <div
539
+ class="micdrop-calendar-container"
540
+ data-venue-id="your-venue-id"
541
+ data-view="calendar"
542
+ ></div>
543
+
544
+ <script src="https://get-micdrop.com/embed/venue-calendar.iife.js"></script>
545
+ ```
546
+
547
+ Or add the script to your theme's footer and use the div anywhere in your content.
548
+
549
+ ## Styling
550
+
551
+ 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.
552
+
553
+ ```css
554
+ /* Example: Custom styling */
555
+ .micdrop-calendar-container {
556
+ max-width: 1200px;
557
+ margin: 0 auto;
558
+ padding: 20px;
559
+ }
560
+ ```
561
+
562
+ **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.
563
+
564
+ ## Theming
565
+
566
+ The calendar supports comprehensive theming via CSS custom properties and JavaScript utilities.
567
+
568
+ ### Using CSS Custom Properties
569
+
570
+ Override the default theme by setting CSS custom properties:
571
+
572
+ ```css
573
+ /* Custom brand colors */
574
+ .micdrop-calendar-container {
575
+ --Brand-Primary: 270 76% 60%; /* Purple */
576
+ --Text-Primary: 0 0% 10%;
577
+ --BG-Primary: 0 0% 100%;
578
+ }
579
+
580
+ /* Dark mode */
581
+ .dark .micdrop-calendar-container,
582
+ [data-theme='dark'] .micdrop-calendar-container {
583
+ --Brand-Primary: 270 76% 70%;
584
+ --Text-Primary: 0 0% 95%;
585
+ --BG-Primary: 0 0% 10%;
586
+ }
587
+ ```
588
+
589
+ ### Available CSS Variables
590
+
591
+ | Variable | Description | Default (Light) |
592
+ | ---------------------- | ----------------------- | -------------------- |
593
+ | `--Brand-Primary` | Primary brand color | `217 91% 60%` (Blue) |
594
+ | `--Text-Primary` | Main text color | `0 0% 0%` |
595
+ | `--Text-Secondary` | Secondary text | `0 0% 40%` |
596
+ | `--BG-Primary` | Main background | `0 0% 100%` |
597
+ | `--BG-Secondary` | Secondary background | `0 0% 98%` |
598
+ | `--Stroke-Primary` | Border colors | `0 0% 80%` |
599
+ | `--Status-OnSale` | "On Sale" badge | `217 91% 60%` |
600
+ | `--Status-SellingFast` | "Selling Fast" badge | `38 92% 50%` |
601
+ | `--Status-SoldOut` | "Sold Out" badge | `0 84% 60%` |
602
+ | `--Today-BG` | Today's date background | `217 91% 97%` |
603
+ | `--Focus-Ring` | Keyboard focus ring | `217 91% 60%` |
604
+
605
+ ### Using JavaScript Theme Utilities
606
+
607
+ ```javascript
608
+ import {
609
+ applyTheme,
610
+ themes,
611
+ generateThemeCSS,
612
+ } from '@getmicdrop/venue-calendar';
613
+
614
+ // Apply a preset theme
615
+ applyTheme(themes.dark);
616
+
617
+ // Apply to a specific container
618
+ const container = document.querySelector('.micdrop-calendar-container');
619
+ applyTheme(themes.dark, container);
620
+
621
+ // Create a custom theme
622
+ const myTheme = {
623
+ brandPrimary: '270 76% 60%', // Purple
624
+ textPrimary: '0 0% 10%',
625
+ bgPrimary: '0 0% 100%',
626
+ statusOnSale: '142 71% 45%', // Green for on sale
627
+ };
628
+ applyTheme(myTheme);
629
+
630
+ // Generate CSS string for embedding
631
+ const cssString = generateThemeCSS(myTheme);
632
+ console.log(cssString);
633
+ // Output: :root { --Brand-Primary: 270 76% 60%; ... }
634
+ ```
635
+
636
+ ### Preset Themes
637
+
638
+ Three themes are included out of the box:
639
+
640
+ ```javascript
641
+ import { themes } from '@getmicdrop/venue-calendar';
642
+
643
+ // Light theme (default)
644
+ applyTheme(themes.light);
645
+
646
+ // Dark theme
647
+ applyTheme(themes.dark);
648
+
649
+ // High contrast (accessibility)
650
+ applyTheme(themes.highContrast);
651
+ ```
652
+
653
+ ### Automatic Dark Mode
654
+
655
+ The calendar automatically respects the user's system preference:
656
+
657
+ ```css
658
+ /* Automatically applied when user prefers dark mode */
659
+ @media (prefers-color-scheme: dark) {
660
+ /* Dark theme variables are applied */
661
+ }
662
+ ```
663
+
664
+ You can also manually toggle dark mode:
665
+
666
+ ```html
667
+ <!-- Add 'dark' class to enable dark theme -->
668
+ <div class="dark">
669
+ <div class="micdrop-calendar-container" data-venue-id="123"></div>
670
+ </div>
671
+
672
+ <!-- Or use data-theme attribute -->
673
+ <div data-theme="dark">
674
+ <div class="micdrop-calendar-container" data-venue-id="123"></div>
675
+ </div>
676
+ ```
677
+
678
+ ## Localization and the CDN bundle
679
+
680
+ The calendar and the checkout speak thirteen languages. English is built into the
681
+ bundle; the other twelve are **fetched at runtime**, one file, only for the
682
+ locale actually in use — so a buyer never downloads twelve languages to read one.
683
+ This is what keeps the CDN bundle at ~594 KB gzipped instead of ~686 KB.
684
+
685
+ For the npm package nothing is required: your bundler code-splits the catalogues
686
+ and addresses them itself.
687
+
688
+ For the `<script>` embed, the bundle works out where to fetch from by looking at
689
+ the URL it was itself served from, and loads a **version-pinned sibling**:
690
+
691
+ ```
692
+ https://get-micdrop.com/embed/venue-calendar.iife.js
693
+ https://get-micdrop.com/embed/locales/<version>/main/es.js ← calendar copy
694
+ https://get-micdrop.com/embed/locales/<version>/flow/es.js ← checkout copy
695
+ ```
696
+
697
+ The calendar itself loads the same way. `venue-calendar.iife.js` is a small
698
+ loader: it fetches the first piece (enough to draw a saved look's card) and,
699
+ once the card is up, the rest of the widget, all from a version-pinned
700
+ `looks/<version>/` directory beside it. Until the rest has loaded, the
701
+ `window.VenueCalendar` functions return promises that resolve to what the
702
+ function returns; after that they are the functions themselves.
703
+
704
+ If you self-host the bundle, **copy the `dist/locales/`, `dist/looks/` and
705
+ `dist/checkout-libs/` directories alongside it**.
706
+ Version-pinned paths mean new releases only ever add files; they never overwrite
707
+ the catalogue an older pinned bundle is using.
708
+
709
+ ### When the fetch cannot happen
710
+
711
+ If the locale file cannot be loaded — a Content-Security-Policy that allowlists
712
+ the exact script file rather than the origin, an offline CDN, or a
713
+ `<script type="module">` embed where the bundle cannot see its own URL — the
714
+ calendar simply stays in English. Nothing errors and nothing breaks; the widget
715
+ renders and sells tickets exactly as it does for an English buyer.
716
+
717
+ The calendar's own files are a different matter: if `looks/<version>/` cannot
718
+ be loaded, nothing draws, and the console names the file that failed. The two
719
+ overrides below fix the URL case; a Content-Security-Policy has to allow the
720
+ `get-micdrop.com/embed/` directory, not one file in it.
721
+
722
+ Two ways to point it at the right place when it cannot work it out:
723
+
724
+ ```html
725
+ <!-- Option A: tell it before the bundle loads -->
726
+ <script>
727
+ window.__MICDROP_CALENDAR_ASSET_BASE__ = 'https://get-micdrop.com/embed/';
728
+ </script>
729
+ <script
730
+ defer
731
+ src="https://get-micdrop.com/embed/venue-calendar.iife.js"
732
+ ></script>
733
+
734
+ <!-- Option B: put it on the tag -->
735
+ <script
736
+ defer
737
+ src="https://get-micdrop.com/embed/venue-calendar.iife.js"
738
+ data-vc-base="https://get-micdrop.com/embed/"
739
+ ></script>
740
+ ```
741
+
742
+ `defer` and `async` need no override — a classic script still knows its own URL.
743
+
744
+ ### First paint is always English
745
+
746
+ A non-English buyer sees English for a moment on a cold load and the page swaps
747
+ to their language when the catalogue arrives. This is deliberate: the calendar
748
+ paints immediately rather than waiting on a translation file.
749
+
750
+ ## Browser Support
751
+
752
+ - Chrome (latest)
753
+ - Firefox (latest)
754
+ - Safari (latest)
755
+ - Edge (latest)
756
+ - Mobile browsers (iOS Safari, Chrome Mobile)
757
+
758
+ ## Development
759
+
760
+ ### Building the Package
761
+
762
+ ```bash
763
+ # Install dependencies
764
+ npm install
765
+
766
+ # Build the library
767
+ npm run build:lib
768
+
769
+ # Development mode (SvelteKit app)
770
+ npm run dev
771
+
772
+ # Preview production build
773
+ npm run preview
774
+ ```
775
+
776
+ `build:lib` is three passes and the order matters: the IIFE pass runs first and
777
+ owns the `dist/` wipe, the ES/UMD pass appends to it, and
778
+ `vite.config.locales.js` appends the runtime locale catalogues
779
+ (`dist/locales/<version>/{main,flow}/<tag>.js`) last. `npm run size` fails if the
780
+ bundle and its catalogues do not both come out of that.
781
+
782
+ ### Project Structure
783
+
784
+ ```
785
+ venue-calendar/
786
+ ├── src/
787
+ │ ├── components/ # Svelte components
788
+ │ │ ├── Calendar/
789
+ │ │ ├── CalendarContainer/
790
+ │ │ └── Button/
791
+ │ ├── lib/ # Library entry points
792
+ │ │ ├── VenueCalendar.js
793
+ │ │ └── web-component.js
794
+ │ └── routes/ # SvelteKit routes (for dev)
795
+ ├── dist/ # Built package (generated)
796
+ │ └── locales/<version>/ # Runtime locale catalogues for the CDN bundle
797
+ ├── package.json
798
+ ├── vite.config.lib.js # Library build config (ES/UMD + IIFE passes)
799
+ ├── vite.config.locales.js # Emits the runtime locale catalogues
800
+ └── README.md
801
+ ```
802
+
803
+ ### Lockfile policy
804
+
805
+ `package-lock.json` is the single canonical lockfile — CI and the publish
806
+ workflow install with `npm ci`. Do not commit `yarn.lock` or
807
+ `pnpm-lock.yaml` (both gitignored). Local dev machines may use pnpm for the
808
+ svelte-components symlink workflow, but dependency changes must land in
809
+ `package-lock.json` via npm.
810
+
811
+ ## API Reference
812
+
813
+ ### `initVenueCalendar(options)`
814
+
815
+ Initialize a calendar instance.
816
+
817
+ **Parameters:**
818
+
819
+ - `options` (Object): Configuration options
820
+
821
+ **Returns:** Svelte component instance
822
+
823
+ **Example:**
824
+
825
+ ```javascript
826
+ const calendar = initVenueCalendar({
827
+ target: '#calendar',
828
+ venueId: 'venue-123',
829
+ view: 'calendar',
830
+ });
831
+ ```
832
+
833
+ ### `autoMount()`
834
+
835
+ Automatically mount calendars to all elements with class `micdrop-calendar-container`.
836
+
837
+ **Example:**
838
+
839
+ ```javascript
840
+ import { autoMount } from '@getmicdrop/venue-calendar';
841
+ autoMount();
842
+ ```
843
+
844
+ ### Component Events
845
+
846
+ The calendar component emits events that you can listen to:
847
+
848
+ ```javascript
849
+ const calendar = initVenueCalendar({
850
+ target: '#calendar',
851
+ // ... other options
852
+ });
853
+
854
+ // Listen to component events (if using Svelte component directly)
855
+ calendar.$on('eventClick', event => {
856
+ console.log('Event clicked:', event.detail);
857
+ });
858
+ ```
859
+
860
+ ## Troubleshooting
861
+
862
+ ### Calendar not appearing
863
+
864
+ 1. **Check the script is loaded**: Open browser console and verify no errors
865
+ 2. **Verify container exists**: Make sure the target element exists in the DOM
866
+ 3. **Check data attributes**: Ensure attributes are correctly formatted with `data-` prefix
867
+
868
+ ### Styles not applying
869
+
870
+ 1. **CSS not loaded**: The styles are bundled in the JS file and auto-injected — no separate stylesheet needed
871
+ 2. **CSS conflicts**: Check if other styles are overriding the calendar styles
872
+ 3. **Bundle didn't load**: Confirm the `<script src>` points at the Micdrop-hosted bundle and returns 200 (not 404)
873
+
874
+ ### Events not showing
875
+
876
+ 1. **Check event data format**: Ensure events match the expected structure
877
+ 2. **Date format**: Use ISO 8601 format for dates (`YYYY-MM-DDTHH:mm:ssZ`)
878
+ 3. **Venue ID**: Verify the venue ID is correct
879
+
880
+ ## Contributing
881
+
882
+ Contributions are welcome! Please feel free to submit a Pull Request.
883
+
884
+ ## License
885
+
886
+ MIT © MicDrop
887
+
888
+ ## Support
889
+
890
+ For issues, questions, or feature requests, please visit:
891
+ https://github.com/get-micdrop/venue-calendar/issues
892
+
893
+ ---
894
+
895
+ Made with ❤️ by the MicDrop team