@majapisoftwares/ui 5.0.0 → 6.1.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.
Files changed (68) hide show
  1. package/AGENTS.md +11 -1
  2. package/README.md +19 -11
  3. package/THIRD_PARTY_NOTICES.md +25 -1
  4. package/components.json +1 -1
  5. package/docs/MIGRATION-6.0.md +33 -0
  6. package/docs/STANDARDS.md +11 -6
  7. package/docs/VERSIONING.md +28 -0
  8. package/package.json +19 -10
  9. package/scripts/check-standards.mjs +2 -0
  10. package/src/components/ui/accordion.tsx +82 -0
  11. package/src/components/ui/alert-dialog.tsx +187 -0
  12. package/src/components/ui/alert.tsx +2 -2
  13. package/src/components/ui/avatar.tsx +108 -0
  14. package/src/components/ui/badge.tsx +1 -1
  15. package/src/components/ui/breadcrumb.tsx +125 -0
  16. package/src/components/ui/button-group.tsx +89 -0
  17. package/src/components/ui/button.tsx +12 -16
  18. package/src/components/ui/card.tsx +101 -0
  19. package/src/components/ui/checkbox.tsx +27 -0
  20. package/src/components/ui/collapsible.tsx +21 -0
  21. package/src/components/ui/context-menu.tsx +271 -0
  22. package/src/components/ui/dialog.tsx +161 -0
  23. package/src/components/ui/dropdown-menu.tsx +271 -0
  24. package/src/components/ui/empty.tsx +4 -4
  25. package/src/components/ui/field.tsx +6 -6
  26. package/src/components/ui/hover-card.tsx +50 -0
  27. package/src/components/ui/input-group.tsx +155 -0
  28. package/src/components/ui/input.tsx +1 -1
  29. package/src/components/ui/kbd.tsx +28 -0
  30. package/src/components/ui/orb.css +1002 -0
  31. package/src/components/ui/orb.tsx +752 -0
  32. package/src/components/ui/popover.tsx +89 -0
  33. package/src/components/ui/progress.tsx +82 -0
  34. package/src/components/ui/radio-group.tsx +37 -0
  35. package/src/components/ui/scroll-area.tsx +54 -0
  36. package/src/components/ui/select.tsx +204 -0
  37. package/src/components/ui/sheet.tsx +8 -6
  38. package/src/components/ui/sidebar.tsx +1035 -0
  39. package/src/components/ui/skeleton.tsx +1 -1
  40. package/src/components/ui/slider.tsx +53 -0
  41. package/src/components/ui/sonner.tsx +44 -0
  42. package/src/components/ui/spinner.tsx +32 -0
  43. package/src/components/ui/switch.tsx +31 -0
  44. package/src/components/ui/table.tsx +10 -4
  45. package/src/components/ui/tabs.tsx +2 -2
  46. package/src/components/ui/textarea.tsx +19 -0
  47. package/src/components/ui/toggle-group.tsx +89 -0
  48. package/src/components/ui/toggle.tsx +44 -0
  49. package/src/components/ui/tooltip.tsx +65 -0
  50. package/src/hooks/use-mobile.ts +20 -0
  51. package/src/lib/i18n.tsx +109 -0
  52. package/src/patterns/app-shell.tsx +162 -53
  53. package/src/patterns/data-table.tsx +2441 -180
  54. package/src/patterns/details-sheet.tsx +10 -7
  55. package/src/patterns/filter-tabs.tsx +53 -0
  56. package/src/patterns/majapi-logo.tsx +32 -0
  57. package/src/patterns/page-header.tsx +44 -22
  58. package/src/patterns/preferences.tsx +172 -0
  59. package/src/patterns/status-badge.tsx +4 -3
  60. package/src/patterns/user-menu.tsx +218 -0
  61. package/src/patterns/workspace-switcher.tsx +173 -0
  62. package/src/styles.css +41 -341
  63. package/src/templates/admin-dashboard.tsx +14 -33
  64. package/src/templates/auth-layout.tsx +18 -24
  65. package/src/templates/login-form.tsx +97 -65
  66. package/src/templates/signup-form.tsx +139 -99
  67. package/src/components/ui/native-select.tsx +0 -66
  68. package/src/vite-env.d.ts +0 -1
package/AGENTS.md CHANGED
@@ -1,8 +1,18 @@
1
1
  # Padrão obrigatório do Workbench
2
2
 
3
+ - Apps e catálogo usam a versão estável mais recente do Next.js (confira com `npm view next version` antes de mudar dependências). O catálogo (`catalog/`) é um app Next.js como qualquer outro, com a mesma estrutura do starter; não use Vite nem outro framework.
4
+ - Toda mudança na biblioteca que afete apps entra no mesmo commit no starter (`starters/next-app`) e no catálogo. O starter sempre depende da versão atual da biblioteca (o teste `npm run test:starter` falha se divergir) e deve compilar e passar lint/typecheck/build com ela.
5
+ - Todo app é bilíngue: inglês (`en`) e português (`pt-BR`). Textos de UI ficam em catálogos `defineMessages` com as duas línguas; nada de texto fixo em componentes. Use `Preferences`/`PreferenceControls` da biblioteca para idioma e tema.
6
+ - Mantenha o texto mínimo: sem descrições, avisos ou rótulos que não ajudem a tarefa.
7
+ - `patterns/data-table` (TanStack Table, vindo da Numa) é a única tabela de dados: filtros por coluna, ordenação múltipla, colunas reordenáveis/ocultáveis/redimensionáveis, seleção, persistência e estados. Melhorias de tabela são feitas aqui e chegam a todos os apps por versão; apps não mantêm cópias próprias.
8
+ - Ao trazer componentes de outros apps, remova sobrescritas visuais (arredondamento, altura, sombra, padding, cores) e deixe os primitivos da biblioteca definirem o visual; containers usam os raios já adotados aqui (ex.: rounded-2xl em tabelas e listas).
9
+ - Seleção usa sempre o Select (Base UI) da biblioteca; não use select nativo nem NativeSelect (o check:standards bloqueia).
10
+ - Versionamento segue docs/VERSIONING.md (SemVer sobre a API pública). Prefira mudanças aditivas e deprecação a remoções; major só com aprovação explícita do responsável.
11
+ - Teste localmente com portless (`npm run dev` já roda via portless; o catálogo abre em https://majapi-ui.localhost). Não use portas fixas nem localhost:PORT nas instruções.
12
+
3
13
  - Novos apps web devem partir de starters/next-app, criado por npm run create:app -- <diretorio-novo>. O starter usa a versão estável do Next.js fixada e validada no manifesto; atualizações devem manter lockfile, CI e revisão de compatibilidade.
4
14
 
5
- - A base de primitivas é shadcn sobre Base UI (base-nova), não Radix. Use render para composição e nativeButton=false quando Button renderizar Link/a. Preserve as fronteiras use client da biblioteca em consumidores Next.js.
15
+ - A base de primitivas é shadcn sobre Base UI (base-luma), não Radix. Use render para composição e nativeButton=false quando Button renderizar Link/a. Preserve as fronteiras use client da biblioteca em consumidores Next.js.
6
16
 
7
17
  - Esta biblioteca pública é a base obrigatória de todos os apps atuais e futuros do Workbench, incluindo administrativos, dashboards, login e cadastro. Leia README.md e docs/STANDARDS.md antes de alterar interfaces.
8
18
  - Antes de criar UI em um app, procure o componente, variante ou padrão existente aqui. Reutilize-o diretamente como dependência.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Majapi UI
2
2
 
3
- Biblioteca pública `@majapisoftwares/ui`, base obrigatória de código, interface e experiência de **todos os apps atuais e futuros do Workbench**, baseada em shadcn/ui (**Base UI / base-nova**), React e Tailwind CSS v4. Repositório: https://github.com/majapisoftwares/workbench-ui.
3
+ Biblioteca pública `@majapisoftwares/ui`, base obrigatória de código, interface e experiência de **todos os apps atuais e futuros do Workbench**, baseada em shadcn/ui (**Base UI / base-luma**), React e Tailwind CSS v4. Repositório: https://github.com/majapisoftwares/workbench-ui.
4
4
 
5
5
  ## Regra central
6
6
 
@@ -10,7 +10,7 @@ Composição de páginas, lógica de domínio, dados e permissões pertencem ao
10
10
 
11
11
  ## Criar um app Next.js
12
12
 
13
- O starter completo fica em [starters/next-app](starters/next-app), com **Next.js 16.4.3** (latest estável verificado na criação), React, App Router, TypeScript, Tailwind e esta biblioteca já configurados. A dependência da UI é uma versão npm fixa; não depende de caminhos para este checkout.
13
+ O starter completo fica em [starters/next-app](starters/next-app), com **Next.js 16.4.3** (versão estável mais recente; mantenha atualizada), React, App Router, TypeScript, Tailwind e esta biblioteca já configurados. A dependência da UI é uma versão npm fixa; não depende de caminhos para este checkout.
14
14
 
15
15
  Dentro deste repositório:
16
16
 
@@ -21,22 +21,24 @@ npm ci
21
21
  npm run dev
22
22
  ```
23
23
 
24
- O comando não sobrescreve diretórios existentes. Copia dashboard, login, cadastro, carregamento/erro/404, regras de agentes, lockfile e CI, sem node_modules, builds ou secrets. Customize por componentes, props e slots. As telas de autenticação são demonstrativas e devem ser conectadas à autenticação real do produto.
24
+ O app gerado é bilíngue (en/pt-BR, textos em `src/messages.ts`) e roda via [portless](https://www.npmjs.com/package/portless) em `https://<nome>.localhost`. O comando não sobrescreve diretórios existentes. Copia dashboard, login, cadastro, carregamento/erro/404, regras de agentes, lockfile e CI, sem node_modules, builds ou secrets. Customize por componentes, props e slots. As telas de autenticação são demonstrativas e devem ser conectadas à autenticação real do produto.
25
25
 
26
26
  ## Catálogo visual
27
27
 
28
+ O catálogo em [catalog/](catalog) é um app Next.js com a mesma estrutura do starter, exportado estaticamente para S3 + CloudFront pelo workflow `deploy-catalog` a cada push na `main`.
29
+
28
30
  ```powershell
29
31
  npm ci
30
32
  npm run dev
31
33
  ```
32
34
 
33
- Abra http://127.0.0.1:4318. O catálogo permite revisar componentes, temas claro/escuro, tabela com busca/ordenação/paginação/filtro, detalhes e estados de vazio/erro/carregamento. Usa dados demonstrativos explícitos, sem AWS ou credenciais.
35
+ Abra https://majapi-ui.localhost (via portless). O catálogo permite revisar componentes, temas claro/escuro, tabela com busca/ordenação/paginação/filtro, detalhes e estados de vazio/erro/carregamento. Usa dados demonstrativos explícitos, sem AWS ou credenciais.
34
36
 
35
37
  ## Consumir
36
38
 
37
- Pacote público **@majapisoftwares/ui@5.0.0**, publicado na organização npm existente. Instale com `npm install --save-exact @majapisoftwares/ui@5.0.0` e versione o lockfile. O pacote exporta fontes TSX para bundlers React/TypeScript (Vite ou Next.js), não para execução direta em Node sem transformação. O starter e o gerador permanecem neste repositório Git, separados do pacote npm da biblioteca.
39
+ Pacote público **@majapisoftwares/ui@6.0.0**, publicado na organização npm existente. Instale com `npm install --save-exact @majapisoftwares/ui@6.0.0` e versione o lockfile. O pacote exporta fontes TSX para bundlers React/TypeScript (Vite ou Next.js), não para execução direta em Node sem transformação. O starter e o gerador permanecem neste repositório Git, separados do pacote npm da biblioteca.
38
40
 
39
- O catálogo público está em **https://ui.majapi.com**. A versão 5 substitui a base 4.x com uma mudança incompatível de API e a base antes chamada @workbench/ui. Apps existentes em ^4 permanecem nessa linha até migração explícita.
41
+ O catálogo público está em **https://ui.majapi.com**. A versão 6 muda AppShell, AdminDashboard e os templates de autenticação; veja [docs/MIGRATION-6.0.md](docs/MIGRATION-6.0.md). A versão 5 substitui a base 4.x com uma mudança incompatível de API e a base antes chamada @workbench/ui. Apps existentes em ^4 permanecem nessa linha até migração explícita.
40
42
 
41
43
  ```tsx
42
44
  import { Button } from "@majapisoftwares/ui/components/button";
@@ -45,18 +47,23 @@ import { AdminDashboard } from "@majapisoftwares/ui/templates/admin-dashboard";
45
47
  import { AuthLayout } from "@majapisoftwares/ui/templates/auth-layout";
46
48
  import { LoginForm } from "@majapisoftwares/ui/templates/login-form";
47
49
  import { SignupForm } from "@majapisoftwares/ui/templates/signup-form";
50
+ import {
51
+ Preferences,
52
+ PreferenceControls,
53
+ } from "@majapisoftwares/ui/patterns/preferences";
54
+ import { defineMessages, useMessages } from "@majapisoftwares/ui/lib/i18n";
48
55
  import "@majapisoftwares/ui/styles.css";
49
56
  ```
50
57
 
51
- O CSS inclui os tokens, fontes locais e o Tailwind, com `@source` da biblioteca. O app precisa processar esse CSS com Tailwind v4. Faça dedupe de react/react-dom no Vite se estiver desenvolvendo com um checkout local. Dependências de catálogo, como Vite, não entram no código executado pelo consumidor.
58
+ O CSS inclui os tokens, fontes locais e o Tailwind, com `@source` da biblioteca. O app precisa processar esse CSS com Tailwind v4.
52
59
 
53
60
  ## Contribuir e validar
54
61
 
55
- Templates iniciais: **AdminDashboard**, **AuthLayout**, **LoginForm** e **SignupForm**. Configure marca, navegação, textos, dados, ações e conteúdo por props/slots React. Variações de tema são definidas e documentadas na biblioteca. Formulários recebem callbacks tipados; autenticação real, validação no servidor, sessões e autorização pertencem ao app. O catálogo demonstra os templates sem enviar ou persistir senhas.
62
+ A sidebar do `AppShell` tem três modos (expandida, compacta e escondida), redimensionável pela borda, com troca de modo pela largura, menu de clique direito e Ctrl/⌘+B. Templates iniciais: **AdminDashboard**, **AuthLayout**, **LoginForm** e **SignupForm**. Configure marca, navegação, textos, dados, ações e conteúdo por props/slots React. Variações de tema são definidas e documentadas na biblioteca. Formulários recebem callbacks tipados; autenticação real, validação no servidor, sessões e autorização pertencem ao app. O catálogo demonstra os templates sem enviar ou persistir senhas.
56
63
 
57
64
  1. Leia o padrão e procure o primitivo/padrão existente.
58
- 2. Adicione uma API reutilizável ou variante oficial, com defaults compatíveis.
59
- 3. Documente motivos, estados e migração; adicione exemplo no catálogo.
65
+ 2. Adicione uma API reutilizável ou variante oficial, com defaults compatíveis e textos em en/pt-BR.
66
+ 3. Documente motivos, estados e migração; adicione exemplo no catálogo e atualize o starter no mesmo commit.
60
67
  4. Valide tipos, lint, padrões, build, teclado e visual desktop/mobile.
61
68
  5. Publique o commit e atualize o consumidor para esse commit; registre no plano.
62
69
 
@@ -65,7 +72,8 @@ npm run typecheck
65
72
  npm run lint
66
73
  npm run check:standards
67
74
  npm run build
75
+ npm run test:starter
68
76
  git diff --check
69
77
  ```
70
78
 
71
- Os componentes foram instalados pelo CLI shadcn 4.22.1, preset base-nova e Base UI 1.9.0. A versão 0.2 migra a base anterior Radix; veja [docs/MIGRATION-0.2.md](docs/MIGRATION-0.2.md). Atribuição upstream: [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). O Cloud Manager é o primeiro consumidor real.
79
+ Os componentes foram instalados pelo CLI shadcn 4.22.1, preset base-luma e Base UI 1.9.0. A versão 0.2 migra a base anterior Radix; veja [docs/MIGRATION-0.2.md](docs/MIGRATION-0.2.md). Atribuição upstream: [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). O Cloud Manager é o primeiro consumidor real.
@@ -1,6 +1,6 @@
1
1
  # Third-party notices
2
2
 
3
- The primitives in src/components/ui were generated with shadcn/ui 4.22.1, base-nova preset (Base UI), from https://github.com/shadcn-ui/ui and https://ui.shadcn.com. They are distributed under the MIT License:
3
+ The primitives in src/components/ui were generated with shadcn/ui 4.22.1, base-luma preset (Base UI), from https://github.com/shadcn-ui/ui and https://ui.shadcn.com. They are distributed under the MIT License:
4
4
 
5
5
  MIT License
6
6
 
@@ -12,4 +12,28 @@ The above copyright notice and this permission notice shall be included in all c
12
12
 
13
13
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
14
 
15
+ src/components/ui/orb.tsx and orb.css are adapted from AICSS Orbs (https://github.com/kvnkld/aicss, registry https://www.aicss.dev/r/orbs.json): colors mapped to theme tokens, labels translated, CSS module turned into orb-prefixed global CSS. MIT License:
16
+
17
+ MIT License
18
+
19
+ Copyright (c) 2026 AICSS
20
+
21
+ Permission is hereby granted, free of charge, to any person obtaining a copy
22
+ of this software and associated documentation files (the "Software"), to deal
23
+ in the Software without restriction, including without limitation the rights
24
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25
+ copies of the Software, and to permit persons to whom the Software is
26
+ furnished to do so, subject to the following conditions:
27
+
28
+ The above copyright notice and this permission notice shall be included in all
29
+ copies or substantial portions of the Software.
30
+
31
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37
+ SOFTWARE.
38
+
15
39
  Other dependencies retain their own licenses in their package metadata. This notice does not change ownership or licensing of Workbench-authored code.
package/components.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://ui.shadcn.com/schema.json",
3
- "style": "base-nova",
3
+ "style": "base-luma",
4
4
  "rsc": false,
5
5
  "tsx": true,
6
6
  "tailwind": {
@@ -0,0 +1,33 @@
1
+ # Migração para 6.0
2
+
3
+ Visual padrão do shadcn (tema neutral, Geist), textos em inglês e português e nova sidebar.
4
+
5
+ ## Idiomas
6
+
7
+ Envolva o app com `Preferences` (idioma + tema) ou `LocaleProvider`. Os textos embutidos da biblioteca seguem o idioma ativo. Textos do app vão em `defineMessages({ en, "pt-BR" })` e são lidos com `useMessages`.
8
+
9
+ ## AppShell / AdminDashboard
10
+
11
+ - `brand`, `brandMark` → `logo` (marca quadrada, fica fixa em todos os modos) e `name` (texto que some em fade no modo compacto).
12
+ - `navigation` passa a ser uma lista de grupos: `[{ label?, items: [...] }]`.
13
+ - `context` → `header` (ex.: breadcrumb) e `actions` (ex.: `PreferenceControls`).
14
+ - `defaultMode` (`open` | `compact` | `closed`) e `defaultWidth` opcionais; o estado é salvo no cookie `sidebar_state` (`parseSidebarCookie` lê no servidor).
15
+ - `AdminDashboard`: `actions` da página → `pageActions`; `description` opcional.
16
+
17
+ ## Autenticação
18
+
19
+ - `AuthLayout`: `title`, `description`, `aside`, `brandMark` removidos; `brand` e `footer` opcionais.
20
+ - `LoginForm`/`SignupForm`: títulos e rótulos têm padrão traduzido; props de texto continuam aceitas para sobrescrever.
21
+
22
+ ## Outros
23
+
24
+ - `PageHeader`: `eyebrow` removido, `description` opcional.
25
+ - `DetailsSheet`: `description` opcional.
26
+ - `DataTable` agora é a tabela completa da Numa, sobre TanStack Table:
27
+ - `columns` são `ColumnDef` do TanStack (`accessorKey`/`accessorFn`, `cell`, `meta: { label, align, filter }`); `rows` → `data`.
28
+ - Busca global embutida (`searchPlaceholder`, `initialGlobalFilter`); `searchValue`/`searchLabel`/`defaultQuery` saem.
29
+ - `onOpen`/`rowLabel` → `onRowClick`/`getRowLabel`; `filters` → `toolbarStart`/`toolbarEnd`/`toolbarBelow` (recebem a instância da tabela).
30
+ - Filtros por coluna (`text`, `multi` com incluir/excluir, `number-range`, `date-range`, `custom`), ordenação múltipla, colunas reordenáveis/ocultáveis/redimensionáveis, seleção de linhas e persistência em `localStorage` com `storageKey`.
31
+ - Estados mantidos: `loading`, `error` + `onRetry`, `warning`, `unavailable`, `emptyState`/`emptyMessage`.
32
+ - Classes `wb-*` removidas.
33
+ - `NativeSelect` removido: use `Select` (Base UI) com `items` para o rótulo do valor. O `check:ui` acusa imports de `native-select`. Variante `navigation` do Button removida.
package/docs/STANDARDS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Padrão v1 de código e UI/UX
2
2
 
3
- Base atual: shadcn base-nova com @base-ui/react. Os componentes interativos expõem fronteiras use client para uso no Next.js App Router. A migração da API Radix está documentada em MIGRATION-0.2.md.
3
+ Base atual: shadcn base-luma com @base-ui/react. Os componentes interativos expõem fronteiras use client para uso no Next.js App Router. A migração da API Radix está documentada em MIGRATION-0.2.md.
4
4
 
5
5
  ## Arquitetura e código
6
6
 
@@ -8,19 +8,21 @@ TypeScript em modo strict, React com componentes funcionais e composição. APIs
8
8
 
9
9
  ## Base visual
10
10
 
11
- `src/styles.css` é a fonte dos tokens de cor, tipografia, raio, bordas, foco, estados e estrutura. Paleta neutra com ação primária terracota; body Geist, títulos Georgia; foco visível e espaço consistente. Cores são semânticas: background, foreground, primary, muted, destructive, success e warning. Temas claro/escuro preservam a semântica. Tokens e padrões podem evoluir nesta biblioteca, nunca por overrides locais em um produto.
11
+ `src/styles.css` é a fonte dos tokens de cor, tipografia, raio, bordas, foco, estados e estrutura. Preset base-luma do shadcn (o mesmo da home de ui.shadcn.com: cantos bem arredondados, campos com fundo suave, sombras leves) sobre o tema neutral, fonte Geist, foco visível e espaço consistente. Cores são semânticas: background, foreground, primary, muted, destructive, success e warning. Temas claro/escuro preservam a semântica. Tokens e padrões podem evoluir nesta biblioteca, nunca por overrides locais em um produto.
12
12
 
13
- Os 13 primitivos shadcn são Button, Badge, Input, Table, Sheet, Alert, Empty, NativeSelect, Separator, Skeleton, Tabs, Field e Label. Os sete padrões compostos são AppShell, PageHeader, MetricStrip, StatusBadge, DataTable, DetailsSheet e DetailList. Os quatro templates React são AdminDashboard, AuthLayout, LoginForm e SignupForm. Marca, navegação, textos, dados, ações e slots são configuráveis. Todos os apps atuais e futuros devem consumir esta base pública; ela não conhece AWS ou outro domínio.
13
+ Primitivos shadcn ficam em src/components/ui (Button, Badge, Card, Input, Table, Sheet, Sidebar, Tooltip, DropdownMenu, ContextMenu, Breadcrumb, Avatar, Checkbox, Collapsible, Alert, Empty, Select, Separator, Skeleton, Tabs, Field e Label). Padrões compostos: AppShell (slots `sidebarHeader` e `footer`), WorkspaceSwitcher, UserMenu, PageHeader, MetricStrip, StatusBadge, DataTable, DetailsSheet, DetailList, Preferences, PreferenceControls e MajapiLogo. Templates React: AdminDashboard, AuthLayout, LoginForm e SignupForm. Marca, navegação, textos, dados, ações e slots são configuráveis. Todos os apps atuais e futuros devem consumir esta base pública; ela não conhece AWS ou outro domínio.
14
14
 
15
15
  ## Comportamentos de UX
16
16
 
17
17
  - Ação primária destaca o objetivo; secundária usa outline/secondary; destrutiva usa variante própria e depende das autorizações do app.
18
- - Busca, filtros, ordenação por coluna e paginação seguem DataTable. Filtros de domínio são slots tipados do app. Não reconstruir tabelas por tela. Persistência não é habilitada por padrão para evitar guardar buscas sensíveis; quando necessária, deve evoluir como capacidade compartilhada com controle explícito.
18
+ - Busca, filtros, ordenação por coluna e paginação seguem DataTable (TanStack Table; origem: tabela da Numa). Filtros por coluna usam `meta.filter`; preferências do usuário persistem por `storageKey`. Telas centradas em uma tabela usam `AppShell fillHeight` + `DataTable fillHeight`: a tabela ocupa a altura restante e rola internamente, com cabeçalho, busca e paginação sempre visíveis.
19
+ - Áreas com rolagem interna usam `scroll-fade` do shadcn (só `scroll-fade-b` quando há cabeçalho fixo dentro do contêiner) e as barras de rolagem finas globais, que aparecem com o ponteiro sobre a área e seguem o tema (`color-scheme`).
20
+ - Espaçamento da página em uma escala só: padding, gap entre seções e entre cards de 16px no mobile e 24px a partir de md; o cabeçalho da página e o da sidebar têm a mesma altura (56px). Filtros de domínio são slots tipados do app. Não reconstruir tabelas por tela. Persistência não é habilitada por padrão para evitar guardar buscas sensíveis; quando necessária, deve evoluir como capacidade compartilhada com controle explícito.
19
21
  - Estados com texto e cor: sucesso, aviso, erro e desconhecido. Nunca traduzir ausência de informação em zero ou sucesso.
20
22
  - Carregamento usa Skeleton, vazio usa Empty, falhas usam Alert e detalhes usam DetailsSheet com título, descrição, foco preso ao overlay, Escape e devolução do foco ao fechar.
21
23
  - Em telas pequenas, controles têm texto de pelo menos 16px e alvo de 44px, navegação se reorganiza e tabelas têm rolagem interna, sem rolagem horizontal da página.
22
24
  - Preferência por movimento reduzido é respeitada. A ordem de leitura deve acompanhar a ordem visual. Ícones sozinhos exigem nome acessível.
23
- - Distinguir dados demonstrativos de dados reais; mostrar observação e cobertura em telas de infraestrutura. Comunicação e labels em português no v1.
25
+ - Distinguir dados demonstrativos de dados reais; mostrar observação e cobertura em telas de infraestrutura. Todo texto existe em inglês e português (`defineMessages`); texto mínimo, sem descrições que não ajudem a tarefa.
24
26
 
25
27
  ## Evolução sem customizações isoladas
26
28
 
@@ -30,7 +32,10 @@ AGENTS.md nos apps exige contribuição upstream antes de uso. O verificador est
30
32
 
31
33
  ## Adaptações do upstream
32
34
 
33
- - Sheet usa import interno relativo para funcionar como pacote e traduz o nome acessível do fechamento para português.
35
+ - Imports internos relativos e `"use client"` são aplicados por `npm run normalize:ui` após o CLI do shadcn.
36
+ - Sheet traduz o nome acessível do fechamento pelo idioma ativo.
37
+ - Sidebar: três modos (open/compact/closed), largura redimensionável (208–448px) com troca de modo por largura, menu de clique direito, tooltip que fecha ao clicar e textos traduzidos.
38
+ - use-mobile usa useSyncExternalStore (compatível com SSR e com a regra de hooks).
34
39
  - Field usa imports relativos. Button assume type=button para evitar submissões acidentais; formulários declaram type=submit. Badge inclui variantes success e warning reutilizadas por StatusBadge.
35
40
  - Tokens de identidade, estados semânticos e padrões compostos são mantidos aqui.
36
41
  - Mudanças posteriores no upstream devem ser avaliadas com o diff do CLI, preservando estas decisões e o contrato público.
@@ -0,0 +1,28 @@
1
+ # Versionamento
2
+
3
+ `@majapisoftwares/ui` segue [SemVer](https://semver.org) sobre a **API pública**: subpaths exportados no `package.json`, nomes de componentes, props e seus tipos, variantes, tokens CSS, classes documentadas (ex.: `orb-*`) e comportamento que um app possa ter usado.
4
+
5
+ ## Qual número subir
6
+
7
+ | Mudança | Versão |
8
+ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
9
+ | Correção de bug, acessibilidade, tradução, ajuste visual que não muda a API | **patch** |
10
+ | Componente, padrão, export, prop opcional ou variante novos; mudança de visual padrão (preset, tokens) que não exige alteração no código do app; deprecação | **minor** |
11
+ | Remover ou renomear export, componente, prop, variante ou token; tornar prop obrigatória; mudar o tipo ou o significado de uma prop; mudança de comportamento que obriga o app a mudar código | **major** |
12
+
13
+ Na dúvida, a pergunta é: **um app que compilava com a versão anterior continua compilando e funcionando sem alterações?** Se sim, não é major.
14
+
15
+ ## Evitar majors
16
+
17
+ - Prefira mudanças aditivas: nova prop ao lado da antiga, novo componente em vez de mudar o existente.
18
+ - Para trocar uma API, mantenha a antiga funcionando, marque-a com `@deprecated` (JSDoc, com a alternativa) e só remova no próximo major planejado.
19
+ - Agrupe remoções num único major, com `docs/MIGRATION-<versão>.md` e os consumidores conhecidos atualizados em seguida.
20
+ - Um major precisa de aprovação explícita do responsável antes da publicação; o agente propõe e justifica.
21
+
22
+ ## Publicar
23
+
24
+ 1. Escolha o número pela tabela acima e justifique no commit (`feat:` → minor, `fix:` → patch, `feat!:`/`BREAKING CHANGE:` → major).
25
+ 2. Atualize `version` no `package.json`; o starter acompanha a mesma versão no mesmo commit (`npm run test:starter` verifica).
26
+ 3. Valide (typecheck, lint, check:standards, build, test:starter, starter contra o pacote empacotado).
27
+ 4. `npm publish`, tag `v<versão>` no GitHub e registre no plano.
28
+ 5. Apps fixam a versão exata (`--save-exact`) e atualizam por PR.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@majapisoftwares/ui",
3
- "version": "5.0.0",
3
+ "version": "6.1.0",
4
4
  "description": "Shared Workbench UI and UX standards based on shadcn/ui",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -26,18 +26,21 @@
26
26
  "./components/*": "./src/components/ui/*.tsx",
27
27
  "./patterns/*": "./src/patterns/*.tsx",
28
28
  "./templates/*": "./src/templates/*.tsx",
29
+ "./hooks/*": "./src/hooks/*.ts",
29
30
  "./styles.css": "./src/styles.css",
30
- "./lib/utils": "./src/lib/utils.ts"
31
+ "./lib/utils": "./src/lib/utils.ts",
32
+ "./lib/i18n": "./src/lib/i18n.tsx"
31
33
  },
32
34
  "scripts": {
33
35
  "create:app": "node scripts/create-app.mjs",
34
36
  "test:starter": "node --test scripts/create-app.test.mjs",
35
- "dev": "vite --host 127.0.0.1 --port 4318 --strictPort",
36
- "build": "tsc --noEmit && vite build",
37
+ "dev": "npm run dev -w catalog",
38
+ "build": "node scripts/write-release.mjs && tsc --noEmit && npm run build -w catalog",
37
39
  "typecheck": "tsc --noEmit",
38
40
  "lint": "eslint . --max-warnings=0",
39
41
  "format:check": "prettier --check src examples scripts *.json *.ts *.md docs/*.md",
40
- "check:standards": "node scripts/check-standards.mjs"
42
+ "check:standards": "node scripts/check-standards.mjs",
43
+ "normalize:ui": "node scripts/normalize-ui.mjs && prettier --write src/components/ui src/hooks"
41
44
  },
42
45
  "peerDependencies": {
43
46
  "react": "19.3.1",
@@ -48,28 +51,34 @@
48
51
  },
49
52
  "dependencies": {
50
53
  "@base-ui/react": "1.9.0",
54
+ "@dnd-kit/core": "6.3.1",
55
+ "@dnd-kit/modifiers": "9.0.0",
56
+ "@dnd-kit/sortable": "10.0.0",
57
+ "@dnd-kit/utilities": "3.2.2",
51
58
  "@fontsource-variable/geist": "^5.3.0",
59
+ "@tanstack/react-table": "8.21.3",
52
60
  "class-variance-authority": "0.7.1",
53
61
  "clsx": "2.1.1",
54
62
  "cn": "^0.5.0",
55
63
  "lucide-react": "1.61.0",
56
64
  "shadcn": "^4.22.1",
65
+ "sonner": "^2.0.8",
57
66
  "tailwind-merge": "3.7.0",
58
67
  "tw-animate-css": "^1.4.0"
59
68
  },
60
69
  "devDependencies": {
61
70
  "@eslint/js": "10.0.1",
62
- "@tailwindcss/vite": "4.3.3",
63
71
  "@types/node": "26.6.5",
64
72
  "@types/react": "19.3.0",
65
73
  "@types/react-dom": "19.3.0",
66
- "@vitejs/plugin-react": "6.1.2",
67
74
  "eslint": "10.13.0",
68
75
  "eslint-plugin-react-hooks": "7.1.1",
69
76
  "prettier": "3.9.10",
70
77
  "tailwindcss": "4.3.3",
71
78
  "typescript": "6.0.3",
72
- "typescript-eslint": "8.72.0",
73
- "vite": "8.3.5"
74
- }
79
+ "typescript-eslint": "8.72.0"
80
+ },
81
+ "workspaces": [
82
+ "catalog"
83
+ ]
75
84
  }
@@ -19,6 +19,8 @@ async function visit(path) {
19
19
  if (!consumer && name.startsWith("components/ui/")) continue;
20
20
  if (consumer && /(?:^|\/)components\/ui\//.test(name))
21
21
  violations.push(`${name}: local UI primitive copies are forbidden`);
22
+ if (/components\/native-select|<NativeSelect\b/.test(text))
23
+ violations.push(`${name}: use Select (Base UI), not a native select`);
22
24
  if (/<(?:button|input|select|table|dialog)(?:\s|>)/.test(text))
23
25
  violations.push(`${name}: use the shared UI primitive`);
24
26
  if (
@@ -0,0 +1,82 @@
1
+ "use client";
2
+
3
+ import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion";
4
+ import { cn } from "cn";
5
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
6
+
7
+ function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
8
+ return (
9
+ <AccordionPrimitive.Root
10
+ data-slot="accordion"
11
+ className={cn(
12
+ "flex w-full flex-col overflow-hidden rounded-2xl border",
13
+ className,
14
+ )}
15
+ {...props}
16
+ />
17
+ );
18
+ }
19
+
20
+ function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
21
+ return (
22
+ <AccordionPrimitive.Item
23
+ data-slot="accordion-item"
24
+ className={cn("not-last:border-b data-open:bg-muted/50", className)}
25
+ {...props}
26
+ />
27
+ );
28
+ }
29
+
30
+ function AccordionTrigger({
31
+ className,
32
+ children,
33
+ ...props
34
+ }: AccordionPrimitive.Trigger.Props) {
35
+ return (
36
+ <AccordionPrimitive.Header className="flex">
37
+ <AccordionPrimitive.Trigger
38
+ data-slot="accordion-trigger"
39
+ className={cn(
40
+ "group/accordion-trigger relative flex flex-1 items-start justify-between gap-6 border border-transparent p-4 text-left text-sm font-medium transition-all outline-none hover:underline aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
41
+ className,
42
+ )}
43
+ {...props}
44
+ >
45
+ {children}
46
+ <ChevronDownIcon
47
+ data-slot="accordion-trigger-icon"
48
+ className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden"
49
+ />
50
+ <ChevronUpIcon
51
+ data-slot="accordion-trigger-icon"
52
+ className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline"
53
+ />
54
+ </AccordionPrimitive.Trigger>
55
+ </AccordionPrimitive.Header>
56
+ );
57
+ }
58
+
59
+ function AccordionContent({
60
+ className,
61
+ children,
62
+ ...props
63
+ }: AccordionPrimitive.Panel.Props) {
64
+ return (
65
+ <AccordionPrimitive.Panel
66
+ data-slot="accordion-content"
67
+ className="overflow-hidden px-4 text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
68
+ {...props}
69
+ >
70
+ <div
71
+ className={cn(
72
+ "h-(--accordion-panel-height) pt-0 pb-4 data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
73
+ className,
74
+ )}
75
+ >
76
+ {children}
77
+ </div>
78
+ </AccordionPrimitive.Panel>
79
+ );
80
+ }
81
+
82
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
@@ -0,0 +1,187 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
5
+ import { cn } from "cn";
6
+
7
+ import { Button } from "./button";
8
+
9
+ function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
10
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
11
+ }
12
+
13
+ function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
14
+ return (
15
+ <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
16
+ );
17
+ }
18
+
19
+ function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
20
+ return (
21
+ <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
22
+ );
23
+ }
24
+
25
+ function AlertDialogOverlay({
26
+ className,
27
+ ...props
28
+ }: AlertDialogPrimitive.Backdrop.Props) {
29
+ return (
30
+ <AlertDialogPrimitive.Backdrop
31
+ data-slot="alert-dialog-overlay"
32
+ className={cn(
33
+ "fixed inset-0 isolate z-50 bg-black/30 duration-100 supports-backdrop-filter:backdrop-blur-sm data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
34
+ className,
35
+ )}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function AlertDialogContent({
42
+ className,
43
+ size = "default",
44
+ ...props
45
+ }: AlertDialogPrimitive.Popup.Props & {
46
+ size?: "default" | "sm";
47
+ }) {
48
+ return (
49
+ <AlertDialogPortal>
50
+ <AlertDialogOverlay />
51
+ <AlertDialogPrimitive.Popup
52
+ data-slot="alert-dialog-content"
53
+ data-size={size}
54
+ className={cn(
55
+ "group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-6 rounded-4xl bg-popover p-6 text-popover-foreground shadow-xl ring-1 ring-foreground/5 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-md dark:ring-foreground/10 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
56
+ className,
57
+ )}
58
+ {...props}
59
+ />
60
+ </AlertDialogPortal>
61
+ );
62
+ }
63
+
64
+ function AlertDialogHeader({
65
+ className,
66
+ ...props
67
+ }: React.ComponentProps<"div">) {
68
+ return (
69
+ <div
70
+ data-slot="alert-dialog-header"
71
+ className={cn(
72
+ "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
73
+ className,
74
+ )}
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+
80
+ function AlertDialogFooter({
81
+ className,
82
+ ...props
83
+ }: React.ComponentProps<"div">) {
84
+ return (
85
+ <div
86
+ data-slot="alert-dialog-footer"
87
+ className={cn(
88
+ "flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
89
+ className,
90
+ )}
91
+ {...props}
92
+ />
93
+ );
94
+ }
95
+
96
+ function AlertDialogMedia({
97
+ className,
98
+ ...props
99
+ }: React.ComponentProps<"div">) {
100
+ return (
101
+ <div
102
+ data-slot="alert-dialog-media"
103
+ className={cn(
104
+ "mb-2 inline-flex size-16 items-center justify-center rounded-full bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
105
+ className,
106
+ )}
107
+ {...props}
108
+ />
109
+ );
110
+ }
111
+
112
+ function AlertDialogTitle({
113
+ className,
114
+ ...props
115
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
116
+ return (
117
+ <AlertDialogPrimitive.Title
118
+ data-slot="alert-dialog-title"
119
+ className={cn(
120
+ "font-heading text-lg font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
121
+ className,
122
+ )}
123
+ {...props}
124
+ />
125
+ );
126
+ }
127
+
128
+ function AlertDialogDescription({
129
+ className,
130
+ ...props
131
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
132
+ return (
133
+ <AlertDialogPrimitive.Description
134
+ data-slot="alert-dialog-description"
135
+ className={cn(
136
+ "text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
137
+ className,
138
+ )}
139
+ {...props}
140
+ />
141
+ );
142
+ }
143
+
144
+ function AlertDialogAction({
145
+ className,
146
+ ...props
147
+ }: React.ComponentProps<typeof Button>) {
148
+ return (
149
+ <Button
150
+ data-slot="alert-dialog-action"
151
+ className={cn(className)}
152
+ {...props}
153
+ />
154
+ );
155
+ }
156
+
157
+ function AlertDialogCancel({
158
+ className,
159
+ variant = "outline",
160
+ size = "default",
161
+ ...props
162
+ }: AlertDialogPrimitive.Close.Props &
163
+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
164
+ return (
165
+ <AlertDialogPrimitive.Close
166
+ data-slot="alert-dialog-cancel"
167
+ className={cn(className)}
168
+ render={<Button variant={variant} size={size} />}
169
+ {...props}
170
+ />
171
+ );
172
+ }
173
+
174
+ export {
175
+ AlertDialog,
176
+ AlertDialogAction,
177
+ AlertDialogCancel,
178
+ AlertDialogContent,
179
+ AlertDialogDescription,
180
+ AlertDialogFooter,
181
+ AlertDialogHeader,
182
+ AlertDialogMedia,
183
+ AlertDialogOverlay,
184
+ AlertDialogPortal,
185
+ AlertDialogTitle,
186
+ AlertDialogTrigger,
187
+ };
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
5
5
  import { cn } from "cn";
6
6
 
7
7
  const alertVariants = cva(
8
- "group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
8
+ "group/alert relative grid w-full gap-0.5 rounded-2xl border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
9
9
  {
10
10
  variants: {
11
11
  variant: {
@@ -68,7 +68,7 @@ function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
68
68
  return (
69
69
  <div
70
70
  data-slot="alert-action"
71
- className={cn("absolute top-2 right-2", className)}
71
+ className={cn("absolute top-2.5 right-3", className)}
72
72
  {...props}
73
73
  />
74
74
  );