# Scheduler

> Read-only day, week and month schedule with overlap-aware event blocks, plus SlotPicker, a booking-style Calendar and radio list of available times.

Source: https://docs.nasaqui.com/components/scheduler

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/scheduler.json
```

`Scheduler` shows events on a day, week or month view. The day and week views place each event as a block by
its start and end time; events that overlap share the column side by side. The month view shows chips, with a
"+N more" popover when a day has more than fit. `SlotPicker` is the booking counterpart: a `Calendar` next to
a radio list of the times still free on the chosen day.

Both are controlled by data you pass in. Neither stores events. **Drag to create, move or resize is out of
scope**: an event is changed by your own UI (a dialog, a form) after `onEventClick` or `onSlotSelect`.

## When to use
- Showing appointments, shifts, bookings or meetings across a week or month.
- Letting a person pick a free time to book (`SlotPicker`).

## When not to use
- Choosing a date or a date range: use `Calendar` or `DatePicker`.
- A task board with drag and drop: use `KanbanBoard`.
- A plain chronological list: use a list or table.

## Import
```tsx
import { Scheduler, SlotPicker } from "@fadymondy/nasaq/web";
```
Outside this repo: `@fadymondy/nasaq/web`.

## Quick start
```tsx
import { Scheduler, type SchedulerEvent } from "@fadymondy/nasaq/web";
import { useState } from "react";

const events: SchedulerEvent[] = [
  { id: "1", title: "Design review", start: new Date(2026, 8, 29, 10, 0), end: new Date(2026, 8, 29, 11, 30), tone: "brand" },
  { id: "2", title: "Client call", start: new Date(2026, 8, 29, 10, 30), end: new Date(2026, 8, 29, 11, 0), tone: "success" },
];

export function Example() {
  const [picked, setPicked] = useState("");
  return (
    <>
      <Scheduler
        events={events}
        workingHours={{ start: 8, end: 18 }}
        onSlotSelect={(start, end) => setPicked(`${start.toISOString()} - ${end.toISOString()}`)}
        onEventClick={(event) => setPicked(event.title)}
      />
      <p>{picked}</p>
    </>
  );
}
```

## Anatomy
```
Scheduler                       data-slot="scheduler"  data-view="day|week|month"
├─ toolbar                      data-slot="scheduler-toolbar"   previous, next, Today, title, ToggleGroup of views
└─ grid                         data-slot="scheduler-grid"
   ├─ day / week: slot buttons  data-slot="scheduler-slot"      one per slot per day
   │  └─ event blocks           data-slot="scheduler-event"     data-tone
   └─ month: day cells          data-slot="scheduler-day"       data-today, data-outside
      └─ chips                  data-slot="scheduler-chip"      data-tone

SlotPicker                      data-slot="slot-picker"
├─ Calendar
└─ times                        data-slot="slot-picker-times"
   └─ radios                    data-slot="slot-picker-slot"
```

## API

### Scheduler
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `events` | `SchedulerEvent[]` | required | `{ id, title, start: Date, end: Date, tone? }`. Events crossing midnight show on both days. |
| `view` / `defaultView` / `onViewChange` | `"day" \| "week" \| "month"` | `"week"` | The visible view. |
| `date` / `defaultDate` / `onDateChange` | `Date` | today | Any date inside the visible day, week or month. |
| `workingHours` | `{ start: number; end: number }` | `{ start: 8, end: 18 }` | Hours shown in the day and week views (`end` is exclusive, 1 to 24). Events outside are hidden or clipped. |
| `slotMinutes` | `number` | `30` | Length of one clickable slot (minimum 5). |
| `weekStartsOn` | `0..6` | locale | First day of the week (0 = Sunday). |
| `hour12` | `boolean` | locale | Force 12 or 24 hour time on the axis and in labels. |
| `maxChips` | `number` | `3` | Month chips per day. When there are more events, the last chip becomes "+N more". |
| `onSlotSelect` | `(start: Date, end: Date) => void` | none | Click on an empty slot. In the month view the day number reports the whole day. |
| `onEventClick` | `(event: SchedulerEvent) => void` | none | Click on an event block or chip. |
| `locale` | `string` | Nasaq locale | BCP 47 locale. |
| `dir` | `"ltr" \| "rtl"` | from locale | Force a direction. |
| `today` | `Date` | now | Override "today" for tests and stories. |

`tone` is one of `neutral` (default), `brand`, `success`, `warning`, `danger`, `info`.

### SlotPicker
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `slots` | `SchedulerSlot[]` | required | `{ start: Date; end?: Date; disabled?: boolean }`. Any number of days. |
| `value` / `defaultValue` / `onValueChange` | `Date \| null`, `(start: Date) => void` | `null` | The selected slot's start. |
| `day` / `defaultDay` / `onDayChange` | `Date` | first day with a free slot | The day whose times are listed. |
| `weekStartsOn`, `hour12`, `locale`, `dir`, `today` | as above | | Same meaning as in `Scheduler`. |
| `emptyLabel` | `ReactNode` | localised | Shown when the chosen day has no slots. |

Days before today, or without an enabled slot, are disabled in the calendar.

### Layout helpers
Pure functions, exported for custom renderers and tested without a DOM.

| Export | Signature |
| --- | --- |
| `layoutDayEvents` | `(events, day, rangeStart, rangeEnd, minLength = 15) => PositionedEvent[]`. Minutes from midnight. Returns `{ event, top, height, column, columns }`. |
| `eventBox` | `(positioned, rangeMinutes) => { top, height, insetInlineStart, width }` in percent. |
| `timeSlots` | `({ start, end }, slotMinutes) => number[]`. Slot starts in minutes from midnight. |

## Examples

Arabic, 24-hour axis, Monday first:
```tsx
import { Scheduler } from "@fadymondy/nasaq/web";

export function Arabic() {
  return (
    <Scheduler
      locale="ar-EG"
      hour12={false}
      weekStartsOn={1}
      events={[{ id: "1", title: "مراجعة التصميم", start: new Date(2026, 8, 29, 10), end: new Date(2026, 8, 29, 11), tone: "brand" }]}
    />
  );
}
```

Booking:
```tsx
import { SlotPicker } from "@fadymondy/nasaq/web";
import { useState } from "react";

const slots = [9, 10, 11, 14].map((h) => ({ start: new Date(2026, 9, 1, h) }));

export function Booking() {
  const [value, setValue] = useState<Date | null>(null);
  return <SlotPicker slots={slots} value={value} onValueChange={setValue} />;
}
```

Controlled view and date:
```tsx
import { Scheduler, type SchedulerView } from "@fadymondy/nasaq/web";
import { useState } from "react";

export function Controlled() {
  const [view, setView] = useState<SchedulerView>("month");
  const [date, setDate] = useState(new Date(2026, 8, 1));
  return <Scheduler events={[]} view={view} onViewChange={setView} date={date} onDateChange={setDate} />;
}
```

## Accessibility
| Key | Action |
| --- | --- |
| Tab | Moves between toolbar controls, the grid (one tab stop), events and chips. |
| Arrow Up / Down | Day and week views: previous or next slot in the day. |
| Arrow Left / Right | Day and week views: adjacent day column (physical: in RTL, Left is the next day). |
| Home / End | First or last slot of the day. |
| Enter / Space | Selects the focused slot, event or chip. |
| Arrow keys (SlotPicker times) | Move and select between times; Tab leaves the group. |

- Each slot button is named with its full date and time; events and chips with title and time range.
- The title is an `aria-live="polite"` heading, so moving between periods is announced.
- The view switch is a labelled `ToggleGroup`. `SlotPicker` times are a `radiogroup` of `radio`s.
- Localised by default (English and Arabic): toolbar labels, "+N more", the slot list name and the empty text.

## RTL & i18n
- Day columns run right to left in RTL and the time axis sits on the right. Chevrons mirror.
- The week starts on the locale's first day unless `weekStartsOn` is set.
- The axis uses the locale's 12 or 24 hour format; `hour12` overrides. Digits are Western (Nasaq default).
- Times are wrapped in `<bdi>` so ranges stay ordered inside Arabic text.

## Styling & tokens
Uses `bg-card`, `border-border`, `bg-secondary`, `bg-primary`, `bg-nq-hover`, `bg-nq-selected`, the status
tokens (`nq-success`, `nq-warning`, `nq-danger`, `nq-info`) and `nq-brand` for event tones. Target
`[data-today]`, `[data-outside]` and `[data-tone]`. Pass `className` to the root; do not use raw hex.

## Do / Don't
- Do keep `workingHours` to the hours people schedule in; the grid is one row per slot.
- Do treat `onSlotSelect` as "the user wants to create here" and open your own form.
- Don't rely on tone alone: put the status in the title.
- Don't expect dragging: create, move and resize are not supported.

## Related
- [Calendar](https://docs.nasaqui.com/components/calendar)
- [DatePicker](https://docs.nasaqui.com/components/date-picker)
- [Popover](https://docs.nasaqui.com/components/popover)
- [ToggleGroup](https://docs.nasaqui.com/components/toggle-group)

## Lab
https://docs.nasaqui.com/?path=/docs/components-productivity-scheduler--docs

## Code

### React

```tsx
import { Scheduler, type SchedulerEvent } from "@fadymondy/nasaq/web";
import { useState } from "react";

const events: SchedulerEvent[] = [
  { id: "1", title: "Design review", start: new Date(2026, 8, 29, 10, 0), end: new Date(2026, 8, 29, 11, 30), tone: "brand" },
  { id: "2", title: "Client call", start: new Date(2026, 8, 29, 10, 30), end: new Date(2026, 8, 29, 11, 0), tone: "success" },
];

export function Example() {
  const [picked, setPicked] = useState("");
  return (
    <>
      <Scheduler
        events={events}
        workingHours={{ start: 8, end: 18 }}
        onSlotSelect={(start, end) => setPicked(`${start.toISOString()} - ${end.toISOString()}`)}
        onEventClick={(event) => setPicked(event.title)}
      />
      <p>{picked}</p>
    </>
  );
}
```

### shadcn

```tsx
import { Scheduler, type SchedulerEvent } from "@/components/ui/scheduler";
import { useState } from "react";

const events: SchedulerEvent[] = [
  { id: "1", title: "Design review", start: new Date(2026, 8, 29, 10, 0), end: new Date(2026, 8, 29, 11, 30), tone: "brand" },
  { id: "2", title: "Client call", start: new Date(2026, 8, 29, 10, 30), end: new Date(2026, 8, 29, 11, 0), tone: "success" },
];

export function Example() {
  const [picked, setPicked] = useState("");
  return (
    <>
      <Scheduler
        events={events}
        workingHours={{ start: 8, end: 18 }}
        onSlotSelect={(start, end) => setPicked(`${start.toISOString()} - ${end.toISOString()}`)}
        onEventClick={(event) => setPicked(event.title)}
      />
      <p>{picked}</p>
    </>
  );
}
```

### Vue

```vue
<script setup lang="ts">
import { NqScheduler, type SchedulerEvent } from "@fadymondy/nasaq/vue";
import { ref } from "vue";

const events: SchedulerEvent[] = [
  { id: "1", title: "Design review", start: new Date(2026, 8, 29, 10, 0), end: new Date(2026, 8, 29, 11, 30), tone: "brand" },
  { id: "2", title: "Client call", start: new Date(2026, 8, 29, 10, 30), end: new Date(2026, 8, 29, 11, 0), tone: "success" },
];
const picked = ref("");
</script>

<template>
  <NqScheduler
    :events="events"
    :working-hours="{ start: 8, end: 18 }"
    @slot-select="(start, end) => (picked = `${start.toISOString()} - ${end.toISOString()}`)"
    @event-click="(event) => (picked = event.title)"
  />
  <p>{{ picked }}</p>
</template>
```

### Blade

```blade
@php
    $events = [
        ['id' => '1', 'title' => 'Design review', 'start' => '2026-09-29T10:00', 'end' => '2026-09-29T11:30', 'tone' => 'brand'],
        ['id' => '2', 'title' => 'Client call', 'start' => '2026-09-29T10:30', 'end' => '2026-09-29T11:00', 'tone' => 'success'],
    ];
@endphp
<x-nq::scheduler :events="$events" :working-hours="['start' => 8, 'end' => 18]" date="2026-09-29" today="2026-09-29" />
```

### HTML + Alpine

```html
<div data-slot="scheduler" data-view="week" dir="ltr" lang="en" x-data="nqScheduler(JSON.parse('{\u0022events\u0022:[{\u0022id\u0022:\u00221\u0022,\u0022title\u0022:\u0022Design review\u0022,\u0022start\u0022:\u00222026-09-29T10:00\u0022,\u0022end\u0022:\u00222026-09-29T11:30\u0022,\u0022tone\u0022:\u0022brand\u0022},{\u0022id\u0022:\u00222\u0022,\u0022title\u0022:\u0022Client call\u0022,\u0022start\u0022:\u00222026-09-29T10:30\u0022,\u0022end\u0022:\u00222026-09-29T11:00\u0022,\u0022tone\u0022:\u0022success\u0022}],\u0022view\u0022:\u0022week\u0022,\u0022date\u0022:\u00222026-09-29\u0022,\u0022today\u0022:\u00222026-09-29\u0022,\u0022workingHours\u0022:{\u0022start\u0022:8,\u0022end\u0022:18},\u0022slotMinutes\u0022:30,\u0022weekStartsOn\u0022:null,\u0022hour12\u0022:null,\u0022maxChips\u0022:3,\u0022locale\u0022:\u0022en\u0022,\u0022dir\u0022:\u0022ltr\u0022,\u0022tones\u0022:{\u0022neutral\u0022:\u0022border-border bg-secondary text-foreground\u0022,\u0022brand\u0022:\u0022border-nq-brand\\\/40 bg-[color-mix(in_oklab,var(--nq-brand)_14%,transparent)] text-foreground\u0022,\u0022success\u0022:\u0022border-nq-success\\\/40 bg-nq-success-soft text-nq-success-text\u0022,\u0022warning\u0022:\u0022border-nq-warning\\\/40 bg-nq-warning-soft text-nq-warning-text\u0022,\u0022danger\u0022:\u0022border-nq-danger\\\/40 bg-nq-danger-soft text-nq-danger-text\u0022,\u0022info\u0022:\u0022border-nq-info\\\/40 bg-nq-info-soft text-nq-info-text\u0022},\u0022strings\u0022:{\u0022today\u0022:\u0022Today\u0022,\u0022previous\u0022:{\u0022day\u0022:\u0022Previous day\u0022,\u0022week\u0022:\u0022Previous week\u0022,\u0022month\u0022:\u0022Previous month\u0022},\u0022next\u0022:{\u0022day\u0022:\u0022Next day\u0022,\u0022week\u0022:\u0022Next week\u0022,\u0022month\u0022:\u0022Next month\u0022},\u0022more\u0022:\u0022+{n} more\u0022,\u0022eventsOn\u0022:\u0022Events on {date}\u0022,\u0022slotsOn\u0022:\u0022Available times on {date}\u0022,\u0022noSlots\u0022:\u0022No times available on this day.\u0022}}'))" x-bind:data-view="view"
    class="flex w-full flex-col gap-3 text-body-sm text-foreground">
    <div data-slot="scheduler-toolbar" class="flex flex-wrap items-center gap-2">
        <div class="flex items-center gap-1">
            <button data-slot="button"
     type="button"                         x-bind:aria-label="previousLabel" aria-label="Previous week" x-on:click="step(-1)" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover size-control-sm p-0">
        <svg data-slot="icon" aria-hidden="true" class="rtl:-scale-x-100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m15 18-6-6 6-6"/>
</svg></button>
            <button data-slot="button"
     type="button"                         x-bind:aria-label="nextLabel" aria-label="Next week" x-on:click="step(1)" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover size-control-sm p-0">
        <svg data-slot="icon" aria-hidden="true" class="rtl:-scale-x-100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m9 18 6-6-6-6"/>
</svg></button>
            <button data-slot="button"
     type="button"                         x-on:click="goToday()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control-sm px-2.5">
        Today</button>
        </div>
        <h2 aria-live="polite" class="me-auto text-body font-semibold"><bdi x-text="title"></bdi></h2>
        <div role="group" data-slot="toggle-group" data-variant="segmented" data-orientation="horizontal"
    x-data="nqToggleGroup(JSON.parse('[\u0022week\u0022]'), false)" x-modelable="value" x-bind="root"
            aria-label="View" x-model="viewValue" class="flex w-fit max-w-full gap-0.5 rounded-control bg-secondary p-0.5">
    <button type="button" data-slot="toggle" aria-pressed="false"
     x-bind="toggle('day')"             class="inline-flex h-7 shrink-0 items-center justify-center gap-1.5 whitespace-nowrap px-3 text-label text-muted-foreground outline-none transition-colors duration-150 ease-nq hover:text-foreground [&_svg]:size-4 [&_svg]:shrink-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus data-disabled:pointer-events-none data-disabled:opacity-50 rounded-[calc(var(--radius-control)-2px)] data-pressed:bg-card data-pressed:text-foreground data-pressed:shadow-xs">Day</button>
            <button type="button" data-slot="toggle" aria-pressed="true"
     x-bind="toggle('week')"      data-pressed         class="inline-flex h-7 shrink-0 items-center justify-center gap-1.5 whitespace-nowrap px-3 text-label text-muted-foreground outline-none transition-colors duration-150 ease-nq hover:text-foreground [&_svg]:size-4 [&_svg]:shrink-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus data-disabled:pointer-events-none data-disabled:opacity-50 rounded-[calc(var(--radius-control)-2px)] data-pressed:bg-card data-pressed:text-foreground data-pressed:shadow-xs">Week</button>
            <button type="button" data-slot="toggle" aria-pressed="false"
     x-bind="toggle('month')"             class="inline-flex h-7 shrink-0 items-center justify-center gap-1.5 whitespace-nowrap px-3 text-label text-muted-foreground outline-none transition-colors duration-150 ease-nq hover:text-foreground [&_svg]:size-4 [&_svg]:shrink-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus data-disabled:pointer-events-none data-disabled:opacity-50 rounded-[calc(var(--radius-control)-2px)] data-pressed:bg-card data-pressed:text-foreground data-pressed:shadow-xs">Month</button>
</div>
    </div>
    <div x-show="view !== 'month'"  data-slot="scheduler-grid" class="overflow-auto rounded-card border border-border bg-card">
        <div class="sticky top-0 z-20 grid border-b border-border bg-card" x-bind:style="columnsStyle">
            <div></div>
            <template x-for="d in days" x-bind:key="d.key">
                <div x-bind:data-today="d.today ? '' : undefined" class="flex flex-col items-center gap-0.5 border-s border-border py-1.5">
                    <span class="text-caption text-muted-foreground" x-text="d.weekday"></span>
                    <span x-text="d.num" x-bind:class="d.today ? 'bg-primary text-primary-foreground' : ''" class="flex size-6 items-center justify-center rounded-full text-label tabular-nums"></span>
                </div>
            </template>
        </div>
        <div class="grid" x-bind:style="columnsStyle" x-on:keydown="onGridKey($event)">
            <div aria-hidden="true">
                <template x-for="h in hourRows" x-bind:key="h.m">
                    <div class="h-8 pe-2 text-end text-caption text-muted-foreground tabular-nums">
                        <span x-show="h.show" x-text="h.label" x-bind:class="h.first ? '' : '-top-2'" class="relative bg-card"></span>
                    </div>
                </template>
            </div>
            <template x-for="d in days" x-bind:key="d.key">
                <div x-bind:data-date="d.key" class="relative border-s border-border">
                    <template x-for="s in d.slots" x-bind:key="s.key">
                        <button type="button" data-slot="scheduler-slot" x-bind:data-col="d.col" x-bind:data-row="s.row" x-bind:tabindex="isFocusCell(d.col, s.row) ? 0 : -1" x-bind:aria-label="s.label"
                            x-bind:class="s.dashed ? 'border-dashed' : ''" x-on:focus="onSlotFocus(d.col, s.row)" x-on:click="pickSlot(s)"
                            class="block h-8 w-full border-b border-border transition-colors duration-150 ease-nq hover:bg-nq-hover outline-none focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-nq-focus"></button>
                    </template>
                    <template x-for="ev in d.events" x-bind:key="ev.id">
                        <button type="button" data-slot="scheduler-event" x-bind:data-tone="ev.tone" x-bind:aria-label="ev.label" x-bind:class="ev.toneClass" x-bind:style="ev.style" x-on:click="pickEvent(ev)"
                            class="absolute z-10 flex min-h-0 flex-col items-start overflow-hidden rounded-[4px] border px-1.5 py-0.5 text-start text-caption outline-none focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-nq-focus">
                            <span class="w-full truncate font-medium" x-text="ev.title"></span>
                            <span x-show="ev.tall" class="w-full truncate tabular-nums opacity-80"><bdi x-text="ev.range"></bdi></span>
                        </button>
                    </template>
                </div>
            </template>
        </div>
    </div>
    <div x-show="view === 'month'"  style="display: none"  data-slot="scheduler-grid" class="overflow-hidden rounded-card border border-border bg-card">
        <div class="grid grid-cols-7 border-b border-border">
            <template x-for="(w, i) in weekdayNames" x-bind:key="i">
                <div class="px-2 py-1.5 text-caption text-muted-foreground" x-text="w"></div>
            </template>
        </div>
        <template x-for="(row, r) in monthRows" x-bind:key="r">
            <div class="grid grid-cols-7">
                <template x-for="day in row" x-bind:key="day.key">
                    <div data-slot="scheduler-day" x-bind:data-date="day.key" x-bind:data-outside="day.outside ? '' : undefined" x-bind:data-today="day.today ? '' : undefined"
                        x-bind:class="day.outside ? 'bg-secondary/40' : ''" class="relative flex min-h-24 min-w-0 flex-col gap-0.5 border-b border-s border-border p-1">
                        <button type="button" x-bind:aria-label="day.label" x-text="day.num" x-on:click="fire('slot-select', { start: day.start, end: day.end })"
                            x-bind:class="(day.outside ? 'text-muted-foreground ' : '') + (day.today ? 'bg-primary text-primary-foreground hover:bg-primary' : '')"
                            class="flex size-6 items-center justify-center self-start rounded-full text-label tabular-nums hover:bg-nq-hover outline-none focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-nq-focus"></button>
                        <template x-for="e in day.chips" x-bind:key="e.id">
                            <button type="button" data-slot="scheduler-chip" x-bind:data-tone="e.tone" x-bind:aria-label="e.label" x-bind:class="e.toneClass" x-on:click="pickEvent(e)"
                                class="flex w-full min-w-0 items-center gap-1 rounded-[4px] border px-1.5 py-0.5 text-start text-caption outline-none focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-nq-focus">
                                <span class="shrink-0 tabular-nums opacity-80"><bdi x-text="e.time"></bdi></span>
                                <span class="truncate font-medium" x-text="e.title"></span>
                            </button>
                        </template>
                        <template x-if="day.hidden > 0">
                            <div class="contents">
                                <button type="button" x-bind:aria-expanded="moreKey === day.key ? 'true' : 'false'" x-on:click="toggleMore(day.key)"
                                    class="rounded-[4px] px-1.5 py-0.5 text-start text-caption font-medium text-muted-foreground hover:bg-nq-hover hover:text-foreground outline-none focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-nq-focus"><bdi x-text="day.more"></bdi></button>
                                <div x-show="moreKey === day.key" style="display: none" role="group" x-bind:aria-label="day.eventsOn" x-on:click.outside="moreKey === day.key && (moreKey = null)" x-on:keydown.escape="moreKey = null"
                                    class="absolute start-0 top-full z-30 flex w-64 flex-col gap-1 rounded-floating border border-border bg-popover p-3 text-popover-foreground shadow-floating">
                                    <p class="text-label font-semibold" x-text="day.eventsOn"></p>
                                    <template x-for="e in day.all" x-bind:key="e.id">
                                        <button type="button" data-slot="scheduler-chip" x-bind:data-tone="e.tone" x-bind:aria-label="e.label" x-bind:class="e.toneClass" x-on:click="pickEvent(e)"
                                            class="flex w-full min-w-0 items-center gap-1 rounded-[4px] border px-1.5 py-0.5 text-start text-caption outline-none focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-nq-focus">
                                            <span class="shrink-0 tabular-nums opacity-80"><bdi x-text="e.time"></bdi></span>
                                            <span class="truncate font-medium" x-text="e.title"></span>
                                        </button>
                                    </template>
                                </div>
                            </div>
                        </template>
                    </div>
                </template>
            </div>
        </template>
    </div>
</div>
```
