npm package: @getmicdrop/venue-calendar
Description: A customizable Svelte calendar component for displaying comedy events
Author: MicDrop
Latest version: 3.5.5

# @getmicdrop/venue-calendar

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.

## Features

✨ **Three View Modes**: List, Gallery, and Calendar views
🎨 **Beautiful UI**: Modern, responsive design built with Tailwind CSS
📱 **Mobile-Friendly**: Swipe gestures, touch-optimized, responsive design
🔌 **Easy Integration**: Works with React, Vue, vanilla JS, and more
🌐 **CDN Ready**: Use directly in HTML via JSDelivr
⚡ **Auto-Mount**: Automatically finds and mounts to designated containers
🎯 **Customizable**: Configure views, navigation, and more
🌙 **Dark Mode**: Built-in light, dark, and high-contrast themes
♿ **Accessible**: ARIA labels, keyboard navigation, screen reader support
🎫 **Event Status**: Visual badges for "On Sale", "Selling Fast", "Sold Out"

## Installation

### NPM/Yarn

```bash
npm install @getmicdrop/venue-calendar
```

or

```bash
yarn add @getmicdrop/venue-calendar
```

### CDN (JSDelivr)

```html
<!-- Latest version -->
<script src="https://cdn.jsdelivr.net/npm/@getmicdrop/venue-calendar/dist/venue-calendar.iife.js"></script>

<!-- Specific version (recommended for production) -->
<script src="https://cdn.jsdelivr.net/npm/@getmicdrop/venue-calendar@1.3.0/dist/venue-calendar.iife.js"></script>
```

## Quick Start

### Method 1: Auto-Mount (Easiest)

Simply add a div with the class `micdrop-calendar-container` and the calendar will automatically mount:

```html
<!DOCTYPE html>
<html>
<head>
  <title>My Comedy Club</title>
</head>
<body>
  <!-- Calendar will auto-mount here -->
  <div class="micdrop-calendar-container" 
       data-venue-id="your-venue-id"
       data-view="calendar"
       data-show-view-options="true"
       data-show-month-switcher="true">
  </div>

  <!-- Load the script -->
  <script src="https://cdn.jsdelivr.net/npm/@getmicdrop/venue-calendar/dist/venue-calendar.iife.js"></script>
</body>
</html>
```

### Method 2: Web Component

Use the custom `<micdrop-calendar>` element:

```html
<!DOCTYPE html>
<html>
<head>
  <title>My Comedy Club</title>
</head>
<body>
  <!-- Web Component -->
  <micdrop-calendar 
    venue-id="your-venue-id"
    view="calendar"
    show-view-options="true"
    show-month-switcher="true">
  </micdrop-calendar>

  <script src="https://cdn.jsdelivr.net/npm/@getmicdrop/venue-calendar/dist/venue-calendar.iife.js"></script>
</body>
</html>
```

### Method 3: JavaScript API

For more control, use the JavaScript API:

```html
<!DOCTYPE html>
<html>
<head>
  <title>My Comedy Club</title>
</head>
<body>
  <div id="my-calendar"></div>

  <script type="module">
    import { initVenueCalendar } from 'https://cdn.jsdelivr.net/npm/@getmicdrop/venue-calendar/dist/venue-calendar.es.js';

    initVenueCalendar({
      target: '#my-calendar',
      venueId: 'your-venue-id',
      view: 'calendar',
      events: [],
      showViewOptions: true,
      showMonthSwitcher: true,
    });
  </script>
</body>
</html>
```

## Framework Integration

### React

```jsx
import React, { useEffect, useRef } from 'react';
import { initVenueCalendar } from '@getmicdrop/venue-calendar';

function VenueCalendarComponent({ venueId, view = 'calendar' }) {
  const calendarRef = useRef(null);
  const instanceRef = useRef(null);

  useEffect(() => {
    if (calendarRef.current) {
      // Initialize calendar
      instanceRef.current = initVenueCalendar({
        target: calendarRef.current,
        venueId,
        view,
        events: [],
        showViewOptions: true,
        showMonthSwitcher: true,
      });
    }

    // Cleanup on unmount
    return () => {
      if (instanceRef.current && instanceRef.current.$destroy) {
        instanceRef.current.$destroy();
      }
    };
  }, [venueId, view]);

  return <div ref={calendarRef}></div>;
}

export default VenueCalendarComponent;
```

**Usage in React App:**

```jsx
import React, { useState } from "react";
import VenueCalendarComponent from "./VenueCalendarComponent";

function App() {
  const [venueId, setVenueId] = useState("comedy-club-123");
  const [view, setView] = useState("calendar");

  return (
    <div style={{ padding: "20px" }}>
      <h1>Event Viewer</h1>
      
      <div style={{ marginBottom: "20px" }}>
        <label>Venue ID:</label>
        <input
          type="text"
          value={venueId}
          onChange={(e) => setVenueId(e.target.value)}
        />
      </div>

      <div style={{ marginBottom: "20px" }}>
        <label>Select View:</label>
        <label>
          <input
            type="radio"
            value="list"
            checked={view === "list"}
            onChange={(e) => setView(e.target.value)}
          />
          List
        </label>
        <label>
          <input
            type="radio"
            value="gallery"
            checked={view === "gallery"}
            onChange={(e) => setView(e.target.value)}
          />
          Gallery
        </label>
        <label>
          <input
            type="radio"
            value="calendar"
            checked={view === "calendar"}
            onChange={(e) => setView(e.target.value)}
          />
          Calendar
        </label>
      </div>

      <VenueCalendarComponent venueId={venueId} view={view} />
    </div>
  );
}

export default App;
```

### Vue 3

```vue
<template>
  <div ref="calendarContainer"></div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue';
import { initVenueCalendar } from '@getmicdrop/venue-calendar';

const props = defineProps({
  venueId: String,
  view: {
    type: String,
    default: 'calendar'
  }
});

const calendarContainer = ref(null);
let calendarInstance = null;

onMounted(() => {
  calendarInstance = initVenueCalendar({
    target: calendarContainer.value,
    venueId: props.venueId,
    view: props.view,
    events: [],
    showViewOptions: true,
    showMonthSwitcher: true,
  });
});

onUnmounted(() => {
  if (calendarInstance && calendarInstance.$destroy) {
    calendarInstance.$destroy();
  }
});

watch(() => props.venueId, (newId) => {
  if (calendarInstance) {
    calendarInstance.$destroy();
    calendarInstance = initVenueCalendar({
      target: calendarContainer.value,
      venueId: newId,
      view: props.view,
      events: [],
      showViewOptions: true,
      showMonthSwitcher: true,
    });
  }
});
</script>
```

### Svelte

```svelte
<script>
  import { VenueCalendar } from '@getmicdrop/venue-calendar';
  import { Calendar, Grid, List } from 'carbon-icons-svelte';
  import { writable } from 'svelte/store';

  let venueId = 'your-venue-id';
  let currentMonth = writable(new Date().getUTCMonth());
  let currentYear = writable(new Date().getUTCFullYear());

  function handleNext() {
    currentMonth.update(m => m + 1);
  }

  function handlePrev() {
    currentMonth.update(m => m - 1);
  }
</script>

<VenueCalendar
  showViewOptions={[
    { id: 0, text: "List view", icon: List },
    { id: 1, text: "Gallery view", icon: Grid },
    { id: 2, text: "Calendar view", icon: Calendar }
  ]}
  showMonthSwitcher={true}
  events={[]}
  {currentMonth}
  {currentYear}
  handleNext={handleNext}
  handlePrev={handlePrev}
  on:eventClick={(e) => console.log('Event clicked:', e.detail)}
/>
```

### Angular

```typescript
import { Component, OnInit, OnDestroy, ElementRef, ViewChild } from '@angular/core';
import { initVenueCalendar } from '@getmicdrop/venue-calendar';

@Component({
  selector: 'app-venue-calendar',
  template: '<div #calendarContainer></div>',
})
export class VenueCalendarComponent implements OnInit, OnDestroy {
  @ViewChild('calendarContainer', { static: true }) calendarContainer!: ElementRef;
  private calendarInstance: any;

  ngOnInit() {
    this.calendarInstance = initVenueCalendar({
      target: this.calendarContainer.nativeElement,
      venueId: 'your-venue-id',
      view: 'calendar',
      events: [],
      showViewOptions: true,
      showMonthSwitcher: true,
    });
  }

  ngOnDestroy() {
    if (this.calendarInstance && this.calendarInstance.$destroy) {
      this.calendarInstance.$destroy();
    }
  }
}
```

## Configuration Options

### Data Attributes (for auto-mount)

| Attribute | Type | Default | Description |
|-----------|------|---------|-------------|
| `data-venue-id` | string | `''` | The venue ID to fetch events for |
| `data-view` | string | `'calendar'` | Initial view: `'list'`, `'gallery'`, or `'calendar'` |
| `data-show-view-options` | boolean | `true` | Show view switcher buttons |
| `data-show-month-switcher` | boolean | `true` | Show month navigation controls |

### JavaScript API Options

```javascript
initVenueCalendar({
  target: '.my-calendar',           // CSS selector or HTMLElement (required)
  venueId: 'venue-123',              // Venue ID (optional)
  view: 'calendar',                  // 'list', 'gallery', or 'calendar' (default: 'calendar')
  events: [],                        // Array of event objects (default: [])
  showViewOptions: true,             // Show view switcher (default: true)
  showMonthSwitcher: true,           // Show month navigation (default: true)
});
```

### Event Object Structure

```javascript
{
  id: 'event-123',
  name: 'Comedy Night',
  date: '2024-10-25T20:00:00Z',
  image: 'https://example.com/image.jpg',
  status: 'On Sale',
  timeline: '8:00 PM - 10:00 PM',
  // ... other fields
}
```

## Views

### Calendar View
The default view showing events in a monthly calendar grid. Perfect for venues with regular shows.

### List View
A vertical list layout showing all upcoming events with details. Great for mobile experiences.

### Gallery View
A grid layout displaying event posters in a gallery format. Ideal for showcasing event imagery.

## WordPress Integration

For WordPress sites, you can add this to your page/post HTML:

```html
<div class="micdrop-calendar-container" 
     data-venue-id="your-venue-id"
     data-view="calendar">
</div>

<script src="https://cdn.jsdelivr.net/npm/@getmicdrop/venue-calendar/dist/venue-calendar.iife.js"></script>
```

Or add the script to your theme's footer and use the div anywhere in your content.

## Styling

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.

```css
/* Example: Custom styling */
.micdrop-calendar-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}
```

## Theming

The calendar supports comprehensive theming via CSS custom properties and JavaScript utilities.

### Using CSS Custom Properties

Override the default theme by setting CSS custom properties:

```css
/* Custom brand colors */
.micdrop-calendar-container {
  --Brand-Primary: 270 76% 60%;  /* Purple */
  --Text-Primary: 0 0% 10%;
  --BG-Primary: 0 0% 100%;
}

/* Dark mode */
.dark .micdrop-calendar-container,
[data-theme="dark"] .micdrop-calendar-container {
  --Brand-Primary: 270 76% 70%;
  --Text-Primary: 0 0% 95%;
  --BG-Primary: 0 0% 10%;
}
```

### Available CSS Variables

| Variable | Description | Default (Light) |
|----------|-------------|-----------------|
| `--Brand-Primary` | Primary brand color | `217 91% 60%` (Blue) |
| `--Text-Primary` | Main text color | `0 0% 0%` |
| `--Text-Secondary` | Secondary text | `0 0% 40%` |
| `--BG-Primary` | Main background | `0 0% 100%` |
| `--BG-Secondary` | Secondary background | `0 0% 98%` |
| `--Stroke-Primary` | Border colors | `0 0% 80%` |
| `--Status-OnSale` | "On Sale" badge | `217 91% 60%` |
| `--Status-SellingFast` | "Selling Fast" badge | `38 92% 50%` |
| `--Status-SoldOut` | "Sold Out" badge | `0 84% 60%` |
| `--Today-BG` | Today's date background | `217 91% 97%` |
| `--Focus-Ring` | Keyboard focus ring | `217 91% 60%` |

### Using JavaScript Theme Utilities

```javascript
import { applyTheme, themes, generateThemeCSS } from '@getmicdrop/venue-calendar';

// Apply a preset theme
applyTheme(themes.dark);

// Apply to a specific container
const container = document.querySelector('.micdrop-calendar-container');
applyTheme(themes.dark, container);

// Create a custom theme
const myTheme = {
  brandPrimary: '270 76% 60%',  // Purple
  textPrimary: '0 0% 10%',
  bgPrimary: '0 0% 100%',
  statusOnSale: '142 71% 45%',  // Green for on sale
};
applyTheme(myTheme);

// Generate CSS string for embedding
const cssString = generateThemeCSS(myTheme);
console.log(cssString);
// Output: :root { --Brand-Primary: 270 76% 60%; ... }
```

### Preset Themes

Three themes are included out of the box:

```javascript
import { themes } from '@getmicdrop/venue-calendar';

// Light theme (default)
applyTheme(themes.light);

// Dark theme
applyTheme(themes.dark);

// High contrast (accessibility)
applyTheme(themes.highContrast);
```

### Automatic Dark Mode

The calendar automatically respects the user's system preference:

```css
/* Automatically applied when user prefers dark mode */
@media (prefers-color-scheme: dark) {
  /* Dark theme variables are applied */
}
```

You can also manually toggle dark mode:

```html
<!-- Add 'dark' class to enable dark theme -->
<div class="dark">
  <div class="micdrop-calendar-container" data-venue-id="123"></div>
</div>

<!-- Or use data-theme attribute -->
<div data-theme="dark">
  <div class="micdrop-calendar-container" data-venue-id="123"></div>
</div>
```

## Browser Support

- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- Mobile browsers (iOS Safari, Chrome Mobile)

## Development

### Building the Package

```bash
# Install dependencies
npm install

# Build the library
npm run build:lib

# Development mode (SvelteKit app)
npm run dev

# Preview production build
npm run preview
```

### Project Structure

```
venue-calendar/
├── src/
│   ├── components/         # Svelte components
│   │   ├── Calendar/
│   │   ├── CalendarContainer/
│   │   └── Button/
│   ├── lib/               # Library entry points
│   │   ├── VenueCalendar.js
│   │   └── web-component.js
│   └── routes/            # SvelteKit routes (for dev)
├── dist/                  # Built package (generated)
├── package.json
├── vite.config.lib.js     # Library build config
└── README.md
```

## API Reference

### `initVenueCalendar(options)`

Initialize a calendar instance.

**Parameters:**
- `options` (Object): Configuration options

**Returns:** Svelte component instance

**Example:**
```javascript
const calendar = initVenueCalendar({
  target: '#calendar',
  venueId: 'venue-123',
  view: 'calendar',
});
```

### `autoMount()`

Automatically mount calendars to all elements with class `micdrop-calendar-container`.

**Example:**
```javascript
import { autoMount } from '@getmicdrop/venue-calendar';
autoMount();
```

### Component Events

The calendar component emits events that you can listen to:

```javascript
const calendar = initVenueCalendar({
  target: '#calendar',
  // ... other options
});

// Listen to component events (if using Svelte component directly)
calendar.$on('eventClick', (event) => {
  console.log('Event clicked:', event.detail);
});
```

## Troubleshooting

### Calendar not appearing

1. **Check the script is loaded**: Open browser console and verify no errors
2. **Verify container exists**: Make sure the target element exists in the DOM
3. **Check data attributes**: Ensure attributes are correctly formatted with `data-` prefix

### Styles not applying

1. **CSS not loaded**: The styles are bundled in the JS file and auto-injected
2. **CSS conflicts**: Check if other styles are overriding the calendar styles
3. **CDN issues**: Try using a specific version instead of `@latest`

### Events not showing

1. **Check event data format**: Ensure events match the expected structure
2. **Date format**: Use ISO 8601 format for dates (`YYYY-MM-DDTHH:mm:ssZ`)
3. **Venue ID**: Verify the venue ID is correct

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## License

MIT © MicDrop

## Support

For issues, questions, or feature requests, please visit:
https://github.com/get-micdrop/venue-calendar/issues

---

Made with ❤️ by the MicDrop team