CalendarView

CalendarView renderiza agendas de eventos no horário local como visualização de dia, semana ou mês. Escolha-o quando pessoas precisam inspecionar posição de eventos e disponibilidade; escolha Calendar quando a tarefa é selecionar datas, não ler uma agenda.

Exemplos

Grade semanal e de horas

Chips de evento são medidos em relação ao contêiner de scroll real, para que um popover opcional seja posicionado ao lado do chip. Os cinco tipos usam cor e forma distintas: sessão sólida, sessão de programa sólida, pendente tracejado, bloqueio hachurado e externo contornado.

Aug 3 – Aug 9, 2026
Mon3Tue4Wed5Thu6Fri7Sat8Sun9
07:0008:0009:0010:0011:0012:0013:0014:0015:0016:0017:0018:0019:0020:00

Visão geral mensal

Células mensais mostram até três eventos e depois um indicador de quantidade restante. Selecionar um dia muda para sua visualização diária, mantendo a data ancorada no horário local.

August 2026
MonTueWedThuFriSatSun

Quando usar

Use CalendarView para navegar por eventos locais concretos, janelas de disponibilidade e espaço livre na grade de horas.

Limitações: eventos sobrepostos no mesmo dia ficam empilhados em largura total; CalendarView não os organiza em colunas. Um evento que cruza meia-noite é renderizado apenas no seu dia local de início. Arrastar eventos e a navegação de grade por teclado pertencem à aplicação.

Prefira outro componente quando:

  • A pessoa seleciona uma data ou intervalo de datas — use Calendar ou DatePicker.
  • Pessoas precisam reservar um instante disponível — use SlotPicker.
  • A tarefa é definir disponibilidade recorrente — use WeeklyScheduleEditor.

Acessibilidade

  • Anterior, Hoje e próximo são botões nativos nomeados. Dia, Semana e Mês são botões em um grupo nomeado e expõem a visualização ativa com aria-pressed.
  • Chips de evento são botões nativos com data, hora e título localizados no nome acessível. Um popover opcional é um diálogo nomeado; ele fecha com Escape, interação de ponteiro fora ou resize para não manter medições antigas.
  • Clicar no espaço vazio da grade de horas é deliberadamente apenas para ponteiro e chama onSlotCreate com um Date local ajustado. Chips de evento e todos os controles da toolbar permanecem acessíveis por teclado.
  • CalendarView não emite landmark de página nem heading, portanto pode ficar dentro do outline do documento da página consumidora. Use labels e locale para traduzir controles e formatação de datas.

API e código

Blade: Adiado por decisão — CalendarView ainda não tem equivalente em Blade.

NomeTipoObrigatóriaDescrição
view'day' | 'week' | 'month'—Controlled displayed view. Default: `"week"`.
defaultView'day' | 'week' | 'month'—Initial displayed view when uncontrolled. Default: `"week"`.
onViewChange(view: 'day' | 'week' | 'month') => void—Called after the displayed view changes.
datestring | Date—Controlled local period anchor as a Date or `YYYY-MM-DD` string.
defaultDatestring | Date—Initial local period anchor as a Date or `YYYY-MM-DD` string.
onDateChange(isoDate: string) => void—Called with the local `YYYY-MM-DD` anchor after it changes.
eventsCalendarViewEvent[]—Events rendered in the visible local day, week, or month.
availabilityRecord<number, CalendarViewAvailability[]>—Weekly local availability windows keyed by `0` (Sunday) through `6` (Saturday).
startHournumber—First visible hour in day and week grids. Default: `7`.
endHournumber—Hour at which the day and week grids end. Default: `21`.
weekStartsOn0 | 1 | 2 | 3 | 4 | 5 | 6—First weekday, where `0` is Sunday and `6` is Saturday. Default: `1`.
onEventOpen(event: CalendarViewEvent) => void—Called when an event chip is opened.
renderEventPopover(event: CalendarViewEvent, close: () => void) => ReactNode—Renders the optional event-summary popover. Receives a close callback.
onSlotCreate(start: Date) => void—Called with a snapped local Date after an empty hour-grid area is clicked.
slotStepnumber—Local-minute increment for empty-grid clicks. Default: `30`.
toolbarActionsReactNode—Extra content rendered at the end of the toolbar.
localestring—BCP 47 locale used for all weekday and month formatting. Default: `"en-US"`.
labelsCalendarViewLabels—Translatable visible and accessible labels, merged over English defaults.

x-data="lyraCalendarView({ … })"

OpçãoTipoObrigatóriaDescrição
defaultViewView—Initial view, default `week`. Can be synchronized with `x-modelable="view"`.
defaultDatestring | Date—Initial local anchor, default today. Can be synchronized with `x-modelable="date"`.
eventsLyraCalendarViewEvent[]—
availabilityRecord<number, LyraCalendarViewAvailability[]>—
startHournumber—
endHournumber—
weekStartsOn0 | 1 | 2 | 3 | 4 | 5 | 6—
slotStepnumber—
localestring—
labelsLyraCalendarViewLabels—

HTML canônico sem runtime de comportamento (forneça o posicionamento de eventos e as interações):

html
<div class="lyra-calview">
  <div class="lyra-calview__toolbar">
    <button class="lyra-calview__nav" type="button" aria-label="Período anterior">‹</button>
    <span class="lyra-calview__title">agosto de 2026</span>
    <span class="lyra-calview__seg" role="group" aria-label="Visualização do calendário"
      ><button type="button" aria-pressed="true">Semana</button></span
    >
  </div>
  <div class="lyra-calview__grid">
    <div class="lyra-calview__col">
      <button class="lyra-calview__evt lyra-calview__evt--pending" type="button">
        10:00–11:00 Aguardando aprovação
      </button>
    </div>
  </div>
</div>

lyraCalendarView anima o HTML servido. Passe events, availability por dia da semana, startHour, endHour, weekStartsOn, slotStep, locale e labels nas opções. view (day, week ou month) e date (ISO local) são estados editáveis. defaultView e defaultDate definem o estado inicial. Use x-modelable="date" na raiz para controlar a data externamente; um escopo Alpine aninhado pode ligar view com x-modelable="view". Mudanças externas dos modelos não disparam eventos de interação.

Os eventos na raiz carregam os mesmos valores dos callbacks React: lyra:view-change leva a visualização, lyra:change leva YYYY-MM-DD local, lyra:event-open leva o objeto do evento e lyra:slot-create leva um Date local ajustado a slotStep. Ações da toolbar e conteúdo do popover são markup da aplicação. O popover é posicionado pelo chip e fecha com pointerdown fora, Escape ou resize. Inclua .lyra-calview__pop quando precisar dele.

html
<div
  class="lyra-calview"
  x-data="lyraCalendarView({
  defaultDate: '2026-08-12', locale: 'pt-BR', events: calendarEvents,
  availability: { 3: [{ start: '08:00', end: '17:00' }] },
  labels: {
    previous: 'Período anterior',
    today: 'Hoje',
    next: 'Próximo período',
    view: 'Visualização do calendário',
    day: 'Dia',
    week: 'Semana',
    month: 'Mês',
  }
})"
  x-modelable="date"
  x-model="selectedDate"
  @lyra:event-open="showEvent($event.detail)"
  @lyra:slot-create="createAt($event.detail)"
>
  <div class="lyra-calview__toolbar">
    <button
      class="lyra-calview__nav"
      type="button"
      :aria-label="label('previous')"
      @click="navigate(-1)"
    >
      ‹
    </button>
    <button
      class="lyra-calview__nav"
      type="button"
      :aria-label="label('today')"
      @click="setDate(new Date())"
    >
      ●
    </button>
    <button
      class="lyra-calview__nav"
      type="button"
      :aria-label="label('next')"
      @click="navigate(1)"
    >
      ›
    </button>
    <span class="lyra-calview__title" x-text="title()"></span>
    <span class="lyra-calview__seg" role="group" :aria-label="label('view')">
      <button
        type="button"
        :aria-pressed="view === 'day'"
        @click="setView('day')"
        x-text="label('day')"
      ></button>
      <button
        type="button"
        :aria-pressed="view === 'week'"
        @click="setView('week')"
        x-text="label('week')"
      ></button>
      <button
        type="button"
        :aria-pressed="view === 'month'"
        @click="setView('month')"
        x-text="label('month')"
      ></button>
    </span>
    <!-- Place application toolbar actions here. -->
  </div>
  <div x-show="view !== 'month'">
    <div class="lyra-calview__head" :style="gridStyle()">
      <span></span>
      <template x-for="day in days()" :key="day.key"
        ><span class="lyra-calview__head-cell" :class="dayClass(day)"
          ><span x-text="weekdayLabel(day)"></span
          ><strong x-text="day.date.getDate()"></strong></span
      ></template>
    </div>
    <div class="lyra-calview__scroll" tabindex="0" aria-label="Horas do calendário">
      <div class="lyra-calview__grid" :style="gridStyle()">
        <div class="lyra-calview__ruler" :style="{ height: gridHeight() + 'px' }">
          <template x-for="hour in hours()" :key="hour"
            ><span
              class="lyra-calview__hour"
              :style="hourStyle(hour)"
              x-text="String(hour).padStart(2, '0') + ':00'"
            ></span
          ></template>
        </div>
        <template x-for="day in days()" :key="day.key"
          ><div
            class="lyra-calview__col"
            :style="{ height: gridHeight() + 'px', backgroundSize: '100% 48px' }"
            @click="createSlot($event, day)"
          >
            <template x-for="(window, index) in day.windows" :key="index"
              ><span class="lyra-calview__avail" :style="availabilityStyle(window)"></span
            ></template>
            <span
              class="lyra-calview__now"
              x-show="nowStyle(day)"
              :style="nowStyle(day) || {}"
            ></span>
            <template x-for="item in day.events" :key="item.id"
              ><button
                class="lyra-calview__evt"
                :class="{ ['lyra-calview__evt--' + item.kind]: true }"
                :style="eventStyle(item)"
                type="button"
                :aria-label="eventLabel(item)"
                @click="openEvent($event, item)"
              >
                <span
                  class="lyra-calview__evt-time"
                  x-text="time(item.startDate) + '–' + time(item.endDate)"
                ></span>
                <span
                  class="lyra-calview__evt-title"
                  x-show="parseFloat(eventStyle(item).height) >= 34"
                  x-text="item.title"
                ></span></button
            ></template></div
        ></template>
      </div>
      <div
        class="lyra-calview__pop"
        role="dialog"
        :aria-label="popover?.event.title"
        x-show="popover"
        :style="popover ? { top: popover.top + 'px', left: popover.left + 'px' } : {}"
      >
        <!-- Render popover?.event with application markup. -->
        <button type="button" @click="closePopover()">Fechar</button>
      </div>
    </div>
  </div>
  <div class="lyra-calview__mgrid" x-show="view === 'month'">
    <template x-for="(name, index) in weekdays()" :key="index"
      ><span class="lyra-calview__head-cell" x-text="name"></span
    ></template>
    <template x-for="day in days()" :key="day.key"
      ><button
        class="lyra-calview__mcell"
        type="button"
        :class="monthClass(day)"
        @click="selectMonthDay(day)"
      >
        <span class="lyra-calview__mday" x-text="day.date.getDate()"></span>
        <template x-for="item in monthEvents(day)" :key="item.id"
          ><span
            class="lyra-calview__mevt"
            :class="{ ['lyra-calview__evt--' + item.kind]: true }"
            x-text="time(item.startDate) + ' ' + item.title"
          ></span
        ></template>
        <span
          class="lyra-calview__more"
          x-show="monthMore(day)"
          x-text="'+' + monthMore(day)"
        ></span></button
    ></template>
  </div>
</div>