Shadwire
Componentes

Date Picker

Uma receita: Popover + Calendar. Não é um componente novo — não existe Ui::DatePickerComponent, você compõe as partes.

Instalação

O Shadwire segue o modelo open-code do shadcn/ui: o código-fonte do componente é copiado para a sua aplicação e passa a ser seu. Instale com a CLI:

shadwire add date-picker --yes

O item date-picker do registry instala estes arquivos:

  • app/components/ui_component.rb
  • app/javascript/controllers/ui_date_picker_controller.js
  • vendor/shadwire/shadwire.css

Este componente usa Stimulus: a aplicação precisa de importmap-rails e stimulus-rails com carregamento automático de controllers.

Ainda não instalou a CLI? gem install shadwire e depois shadwire init.

Uso

O ui_calendar é quem guarda o valor: ele grava a data escolhida em um input escondido (name:) e anuncia ui-calendar:change. O controller ui-date-picker ouve esse evento para escrever a data formatada no rótulo do gatilho e fechar o popover — nada além de apresentação. Sem ele o picker continua funcionando; só não conta ao usuário o que foi escolhido.

<div data-controller="ui-date-picker" data-ui-date-picker-format-value="long">
  <%= ui_popover do %>
    <%= ui_popover_trigger(variant: :outline, class: "w-[212px] justify-between font-normal") do %>
      <span data-ui-date-picker-target="label" data-empty="true"
            class="data-[empty=true]:text-muted-foreground">Selecione uma data</span>
      <%= ui_icon("chevron-down", class: "opacity-50") %>
    <% end %>
    <%= ui_popover_content(align: :start, class: "w-auto! p-0!") do %>

O popover precisa de w-auto! p-0!: o padrão é w-72 com preenchimento, estreito demais para a grade do calendário. O ! vai no fim da classe — é a sintaxe do Tailwind v4.

Composição

data-controller="ui-date-picker"   ligação: formata o rótulo, lê o que é digitado, fecha o popover
`-- Popover
    |-- Popover::Trigger           o campo visível: um Button ou um InputGroup
    |   `-- [data-ui-date-picker-target="label" | "input"]
    `-- Popover::Content (w-auto! p-0!)
        `-- Calendar               name:/end_name: gravam o valor no formulário

Exemplos

Os oito arranjos que o shadcn/ui documenta, traduzidos para ViewComponent + Stimulus.

Padrão

Botão que abre o calendário em um popover e passa a exibir a data escolhida.

<%# Date Picker = Popover + Calendar (uma receita, não um componente novo).
    O controller ui-date-picker escreve a data escolhida no rótulo e fecha o popover. %>
<div data-controller="ui-date-picker" data-ui-date-picker-format-value="long">
  <%= ui_popover do %>
    <%= ui_popover_trigger(variant: :outline, class: "w-[212px] justify-between font-normal") do %>
      <span data-ui-date-picker-target="label" data-empty="true" class="data-[empty=true]:text-muted-foreground">
        Selecione uma data
      </span>

Com rótulo

O mesmo gatilho dentro de um Field, associado ao rótulo por id.

<%# O gatilho é um botão comum, então `for:`/`id:` o associam ao rótulo do campo. %>
<%= ui_field(class: "w-48", data: { controller: "ui-date-picker" }) do %>
  <%= ui_field_label(for: "checkin") { "Data" } %>
  <%= ui_popover do %>
    <%= ui_popover_trigger(id: "checkin", variant: :outline, class: "justify-start font-normal") do %>
      <span data-ui-date-picker-target="label" data-empty="true" class="data-[empty=true]:text-muted-foreground">
        Selecione uma data
      </span>

Intervalo

Duas datas em um gatilho só. O popover fecha quando a segunda ponta entra, não na primeira.

<%# `mode: :range` grava as duas pontas: `name:` é o início, `end_name:` é o fim. %>
<%= ui_field(class: "w-72", data: { controller: "ui-date-picker", ui_date_picker_format_value: "medium" }) do %>
  <%= ui_field_label(for: "stay") { "Período da estadia" } %>
  <%= ui_popover do %>
    <%= ui_popover_trigger(id: "stay", variant: :outline, class: "justify-start gap-2 px-2.5 font-normal") do %>
      <%= ui_icon("calendar", class: "opacity-50") %>
      <span data-ui-date-picker-target="label" data-empty="true" class="truncate data-[empty=true]:text-muted-foreground">
        Selecione o período

Data de nascimento

Legenda com selects de mês e ano e max: hoje — ninguém nasceu semana que vem.

<%# `caption_layout: :dropdown` troca o rótulo do mês por selects de mês e ano —
    indispensável quando o alvo está a décadas de distância. `max:` limita o
    calendário e, de quebra, o intervalo oferecido no select de ano. %>
<%= ui_field(class: "w-52", data: { controller: "ui-date-picker", ui_date_picker_format_value: "short" }) do %>
  <%= ui_field_label(for: "born-on") { "Data de nascimento" } %>
  <%= ui_popover do %>
    <%= ui_popover_trigger(id: "born-on", variant: :outline, class: "justify-between font-normal") do %>
      <span data-ui-date-picker-target="label" data-empty="true" class="data-[empty=true]:text-muted-foreground">

Com campo de texto

Digite a data ou escolha no calendário: o texto vira data e a data volta formatada. ↓ abre o popover.

<%# Campo de texto + calendário: o que for digitado é interpretado e leva o
    calendário até a data; o que for escolhido no calendário volta formatado
    para o campo. ↓ no campo abre o popover. %>
<%= ui_field(class: "w-60", data: { controller: "ui-date-picker" }) do %>
  <%= ui_field_label(for: "subscribed-on") { "Data da assinatura" } %>
  <%= ui_input_group do %>
    <%= ui_input_group_input(
          id: "subscribed-on",

Data e hora

O picker resolve o dia; um input type="time" nativo resolve a hora.

<%# Data e hora são dois campos: o picker cuida do dia, um `input type="time"`
    nativo cuida da hora. O indicador nativo do navegador fica escondido para
    não competir com o ícone do campo. %>
<div class="flex max-w-xs gap-4">
  <%= ui_field(data: { controller: "ui-date-picker" }) do %>
    <%= ui_field_label(for: "meeting-on") { "Data" } %>
    <%= ui_popover do %>
      <%= ui_popover_trigger(id: "meeting-on", variant: :outline, class: "w-36 justify-between font-normal") do %>

Linguagem natural

Aceita "hoje", "amanhã", "em 2 semanas", "próxima sexta". A tabela de frases mora no controller — é código seu.

Sua publicação sai em .

<%# `natural_language_value: true` liga o vocabulário relativo do controller:
    "hoje", "amanhã", "em 2 semanas", "próxima sexta" — além dos formatos
    01/08/2026, 2026-08-01 e "1 de agosto de 2026". A tabela de frases é código
    seu: edite o controller para atender o idioma que você publica. %>
<%= ui_field(class: "max-w-xs", data: { controller: "ui-date-picker", ui_date_picker_natural_language_value: "true" }) do %>
  <%= ui_field_label(for: "publish-on") { "Agendar publicação" } %>
  <%= ui_input_group do %>
    <%= ui_input_group_input(

RTL

dir: :rtl espelha a grade, as setas e as teclas ←/→; month_names:/day_names: traduzem a legenda.

<%# `dir: :rtl` espelha a grade, as setas de navegação e as teclas ←/→.
    `month_names:`/`day_names:` traduzem a legenda e o cabeçalho das colunas;
    `lang` e `locale-value` cuidam da data formatada e dos rótulos ARIA. %>
<% month_names = %w[يناير فبراير مارس أبريل مايو يونيو يوليو أغسطس سبتمبر أكتوبر نوفمبر ديسمبر] %>
<% day_names = %w[أحد إثنين ثلاثاء أربعاء خميس جمعة سبت] %>

<div dir="rtl" lang="ar" data-controller="ui-date-picker" data-ui-date-picker-locale-value="ar-EG">
  <%= ui_popover do %>

Referência da API

O que o calendário aceita está em Calendar. Abaixo, só o controller de ligação — atributos data-* no elemento que envolve o popover e o campo.

Atributo Padrão Descrição
format-value long full, long, medium, short ou iso.
locale-value BCP 47 (pt-BR, ar-EG). Vazio segue o idioma do navegador.
separator-value  –  O que separa as duas pontas de um intervalo no rótulo.
date-order-value dmy Como ler 01/06/2026 ao digitar. mdy para o formato americano.
close-on-select-value true Fecha o popover ao escolher. Em mode: :range, só quando a segunda data entra.
natural-language-value false Aceita frases relativas (“amanhã”, “em 2 semanas”, “próxima sexta”) no campo de texto.
target="label" Elemento cujo texto vira a data escolhida; ganha data-empty para o estado vazio.
target="input" Campo de texto editável: o que for digitado é interpretado, abre o popover.
target="preview" Eco opcional da data já interpretada, para uma linha de apoio abaixo do campo.