@rendra-ui/web 2.3.0 → 2.5.0
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.
- package/README.md +107 -61
- package/bin/rendra.mjs +19 -7
- package/dist/base.css +33 -1
- package/dist/{button-DWhICtIG.js → button-BzJgR2gH.js} +86 -21
- package/dist/button-BzJgR2gH.js.map +1 -0
- package/dist/chart.js +61 -15
- package/dist/chart.js.map +1 -1
- package/dist/cli/auditar.js +66 -16
- package/dist/cli/auditar.js.map +1 -1
- package/dist/cli/codigos.js +2 -2
- package/dist/cli/executar-auditar.js +634 -0
- package/dist/cli/executar-auditar.js.map +1 -0
- package/dist/cli/help.js +7 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/trocar.js +2 -2
- package/dist/cli/typescript-loader.js +1 -1
- package/dist/{cn-DDZQKUVV.js → cn-Cw7M5kEP.js} +2 -2
- package/dist/{cn-DDZQKUVV.js.map → cn-Cw7M5kEP.js.map} +1 -1
- package/dist/{components-Cq_PmN4y.js → components-DkpspH9h.js} +21 -4
- package/dist/components-DkpspH9h.js.map +1 -0
- package/dist/components.css +274 -32
- package/dist/document-viewer.js +6 -6
- package/dist/document-viewer.js.map +1 -1
- package/dist/{empty-state-C491qfzf.js → empty-state-Cdb1vkMT.js} +81 -37
- package/dist/empty-state-Cdb1vkMT.js.map +1 -0
- package/dist/features-max--5VEt6M7.js +3 -0
- package/dist/index.js +1170 -390
- package/dist/index.js.map +1 -1
- package/dist/motion-context-5BRIQAJm.js +107 -0
- package/dist/motion-context-5BRIQAJm.js.map +1 -0
- package/dist/{rendra-provider-BRtk5Qa1.js → rendra-provider-CRe0XdOU.js} +9 -5
- package/dist/rendra-provider-CRe0XdOU.js.map +1 -0
- package/dist/rich-text-editor.js +4 -4
- package/dist/router-bridge.js +5 -4
- package/dist/router-bridge.js.map +1 -1
- package/dist/tokens.css +73 -15
- package/dist/tooltip-BAMSaGbS.js +224 -0
- package/dist/tooltip-BAMSaGbS.js.map +1 -0
- package/dist/types/components/app-shell/app-shell.d.ts +22 -2
- package/dist/types/components/app-shell/shell-context.d.ts +7 -1
- package/dist/types/components/app-shell/types.d.ts +23 -0
- package/dist/types/components/layout/page-header.d.ts +17 -1
- package/dist/types/components/rendra-provider.d.ts +7 -1
- package/dist/types/components/rendra-router-bridge.d.ts +3 -1
- package/dist/types/components/ui/accordion.d.ts +1 -1
- package/dist/types/components/ui/badge.d.ts +1 -1
- package/dist/types/components/ui/brand-feedback-icon.d.ts +20 -3
- package/dist/types/components/ui/button-group.d.ts +8 -1
- package/dist/types/components/ui/button.d.ts +1 -1
- package/dist/types/components/ui/dropdown-menu.d.ts +5 -5
- package/dist/types/components/ui/empty-state.d.ts +4 -0
- package/dist/types/components/ui/error-page.d.ts +6 -1
- package/dist/types/components/ui/modal.d.ts +4 -0
- package/dist/types/components/ui/picker-panel.d.ts +1 -1
- package/dist/types/components/ui/popover.d.ts +3 -3
- package/dist/types/components/ui/radio-group.d.ts +1 -1
- package/dist/types/components/ui/skeleton.d.ts +5 -1
- package/dist/types/components/ui/stat-card.d.ts +1 -1
- package/dist/types/components/ui/table.d.ts +0 -6
- package/dist/types/components/ui/tooltip.d.ts +1 -1
- package/dist/types/components/ui/wizard.d.ts +14 -1
- package/dist/types/hooks/use-reduced-motion.d.ts +3 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/lib/initials.d.ts +2 -0
- package/dist/types/lib/motion/count.d.ts +17 -0
- package/dist/types/lib/motion/features-max.d.ts +1 -0
- package/dist/types/lib/motion/index.d.ts +5 -0
- package/dist/types/lib/motion/motion-context.d.ts +43 -0
- package/dist/types/lib/motion/native-events.d.ts +16 -0
- package/dist/types/lib/motion/presets.d.ts +92 -0
- package/dist/types/lib/motion/reorder.d.ts +7 -0
- package/dist/types/lib/motion/tokens.d.ts +66 -0
- package/dist/types/lib/motion/use-motion-presets.d.ts +8 -0
- package/dist/{use-breakpoint-C3ivDt2R.js → use-breakpoint-O9eqFmsF.js} +2 -2
- package/dist/{use-breakpoint-C3ivDt2R.js.map → use-breakpoint-O9eqFmsF.js.map} +1 -1
- package/dist/use-motion-presets-BrkgL8lG.js +401 -0
- package/dist/use-motion-presets-BrkgL8lG.js.map +1 -0
- package/dist/widget-grid.js +3 -3
- package/package.json +4 -1
- package/dist/button-DWhICtIG.js.map +0 -1
- package/dist/components-Cq_PmN4y.js.map +0 -1
- package/dist/empty-state-C491qfzf.js.map +0 -1
- package/dist/rendra-provider-BRtk5Qa1.js.map +0 -1
- package/dist/tooltip-DsNlnBSY.js +0 -105
- package/dist/tooltip-DsNlnBSY.js.map +0 -1
package/README.md
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
**Veja funcionando, sem instalar nada:** [página de apresentação](https://bsmagalhaes.github.io/rendra-ui-web/) · [demo do app](https://bsmagalhaes.github.io/rendra-ui-web/demo/) · [galeria com códigos de modelo](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/) · [Storybook](https://bsmagalhaes.github.io/rendra-ui-web/storybook/) · [vitrine de componentes](https://bsmagalhaes.github.io/rendra-ui-web/demo/componentes/) · [atendimento](https://bsmagalhaes.github.io/rendra-ui-web/demo/atendimento/) · [agenda](https://bsmagalhaes.github.io/rendra-ui-web/demo/agenda/) · [kanban](https://bsmagalhaes.github.io/rendra-ui-web/demo/kanban/). No demo, o menu do avatar troca modelo, paleta, modo e layout ao vivo.
|
|
8
8
|
|
|
9
|
-
| Atendimento omnichannel
|
|
10
|
-
|
|
|
11
|
-
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=safira-atendimento) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=safira-dashboard-ajuste) |
|
|
12
12
|
|
|
13
|
-
| Calendário e agenda
|
|
14
|
-
|
|
|
15
|
-
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=safira-calendario) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=aurora-kanban) |
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -64,44 +64,44 @@ Cada template tem formato, fonte, símbolo e paleta próprios.
|
|
|
64
64
|
|
|
65
65
|
**Rendra Safira**: quadrado, como uma pedra lapidada. Azul #0B6FE0, verde #98D10A e fonte Poppins.
|
|
66
66
|
|
|
67
|
-
| Painel
|
|
68
|
-
|
|
|
69
|
-
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=safira-painel) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=safira-clientes) |
|
|
70
70
|
|
|
71
71
|
**Rendra Equilíbrio**: o meio-termo, nem quadrado nem 100% arredondado. Violeta, ciano e fonte DM Sans.
|
|
72
72
|
|
|
73
|
-
| Painel
|
|
74
|
-
|
|
|
75
|
-
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=equilibrio-painel) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=equilibrio-clientes) |
|
|
76
76
|
|
|
77
77
|
**Rendra Aurora**: 100% arredondado, como um novo dia. Verde-petróleo, laranja e fonte Inter.
|
|
78
78
|
|
|
79
|
-
| Painel
|
|
80
|
-
|
|
|
81
|
-
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=aurora-painel) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=aurora-clientes) |
|
|
82
82
|
|
|
83
83
|
**Paleta Ardósia**: combinável com qualquer modelo. Azul-ardósia #2E414D e laranja #EA600D; com texto branco pedido, o gerador escurece o botão laranja para #C5510B, o tom mais próximo que passa AA (aqui no modelo Equilíbrio).
|
|
84
84
|
|
|
85
|
-
| Painel
|
|
86
|
-
|
|
|
87
|
-
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=ardosia-painel) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=ardosia-clientes) |
|
|
88
88
|
|
|
89
89
|
### Calendário, agenda e kanban
|
|
90
90
|
|
|
91
|
-
| Agenda da semana (T2-C2)
|
|
92
|
-
|
|
|
93
|
-
| [ | Kanban no celular (T1-C1) |
|
|
92
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
93
|
+
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=equilibrio-agenda) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=safira-kanban-mobile) |
|
|
94
94
|
|
|
95
95
|
<details>
|
|
96
96
|
<summary><strong>Tipos de menu: M1 a M6</strong> (clique para abrir aqui mesmo)</summary>
|
|
97
97
|
|
|
98
|
-
| M1: lateral recolhida, segunda barra
|
|
99
|
-
|
|
|
100
|
-
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=menu-m1) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=menu-m2) |
|
|
101
|
+
| **M3: lateral expandida, segunda barra** | **M4: lateral expandida, submenu na sidebar** |
|
|
102
|
+
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=menu-m3) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=menu-m4) |
|
|
103
|
+
| **M5: superior, lista suspensa** | **M6: superior, mega menu** |
|
|
104
|
+
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=menu-m5) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=menu-m6) |
|
|
105
105
|
|
|
106
106
|
</details>
|
|
107
107
|
|
|
@@ -110,11 +110,11 @@ Cada template tem formato, fonte, símbolo e paleta próprios.
|
|
|
110
110
|
|
|
111
111
|
O modelo (formato, fonte e símbolo) e a paleta (cores, degradês e sidebar) são camadas independentes: qualquer modelo aceita qualquer paleta. São 12 combinações (3 modelos × 4 paletas), todas na mesma tela de detalhe do cliente.
|
|
112
112
|
|
|
113
|
-
| Modelo \ Paleta | Safira
|
|
114
|
-
| --------------------------- |
|
|
115
|
-
| **Safira** (quadrado) | [ | [ | [ | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=matriz-safira-safira) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=matriz-safira-equilibrio) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=matriz-safira-aurora) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=matriz-safira-ardosia) |
|
|
116
|
+
| **Equilíbrio** (meio-termo) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=matriz-equilibrio-safira) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=matriz-equilibrio-equilibrio) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=matriz-equilibrio-aurora) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=matriz-equilibrio-ardosia) |
|
|
117
|
+
| **Aurora** (arredondado) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=matriz-aurora-safira) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=matriz-aurora-equilibrio) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=matriz-aurora-aurora) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=matriz-aurora-ardosia) |
|
|
118
118
|
|
|
119
119
|
</details>
|
|
120
120
|
|
|
@@ -123,26 +123,26 @@ O modelo (formato, fonte e símbolo) e a paleta (cores, degradês e sidebar) sã
|
|
|
123
123
|
|
|
124
124
|
Mesmo componente, mesma API: no celular a tabela vira cards, a sidebar vira gaveta e a navegação ganha uma barra inferior.
|
|
125
125
|
|
|
126
|
-
| Safira
|
|
127
|
-
|
|
|
128
|
-
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=safira-mobile) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=equilibrio-mobile) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=aurora-mobile) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=equilibrio-mobile-tabela) |
|
|
129
129
|
|
|
130
130
|
</details>
|
|
131
131
|
|
|
132
132
|
<details>
|
|
133
133
|
<summary><strong>Modo escuro e outras telas</strong> (clique para abrir aqui mesmo)</summary>
|
|
134
134
|
|
|
135
|
-
| Modo escuro (Safira)
|
|
136
|
-
|
|
|
137
|
-
| [ | Modo escuro (Equilíbrio) |
|
|
136
|
+
| --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
137
|
+
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=safira-escuro) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=equilibrio-escuro) |
|
|
138
138
|
|
|
139
|
-
| Drawer com rodapé fixo (30/70)
|
|
140
|
-
|
|
|
141
|
-
| [ | Mega menu no menu superior |
|
|
140
|
+
| --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
141
|
+
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=safira-drawer) | [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=aurora-mega-menu) |
|
|
142
142
|
|
|
143
|
-
| Login (Aurora)
|
|
144
|
-
|
|
|
145
|
-
| [ |
|
|
144
|
+
| ------------------------------------------------------------------------------------------------------------------- |
|
|
145
|
+
| [](https://bsmagalhaes.github.io/rendra-ui-web/demo/galeria/?imagem=aurora-login) |
|
|
146
146
|
|
|
147
147
|
</details>
|
|
148
148
|
|
|
@@ -170,18 +170,18 @@ A marca fica isolada: as 4 cores e o degradê em `src/brand/palettes.ts`, o mode
|
|
|
170
170
|
|
|
171
171
|
Todos em `src/components/ui`, um por finalidade, com o ícone de feedback da marca onde houver feedback.
|
|
172
172
|
|
|
173
|
-
| Grupo | Componentes
|
|
174
|
-
| ---------------- |
|
|
175
|
-
| **Ações** | Button (6 variantes, tamanhos, ícone, só ícone, carregando, largura total), ButtonGroup (agrupado ou segmentado), DropdownMenu e ActionBar (regra 100%, 30/70 e menu)
|
|
176
|
-
| **Formulário** | Input (máscaras de CPF, CNPJ, CPF ou CNPJ automático, telefone com seletor de DDI embutido e +55 por padrão, CEP, data, hora, moeda e percentual, com ícone, limpar e senha), Textarea com contador, Select (busca, múltiplo com chips, selecionar todos, contador, criar opção, busca remota), Checkbox com indeterminado e grupo, Radio em lista e em cards, Switch, DatePicker (data, período e horário, em pt-BR), Slider, Upload com arrastar e soltar e progresso, OtpInput (2FA), Field, Form, FormField e FormSection com React Hook Form e Zod, e validadores de CPF, CNPJ, telefone, CEP e data
|
|
177
|
-
| **Navegação** | Tabs (linha ou pílula, vira seletor quando não cabe), Breadcrumb, Pagination (completa, compacta ou "carregar mais") e Wizard/Stepper (etapas concluída, atual, pendente e com erro, com validação por etapa)
|
|
178
|
-
| **Dados** | Table com TanStack Table (seleção, expansão, ordenação, busca, colunas visíveis, ações em massa e por linha, densidade, estados vazio, carregando e erro, forma de cards no celular e **modo remoto**: 15 por página, carregando página por página, com busca e filtros em todos os registros), Card, StatCard, Badge, Avatar e grupo, List, Timeline, Accordion e Chart com Recharts (linha, barra, área e pizza)
|
|
179
|
-
| **Feedback** | BrandFeedbackIcon animado (check se desenhando, X se riscando), Toast com Sonner, Alert, Modal (confirmação, destrutiva, informativo e formulário), InfoHint (texto orientativo num ícone de informação que abre modal; também como `help` em PageHeader, CardTitle e FormSection), Drawer (header e rodapé fixos, confirmação de descarte, 30%, 40%, 50% ou 75% da tela), Popover, Tooltip, EmptyState, ErrorPage (404 e 500), Skeleton e Progress
|
|
180
|
-
| **Atendimento** | Chat omnichannel: ConversationList (foto com o logotipo do canal: WhatsApp, WhatsApp Web, Instagram, Facebook, TikTok, Google Meu Negócio e Reclame Aqui), ChatThread (cliente, atendente, robô e avisos; botão direito ou "…" para responder, reagir, editar e excluir; citação que leva à mensagem original; áudio em 1x, 1,5x e 2x; baixar anexos) e ChatComposer (textarea que cresce, anexos, emoji, mensagens rápidas, gravação de áudio, arrastar e soltar ou colar prints; no celular, campo, enviar e um botão de ações)
|
|
181
|
-
| **Conteúdo** | RichTextEditor (Tiptap): formatação completa, listas, alinhamento, links, tabela, imagem com 4 alças para redimensionar, colar print, arrastar imagens e modo HTML
|
|
182
|
-
| **Painel** | WidgetGrid: widgets que se arrastam e redimensionam no modo "Ajustar dashboard", com a arrumação salva; Chart com velocímetro de meta, funil com conversão, barras com linha, barras coloridas e empilhadas
|
|
183
|
-
| **Planejamento** | Calendar (mês, semana, dia e agenda em lista, no mesmo componente; no celular vira mês compacto com a lista do dia), Kanban (altura da tela, 5 etapas visíveis e rolagem lateral também pelo touchpad, rolagem infinita por etapa, (+) no título da etapa, totais em R$ de P&S e MRR por etapa e por cartão, arrastar no computador, menu "Mover para" no toque e no teclado, uma coluna por vez no celular, limite por coluna) e ImageViewer (imagens em popup, com setas, Esc, arrastar e pinça)
|
|
184
|
-
| **Layout** | Container (respiro igual em todos os lados), Stack, Inline, Grid (por tela ou por container query), Section e PageHeader
|
|
173
|
+
| Grupo | Componentes |
|
|
174
|
+
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
175
|
+
| **Ações** | Button (6 variantes, tamanhos, ícone, só ícone, carregando, largura total), ButtonGroup (agrupado, na horizontal, vertical ou responsivo, ou segmentado), DropdownMenu e ActionBar (regra 100%, 30/70 e menu) |
|
|
176
|
+
| **Formulário** | Input (máscaras de CPF, CNPJ, CPF ou CNPJ automático, telefone com seletor de DDI embutido e +55 por padrão, CEP, data, hora, moeda e percentual, com ícone, limpar e senha), Textarea com contador, Select (busca, múltiplo com chips, selecionar todos, contador, criar opção, busca remota), Checkbox com indeterminado e grupo, Radio em lista e em cards, Switch, DatePicker (data, período e horário, em pt-BR), Slider, Upload com arrastar e soltar e progresso, OtpInput (2FA), Field, Form, FormField e FormSection com React Hook Form e Zod, e validadores de CPF, CNPJ, telefone, CEP e data |
|
|
177
|
+
| **Navegação** | Tabs (linha ou pílula, vira seletor quando não cabe), Breadcrumb, Pagination (completa, compacta ou "carregar mais") e Wizard/Stepper (rótulos próprios e etapa opcional com Pular em "Mais ações"; etapas concluída, atual, pendente e com erro, com validação por etapa) |
|
|
178
|
+
| **Dados** | Table com TanStack Table (seleção, expansão, ordenação, busca, colunas visíveis, ações em massa e por linha, densidade, estados vazio, carregando e erro, forma de cards no celular e **modo remoto**: 15 por página, carregando página por página, com busca e filtros em todos os registros), Card, StatCard, Badge, Avatar e grupo, List, Timeline, Accordion e Chart com Recharts (linha, barra, área e pizza) |
|
|
179
|
+
| **Feedback** | BrandFeedbackIcon animado (check se desenhando, X se riscando; cor da marca, selo ao lado e entrada `encaixe` por opção), Toast com Sonner, Alert, Modal (confirmação, destrutiva, informativo, com segundo botão opcional por `cancelLabel`, e formulário), InfoHint (texto orientativo num ícone de informação que abre modal; também como `help` em PageHeader, CardTitle e FormSection), Drawer (header e rodapé fixos, confirmação de descarte, 30%, 40%, 50% ou 75% da tela), Popover, Tooltip, EmptyState (`className="py-0 md:py-0"` zera o respiro vertical), ErrorPage (404 e 500, com `back={false}` para tirar o Voltar), Skeleton e Progress |
|
|
180
|
+
| **Atendimento** | Chat omnichannel: ConversationList (foto com o logotipo do canal: WhatsApp, WhatsApp Web, Instagram, Facebook, TikTok, Google Meu Negócio e Reclame Aqui), ChatThread (cliente, atendente, robô e avisos; botão direito ou "…" para responder, reagir, editar e excluir; citação que leva à mensagem original; áudio em 1x, 1,5x e 2x; baixar anexos) e ChatComposer (textarea que cresce, anexos, emoji, mensagens rápidas, gravação de áudio, arrastar e soltar ou colar prints; no celular, campo, enviar e um botão de ações) |
|
|
181
|
+
| **Conteúdo** | RichTextEditor (Tiptap): formatação completa, listas, alinhamento, links, tabela, imagem com 4 alças para redimensionar, colar print, arrastar imagens e modo HTML |
|
|
182
|
+
| **Painel** | WidgetGrid: widgets que se arrastam e redimensionam no modo "Ajustar dashboard", com a arrumação salva; Chart com velocímetro de meta, funil com conversão, barras com linha, barras coloridas e empilhadas |
|
|
183
|
+
| **Planejamento** | Calendar (mês, semana, dia e agenda em lista, no mesmo componente; no celular vira mês compacto com a lista do dia), Kanban (altura da tela, 5 etapas visíveis e rolagem lateral também pelo touchpad, rolagem infinita por etapa, (+) no título da etapa, totais em R$ de P&S e MRR por etapa e por cartão, arrastar no computador, menu "Mover para" no toque e no teclado, uma coluna por vez no celular, limite por coluna) e ImageViewer (imagens em popup, com setas, Esc, arrastar e pinça) |
|
|
184
|
+
| **Layout** | Container (respiro igual em todos os lados), Stack, Inline, Grid (por tela ou por container query), Section e PageHeader (título no corpo do celular por `bodyTitle`, sem reservar o bloco do título por `titleSpace`) |
|
|
185
185
|
|
|
186
186
|
### AppShell
|
|
187
187
|
|
|
@@ -191,22 +191,41 @@ Tudo configurável por props, com o padrão em `src/config/layout.ts`:
|
|
|
191
191
|
- sidebar **recolhida** (só ícones, padrão) ou **expandida**, com opção de **abrir por cima do conteúdo** ao passar o mouse;
|
|
192
192
|
- submenu em **segunda barra lateral** ou **dentro da sidebar**, e, no menu superior, **submenu navegável** ou **mega menu** com seções;
|
|
193
193
|
- header sempre fixo, com título da página e trilha, busca global (**Ctrl+K**), notificações, tema e menu do usuário;
|
|
194
|
+
- logo no header do celular, no lugar do título e da trilha, pela prop `headerLogo`;
|
|
195
|
+
- seletor de espaço (empresa, unidade) no topo do menu do usuário, pela prop `workspaces`;
|
|
196
|
+
- menu do usuário enxuto: `userMenu` esconde Tema, Template ou Layout, um a um;
|
|
194
197
|
- no celular, gaveta de navegação e barra inferior com até 4 itens.
|
|
195
198
|
|
|
196
199
|
### Telas base
|
|
197
200
|
|
|
198
201
|
Login, esqueci a senha, verificação em duas etapas (2FA), nova senha, cadastro, painel com indicadores e gráficos, listagem com barra de ferramentas e filtros, detalhe com abas, formulário longo em seções, cadastro em wizard, configurações com navegação interna, criação e edição de registro em drawer (com busca de CNPJ e CEP), tarefas, atendimento omnichannel, agenda, funil de vendas em kanban, painel em widgets, galeria e página 404.
|
|
199
202
|
|
|
203
|
+
### Movimento (opcional, desligado por padrão)
|
|
204
|
+
|
|
205
|
+
O `RendraProvider` (e o `RendraRouterBridge`) aceita a prop `motion`: `'off'` (o padrão) ou `'on'`. Em `'on'` os componentes animam com o Motion (`motion` 14, já em `dependencies`); em `'off'` nada do Motion é montado. A linguagem é uma só: o que se repete dezenas de vezes por dia (tabela, abas, teclado) não anima a entrada; o toque responde em 120 ms; overlays entram em mola sem quique e saem mais rápido que entram, a partir do gatilho; nada parte de `scale(0)`; movimento reduzido vira só opacidade.
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
<RendraProvider motion="on">{/* ... */}</RendraProvider>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Com React Router, ponha a prop na ponte, que já entrega ao provider o link, o caminho atual e a navegação: `<RendraRouterBridge motion="on">`. Montando o `RendraProvider` direto, ele também exige `linkComponent`, `useCurrentPath`, `navigate` e `goBack`.
|
|
212
|
+
|
|
213
|
+
- **Tokens** (variáveis CSS que o host sobrescreve): `--rendra-duration-press|hover|fast|base|move|slow|draw|pulse`, `--rendra-reveal-enter|stagger|count|draw` (a chegada do painel, mais lenta, de uso único) e as curvas `--rendra-ease-out|in-out|spring|move|drawer`, com os aliases do Tailwind `ease-move` e `ease-drawer`.
|
|
214
|
+
- **Presets** em `src/lib/motion` (`presets.ts`, `tokens.ts`; no registry do shadcn vão todos os arquivos da pasta): `press`, `open`, `sheet`, `tooltip`, `swap`, `indicator`, `move`, `settle`, `reveal`, `stagger`. Os nomes antigos (`fade`, `scale`, `pop`, `slide`, `layout`, `tap`, `bump`) continuam.
|
|
215
|
+
- **Telas de comparação**: `/componentes-motion` (as mesmas seções de `/componentes`, com a chave Ligado/Desligado) e `/dashboard-motion` (painel que chega com contagem, gráficos desenhando e cascata). Ficam fora das suítes de captura.
|
|
216
|
+
- **O `off` também mudou nesta versão** (2.5.0), só em tempo, curva, origem e estado intermediário, nunca em quadro parado: o toque do botão é de 120 ms a 0,97; popover, menu, tooltip e painel do Select nascem do gatilho; toda saída de overlay dura 120 ms; o tooltip em `instant-open` não anima; a bolinha do radio e o check do checkbox partem de 0,9, não de zero; com movimento reduzido os overlays só esmaecem. As 152 capturas de referência não mudam.
|
|
217
|
+
- `npm run test:motion` mede tempo, escala, origem e gesto no navegador (`BASE_URL` para o seu servidor).
|
|
218
|
+
|
|
200
219
|
### Vitrine, Storybook e testes
|
|
201
220
|
|
|
202
221
|
- **Vitrine** na rota `/componentes`: cada componente com variantes, tamanhos e estados (padrão, hover, foco, ativo, desabilitado, carregando, erro) e as regras de composição.
|
|
203
222
|
- **Storybook 10** com mais de 100 stories: viewport de celular por padrão, com tablet e desktop, e alternância de template, paleta e modo claro/escuro na barra.
|
|
204
223
|
- **Testes de layout com Playwright** (`npm run test:layout`): 16 rotas × 5 larguras (360, 390, 768, 1280 e 1920) × 3 templates no modo claro, mais 360 e 1280 no escuro, somando 336 testes. Falham se houver rolagem horizontal, elemento maior que a tela, toque menor que 44px no celular ou erro de JavaScript, e geram capturas de todas as telas.
|
|
205
224
|
- **Testes de acessibilidade com axe-core** (`npm run test:a11y`): 144 testes, todas as rotas nos três templates, claro e escuro, mais a paleta Ardósia e o celular. Falham em qualquer violação WCAG 2.1 A ou AA, inclusive contraste.
|
|
206
|
-
- **Regressão visual** (`npm run test:visual`):
|
|
225
|
+
- **Regressão visual** (`npm run test:visual`): 152 capturas comparadas com a referência aprovada. Pegam cor que mudou, espaçamento que quebrou e ícone que sumiu. As referências são geradas no Linux do CI; depois de uma mudança visual intencional, aplique o rótulo `atualizar-visual` no Pull Request.
|
|
207
226
|
- **Testes unitários e de componente com Vitest e Testing Library** (`npm test`; `npm run test:coverage` com o piso por arquivo): comportamento de Table (ordenação, busca, paginação, seleção, ações em massa, modo remoto e cartões no celular), Select, Input (máscaras, centavos e busca de CEP e CNPJ), Modal, Drawer, campos de formulário e navegação, além de validadores, máscaras, buscas e o gerador de paletas (AA garantido em todas). Cada componente tem o teste ao lado, e ele vai junto quando você copia o componente para outro projeto.
|
|
208
227
|
- **CI no GitHub Actions**: todo Pull Request passa por tipos, lint, formatação, regras, unitários, build, Storybook e os três testes no navegador. A `main` só aceita merge com tudo verde. Os alertas de segurança do GitHub avisam de dependência vulnerável; as atualizações de versão são feitas à mão (veja o `CONTRIBUTING.md`).
|
|
209
|
-
- **Verificador de regras** (`npm run check:rules`): barra cor fixa, valor arbitrário, degrau fora da escala, estilo inline, fonte fixa, `100vh` e arquivos duplicados ou mobile.
|
|
228
|
+
- **Verificador de regras** (`npm run check:rules`): barra cor fixa, valor arbitrário, degrau fora da escala, estilo inline, fonte fixa, `100vh` e arquivos duplicados ou mobile. A mesma varredura, com as 17 regras, roda em qualquer projeto por `npx @rendra-ui/web auditar --all`.
|
|
210
229
|
|
|
211
230
|
---
|
|
212
231
|
|
|
@@ -359,6 +378,8 @@ import '@rendra-ui/web/components.css'
|
|
|
359
378
|
|
|
360
379
|
O CSS já sai compilado (estratégia A: o host recebe `tokens.css`, `base.css` e `components.css` prontos, sem precisar ter o Tailwind instalado, e sem nenhuma variável do namespace do Tailwind vazar para o seu tema). `react-router` fica fora das dependências: quem usa React Router importa a ponte, `@rendra-ui/web/router-bridge`; quem não usa, não precisa dela. Os componentes pesados (`document-viewer`, `rich-text-editor`, `chart`, `widget-grid`) são subcaminhos próprios, para não engordar quem não usa.
|
|
361
380
|
|
|
381
|
+
**O que o `components.css` traz, e o que não traz.** Ele contém só as classes que os componentes do pacote usam por dentro e as `@utility` do Rendra. Classes de composição das suas próprias telas (`p-12`, `max-w-sm`, `flex-col`, `mb-0`) não estão garantidas: num host com Tailwind v4, deixe o seu Tailwind gerá-las e reproduza no `@theme` a escala do Rendra (espaço 0, px, 1, 2, 3, 4, 6, 8, 12, 16 e 24, raios por papel, cores `--rendra-*`); num host sem Tailwind, escreva o CSS de composição à mão com as variáveis `--rendra-*`. A receita completa e a lista de todas as `@utility` estão em [`docs/COMO_APLICAR.md`](docs/COMO_APLICAR.md), na seção "O CSS do pacote e as classes das suas telas".
|
|
382
|
+
|
|
362
383
|
**Publicação:** uma tag `vX.Y.Z` empurrada para o repositório (`git push origin vX.Y.Z`) dispara o `.github/workflows/publish.yml`, que builda o pacote, roda o `verify:pack` e publica com `npm publish --provenance --access public`, depois de conferir que a tag bate com a `version` do `package.json`. Com o _trusted publishing_ do npm configurado para este repositório, o workflow dispensa o segredo `NPM_TOKEN`.
|
|
363
384
|
|
|
364
385
|
### CLI (`rendra`)
|
|
@@ -367,13 +388,31 @@ Três comandos. Forma segura, que não exige instalar nada: `npx @rendra-ui/web
|
|
|
367
388
|
|
|
368
389
|
```bash
|
|
369
390
|
npx @rendra-ui/web codigos # lista o catálogo de códigos de componente
|
|
370
|
-
npx @rendra-ui/web auditar [pasta]
|
|
391
|
+
npx @rendra-ui/web auditar [pasta...] [--all] [--ignore a,b] # regras de DESIGN_RULES.md no projeto de destino
|
|
371
392
|
npx @rendra-ui/web trocar <DE> <PARA> --dry-run # simula a troca de uma variante do catálogo pela outra
|
|
372
393
|
```
|
|
373
394
|
|
|
374
395
|
Nota: `npx rendra <comando>` só funciona com o pacote instalado neste projeto (ou instalado global, como `rendra <comando>` direto). Sem instalar, não use `npx rendra`: o nome `rendra` no npm pertence a outro pacote, não a este.
|
|
375
396
|
|
|
376
|
-
`rendra auditar` aplica sete regras estáticas, sem IA (cor fixa, valor arbitrário, estilo inline, fonte fixa, `100vh`, degrau fora da escala, raio fixo), o mesmo tipo de checagem do `check:rules` deste repositório, aplicado a qualquer projeto.
|
|
397
|
+
`rendra auditar` aplica, por padrão, sete regras estáticas, sem IA (cor fixa, valor arbitrário, estilo inline, fonte fixa, `100vh`, degrau fora da escala, raio fixo), o mesmo tipo de checagem do `check:rules` deste repositório, aplicado a qualquer projeto. Sem flag e sem `rendra.config.json`, a saída e o código de saída são os de sempre (1 com violação, 0 sem).
|
|
398
|
+
|
|
399
|
+
**`--all`** liga as 17 regras do `check:rules`: as sete acima mais `var-inline`, `svg-marca`, `classe-dinamica`, `texto-estreito`, `primaria-como-texto`, `componente-duplicado`, `css-fora-do-lugar`, `botao-solto`, `texto-orientativo` e `variavel-sem-prefixo-rendra`. Use quando o projeto segue o vocabulário e as pastas do Rendra (`--rendra-*`, `src/brand`, `src/styles`, telas em `src/pages/app`); num projeto que não segue, desligue as regras que não fazem sentido em `rendra.config.json`. Com `--all`, pastas de marca e de estilos só passam pelo prefixo `--rendra-`, e `.test.`, `.stories.`, `node_modules`, `dist` e `build` não são varridos.
|
|
400
|
+
|
|
401
|
+
**`--ignore a,b`** pula pastas, por prefixo de pasta relativo ao projeto (sem glob): `src/brand` pula `src/brand` e tudo dentro, mas não `src/brand-novo`. Vale também `--ignore a --ignore b` e `--ignore=a,b`, com ou sem `--all`. **Várias pastas:** `auditar src extra` varre as duas. Com uma só pasta, ela é a base do projeto (o `file` da saída sai relativo a ela e o `rendra.config.json` é lido dela); com duas ou mais, a base é a pasta atual. Pasta fora da base dá erro claro, sem varrer nada.
|
|
402
|
+
|
|
403
|
+
**`rendra.config.json`** é opcional, fica na base do projeto e aceita cinco chaves, todas opcionais:
|
|
404
|
+
|
|
405
|
+
```json
|
|
406
|
+
{
|
|
407
|
+
"pastas": ["src", "extra"],
|
|
408
|
+
"ignorar": ["src/gerado"],
|
|
409
|
+
"regras": { "botao-solto": false, "cor-fixa": false },
|
|
410
|
+
"pastasMarca": ["src/brand", "src/styles"],
|
|
411
|
+
"pastasTelas": ["src/pages/app"]
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
`pastas` substitui o atalho para `src`; `ignorar` tem o efeito de `--ignore`; `regras` desliga qualquer um dos 17 identificadores (`false`), com ou sem `--all`; `pastasMarca` e `pastasTelas` valem com `--all`, e os padrões são os do exemplo. A flag vence o config e não soma: `--ignore` substitui `ignorar`, e duas ou mais pastas posicionais substituem `pastas`. JSON inválido, chave desconhecida, tipo errado ou regra desconhecida dão mensagem em português e código de saída 1, assim como flag desconhecida (`--foo`) e `--ignore` sem valor. `rendra auditar --help` mostra a ajuda geral. `rendra trocar` reescreve a prop literal que distingue duas variantes do catálogo (`ABA-001` para `ABA-002`, por exemplo), sempre com `--dry-run` primeiro: um elemento sem a prop conta como a variante padrão do componente (troca também esse caso, inserindo ou removendo a prop conforme o lado); prop dinâmica (`variant={x}`) nunca é reescrita, só listada para revisão manual, e troca entre componentes diferentes nunca edita, só aponta onde o componente aparece. Só `rendra trocar` precisa do `typescript` instalado no projeto de destino (`peerDependency` opcional do pacote), para ler e reescrever o JSX; sem ele, avisa e sai com erro.
|
|
377
416
|
|
|
378
417
|
### Skill de migração parcial
|
|
379
418
|
|
|
@@ -436,3 +475,10 @@ Contribuições são bem-vindas pelo fluxo descrito em [CONTRIBUTING.md](CONTRIB
|
|
|
436
475
|
## Créditos
|
|
437
476
|
|
|
438
477
|
Construído sobre Tailwind CSS, shadcn/ui, Radix UI, Lucide, TanStack Table, cmdk, Sonner, React Hook Form, Zod, IMask, React Day Picker, Recharts, Storybook e Playwright. As fontes Poppins, Inter e DM Sans são distribuídas sob a SIL Open Font License. Os símbolos dos templates são ilustrativos. O logotipo é montado pelo componente `BrandLogo` com as cores da paleta ativa.
|
|
478
|
+
|
|
479
|
+
### Ferramentas de movimento
|
|
480
|
+
|
|
481
|
+
A linguagem de movimento do Rendra (tempo, curva, origem, mola e o que não anima) foi desenhada e auditada com duas skills de agente de código. Elas ficam só na máquina de quem desenvolve; o Rendra não as redistribui.
|
|
482
|
+
|
|
483
|
+
- **Emil Kowalski**: as skills `emil-design-eng`, `animate`, `review-animations`, `animation-performance`, `find-animation-opportunities` e `improve-animations`, sob a licença **MIT**. Repositório de origem: [github.com/emilkowalski/skills](https://github.com/emilkowalski/skills). As regras de frequência, de assimetria entre entrada e saída, de origem do gatilho e do orçamento de tempo que o motion deste projeto segue vêm do trabalho dele.
|
|
484
|
+
- **Impeccable**: a skill `impeccable` (modo Operar: `audit`, `polish`, `harden`), sob a licença **Apache 2.0**. Repositório de origem: [github.com/pbakaus/impeccable](https://github.com/pbakaus/impeccable).
|
package/bin/rendra.mjs
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Três subcomandos, mais `--help`/sem argumento (ver printHelp, `src/cli/help.ts`):
|
|
9
9
|
* rendra codigos lista o catálogo de códigos de componente
|
|
10
|
-
* rendra auditar [
|
|
10
|
+
* rendra auditar [pasta...] [--all] [--ignore a,b]
|
|
11
|
+
* regras de DESIGN_RULES.md (padrão: cwd atual e as sete
|
|
12
|
+
* genéricas; --all liga as 17; rendra.config.json opcional)
|
|
11
13
|
* rendra trocar <DE> <PARA> [--dry-run] troca a variante DE pela PARA no projeto
|
|
12
14
|
*/
|
|
13
15
|
import { existsSync } from 'node:fs'
|
|
@@ -33,7 +35,7 @@ function printAudit(violations) {
|
|
|
33
35
|
}
|
|
34
36
|
for (const v of violations) {
|
|
35
37
|
console.error(`\n${v.file}:${v.line} [${v.rule}] ${v.message}`)
|
|
36
|
-
console.error(` ${v.source.slice(0, 160)}`)
|
|
38
|
+
if (v.source) console.error(` ${v.source.slice(0, 160)}`)
|
|
37
39
|
}
|
|
38
40
|
console.error(`\n${violations.length} violação(ões) das regras de design.`)
|
|
39
41
|
}
|
|
@@ -71,11 +73,21 @@ async function main() {
|
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
if (command === 'auditar') {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
// Toda a decisão (argumentos, base, config, precedência) mora em src/cli/executar-auditar.ts,
|
|
77
|
+
// com teste: aqui só se chama, imprime e define o código de saída.
|
|
78
|
+
const { executarAuditar } = await importDist('executar-auditar')
|
|
79
|
+
const resultado = executarAuditar(rest, process.cwd())
|
|
80
|
+
if ('erro' in resultado) {
|
|
81
|
+
console.error(resultado.erro)
|
|
82
|
+
process.exitCode = 1
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
if ('ajuda' in resultado) {
|
|
86
|
+
console.log(resultado.ajuda)
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
printAudit(resultado.violacoes)
|
|
90
|
+
process.exitCode = resultado.violacoes.length > 0 ? 1 : 0
|
|
79
91
|
return
|
|
80
92
|
}
|
|
81
93
|
|
package/dist/base.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Rendra Design System v2.
|
|
1
|
+
/*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
|
|
2
2
|
|
|
3
3
|
@layer base {
|
|
4
4
|
*,
|
|
@@ -241,6 +241,38 @@
|
|
|
241
241
|
animation-iteration-count: 1 !important;
|
|
242
242
|
transition-duration: 0.01ms !important;
|
|
243
243
|
scroll-behavior: auto !important;
|
|
244
|
+
--tw-enter-scale: 1 !important;
|
|
245
|
+
--tw-enter-translate-x: 0 !important;
|
|
246
|
+
--tw-enter-translate-y: 0 !important;
|
|
247
|
+
--tw-enter-rotate: 0 !important;
|
|
248
|
+
--tw-enter-blur: 0 !important;
|
|
249
|
+
--tw-exit-scale: 1 !important;
|
|
250
|
+
--tw-exit-translate-x: 0 !important;
|
|
251
|
+
--tw-exit-translate-y: 0 !important;
|
|
252
|
+
--tw-exit-rotate: 0 !important;
|
|
253
|
+
--tw-exit-blur: 0 !important;
|
|
254
|
+
}
|
|
255
|
+
[data-state='open'],
|
|
256
|
+
[data-state='closed'],
|
|
257
|
+
[data-state='delayed-open'],
|
|
258
|
+
[data-state='instant-open'] {
|
|
259
|
+
animation-duration: var(--rendra-duration-base) !important;
|
|
260
|
+
}
|
|
261
|
+
@keyframes rd-reduced-in {
|
|
262
|
+
from {
|
|
263
|
+
opacity: 0;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
@keyframes rd-reduced-out {
|
|
267
|
+
to {
|
|
268
|
+
opacity: 0;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
[data-state='open'].overflow-hidden {
|
|
272
|
+
animation-name: rd-reduced-in !important;
|
|
273
|
+
}
|
|
274
|
+
[data-state='closed'].overflow-hidden {
|
|
275
|
+
animation-name: rd-reduced-out !important;
|
|
244
276
|
}
|
|
245
277
|
}
|
|
246
278
|
}
|
|
@@ -1,24 +1,14 @@
|
|
|
1
|
-
/*! Rendra Design System v2.
|
|
2
|
-
import { t as cn } from "./cn-
|
|
3
|
-
import {
|
|
1
|
+
/*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
|
|
2
|
+
import { t as cn } from "./cn-Cw7M5kEP.js";
|
|
3
|
+
import { r as useMotionMode, s as usePrefersReducedMotion } from "./motion-context-5BRIQAJm.js";
|
|
4
|
+
import { t as useMotionPresets } from "./use-motion-presets-BrkgL8lG.js";
|
|
5
|
+
import { p as resolveCatalogCode } from "./components-DkpspH9h.js";
|
|
4
6
|
import { Loader2 } from "lucide-react";
|
|
7
|
+
import { m } from "motion/react";
|
|
5
8
|
import { Slot } from "radix-ui";
|
|
9
|
+
import { forwardRef } from "react";
|
|
6
10
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
-
import { forwardRef, useSyncExternalStore } from "react";
|
|
8
11
|
import { cva } from "class-variance-authority";
|
|
9
|
-
//#region src/hooks/use-reduced-motion.ts
|
|
10
|
-
function subscribe(cb) {
|
|
11
|
-
const mq = matchMedia("(prefers-reduced-motion: reduce)");
|
|
12
|
-
mq.addEventListener("change", cb);
|
|
13
|
-
return () => mq.removeEventListener("change", cb);
|
|
14
|
-
}
|
|
15
|
-
function getCurrent() {
|
|
16
|
-
return matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
17
|
-
}
|
|
18
|
-
function usePrefersReducedMotion() {
|
|
19
|
-
return useSyncExternalStore(subscribe, getCurrent, () => false);
|
|
20
|
-
}
|
|
21
|
-
//#endregion
|
|
22
12
|
//#region src/components/ui/spinner.tsx
|
|
23
13
|
var sizeClass = {
|
|
24
14
|
sm: "size-icon-sm",
|
|
@@ -48,13 +38,44 @@ function Spinner({ size = "md", label, className }) {
|
|
|
48
38
|
});
|
|
49
39
|
}
|
|
50
40
|
//#endregion
|
|
41
|
+
//#region src/lib/motion/native-events.ts
|
|
42
|
+
var NATIVE_EVENTS = {
|
|
43
|
+
onDrag: "drag",
|
|
44
|
+
onDragStart: "dragstart",
|
|
45
|
+
onDragEnd: "dragend",
|
|
46
|
+
onAnimationStart: "animationstart"
|
|
47
|
+
};
|
|
48
|
+
/** Separa as quatro chaves em conflito; o resto segue para o elemento do Motion. */
|
|
49
|
+
function splitNativeEvents(props) {
|
|
50
|
+
const native = {};
|
|
51
|
+
const rest = {};
|
|
52
|
+
for (const [key, value] of Object.entries(props)) if (key in NATIVE_EVENTS) native[key] = value;
|
|
53
|
+
else rest[key] = value;
|
|
54
|
+
return {
|
|
55
|
+
native,
|
|
56
|
+
rest
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Liga os manipuladores como ouvintes nativos. Devolve a função que os remove. */
|
|
60
|
+
function bindNativeEvents(el, native) {
|
|
61
|
+
const bound = [];
|
|
62
|
+
for (const [key, type] of Object.entries(NATIVE_EVENTS)) {
|
|
63
|
+
const handler = native[key];
|
|
64
|
+
if (!handler) continue;
|
|
65
|
+
const listener = ((event) => handler(event));
|
|
66
|
+
el.addEventListener(type, listener);
|
|
67
|
+
bound.push([type, listener]);
|
|
68
|
+
}
|
|
69
|
+
return () => bound.forEach(([type, listener]) => el.removeEventListener(type, listener));
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
51
72
|
//#region src/components/ui/button.tsx
|
|
52
73
|
/**
|
|
53
74
|
* Botão único do sistema. Variantes, tamanho, ícone, só ícone, carregamento e
|
|
54
75
|
* largura total são props: nunca crie outro arquivo de botão.
|
|
55
76
|
* Altura vem de h-control-*: 44px ou mais no mobile, compacta a partir de md.
|
|
56
77
|
*/
|
|
57
|
-
var buttonVariants = cva("inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-control font-medium whitespace-nowrap transition-[color,background-color,border-color,box-shadow,transform] duration-
|
|
78
|
+
var buttonVariants = cva("inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-control font-medium whitespace-nowrap transition-[color,background-color,border-color,box-shadow,transform] duration-(--rendra-duration-press) ease-out select-none active:scale-97 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
|
|
58
79
|
variants: {
|
|
59
80
|
variant: {
|
|
60
81
|
primary: "bg-primary text-primary-foreground shadow-sm hover:bg-primary-hover hover:text-primary-hover-foreground hover:shadow-md active:bg-primary-hover active:text-primary-hover-foreground active:shadow-sm",
|
|
@@ -106,13 +127,43 @@ var buttonVariants = cva("inline-flex shrink-0 cursor-pointer items-center justi
|
|
|
106
127
|
fullWidth: false
|
|
107
128
|
}
|
|
108
129
|
});
|
|
130
|
+
/**
|
|
131
|
+
* O botão de `on`: `m.button` com toque discreto (whileTap). Os eventos que o Motion toma
|
|
132
|
+
* (`onDrag*` e `onAnimationStart`) saem das props dele e viram ouvintes nativos no elemento,
|
|
133
|
+
* então o consumidor continua recebendo o evento. Só existe em `on`; por isso os hooks de motion
|
|
134
|
+
* moram aqui e o `off` não paga nada.
|
|
135
|
+
*/
|
|
136
|
+
var MotionButton = forwardRef(function MotionButton(props, ref) {
|
|
137
|
+
const presets = useMotionPresets();
|
|
138
|
+
const { native, rest } = splitNativeEvents(props);
|
|
139
|
+
const attach = (el) => {
|
|
140
|
+
assignRef(ref, el);
|
|
141
|
+
if (!el) return;
|
|
142
|
+
const unbind = bindNativeEvents(el, native);
|
|
143
|
+
return () => {
|
|
144
|
+
unbind();
|
|
145
|
+
assignRef(ref, null);
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
return /* @__PURE__ */ jsx(m.button, {
|
|
149
|
+
ref: attach,
|
|
150
|
+
whileTap: presets.press.whileTap,
|
|
151
|
+
transition: presets.press.transition,
|
|
152
|
+
...rest
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
function assignRef(ref, value) {
|
|
156
|
+
if (typeof ref === "function") ref(value);
|
|
157
|
+
else if (ref) ref.current = value;
|
|
158
|
+
}
|
|
109
159
|
var Button = forwardRef(function Button({ className, variant, size, icon, iconRight, iconOnly = false, loading = false, fullWidth = false, asChild = false, disabled, type = "button", children, ...props }, ref) {
|
|
160
|
+
const on = useMotionMode() === "on";
|
|
110
161
|
const classes = cn(buttonVariants({
|
|
111
162
|
variant,
|
|
112
163
|
size,
|
|
113
164
|
iconOnly,
|
|
114
165
|
fullWidth
|
|
115
|
-
}), className);
|
|
166
|
+
}), on && !asChild && "transition-[color,background-color,border-color,box-shadow] active:scale-100", className);
|
|
116
167
|
const code = resolveCatalogCode("Button", { variant: variant ?? "primary" });
|
|
117
168
|
if (asChild) return /* @__PURE__ */ jsx(Slot.Root, {
|
|
118
169
|
ref,
|
|
@@ -122,6 +173,20 @@ var Button = forwardRef(function Button({ className, variant, size, icon, iconRi
|
|
|
122
173
|
children
|
|
123
174
|
});
|
|
124
175
|
const leading = loading ? /* @__PURE__ */ jsx(Spinner, { size: size === "lg" ? "md" : "sm" }) : icon;
|
|
176
|
+
if (on) return /* @__PURE__ */ jsxs(MotionButton, {
|
|
177
|
+
ref,
|
|
178
|
+
type,
|
|
179
|
+
"data-rendra": code,
|
|
180
|
+
className: classes,
|
|
181
|
+
disabled: disabled || loading,
|
|
182
|
+
"aria-busy": loading || void 0,
|
|
183
|
+
...props,
|
|
184
|
+
children: [
|
|
185
|
+
leading,
|
|
186
|
+
iconOnly ? loading ? null : children : children,
|
|
187
|
+
!iconOnly && iconRight
|
|
188
|
+
]
|
|
189
|
+
});
|
|
125
190
|
return /* @__PURE__ */ jsxs("button", {
|
|
126
191
|
ref,
|
|
127
192
|
type,
|
|
@@ -138,6 +203,6 @@ var Button = forwardRef(function Button({ className, variant, size, icon, iconRi
|
|
|
138
203
|
});
|
|
139
204
|
});
|
|
140
205
|
//#endregion
|
|
141
|
-
export {
|
|
206
|
+
export { buttonVariants as n, Spinner as r, Button as t };
|
|
142
207
|
|
|
143
|
-
//# sourceMappingURL=button-
|
|
208
|
+
//# sourceMappingURL=button-BzJgR2gH.js.map
|