@majapisoftwares/ui 6.0.0 → 6.1.2
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/AGENTS.md +27 -27
- package/README.md +79 -79
- package/THIRD_PARTY_NOTICES.md +39 -39
- package/components.json +25 -25
- package/docs/MIGRATION-0.2.md +20 -20
- package/docs/MIGRATION-6.0.md +33 -33
- package/docs/NEXT-STARTER-VALIDATION.md +14 -14
- package/docs/STANDARDS.md +41 -41
- package/docs/VALIDATION.md +11 -11
- package/docs/VERSIONING.md +28 -28
- package/docs/validation/preference-titles.md +22 -0
- package/package.json +88 -84
- package/scripts/check-standards.mjs +41 -41
- package/src/components/ui/accordion.tsx +82 -82
- package/src/components/ui/alert-dialog.tsx +187 -187
- package/src/components/ui/alert.tsx +77 -77
- package/src/components/ui/avatar.tsx +108 -108
- package/src/components/ui/badge.tsx +55 -55
- package/src/components/ui/breadcrumb.tsx +125 -125
- package/src/components/ui/button-group.tsx +89 -89
- package/src/components/ui/button.tsx +59 -59
- package/src/components/ui/card.tsx +101 -101
- package/src/components/ui/checkbox.tsx +27 -27
- package/src/components/ui/collapsible.tsx +21 -21
- package/src/components/ui/context-menu.tsx +271 -271
- package/src/components/ui/dialog.tsx +161 -161
- package/src/components/ui/dropdown-menu.tsx +271 -271
- package/src/components/ui/empty.tsx +105 -105
- package/src/components/ui/field.tsx +238 -238
- package/src/components/ui/hover-card.tsx +50 -50
- package/src/components/ui/input-group.tsx +155 -155
- package/src/components/ui/input.tsx +21 -21
- package/src/components/ui/kbd.tsx +28 -28
- package/src/components/ui/label.tsx +19 -19
- package/src/components/ui/orb.css +1002 -1002
- package/src/components/ui/orb.tsx +752 -752
- package/src/components/ui/popover.tsx +89 -89
- package/src/components/ui/progress.tsx +82 -82
- package/src/components/ui/radio-group.tsx +37 -37
- package/src/components/ui/scroll-area.tsx +54 -54
- package/src/components/ui/select.tsx +204 -204
- package/src/components/ui/separator.tsx +24 -24
- package/src/components/ui/sheet.tsx +139 -139
- package/src/components/ui/sidebar.tsx +1039 -1035
- package/src/components/ui/skeleton.tsx +15 -15
- package/src/components/ui/slider.tsx +53 -53
- package/src/components/ui/sonner.tsx +44 -44
- package/src/components/ui/spinner.tsx +32 -32
- package/src/components/ui/switch.tsx +31 -31
- package/src/components/ui/table.tsx +121 -121
- package/src/components/ui/tabs.tsx +81 -81
- package/src/components/ui/textarea.tsx +19 -19
- package/src/components/ui/toggle-group.tsx +89 -89
- package/src/components/ui/toggle.tsx +44 -44
- package/src/components/ui/tooltip.tsx +65 -65
- package/src/hooks/use-mobile.ts +20 -20
- package/src/lib/i18n.tsx +109 -109
- package/src/lib/utils.ts +1 -1
- package/src/patterns/app-shell.tsx +177 -175
- package/src/patterns/data-table.tsx +2486 -2486
- package/src/patterns/details-sheet.tsx +54 -53
- package/src/patterns/filter-tabs.tsx +53 -53
- package/src/patterns/majapi-logo.tsx +32 -32
- package/src/patterns/page-header.tsx +62 -62
- package/src/patterns/preferences.tsx +15 -7
- package/src/patterns/status-badge.tsx +24 -24
- package/src/patterns/user-menu.tsx +218 -0
- package/src/patterns/workspace-switcher.tsx +176 -0
- package/src/styles.css +165 -165
- package/src/templates/admin-dashboard.tsx +44 -44
- package/src/templates/auth-layout.tsx +30 -30
- package/src/templates/login-form.tsx +129 -129
- package/src/templates/signup-form.tsx +177 -177
package/docs/VALIDATION.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# Validação da base v1
|
|
2
|
-
|
|
3
|
-
- TypeScript strict, ESLint sem warnings, verificador de padrões, Prettier e build Vite aprovados.
|
|
4
|
-
- CI pública executa npm ci, typecheck, lint, check:standards e build, sem secrets ou acesso à AWS.
|
|
5
|
-
- Catálogo renderizado em navegador real em desktop e mobile (viewport de 500px), incluindo template administrativo, login e cadastro.
|
|
6
|
-
- Cadastro demonstrativo: confirmação divergente de senha foi bloqueada, campo marcado aria-invalid e erro anunciado por role=alert. Nenhuma informação foi enviada ou persistida.
|
|
7
|
-
- DataTable demonstrativa possui busca, filtro por região, ordenação e paginação; DetailsSheet usa Radix com título e descrição. Estados de vazio, erro e carregamento estão expostos no catálogo.
|
|
8
|
-
- O primeiro consumidor real é o Cloud Manager, que importa a biblioteca por commit público fixo e continua exibindo o inventário real AWS. Detalhes de infraestrutura e credenciais permanecem no repo privado do produto.
|
|
9
|
-
- Ajustes de navegação e toolbar mobile foram feitos nos componentes compartilhados e publicados antes de atualizar o consumidor, exercitando a regra de contribuição upstream.
|
|
10
|
-
|
|
11
|
-
Esta revisão é uma validação inicial de implementação, não uma certificação de acessibilidade nem prova de integração de autenticação. Backend de login/cadastro pertence a cada produto e deve ser validado pelo consumidor.
|
|
1
|
+
# Validação da base v1
|
|
2
|
+
|
|
3
|
+
- TypeScript strict, ESLint sem warnings, verificador de padrões, Prettier e build Vite aprovados.
|
|
4
|
+
- CI pública executa npm ci, typecheck, lint, check:standards e build, sem secrets ou acesso à AWS.
|
|
5
|
+
- Catálogo renderizado em navegador real em desktop e mobile (viewport de 500px), incluindo template administrativo, login e cadastro.
|
|
6
|
+
- Cadastro demonstrativo: confirmação divergente de senha foi bloqueada, campo marcado aria-invalid e erro anunciado por role=alert. Nenhuma informação foi enviada ou persistida.
|
|
7
|
+
- DataTable demonstrativa possui busca, filtro por região, ordenação e paginação; DetailsSheet usa Radix com título e descrição. Estados de vazio, erro e carregamento estão expostos no catálogo.
|
|
8
|
+
- O primeiro consumidor real é o Cloud Manager, que importa a biblioteca por commit público fixo e continua exibindo o inventário real AWS. Detalhes de infraestrutura e credenciais permanecem no repo privado do produto.
|
|
9
|
+
- Ajustes de navegação e toolbar mobile foram feitos nos componentes compartilhados e publicados antes de atualizar o consumidor, exercitando a regra de contribuição upstream.
|
|
10
|
+
|
|
11
|
+
Esta revisão é uma validação inicial de implementação, não uma certificação de acessibilidade nem prova de integração de autenticação. Backend de login/cadastro pertence a cada produto e deve ser validado pelo consumidor.
|
package/docs/VERSIONING.md
CHANGED
|
@@ -1,28 +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.
|
|
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.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Saved-language titles
|
|
2
|
+
|
|
3
|
+
The initial preference state matched server HTML in English. Child title effects
|
|
4
|
+
ran before browser preferences were loaded, briefly writing the English title.
|
|
5
|
+
The pre-paint visibility mask also expired after two seconds, independently of
|
|
6
|
+
whether hydration had completed.
|
|
7
|
+
|
|
8
|
+
`Preferences` now exposes internal readiness to `useDocumentTitle`. Titles are
|
|
9
|
+
written only after the stored/browser language is resolved. The pre-paint mask
|
|
10
|
+
is removed only when React has committed that language. The public API remains
|
|
11
|
+
compatible, including `useDocumentTitle` outside Preferences.
|
|
12
|
+
|
|
13
|
+
The catalog and generated starter consume the shared implementation. Cloud and
|
|
14
|
+
the starter pin the corrected public Git source archive by its full commit SHA;
|
|
15
|
+
no registry authentication or mutable dependency ref is required.
|
|
16
|
+
|
|
17
|
+
Validation: Portuguese and English hydration tests record every document-title
|
|
18
|
+
write and verify that all writes use the selected language. Library typecheck,
|
|
19
|
+
lint, standards and build pass. Generated-starter lint, typecheck, UI standards
|
|
20
|
+
and build pass. Local Chrome refresh shows `Padrões · Majapi UI` and
|
|
21
|
+
`Repositórios · Majapi Cloud`, with visible Portuguese content and the button
|
|
22
|
+
`Conectar repositórios`. No production deployment was performed.
|
package/package.json
CHANGED
|
@@ -1,84 +1,88 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@majapisoftwares/ui",
|
|
3
|
-
"version": "6.
|
|
4
|
-
"description": "Shared
|
|
5
|
-
"publishConfig": {
|
|
6
|
-
"access": "public"
|
|
7
|
-
},
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/majapisoftwares/
|
|
11
|
-
},
|
|
12
|
-
"homepage": "https://ui.majapi.com",
|
|
13
|
-
"type": "module",
|
|
14
|
-
"sideEffects": [
|
|
15
|
-
"**/*.css"
|
|
16
|
-
],
|
|
17
|
-
"files": [
|
|
18
|
-
"scripts/check-standards.mjs",
|
|
19
|
-
"src",
|
|
20
|
-
"components.json",
|
|
21
|
-
"docs",
|
|
22
|
-
"AGENTS.md",
|
|
23
|
-
"THIRD_PARTY_NOTICES.md"
|
|
24
|
-
],
|
|
25
|
-
"exports": {
|
|
26
|
-
"./components/*": "./src/components/ui/*.tsx",
|
|
27
|
-
"./patterns/*": "./src/patterns/*.tsx",
|
|
28
|
-
"./templates/*": "./src/templates/*.tsx",
|
|
29
|
-
"./hooks/*": "./src/hooks/*.ts",
|
|
30
|
-
"./styles.css": "./src/styles.css",
|
|
31
|
-
"./lib/utils": "./src/lib/utils.ts",
|
|
32
|
-
"./lib/i18n": "./src/lib/i18n.tsx"
|
|
33
|
-
},
|
|
34
|
-
"scripts": {
|
|
35
|
-
"create:app": "node scripts/create-app.mjs",
|
|
36
|
-
"test:starter": "node --test scripts/create-app.test.mjs",
|
|
37
|
-
"dev": "npm run dev -w catalog",
|
|
38
|
-
"build": "node scripts/write-release.mjs && tsc --noEmit && npm run build -w catalog",
|
|
39
|
-
"typecheck": "tsc --noEmit",
|
|
40
|
-
"lint": "eslint . --max-warnings=0",
|
|
41
|
-
"format:check": "prettier --check src examples scripts *.json *.ts *.md docs/*.md",
|
|
42
|
-
"check:standards": "node scripts/check-standards.mjs",
|
|
43
|
-
"normalize:ui": "node scripts/normalize-ui.mjs && prettier --write src/components/ui src/hooks"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"react
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"@
|
|
55
|
-
"@dnd-kit/
|
|
56
|
-
"@dnd-kit/
|
|
57
|
-
"@dnd-kit/
|
|
58
|
-
"@
|
|
59
|
-
"@
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"@
|
|
72
|
-
"@types/
|
|
73
|
-
"@types/
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@majapisoftwares/ui",
|
|
3
|
+
"version": "6.1.2",
|
|
4
|
+
"description": "Shared Majapi UI and UX standards based on shadcn/ui",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/majapisoftwares/ui.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://ui.majapi.com",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"sideEffects": [
|
|
15
|
+
"**/*.css"
|
|
16
|
+
],
|
|
17
|
+
"files": [
|
|
18
|
+
"scripts/check-standards.mjs",
|
|
19
|
+
"src",
|
|
20
|
+
"components.json",
|
|
21
|
+
"docs",
|
|
22
|
+
"AGENTS.md",
|
|
23
|
+
"THIRD_PARTY_NOTICES.md"
|
|
24
|
+
],
|
|
25
|
+
"exports": {
|
|
26
|
+
"./components/*": "./src/components/ui/*.tsx",
|
|
27
|
+
"./patterns/*": "./src/patterns/*.tsx",
|
|
28
|
+
"./templates/*": "./src/templates/*.tsx",
|
|
29
|
+
"./hooks/*": "./src/hooks/*.ts",
|
|
30
|
+
"./styles.css": "./src/styles.css",
|
|
31
|
+
"./lib/utils": "./src/lib/utils.ts",
|
|
32
|
+
"./lib/i18n": "./src/lib/i18n.tsx"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"create:app": "node scripts/create-app.mjs",
|
|
36
|
+
"test:starter": "node --test scripts/create-app.test.mjs",
|
|
37
|
+
"dev": "npm run dev -w catalog",
|
|
38
|
+
"build": "node scripts/write-release.mjs && tsc --noEmit && npm run build -w catalog",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"lint": "eslint . --max-warnings=0",
|
|
41
|
+
"format:check": "prettier --check src examples scripts *.json *.ts *.md docs/*.md",
|
|
42
|
+
"check:standards": "node scripts/check-standards.mjs",
|
|
43
|
+
"normalize:ui": "node scripts/normalize-ui.mjs && prettier --write src/components/ui src/hooks",
|
|
44
|
+
"test:preferences": "tsx --test scripts/preferences.test.tsx"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"react": "19.3.1",
|
|
48
|
+
"react-dom": "19.3.1"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=22"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@base-ui/react": "1.9.0",
|
|
55
|
+
"@dnd-kit/core": "6.3.1",
|
|
56
|
+
"@dnd-kit/modifiers": "9.0.0",
|
|
57
|
+
"@dnd-kit/sortable": "10.0.0",
|
|
58
|
+
"@dnd-kit/utilities": "3.2.2",
|
|
59
|
+
"@fontsource-variable/geist": "^5.3.0",
|
|
60
|
+
"@tanstack/react-table": "8.21.3",
|
|
61
|
+
"class-variance-authority": "0.7.1",
|
|
62
|
+
"clsx": "2.1.1",
|
|
63
|
+
"cn": "^0.5.0",
|
|
64
|
+
"lucide-react": "1.61.0",
|
|
65
|
+
"shadcn": "^4.22.1",
|
|
66
|
+
"sonner": "^2.0.8",
|
|
67
|
+
"tailwind-merge": "3.7.0",
|
|
68
|
+
"tw-animate-css": "^1.4.0"
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@eslint/js": "10.0.1",
|
|
72
|
+
"@types/jsdom": "30.0.0",
|
|
73
|
+
"@types/node": "26.6.5",
|
|
74
|
+
"@types/react": "19.3.0",
|
|
75
|
+
"@types/react-dom": "19.3.0",
|
|
76
|
+
"eslint": "10.13.0",
|
|
77
|
+
"eslint-plugin-react-hooks": "7.1.1",
|
|
78
|
+
"jsdom": "29.1.1",
|
|
79
|
+
"prettier": "3.9.10",
|
|
80
|
+
"tailwindcss": "4.3.3",
|
|
81
|
+
"tsx": "4.23.15",
|
|
82
|
+
"typescript": "6.0.3",
|
|
83
|
+
"typescript-eslint": "8.72.0"
|
|
84
|
+
},
|
|
85
|
+
"workspaces": [
|
|
86
|
+
"catalog"
|
|
87
|
+
]
|
|
88
|
+
}
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
-
import { resolve, relative } from "node:path";
|
|
3
|
-
const consumer = process.argv[2];
|
|
4
|
-
const root = resolve(consumer ?? "src");
|
|
5
|
-
const violations = [];
|
|
6
|
-
async function visit(path) {
|
|
7
|
-
for (const entry of await readdir(path, { withFileTypes: true })) {
|
|
8
|
-
if (["node_modules", "dist", ".git"].includes(entry.name)) continue;
|
|
9
|
-
const file = resolve(path, entry.name);
|
|
10
|
-
if (entry.isDirectory()) {
|
|
11
|
-
await visit(file);
|
|
12
|
-
continue;
|
|
13
|
-
}
|
|
14
|
-
if (!/\.[jt]sx?$/.test(entry.name)) continue;
|
|
15
|
-
const text = await readFile(file, "utf8"),
|
|
16
|
-
name = relative(root, file).replaceAll("\\", "/");
|
|
17
|
-
if (/from\s+["'](?:radix-ui|@radix-ui\/)/.test(text))
|
|
18
|
-
violations.push(`${name}: the Workbench foundation uses Base UI`);
|
|
19
|
-
if (!consumer && name.startsWith("components/ui/")) continue;
|
|
20
|
-
if (consumer && /(?:^|\/)components\/ui\//.test(name))
|
|
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`);
|
|
24
|
-
if (/<(?:button|input|select|table|dialog)(?:\s|>)/.test(text))
|
|
25
|
-
violations.push(`${name}: use the shared UI primitive`);
|
|
26
|
-
if (
|
|
27
|
-
/(?:bg|text|border)-(?:red|blue|green|orange|gray|slate|zinc)-\d|(?:bg|text|border)-\[#[a-f\d]/i.test(
|
|
28
|
-
text,
|
|
29
|
-
)
|
|
30
|
-
)
|
|
31
|
-
violations.push(`${name}: use semantic tokens instead of local colors`);
|
|
32
|
-
if (!consumer && /from ["']@\//.test(text))
|
|
33
|
-
violations.push(`${name}: library internals must use portable imports`);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
await visit(root);
|
|
37
|
-
if (violations.length) {
|
|
38
|
-
console.error(violations.join("\n"));
|
|
39
|
-
process.exitCode = 1;
|
|
40
|
-
} else
|
|
41
|
-
console.log(`UI standards passed (${consumer ? "consumer" : "library"}).`);
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { resolve, relative } from "node:path";
|
|
3
|
+
const consumer = process.argv[2];
|
|
4
|
+
const root = resolve(consumer ?? "src");
|
|
5
|
+
const violations = [];
|
|
6
|
+
async function visit(path) {
|
|
7
|
+
for (const entry of await readdir(path, { withFileTypes: true })) {
|
|
8
|
+
if (["node_modules", "dist", ".git"].includes(entry.name)) continue;
|
|
9
|
+
const file = resolve(path, entry.name);
|
|
10
|
+
if (entry.isDirectory()) {
|
|
11
|
+
await visit(file);
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (!/\.[jt]sx?$/.test(entry.name)) continue;
|
|
15
|
+
const text = await readFile(file, "utf8"),
|
|
16
|
+
name = relative(root, file).replaceAll("\\", "/");
|
|
17
|
+
if (/from\s+["'](?:radix-ui|@radix-ui\/)/.test(text))
|
|
18
|
+
violations.push(`${name}: the Workbench foundation uses Base UI`);
|
|
19
|
+
if (!consumer && name.startsWith("components/ui/")) continue;
|
|
20
|
+
if (consumer && /(?:^|\/)components\/ui\//.test(name))
|
|
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`);
|
|
24
|
+
if (/<(?:button|input|select|table|dialog)(?:\s|>)/.test(text))
|
|
25
|
+
violations.push(`${name}: use the shared UI primitive`);
|
|
26
|
+
if (
|
|
27
|
+
/(?:bg|text|border)-(?:red|blue|green|orange|gray|slate|zinc)-\d|(?:bg|text|border)-\[#[a-f\d]/i.test(
|
|
28
|
+
text,
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
violations.push(`${name}: use semantic tokens instead of local colors`);
|
|
32
|
+
if (!consumer && /from ["']@\//.test(text))
|
|
33
|
+
violations.push(`${name}: library internals must use portable imports`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
await visit(root);
|
|
37
|
+
if (violations.length) {
|
|
38
|
+
console.error(violations.join("\n"));
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
} else
|
|
41
|
+
console.log(`UI standards passed (${consumer ? "consumer" : "library"}).`);
|
|
@@ -1,82 +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 };
|
|
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 };
|