@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.
Files changed (73) hide show
  1. package/AGENTS.md +27 -27
  2. package/README.md +79 -79
  3. package/THIRD_PARTY_NOTICES.md +39 -39
  4. package/components.json +25 -25
  5. package/docs/MIGRATION-0.2.md +20 -20
  6. package/docs/MIGRATION-6.0.md +33 -33
  7. package/docs/NEXT-STARTER-VALIDATION.md +14 -14
  8. package/docs/STANDARDS.md +41 -41
  9. package/docs/VALIDATION.md +11 -11
  10. package/docs/VERSIONING.md +28 -28
  11. package/docs/validation/preference-titles.md +22 -0
  12. package/package.json +88 -84
  13. package/scripts/check-standards.mjs +41 -41
  14. package/src/components/ui/accordion.tsx +82 -82
  15. package/src/components/ui/alert-dialog.tsx +187 -187
  16. package/src/components/ui/alert.tsx +77 -77
  17. package/src/components/ui/avatar.tsx +108 -108
  18. package/src/components/ui/badge.tsx +55 -55
  19. package/src/components/ui/breadcrumb.tsx +125 -125
  20. package/src/components/ui/button-group.tsx +89 -89
  21. package/src/components/ui/button.tsx +59 -59
  22. package/src/components/ui/card.tsx +101 -101
  23. package/src/components/ui/checkbox.tsx +27 -27
  24. package/src/components/ui/collapsible.tsx +21 -21
  25. package/src/components/ui/context-menu.tsx +271 -271
  26. package/src/components/ui/dialog.tsx +161 -161
  27. package/src/components/ui/dropdown-menu.tsx +271 -271
  28. package/src/components/ui/empty.tsx +105 -105
  29. package/src/components/ui/field.tsx +238 -238
  30. package/src/components/ui/hover-card.tsx +50 -50
  31. package/src/components/ui/input-group.tsx +155 -155
  32. package/src/components/ui/input.tsx +21 -21
  33. package/src/components/ui/kbd.tsx +28 -28
  34. package/src/components/ui/label.tsx +19 -19
  35. package/src/components/ui/orb.css +1002 -1002
  36. package/src/components/ui/orb.tsx +752 -752
  37. package/src/components/ui/popover.tsx +89 -89
  38. package/src/components/ui/progress.tsx +82 -82
  39. package/src/components/ui/radio-group.tsx +37 -37
  40. package/src/components/ui/scroll-area.tsx +54 -54
  41. package/src/components/ui/select.tsx +204 -204
  42. package/src/components/ui/separator.tsx +24 -24
  43. package/src/components/ui/sheet.tsx +139 -139
  44. package/src/components/ui/sidebar.tsx +1039 -1035
  45. package/src/components/ui/skeleton.tsx +15 -15
  46. package/src/components/ui/slider.tsx +53 -53
  47. package/src/components/ui/sonner.tsx +44 -44
  48. package/src/components/ui/spinner.tsx +32 -32
  49. package/src/components/ui/switch.tsx +31 -31
  50. package/src/components/ui/table.tsx +121 -121
  51. package/src/components/ui/tabs.tsx +81 -81
  52. package/src/components/ui/textarea.tsx +19 -19
  53. package/src/components/ui/toggle-group.tsx +89 -89
  54. package/src/components/ui/toggle.tsx +44 -44
  55. package/src/components/ui/tooltip.tsx +65 -65
  56. package/src/hooks/use-mobile.ts +20 -20
  57. package/src/lib/i18n.tsx +109 -109
  58. package/src/lib/utils.ts +1 -1
  59. package/src/patterns/app-shell.tsx +177 -175
  60. package/src/patterns/data-table.tsx +2486 -2486
  61. package/src/patterns/details-sheet.tsx +54 -53
  62. package/src/patterns/filter-tabs.tsx +53 -53
  63. package/src/patterns/majapi-logo.tsx +32 -32
  64. package/src/patterns/page-header.tsx +62 -62
  65. package/src/patterns/preferences.tsx +15 -7
  66. package/src/patterns/status-badge.tsx +24 -24
  67. package/src/patterns/user-menu.tsx +218 -0
  68. package/src/patterns/workspace-switcher.tsx +176 -0
  69. package/src/styles.css +165 -165
  70. package/src/templates/admin-dashboard.tsx +44 -44
  71. package/src/templates/auth-layout.tsx +30 -30
  72. package/src/templates/login-form.tsx +129 -129
  73. package/src/templates/signup-form.tsx +177 -177
@@ -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.
@@ -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.0.0",
4
- "description": "Shared Workbench 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/workbench-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
- },
45
- "peerDependencies": {
46
- "react": "19.3.1",
47
- "react-dom": "19.3.1"
48
- },
49
- "engines": {
50
- "node": ">=22"
51
- },
52
- "dependencies": {
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",
58
- "@fontsource-variable/geist": "^5.3.0",
59
- "@tanstack/react-table": "8.21.3",
60
- "class-variance-authority": "0.7.1",
61
- "clsx": "2.1.1",
62
- "cn": "^0.5.0",
63
- "lucide-react": "1.61.0",
64
- "shadcn": "^4.22.1",
65
- "sonner": "^2.0.8",
66
- "tailwind-merge": "3.7.0",
67
- "tw-animate-css": "^1.4.0"
68
- },
69
- "devDependencies": {
70
- "@eslint/js": "10.0.1",
71
- "@types/node": "26.6.5",
72
- "@types/react": "19.3.0",
73
- "@types/react-dom": "19.3.0",
74
- "eslint": "10.13.0",
75
- "eslint-plugin-react-hooks": "7.1.1",
76
- "prettier": "3.9.10",
77
- "tailwindcss": "4.3.3",
78
- "typescript": "6.0.3",
79
- "typescript-eslint": "8.72.0"
80
- },
81
- "workspaces": [
82
- "catalog"
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 };