create-freakpunch 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -3
- package/bin/create-freakpunch.mjs +227 -0
- package/files/README.md +19 -0
- package/files/app.config.ts +50 -0
- package/files/locales.ts +10 -0
- package/files/registry.ts +17 -0
- package/package.json +23 -6
- package/template/.dockerignore +17 -0
- package/template/.oxlintrc.json +9 -0
- package/template/AGENTS.md +6 -0
- package/template/BACKEND.md +108 -0
- package/template/CLAUDE.md +297 -0
- package/template/Dockerfile +19 -0
- package/template/_gitignore +26 -0
- package/template/components.json +27 -0
- package/template/design/COMPONENTS.md +145 -0
- package/template/design/DESIGN.md +212 -0
- package/template/design/PATTERNS.md +110 -0
- package/template/index.html +25 -0
- package/template/nginx.conf +32 -0
- package/template/package-lock.json +6484 -0
- package/template/package.json +51 -0
- package/template/public/favicon.svg +1 -0
- package/template/public/robots.txt +2 -0
- package/template/src/app.config.ts +50 -0
- package/template/src/app.roles.ts +24 -0
- package/template/src/components/ui/alert-dialog.tsx +181 -0
- package/template/src/components/ui/avatar.tsx +116 -0
- package/template/src/components/ui/badge.tsx +57 -0
- package/template/src/components/ui/breadcrumb.tsx +130 -0
- package/template/src/components/ui/button.tsx +58 -0
- package/template/src/components/ui/calendar.tsx +277 -0
- package/template/src/components/ui/card.tsx +99 -0
- package/template/src/components/ui/chart.tsx +370 -0
- package/template/src/components/ui/checkbox.tsx +28 -0
- package/template/src/components/ui/combobox.tsx +329 -0
- package/template/src/components/ui/command.tsx +191 -0
- package/template/src/components/ui/dialog.tsx +162 -0
- package/template/src/components/ui/dropdown-menu.tsx +271 -0
- package/template/src/components/ui/empty.tsx +103 -0
- package/template/src/components/ui/field.tsx +236 -0
- package/template/src/components/ui/input-group.tsx +263 -0
- package/template/src/components/ui/input.tsx +45 -0
- package/template/src/components/ui/kbd.tsx +31 -0
- package/template/src/components/ui/label.tsx +17 -0
- package/template/src/components/ui/pagination.tsx +134 -0
- package/template/src/components/ui/popover.tsx +87 -0
- package/template/src/components/ui/radio-group.tsx +35 -0
- package/template/src/components/ui/select.tsx +204 -0
- package/template/src/components/ui/separator.tsx +25 -0
- package/template/src/components/ui/sheet.tsx +139 -0
- package/template/src/components/ui/sidebar.tsx +741 -0
- package/template/src/components/ui/skeleton.tsx +13 -0
- package/template/src/components/ui/sonner.tsx +78 -0
- package/template/src/components/ui/spinner.tsx +42 -0
- package/template/src/components/ui/switch.tsx +29 -0
- package/template/src/components/ui/table.tsx +121 -0
- package/template/src/components/ui/tabs.tsx +191 -0
- package/template/src/components/ui/textarea.tsx +17 -0
- package/template/src/components/ui/toggle-group.tsx +94 -0
- package/template/src/components/ui/toggle.tsx +48 -0
- package/template/src/components/ui/tooltip.tsx +91 -0
- package/template/src/core/api/demo.ts +8 -0
- package/template/src/core/api/http.ts +76 -0
- package/template/src/core/api/mock.ts +88 -0
- package/template/src/core/api/rest.ts +67 -0
- package/template/src/core/api/types.ts +21 -0
- package/template/src/core/audit/audit.service.ts +94 -0
- package/template/src/core/audit/hooks.ts +19 -0
- package/template/src/core/auth/auth-store.ts +52 -0
- package/template/src/core/auth/auth.service.ts +86 -0
- package/template/src/core/auth/can.tsx +39 -0
- package/template/src/core/auth/dev-role-switcher.tsx +64 -0
- package/template/src/core/auth/local-credentials.ts +46 -0
- package/template/src/core/auth/permissions.ts +115 -0
- package/template/src/core/auth/require-auth.tsx +16 -0
- package/template/src/core/auth/require-permission.tsx +24 -0
- package/template/src/core/auth/roles.ts +9 -0
- package/template/src/core/auth/types.ts +20 -0
- package/template/src/core/auth/use-auth.ts +19 -0
- package/template/src/core/company/company.service.ts +94 -0
- package/template/src/core/company/hooks.ts +40 -0
- package/template/src/core/company/password-policy.ts +40 -0
- package/template/src/core/company/types.ts +46 -0
- package/template/src/core/company/use-app-name.ts +7 -0
- package/template/src/core/components/button-link.tsx +14 -0
- package/template/src/core/components/confirm-dialog.tsx +116 -0
- package/template/src/core/components/data-table/data-table.tsx +297 -0
- package/template/src/core/components/data-table/features.ts +24 -0
- package/template/src/core/components/data-table/table-header-cell.tsx +13 -0
- package/template/src/core/components/data-table/toolbar.tsx +100 -0
- package/template/src/core/components/data-table/use-list-url-state.ts +108 -0
- package/template/src/core/components/date-picker.tsx +71 -0
- package/template/src/core/components/file-attachments.tsx +84 -0
- package/template/src/core/components/form-error.tsx +9 -0
- package/template/src/core/components/form-page.tsx +93 -0
- package/template/src/core/components/image-upload.tsx +86 -0
- package/template/src/core/components/page.tsx +75 -0
- package/template/src/core/components/phone-input.tsx +58 -0
- package/template/src/core/components/search-select.tsx +57 -0
- package/template/src/core/components/segmented.tsx +41 -0
- package/template/src/core/components/states.tsx +97 -0
- package/template/src/core/components/status-badge.tsx +26 -0
- package/template/src/core/components/user-avatar.tsx +23 -0
- package/template/src/core/config/runtime.ts +16 -0
- package/template/src/core/config/types.ts +37 -0
- package/template/src/core/i18n/i18n.ts +69 -0
- package/template/src/core/i18n/i18next.d.ts +12 -0
- package/template/src/core/i18n/keys.ts +37 -0
- package/template/src/core/i18n/language-switcher.tsx +45 -0
- package/template/src/core/i18n/types.ts +17 -0
- package/template/src/core/i18n/use-format.ts +31 -0
- package/template/src/core/layout/app-shell.tsx +50 -0
- package/template/src/core/layout/app-sidebar.tsx +285 -0
- package/template/src/core/layout/command-menu.tsx +117 -0
- package/template/src/core/layout/top-bar.tsx +100 -0
- package/template/src/core/lib/csv.ts +27 -0
- package/template/src/core/lib/format.ts +87 -0
- package/template/src/core/lib/phone.ts +50 -0
- package/template/src/core/lib/storage.ts +27 -0
- package/template/src/core/modules/lazy-page.ts +9 -0
- package/template/src/core/modules/navigation.ts +39 -0
- package/template/src/core/modules/types.ts +55 -0
- package/template/src/core/notifications/hooks.ts +84 -0
- package/template/src/core/notifications/notification-center.tsx +251 -0
- package/template/src/core/notifications/notifications.service.ts +132 -0
- package/template/src/core/notifications/types.ts +70 -0
- package/template/src/core/preferences/preferences-store.ts +70 -0
- package/template/src/core/setup/setup-gate.tsx +20 -0
- package/template/src/core/setup/setup.service.ts +101 -0
- package/template/src/core/theme/brand-context.tsx +53 -0
- package/template/src/core/theme/brand.ts +29 -0
- package/template/src/core/theme/theme-context.tsx +62 -0
- package/template/src/core/theme/theme-options.ts +8 -0
- package/template/src/core/theme/theme-toggle.tsx +45 -0
- package/template/src/core/users/hooks.ts +51 -0
- package/template/src/core/users/types.ts +37 -0
- package/template/src/core/users/users.service.ts +167 -0
- package/template/src/hooks/use-mobile.ts +19 -0
- package/template/src/index.css +321 -0
- package/template/src/lib/utils.ts +1 -0
- package/template/src/locales/en/account.ts +180 -0
- package/template/src/locales/en/auth.ts +18 -0
- package/template/src/locales/en/common.ts +151 -0
- package/template/src/locales/en/company.ts +380 -0
- package/template/src/locales/en/dashboard.ts +46 -0
- package/template/src/locales/en/errors.ts +18 -0
- package/template/src/locales/en/notifications.ts +43 -0
- package/template/src/locales/en/settings.ts +25 -0
- package/template/src/locales/en/setup.ts +45 -0
- package/template/src/locales/index.ts +58 -0
- package/template/src/locales/tr/account.ts +184 -0
- package/template/src/locales/tr/auth.ts +22 -0
- package/template/src/locales/tr/common.ts +154 -0
- package/template/src/locales/tr/company.ts +384 -0
- package/template/src/locales/tr/dashboard.ts +50 -0
- package/template/src/locales/tr/errors.ts +22 -0
- package/template/src/locales/tr/notifications.ts +47 -0
- package/template/src/locales/tr/settings.ts +29 -0
- package/template/src/locales/tr/setup.ts +49 -0
- package/template/src/main.tsx +16 -0
- package/template/src/modules/locales.ts +10 -0
- package/template/src/modules/registry.ts +17 -0
- package/template/src/pages/dashboard/dashboard-page.tsx +55 -0
- package/template/src/pages/dashboard/quick-actions.tsx +37 -0
- package/template/src/pages/login-page.tsx +152 -0
- package/template/src/pages/no-access-page.tsx +11 -0
- package/template/src/pages/not-found-page.tsx +61 -0
- package/template/src/pages/settings/account/account.service.ts +128 -0
- package/template/src/pages/settings/account/components/mock-qr.tsx +50 -0
- package/template/src/pages/settings/account/components/password-strength.tsx +38 -0
- package/template/src/pages/settings/account/components/two-factor-dialog.tsx +140 -0
- package/template/src/pages/settings/account/hooks.ts +73 -0
- package/template/src/pages/settings/account/notifications-page.tsx +103 -0
- package/template/src/pages/settings/account/preferences-page.tsx +157 -0
- package/template/src/pages/settings/account/profile-page.tsx +242 -0
- package/template/src/pages/settings/account/security-page.tsx +364 -0
- package/template/src/pages/settings/account/types.ts +37 -0
- package/template/src/pages/settings/company/audit-log-page.tsx +126 -0
- package/template/src/pages/settings/company/branding-page.tsx +278 -0
- package/template/src/pages/settings/company/components/invite-dialog.tsx +106 -0
- package/template/src/pages/settings/company/components/read-only-notice.tsx +13 -0
- package/template/src/pages/settings/company/danger-zone-page.tsx +127 -0
- package/template/src/pages/settings/company/general-page.tsx +234 -0
- package/template/src/pages/settings/company/integrations/api-keys-panel.tsx +170 -0
- package/template/src/pages/settings/company/integrations/hooks.ts +37 -0
- package/template/src/pages/settings/company/integrations/integrations-page.tsx +57 -0
- package/template/src/pages/settings/company/integrations/integrations.service.ts +153 -0
- package/template/src/pages/settings/company/integrations/settings-forms.tsx +299 -0
- package/template/src/pages/settings/company/integrations/webhooks-panel.tsx +179 -0
- package/template/src/pages/settings/company/modules-page.tsx +65 -0
- package/template/src/pages/settings/company/roles-page.tsx +116 -0
- package/template/src/pages/settings/company/security-policies-page.tsx +164 -0
- package/template/src/pages/settings/company/users-page.tsx +262 -0
- package/template/src/pages/settings/settings-layout.tsx +57 -0
- package/template/src/pages/settings/settings-nav.ts +69 -0
- package/template/src/pages/settings/settings-routes.tsx +49 -0
- package/template/src/pages/setup-page.tsx +387 -0
- package/template/src/providers.tsx +68 -0
- package/template/src/router.tsx +59 -0
- package/template/tools/check-design.mjs +73 -0
- package/template/tools/new-module.mjs +90 -0
- package/template/tools/templates/module/api/__id__.mock.ts.tpl +17 -0
- package/template/tools/templates/module/api/__id__.service.ts.tpl +51 -0
- package/template/tools/templates/module/hooks.ts.tpl +57 -0
- package/template/tools/templates/module/index.ts.tpl +44 -0
- package/template/tools/templates/module/locales/en.ts.tpl +51 -0
- package/template/tools/templates/module/locales/tr.ts.tpl +55 -0
- package/template/tools/templates/module/pages/__id__-detail-page.tsx.tpl +103 -0
- package/template/tools/templates/module/pages/__id__-form-page.tsx.tpl +121 -0
- package/template/tools/templates/module/pages/__id__-list-page.tsx.tpl +234 -0
- package/template/tools/templates/module/types.ts.tpl +32 -0
- package/template/tsconfig.app.json +32 -0
- package/template/tsconfig.json +12 -0
- package/template/tsconfig.node.json +23 -0
- package/template/vite.config.ts +47 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fp-admin",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc -b && vite build",
|
|
9
|
+
"lint": "oxlint",
|
|
10
|
+
"check": "node tools/check-design.mjs && npm run lint && npm run build",
|
|
11
|
+
"new-module": "node tools/new-module.mjs",
|
|
12
|
+
"preview": "vite preview",
|
|
13
|
+
"fp:update": "npx create-freakpunch@latest update"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@base-ui/react": "^1.9.0",
|
|
17
|
+
"@fontsource-variable/inter": "^5.3.0",
|
|
18
|
+
"@hookform/resolvers": "^6.0.2",
|
|
19
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
20
|
+
"@tanstack/react-query": "^5.105.0",
|
|
21
|
+
"@tanstack/react-table": "^9.3.1",
|
|
22
|
+
"class-variance-authority": "^0.7.1",
|
|
23
|
+
"cmdk": "^1.1.1",
|
|
24
|
+
"cn": "^0.5.0",
|
|
25
|
+
"date-fns": "^4.4.0",
|
|
26
|
+
"i18next": "^26.5.11",
|
|
27
|
+
"lucide-react": "^1.63.0",
|
|
28
|
+
"next-themes": "^0.4.6",
|
|
29
|
+
"react": "^19.3.1",
|
|
30
|
+
"react-day-picker": "^10.0.2",
|
|
31
|
+
"react-dom": "^19.3.1",
|
|
32
|
+
"react-hook-form": "^7.91.0",
|
|
33
|
+
"react-i18next": "^17.1.9",
|
|
34
|
+
"react-router": "^8.5.1",
|
|
35
|
+
"recharts": "^3.8.0",
|
|
36
|
+
"shadcn": "^4.23.0",
|
|
37
|
+
"sonner": "^2.0.8",
|
|
38
|
+
"tailwindcss": "^4.3.3",
|
|
39
|
+
"tw-animate-css": "^1.4.0",
|
|
40
|
+
"zod": "^4.6.7"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^24.19.2",
|
|
44
|
+
"@types/react": "^19.3.0",
|
|
45
|
+
"@types/react-dom": "^19.3.0",
|
|
46
|
+
"@vitejs/plugin-react": "^6.1.2",
|
|
47
|
+
"oxlint": "^1.89.0",
|
|
48
|
+
"typescript": "~7.0.2",
|
|
49
|
+
"vite": "^8.3.5"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="8" fill="#0a0a0a"/><text x="16" y="22" font-family="Inter,system-ui,sans-serif" font-size="17" font-weight="700" fill="#fff" text-anchor="middle">f</text></svg>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { AppConfig, AuthMode } from '@/core/config/types'
|
|
2
|
+
|
|
3
|
+
/** This panel's settings. The company name and logo come from the setup wizard and Settings. */
|
|
4
|
+
export const appConfig = {
|
|
5
|
+
name: 'Admin',
|
|
6
|
+
/** A preset key from src/core/theme/brand.ts, or a custom { color, foreground } pair. */
|
|
7
|
+
brand: 'indigo',
|
|
8
|
+
|
|
9
|
+
workspaces: [{ id: 'main', name: 'Admin' }],
|
|
10
|
+
|
|
11
|
+
/** Sidebar group order; labels are translation keys. Modules attach nav items to these ids. */
|
|
12
|
+
navGroups: [
|
|
13
|
+
{ id: 'overview', label: 'common:nav.groups.overview' },
|
|
14
|
+
{ id: 'catalog', label: 'common:nav.groups.catalog' },
|
|
15
|
+
{ id: 'content', label: 'common:nav.groups.content' },
|
|
16
|
+
{ id: 'system', label: 'common:nav.groups.system' },
|
|
17
|
+
],
|
|
18
|
+
|
|
19
|
+
/** Turn modules on/off. `npm run new-module` adds them here. */
|
|
20
|
+
modules: {
|
|
21
|
+
// fp:modules
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
/** No marketing page: `/` opens the panel. */
|
|
25
|
+
landing: false,
|
|
26
|
+
|
|
27
|
+
/** Built-in areas, off until a project needs them. Setting one to true adds its pages and menu items. */
|
|
28
|
+
features: {
|
|
29
|
+
notifications: false,
|
|
30
|
+
users: false,
|
|
31
|
+
roles: false,
|
|
32
|
+
securityPolicies: false,
|
|
33
|
+
modules: false,
|
|
34
|
+
integrations: false,
|
|
35
|
+
auditLog: false,
|
|
36
|
+
dangerZone: false,
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
/** Default ISO 4217 currency for the setup wizard. */
|
|
40
|
+
currency: 'USD',
|
|
41
|
+
|
|
42
|
+
/** Real REST API base URL. */
|
|
43
|
+
apiBaseUrl: import.meta.env.VITE_API_URL ?? '/api',
|
|
44
|
+
|
|
45
|
+
/** mock (no backend, data stays in this browser) | bearer (JWT) | cookie (Laravel Sanctum). See BACKEND.md. */
|
|
46
|
+
auth: {
|
|
47
|
+
mode: (import.meta.env.VITE_AUTH_MODE ?? 'mock') as AuthMode,
|
|
48
|
+
csrfUrl: import.meta.env.VITE_CSRF_URL ?? '/sanctum/csrf-cookie',
|
|
49
|
+
},
|
|
50
|
+
} satisfies AppConfig
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Permission } from '@/core/auth/permissions'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Project-specific access rules. The Owner and Admin automatically get every
|
|
5
|
+
* custom permission; other roles get what is listed under `permissions`.
|
|
6
|
+
* Labels live here (EN + TR) so core translation files stay untouched.
|
|
7
|
+
*
|
|
8
|
+
* Example:
|
|
9
|
+
* customPermissions = [{ id: 'orders.refund', label: { en: 'Refund orders', tr: 'Sipariş iade et' } }] as const
|
|
10
|
+
* customRoles = {
|
|
11
|
+
* support: {
|
|
12
|
+
* label: { en: 'Support', tr: 'Destek' },
|
|
13
|
+
* description: { en: 'Handles orders and refunds.', tr: 'Siparişleri ve iadeleri yönetir.' },
|
|
14
|
+
* permissions: ['dashboard.view', 'orders.view', 'orders.refund'],
|
|
15
|
+
* },
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
export type LocalizedText = { en: string; tr: string }
|
|
19
|
+
|
|
20
|
+
export type CustomRole = { label: LocalizedText; description: LocalizedText; permissions: readonly Permission[] }
|
|
21
|
+
|
|
22
|
+
export const customPermissions = [] as const satisfies readonly { id: string; label: LocalizedText }[]
|
|
23
|
+
|
|
24
|
+
export const customRoles = {} satisfies Record<string, CustomRole>
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
import { Button } from "@/components/ui/button"
|
|
6
|
+
|
|
7
|
+
function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
|
|
8
|
+
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
|
|
12
|
+
return (
|
|
13
|
+
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
|
|
18
|
+
return (
|
|
19
|
+
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function AlertDialogOverlay({
|
|
24
|
+
className,
|
|
25
|
+
...props
|
|
26
|
+
}: AlertDialogPrimitive.Backdrop.Props) {
|
|
27
|
+
return (
|
|
28
|
+
<AlertDialogPrimitive.Backdrop
|
|
29
|
+
data-slot="alert-dialog-overlay"
|
|
30
|
+
className={cn(
|
|
31
|
+
"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",
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function AlertDialogContent({
|
|
40
|
+
className,
|
|
41
|
+
size = "default",
|
|
42
|
+
...props
|
|
43
|
+
}: AlertDialogPrimitive.Popup.Props & {
|
|
44
|
+
size?: "default" | "sm"
|
|
45
|
+
}) {
|
|
46
|
+
return (
|
|
47
|
+
<AlertDialogPortal>
|
|
48
|
+
<AlertDialogOverlay />
|
|
49
|
+
<AlertDialogPrimitive.Popup
|
|
50
|
+
data-slot="alert-dialog-content"
|
|
51
|
+
data-size={size}
|
|
52
|
+
className={cn(
|
|
53
|
+
"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-(--shadow-dialog) ring-1 ring-border duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-md 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",
|
|
54
|
+
className
|
|
55
|
+
)}
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
</AlertDialogPortal>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function AlertDialogHeader({
|
|
63
|
+
className,
|
|
64
|
+
...props
|
|
65
|
+
}: React.ComponentProps<"div">) {
|
|
66
|
+
return (
|
|
67
|
+
<div
|
|
68
|
+
data-slot="alert-dialog-header"
|
|
69
|
+
className={cn(
|
|
70
|
+
"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]",
|
|
71
|
+
className
|
|
72
|
+
)}
|
|
73
|
+
{...props}
|
|
74
|
+
/>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function AlertDialogFooter({
|
|
79
|
+
className,
|
|
80
|
+
...props
|
|
81
|
+
}: React.ComponentProps<"div">) {
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
data-slot="alert-dialog-footer"
|
|
85
|
+
className={cn(
|
|
86
|
+
"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",
|
|
87
|
+
className
|
|
88
|
+
)}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function AlertDialogMedia({
|
|
95
|
+
className,
|
|
96
|
+
...props
|
|
97
|
+
}: React.ComponentProps<"div">) {
|
|
98
|
+
return (
|
|
99
|
+
<div
|
|
100
|
+
data-slot="alert-dialog-media"
|
|
101
|
+
className={cn(
|
|
102
|
+
"mb-2.5 inline-flex size-12 items-center justify-center rounded-lg bg-fill-2 sm:group-data-[size=default]/alert-dialog-content:row-span-2 sm:group-data-[size=default]/alert-dialog-content:mb-0 *:[svg:not([class*='size-'])]:size-5",
|
|
103
|
+
className
|
|
104
|
+
)}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function AlertDialogTitle({
|
|
111
|
+
className,
|
|
112
|
+
...props
|
|
113
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
|
|
114
|
+
return (
|
|
115
|
+
<AlertDialogPrimitive.Title
|
|
116
|
+
data-slot="alert-dialog-title"
|
|
117
|
+
className={cn(
|
|
118
|
+
"font-heading text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
|
|
119
|
+
className
|
|
120
|
+
)}
|
|
121
|
+
{...props}
|
|
122
|
+
/>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function AlertDialogDescription({
|
|
127
|
+
className,
|
|
128
|
+
...props
|
|
129
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
|
|
130
|
+
return (
|
|
131
|
+
<AlertDialogPrimitive.Description
|
|
132
|
+
data-slot="alert-dialog-description"
|
|
133
|
+
className={cn(
|
|
134
|
+
"text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
|
|
135
|
+
className
|
|
136
|
+
)}
|
|
137
|
+
{...props}
|
|
138
|
+
/>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function AlertDialogAction({
|
|
143
|
+
className,
|
|
144
|
+
...props
|
|
145
|
+
}: React.ComponentProps<typeof Button>) {
|
|
146
|
+
return (
|
|
147
|
+
<Button data-slot="alert-dialog-action" className={className} {...props} />
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function AlertDialogCancel({
|
|
152
|
+
className,
|
|
153
|
+
variant = "outline",
|
|
154
|
+
size = "default",
|
|
155
|
+
...props
|
|
156
|
+
}: AlertDialogPrimitive.Close.Props &
|
|
157
|
+
Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
|
|
158
|
+
return (
|
|
159
|
+
<AlertDialogPrimitive.Close
|
|
160
|
+
data-slot="alert-dialog-cancel"
|
|
161
|
+
className={className}
|
|
162
|
+
render={<Button variant={variant} size={size} />}
|
|
163
|
+
{...props}
|
|
164
|
+
/>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export {
|
|
169
|
+
AlertDialog,
|
|
170
|
+
AlertDialogAction,
|
|
171
|
+
AlertDialogCancel,
|
|
172
|
+
AlertDialogContent,
|
|
173
|
+
AlertDialogDescription,
|
|
174
|
+
AlertDialogFooter,
|
|
175
|
+
AlertDialogHeader,
|
|
176
|
+
AlertDialogMedia,
|
|
177
|
+
AlertDialogOverlay,
|
|
178
|
+
AlertDialogPortal,
|
|
179
|
+
AlertDialogTitle,
|
|
180
|
+
AlertDialogTrigger,
|
|
181
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar"
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
function Avatar({
|
|
8
|
+
className,
|
|
9
|
+
size = "default",
|
|
10
|
+
shape = "round",
|
|
11
|
+
...props
|
|
12
|
+
}: AvatarPrimitive.Root.Props & {
|
|
13
|
+
size?: "default" | "sm" | "lg" | "xs" | "xl"
|
|
14
|
+
shape?: "round" | "square"
|
|
15
|
+
}) {
|
|
16
|
+
return (
|
|
17
|
+
<AvatarPrimitive.Root
|
|
18
|
+
data-slot="avatar"
|
|
19
|
+
data-size={size}
|
|
20
|
+
data-shape={shape}
|
|
21
|
+
className={cn(
|
|
22
|
+
"group/avatar relative flex size-8 shrink-0 rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6",
|
|
23
|
+
"data-[shape=square]:rounded-md data-[shape=square]:data-[size=lg]:rounded-lg data-[shape=square]:data-[size=sm]:rounded-sm data-[size=xl]:size-12 data-[shape=square]:data-[size=xl]:rounded-lg data-[size=xs]:size-5 data-[shape=square]:data-[size=xs]:rounded-sm",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
|
|
32
|
+
return (
|
|
33
|
+
<AvatarPrimitive.Image
|
|
34
|
+
data-slot="avatar-image"
|
|
35
|
+
className={cn(
|
|
36
|
+
"aspect-square size-full rounded-[inherit] object-cover",
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function AvatarFallback({
|
|
45
|
+
className,
|
|
46
|
+
...props
|
|
47
|
+
}: AvatarPrimitive.Fallback.Props) {
|
|
48
|
+
return (
|
|
49
|
+
<AvatarPrimitive.Fallback
|
|
50
|
+
data-slot="avatar-fallback"
|
|
51
|
+
className={cn(
|
|
52
|
+
"flex size-full items-center justify-center rounded-[inherit] bg-fill-2 text-sm text-muted-foreground group-data-[size=sm]/avatar:text-2xs group-data-[size=xs]/avatar:text-2xs",
|
|
53
|
+
"[&>svg]:size-4 group-data-[size=lg]/avatar:[&>svg]:size-5 group-data-[size=xl]/avatar:[&>svg]:size-5 group-data-[size=xs]/avatar:[&>svg]:size-3",
|
|
54
|
+
className
|
|
55
|
+
)}
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
|
|
62
|
+
return (
|
|
63
|
+
<span
|
|
64
|
+
data-slot="avatar-badge"
|
|
65
|
+
className={cn(
|
|
66
|
+
"absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none",
|
|
67
|
+
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
|
68
|
+
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
|
|
69
|
+
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
|
|
70
|
+
"group-data-[size=xs]/avatar:size-2 group-data-[size=xs]/avatar:ring-1 group-data-[size=xs]/avatar:[&>svg]:hidden",
|
|
71
|
+
"group-data-[size=xl]/avatar:size-3 group-data-[size=xl]/avatar:[&>svg]:size-2",
|
|
72
|
+
className
|
|
73
|
+
)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
data-slot="avatar-group"
|
|
83
|
+
className={cn(
|
|
84
|
+
"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
|
|
85
|
+
className
|
|
86
|
+
)}
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function AvatarGroupCount({
|
|
93
|
+
className,
|
|
94
|
+
...props
|
|
95
|
+
}: React.ComponentProps<"div">) {
|
|
96
|
+
return (
|
|
97
|
+
<div
|
|
98
|
+
data-slot="avatar-group-count"
|
|
99
|
+
className={cn(
|
|
100
|
+
"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
|
|
101
|
+
"group-has-data-[size=sm]/avatar-group:text-2xs group-has-data-[size=xl]/avatar-group:size-12 group-has-data-[size=xs]/avatar-group:size-5 group-has-data-[size=xs]/avatar-group:text-2xs group-has-data-[size=xl]/avatar-group:[&>svg]:size-5 group-has-data-[size=xs]/avatar-group:[&>svg]:size-3",
|
|
102
|
+
className
|
|
103
|
+
)}
|
|
104
|
+
{...props}
|
|
105
|
+
/>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export {
|
|
110
|
+
Avatar,
|
|
111
|
+
AvatarImage,
|
|
112
|
+
AvatarFallback,
|
|
113
|
+
AvatarGroup,
|
|
114
|
+
AvatarGroupCount,
|
|
115
|
+
AvatarBadge,
|
|
116
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { mergeProps } from "@base-ui/react/merge-props"
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
const badgeVariants = cva(
|
|
7
|
+
"group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full py-0.5 text-2xs font-medium whitespace-nowrap outline-1 -outline-offset-1 outline-transparent transition-all outline-solid focus-visible:ring-3 focus-visible:ring-ring/30 focus-visible:outline-ring aria-invalid:ring-destructive/20 aria-invalid:outline-destructive dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
12
|
+
secondary: "bg-fill-2 text-foreground",
|
|
13
|
+
destructive:
|
|
14
|
+
"bg-destructive/10 text-destructive-foreground focus-visible:ring-destructive/20 focus-visible:outline-destructive/50 [a]:hover:bg-destructive/15",
|
|
15
|
+
outline:
|
|
16
|
+
"bg-control text-foreground outline-1 -outline-offset-1 outline-border [a]:hover:bg-fill-2",
|
|
17
|
+
ghost: "text-foreground hover:bg-fill-2",
|
|
18
|
+
link: "text-primary underline underline-offset-4",
|
|
19
|
+
},
|
|
20
|
+
size: {
|
|
21
|
+
default:
|
|
22
|
+
"h-5 min-w-5 px-2 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
|
|
23
|
+
lg: "h-6 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&>svg]:size-4!",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
variant: "default",
|
|
28
|
+
size: "default",
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
function Badge({
|
|
34
|
+
className,
|
|
35
|
+
variant = "default",
|
|
36
|
+
size = "default",
|
|
37
|
+
render,
|
|
38
|
+
...props
|
|
39
|
+
}: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
|
|
40
|
+
return useRender({
|
|
41
|
+
defaultTagName: "span",
|
|
42
|
+
props: mergeProps<"span">(
|
|
43
|
+
{
|
|
44
|
+
className: cn(badgeVariants({ variant, size }), className),
|
|
45
|
+
},
|
|
46
|
+
props
|
|
47
|
+
),
|
|
48
|
+
render,
|
|
49
|
+
state: {
|
|
50
|
+
slot: "badge",
|
|
51
|
+
variant,
|
|
52
|
+
size,
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { Badge, badgeVariants }
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { mergeProps } from "@base-ui/react/merge-props"
|
|
3
|
+
import { useRender } from "@base-ui/react/use-render"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
import { useTranslation } from "react-i18next"
|
|
6
|
+
import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
|
|
7
|
+
|
|
8
|
+
function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
|
|
9
|
+
const { t } = useTranslation()
|
|
10
|
+
return (
|
|
11
|
+
<nav
|
|
12
|
+
aria-label={t("shell.breadcrumb")}
|
|
13
|
+
data-slot="breadcrumb"
|
|
14
|
+
className={className}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
|
21
|
+
return (
|
|
22
|
+
<ol
|
|
23
|
+
data-slot="breadcrumb-list"
|
|
24
|
+
className={cn(
|
|
25
|
+
"flex flex-wrap items-center gap-2 text-sm wrap-break-word text-muted-foreground",
|
|
26
|
+
className
|
|
27
|
+
)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
34
|
+
return (
|
|
35
|
+
<li
|
|
36
|
+
data-slot="breadcrumb-item"
|
|
37
|
+
className={cn(
|
|
38
|
+
"inline-flex items-center gap-1.5 has-data-[slot=breadcrumb-ellipsis]:-my-0.5 [&_svg:not([class*='size-'])]:size-4",
|
|
39
|
+
className
|
|
40
|
+
)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function BreadcrumbLink({
|
|
47
|
+
className,
|
|
48
|
+
render,
|
|
49
|
+
...props
|
|
50
|
+
}: useRender.ComponentProps<"a">) {
|
|
51
|
+
return useRender({
|
|
52
|
+
defaultTagName: "a",
|
|
53
|
+
props: mergeProps<"a">(
|
|
54
|
+
{
|
|
55
|
+
className: cn("transition-colors hover:text-foreground", className),
|
|
56
|
+
},
|
|
57
|
+
props
|
|
58
|
+
),
|
|
59
|
+
render,
|
|
60
|
+
state: {
|
|
61
|
+
slot: "breadcrumb-link",
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
|
67
|
+
return (
|
|
68
|
+
<span
|
|
69
|
+
data-slot="breadcrumb-page"
|
|
70
|
+
role="link"
|
|
71
|
+
aria-disabled="true"
|
|
72
|
+
aria-current="page"
|
|
73
|
+
className={cn("font-normal text-foreground", className)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function BreadcrumbSeparator({
|
|
80
|
+
children,
|
|
81
|
+
className,
|
|
82
|
+
...props
|
|
83
|
+
}: React.ComponentProps<"li">) {
|
|
84
|
+
return (
|
|
85
|
+
<li
|
|
86
|
+
data-slot="breadcrumb-separator"
|
|
87
|
+
role="presentation"
|
|
88
|
+
aria-hidden="true"
|
|
89
|
+
className={cn(
|
|
90
|
+
"inline-flex min-w-4 items-center justify-center text-2xs font-medium [&>svg]:size-4",
|
|
91
|
+
className
|
|
92
|
+
)}
|
|
93
|
+
{...props}
|
|
94
|
+
>
|
|
95
|
+
{children ?? <ChevronRightIcon className="rtl:-scale-x-100" />}
|
|
96
|
+
</li>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function BreadcrumbEllipsis({
|
|
101
|
+
className,
|
|
102
|
+
...props
|
|
103
|
+
}: React.ComponentProps<"span">) {
|
|
104
|
+
const { t } = useTranslation()
|
|
105
|
+
return (
|
|
106
|
+
<span
|
|
107
|
+
data-slot="breadcrumb-ellipsis"
|
|
108
|
+
role="presentation"
|
|
109
|
+
aria-hidden="true"
|
|
110
|
+
className={cn(
|
|
111
|
+
"flex size-6 items-center justify-center text-muted-foreground transition-colors group-hover/button:text-foreground in-aria-expanded:text-foreground",
|
|
112
|
+
className
|
|
113
|
+
)}
|
|
114
|
+
{...props}
|
|
115
|
+
>
|
|
116
|
+
<MoreHorizontalIcon className="size-4" />
|
|
117
|
+
<span className="sr-only">{t("a11y.more")}</span>
|
|
118
|
+
</span>
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export {
|
|
123
|
+
Breadcrumb,
|
|
124
|
+
BreadcrumbList,
|
|
125
|
+
BreadcrumbItem,
|
|
126
|
+
BreadcrumbLink,
|
|
127
|
+
BreadcrumbPage,
|
|
128
|
+
BreadcrumbSeparator,
|
|
129
|
+
BreadcrumbEllipsis,
|
|
130
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Button as ButtonPrimitive } from "@base-ui/react/button"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
const buttonVariants = cva(
|
|
6
|
+
"group/button inline-flex shrink-0 items-center justify-center text-xs font-medium whitespace-nowrap outline-1 -outline-offset-1 outline-transparent transition-all outline-solid select-none focus-visible:ring-2 focus-visible:ring-ring/30 focus-visible:outline-ring active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:ring-2 aria-invalid:ring-destructive/20 aria-invalid:outline-destructive dark:aria-invalid:ring-destructive/40 dark:aria-invalid:outline-destructive/50 [&_[data-badge]]:inline-flex [&_[data-badge]]:h-5 [&_[data-badge]]:min-w-5 [&_[data-badge]]:shrink-0 [&_[data-badge]]:items-center [&_[data-badge]]:justify-center [&_[data-badge]]:rounded-xs [&_[data-badge]]:px-1 [&_[data-badge]]:text-2xs [&_[data-badge]]:font-medium [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default:
|
|
11
|
+
"bg-primary text-primary-foreground hover:bg-primary/90 disabled:bg-muted-foreground [&_[data-badge]]:bg-primary-foreground/20",
|
|
12
|
+
outline:
|
|
13
|
+
"bg-control text-foreground outline-control-border hover:bg-control-hover disabled:bg-control-hover aria-expanded:bg-control-hover [&_[data-badge]]:bg-fill-2",
|
|
14
|
+
secondary:
|
|
15
|
+
"bg-fill-2 text-foreground hover:bg-fill-3 disabled:bg-fill-2 aria-expanded:bg-fill-3 [&_[data-badge]]:bg-fill-2",
|
|
16
|
+
ghost:
|
|
17
|
+
"text-foreground hover:bg-fill-2 aria-expanded:bg-fill-2 [&_[data-badge]]:bg-fill-2",
|
|
18
|
+
destructive:
|
|
19
|
+
"text-destructive-foreground outline-destructive/50 hover:bg-fill-1 focus-visible:ring-destructive/20 focus-visible:outline-destructive disabled:bg-fill-1 aria-expanded:bg-fill-1 [&_[data-badge]]:bg-destructive/10",
|
|
20
|
+
link: "text-primary underline-offset-4 hover:underline [&_[data-badge]]:bg-primary/10",
|
|
21
|
+
},
|
|
22
|
+
size: {
|
|
23
|
+
default:
|
|
24
|
+
"h-8 gap-1.5 rounded-lg px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
25
|
+
xs: "h-6 gap-1 rounded-md px-2 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_[data-badge]]:h-4 [&_[data-badge]]:min-w-4 [&_[data-badge]]:px-0.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
26
|
+
sm: "h-7 gap-1.5 rounded-md px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
27
|
+
lg: "h-9 gap-1.5 rounded-lg px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
|
|
28
|
+
xl: "h-10 gap-2 rounded-lg px-3.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
|
|
29
|
+
icon: "size-8 rounded-lg",
|
|
30
|
+
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3.5",
|
|
31
|
+
"icon-sm": "size-7 rounded-md",
|
|
32
|
+
"icon-lg": "size-9 rounded-lg",
|
|
33
|
+
"icon-xl": "size-10 rounded-lg",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
defaultVariants: {
|
|
37
|
+
variant: "default",
|
|
38
|
+
size: "default",
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
function Button({
|
|
44
|
+
className,
|
|
45
|
+
variant = "default",
|
|
46
|
+
size = "default",
|
|
47
|
+
...props
|
|
48
|
+
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
|
|
49
|
+
return (
|
|
50
|
+
<ButtonPrimitive
|
|
51
|
+
data-slot="button"
|
|
52
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { Button, buttonVariants }
|