feat(deps): Tailwind 3 → 4 + swap tailwindcss-animate for tw-animate-css
Ran the official @tailwindcss/upgrade tool: - tailwind.config.ts → @theme directive in globals.css - @tailwind base/components/utilities → @import 'tailwindcss' - postcss.config switched from tailwindcss + autoprefixer to @tailwindcss/postcss (autoprefixer baked in) - focus-visible:outline-none → focus-visible:outline-hidden (the v3 utility was a footgun — outline still showed in forced-colors mode) Reverted the migration tool's over-zealous variant="outline" → variant="outline-solid" rename on CVA prop values; that rename was meant for the Tailwind `outline:` utility, not our Button/Badge component variants. Swapped tailwindcss-animate (v3-style JS plugin) for tw-animate-css (v4-native @import). Same utility surface (animate-spin, animate-in, etc.), one fewer JS plugin in the bundle. Fixed the upgrade tool's malformed dark variant (@custom-variant dark (&:is(class *)) — `class` was being parsed as a tag) to canonical &:where(.dark, .dark *). Verified: tsc 0 errors, eslint 0 errors (16 pre-existing warnings), vitest 1315/1315, next build clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -104,9 +104,9 @@
|
|||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"svgo": "^4.0.1",
|
"svgo": "^4.0.1",
|
||||||
"tailwind-merge": "^3.6.0",
|
"tailwind-merge": "^3.6.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
|
||||||
"tesseract.js": "^7.0.0",
|
"tesseract.js": "^7.0.0",
|
||||||
"ts-pattern": "^5.9.0",
|
"ts-pattern": "^5.9.0",
|
||||||
|
"tw-animate-css": "^1.4.0",
|
||||||
"unpdf": "^1.6.2",
|
"unpdf": "^1.6.2",
|
||||||
"vaul": "^1.1.2",
|
"vaul": "^1.1.2",
|
||||||
"web-vitals": "^5.2.0",
|
"web-vitals": "^5.2.0",
|
||||||
@@ -119,6 +119,7 @@
|
|||||||
"@hookform/devtools": "^4.4.0",
|
"@hookform/devtools": "^4.4.0",
|
||||||
"@next/bundle-analyzer": "^16.2.6",
|
"@next/bundle-analyzer": "^16.2.6",
|
||||||
"@playwright/test": "^1.60.0",
|
"@playwright/test": "^1.60.0",
|
||||||
|
"@tailwindcss/postcss": "^4.3.0",
|
||||||
"@total-typescript/ts-reset": "^0.6.1",
|
"@total-typescript/ts-reset": "^0.6.1",
|
||||||
"@types/archiver": "^7.0.0",
|
"@types/archiver": "^7.0.0",
|
||||||
"@types/iso-3166-2": "^1.0.4",
|
"@types/iso-3166-2": "^1.0.4",
|
||||||
@@ -129,7 +130,6 @@
|
|||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vitejs/plugin-react": "^6.0.1",
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
"@vitest/coverage-v8": "^4.1.6",
|
"@vitest/coverage-v8": "^4.1.6",
|
||||||
"autoprefixer": "^10.5.0",
|
|
||||||
"dotenv": "^17.4.2",
|
"dotenv": "^17.4.2",
|
||||||
"drizzle-kit": "^0.31.10",
|
"drizzle-kit": "^0.31.10",
|
||||||
"drizzle-zod": "^0.8.3",
|
"drizzle-zod": "^0.8.3",
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
"postcss": "^8.5.14",
|
"postcss": "^8.5.14",
|
||||||
"prettier": "^3.8.3",
|
"prettier": "^3.8.3",
|
||||||
"react-grab": "^0.1.34",
|
"react-grab": "^0.1.34",
|
||||||
"tailwindcss": "^3.4.19",
|
"tailwindcss": "^4.3.0",
|
||||||
"tsx": "^4.21.0",
|
"tsx": "^4.21.0",
|
||||||
"typescript": "^6.0.3",
|
"typescript": "^6.0.3",
|
||||||
"vitest": "^4.1.6"
|
"vitest": "^4.1.6"
|
||||||
|
|||||||
696
pnpm-lock.yaml
generated
696
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,7 @@
|
|||||||
/** @type {import('postcss-load-config').Config} */
|
/** @type {import('postcss-load-config').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
'@tailwindcss/postcss': {},
|
||||||
autoprefixer: {},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ export default function DocumensoSettingsPage() {
|
|||||||
<ul className="space-y-1.5">
|
<ul className="space-y-1.5">
|
||||||
<li className="flex items-start gap-2">
|
<li className="flex items-start gap-2">
|
||||||
<CheckCircle2
|
<CheckCircle2
|
||||||
className="mt-0.5 h-4 w-4 flex-shrink-0 text-emerald-600"
|
className="mt-0.5 h-4 w-4 shrink-0 text-emerald-600"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
@@ -211,7 +211,7 @@ export default function DocumensoSettingsPage() {
|
|||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-2">
|
<li className="flex items-start gap-2">
|
||||||
<CheckCircle2
|
<CheckCircle2
|
||||||
className="mt-0.5 h-4 w-4 flex-shrink-0 text-emerald-600"
|
className="mt-0.5 h-4 w-4 shrink-0 text-emerald-600"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
@@ -222,7 +222,7 @@ export default function DocumensoSettingsPage() {
|
|||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-2">
|
<li className="flex items-start gap-2">
|
||||||
<CheckCircle2
|
<CheckCircle2
|
||||||
className="mt-0.5 h-4 w-4 flex-shrink-0 text-emerald-600"
|
className="mt-0.5 h-4 w-4 shrink-0 text-emerald-600"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
@@ -233,7 +233,7 @@ export default function DocumensoSettingsPage() {
|
|||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-2">
|
<li className="flex items-start gap-2">
|
||||||
<CheckCircle2
|
<CheckCircle2
|
||||||
className="mt-0.5 h-4 w-4 flex-shrink-0 text-emerald-600"
|
className="mt-0.5 h-4 w-4 shrink-0 text-emerald-600"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
@@ -245,7 +245,7 @@ export default function DocumensoSettingsPage() {
|
|||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-2">
|
<li className="flex items-start gap-2">
|
||||||
<CheckCircle2
|
<CheckCircle2
|
||||||
className="mt-0.5 h-4 w-4 flex-shrink-0 text-emerald-600"
|
className="mt-0.5 h-4 w-4 shrink-0 text-emerald-600"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
@@ -260,7 +260,7 @@ export default function DocumensoSettingsPage() {
|
|||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-2">
|
<li className="flex items-start gap-2">
|
||||||
<CheckCircle2
|
<CheckCircle2
|
||||||
className="mt-0.5 h-4 w-4 flex-shrink-0 text-emerald-600"
|
className="mt-0.5 h-4 w-4 shrink-0 text-emerald-600"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
@@ -271,7 +271,7 @@ export default function DocumensoSettingsPage() {
|
|||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-2">
|
<li className="flex items-start gap-2">
|
||||||
<CheckCircle2
|
<CheckCircle2
|
||||||
className="mt-0.5 h-4 w-4 flex-shrink-0 text-emerald-600"
|
className="mt-0.5 h-4 w-4 shrink-0 text-emerald-600"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
@@ -283,7 +283,7 @@ export default function DocumensoSettingsPage() {
|
|||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-2">
|
<li className="flex items-start gap-2">
|
||||||
<CheckCircle2
|
<CheckCircle2
|
||||||
className="mt-0.5 h-4 w-4 flex-shrink-0 text-emerald-600"
|
className="mt-0.5 h-4 w-4 shrink-0 text-emerald-600"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ export default function ErrorEventDetailPage() {
|
|||||||
<KV label="Name" value={event.errorName ?? '—'} mono />
|
<KV label="Name" value={event.errorName ?? '—'} mono />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-muted-foreground">Message</p>
|
<p className="text-xs text-muted-foreground">Message</p>
|
||||||
<p className="mt-0.5 font-mono whitespace-pre-wrap break-words">
|
<p className="mt-0.5 font-mono whitespace-pre-wrap wrap-break-word">
|
||||||
{event.errorMessage ?? '—'}
|
{event.errorMessage ?? '—'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -195,7 +195,7 @@ export default function ErrorEventDetailPage() {
|
|||||||
<Copy className="mr-1.5 h-3 w-3" /> Copy
|
<Copy className="mr-1.5 h-3 w-3" /> Copy
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<pre className="mt-1 max-h-96 overflow-auto rounded bg-muted p-2 text-xs font-mono whitespace-pre-wrap break-words">
|
<pre className="mt-1 max-h-96 overflow-auto rounded bg-muted p-2 text-xs font-mono whitespace-pre-wrap wrap-break-word">
|
||||||
{event.errorStack}
|
{event.errorStack}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
@@ -211,7 +211,7 @@ export default function ErrorEventDetailPage() {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<pre className="max-h-64 overflow-auto rounded bg-muted p-2 text-xs font-mono whitespace-pre-wrap break-words">
|
<pre className="max-h-64 overflow-auto rounded bg-muted p-2 text-xs font-mono whitespace-pre-wrap wrap-break-word">
|
||||||
{event.requestBodyExcerpt}
|
{event.requestBodyExcerpt}
|
||||||
</pre>
|
</pre>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ export default function InvoicesPage() {
|
|||||||
|
|
||||||
{/* Delete confirmation */}
|
{/* Delete confirmation */}
|
||||||
{deleteTarget && (
|
{deleteTarget && (
|
||||||
<div className="fixed inset-0 bg-background/80 backdrop-blur-sm z-50 flex items-center justify-center">
|
<div className="fixed inset-0 bg-background/80 backdrop-blur-xs z-50 flex items-center justify-center">
|
||||||
<div className="bg-background border rounded-lg shadow-lg p-6 max-w-sm w-full space-y-4">
|
<div className="bg-background border rounded-lg shadow-lg p-6 max-w-sm w-full space-y-4">
|
||||||
<h3 className="font-semibold">Delete Invoice?</h3>
|
<h3 className="font-semibold">Delete Invoice?</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export default async function PortalDocumentsPage() {
|
|||||||
{documents.map((doc) => (
|
{documents.map((doc) => (
|
||||||
<div key={doc.id} className="bg-white rounded-lg border p-5">
|
<div key={doc.id} className="bg-white rounded-lg border p-5">
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
<FileText className="h-5 w-5 text-gray-400 mt-0.5 flex-shrink-0" />
|
<FileText className="h-5 w-5 text-gray-400 mt-0.5 shrink-0" />
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-start justify-between gap-4 flex-wrap">
|
<div className="flex items-start justify-between gap-4 flex-wrap">
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
@@ -59,7 +59,7 @@ export default async function PortalDocumentsPage() {
|
|||||||
{DOC_TYPE_LABELS[doc.documentType] ?? doc.documentType}
|
{DOC_TYPE_LABELS[doc.documentType] ?? doc.documentType}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 flex-shrink-0">
|
<div className="flex items-center gap-2 shrink-0">
|
||||||
<Badge variant={STATUS_COLORS[doc.status] ?? 'default'}>
|
<Badge variant={STATUS_COLORS[doc.status] ?? 'default'}>
|
||||||
{doc.status.replace(/_/g, ' ')}
|
{doc.status.replace(/_/g, ' ')}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export default async function PortalInvoicesPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right flex-shrink-0">
|
<div className="text-right shrink-0">
|
||||||
<p className="text-lg font-semibold text-gray-900">
|
<p className="text-lg font-semibold text-gray-900">
|
||||||
{formatCurrency(invoice.total, invoice.currency)}
|
{formatCurrency(invoice.total, invoice.currency)}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default async function PortalMyYachtsPage() {
|
|||||||
{yachts.map((y) => (
|
{yachts.map((y) => (
|
||||||
<div key={y.id} className="bg-white rounded-lg border p-5">
|
<div key={y.id} className="bg-white rounded-lg border p-5">
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
<Sailboat className="h-5 w-5 text-gray-400 mt-0.5 flex-shrink-0" />
|
<Sailboat className="h-5 w-5 text-gray-400 mt-0.5 shrink-0" />
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-start justify-between gap-4 flex-wrap">
|
<div className="flex items-start justify-between gap-4 flex-wrap">
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default async function ScannerLayout({
|
|||||||
return (
|
return (
|
||||||
<QueryProvider>
|
<QueryProvider>
|
||||||
<PortProvider ports={[port]} defaultPortId={port.id}>
|
<PortProvider ports={[port]} defaultPortId={port.id}>
|
||||||
<div className="min-h-[100dvh] bg-background">{children}</div>
|
<div className="min-h-dvh bg-background">{children}</div>
|
||||||
</PortProvider>
|
</PortProvider>
|
||||||
</QueryProvider>
|
</QueryProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,183 @@
|
|||||||
@tailwind base;
|
@import 'tailwindcss';
|
||||||
@tailwind components;
|
@import 'tw-animate-css';
|
||||||
@tailwind utilities;
|
|
||||||
|
@custom-variant dark (&:where(.dark, .dark *));
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--color-border: hsl(var(--border));
|
||||||
|
--color-input: hsl(var(--input));
|
||||||
|
--color-ring: hsl(var(--ring));
|
||||||
|
--color-background: hsl(var(--background));
|
||||||
|
--color-foreground: hsl(var(--foreground));
|
||||||
|
|
||||||
|
--color-primary: hsl(var(--primary));
|
||||||
|
--color-primary-foreground: hsl(var(--primary-foreground));
|
||||||
|
|
||||||
|
--color-secondary: hsl(var(--secondary));
|
||||||
|
--color-secondary-foreground: hsl(var(--secondary-foreground));
|
||||||
|
|
||||||
|
--color-destructive: hsl(var(--destructive));
|
||||||
|
--color-destructive-foreground: hsl(var(--destructive-foreground));
|
||||||
|
|
||||||
|
--color-muted: hsl(var(--muted));
|
||||||
|
--color-muted-foreground: hsl(var(--muted-foreground));
|
||||||
|
|
||||||
|
--color-accent: hsl(var(--accent));
|
||||||
|
--color-accent-foreground: hsl(var(--accent-foreground));
|
||||||
|
|
||||||
|
--color-popover: hsl(var(--popover));
|
||||||
|
--color-popover-foreground: hsl(var(--popover-foreground));
|
||||||
|
|
||||||
|
--color-card: hsl(var(--card));
|
||||||
|
--color-card-foreground: hsl(var(--card-foreground));
|
||||||
|
|
||||||
|
--color-brand-50: #d8e5f4;
|
||||||
|
--color-brand-100: #b1cbe9;
|
||||||
|
--color-brand-200: #89b0de;
|
||||||
|
--color-brand-300: #6196d3;
|
||||||
|
--color-brand-400: #3a7bc8;
|
||||||
|
--color-brand-500: #2f6ab5;
|
||||||
|
--color-brand-600: #255a9e;
|
||||||
|
--color-brand-700: #1c4a87;
|
||||||
|
--color-brand: #3a7bc8;
|
||||||
|
--color-brand-dark: #1e2844;
|
||||||
|
|
||||||
|
--color-navy-50: #cdcfd6;
|
||||||
|
--color-navy-100: #9ea1af;
|
||||||
|
--color-navy-200: #71768a;
|
||||||
|
--color-navy-300: #474e66;
|
||||||
|
--color-navy-400: #1e2844;
|
||||||
|
--color-navy-500: #171f35;
|
||||||
|
--color-navy-600: #101625;
|
||||||
|
--color-navy: #1e2844;
|
||||||
|
|
||||||
|
--color-sage: #dae3c1;
|
||||||
|
--color-sage-light: #edf1e2;
|
||||||
|
--color-sage-dark: #b8c49e;
|
||||||
|
|
||||||
|
--color-mint: #add5b3;
|
||||||
|
--color-mint-light: #d6ead9;
|
||||||
|
--color-mint-dark: #7dba85;
|
||||||
|
|
||||||
|
--color-teal: #83aab1;
|
||||||
|
--color-teal-light: #b1cdd2;
|
||||||
|
--color-teal-dark: #5a8a92;
|
||||||
|
|
||||||
|
--color-purple: #685aa3;
|
||||||
|
--color-purple-light: #a49ac6;
|
||||||
|
--color-purple-dark: #4d4280;
|
||||||
|
|
||||||
|
--color-success: #2d8a4e;
|
||||||
|
--color-success-bg: #e8f5e9;
|
||||||
|
--color-success-border: #a5d6a7;
|
||||||
|
|
||||||
|
--color-warning: #e6a817;
|
||||||
|
--color-warning-bg: #fff8e1;
|
||||||
|
--color-warning-border: #ffe082;
|
||||||
|
|
||||||
|
--color-error: #d32f2f;
|
||||||
|
--color-error-bg: #ffebee;
|
||||||
|
--color-error-border: #ef9a9a;
|
||||||
|
|
||||||
|
--color-sidebar: #1e2844;
|
||||||
|
--color-sidebar-text: #cdcfd6;
|
||||||
|
--color-sidebar-hover: #171f35;
|
||||||
|
--color-sidebar-active: #3a7bc8;
|
||||||
|
--color-sidebar-divider: #474e66;
|
||||||
|
|
||||||
|
--font-sans: Inter, system-ui, -apple-system, Arial, sans-serif;
|
||||||
|
--font-mono: JetBrains Mono, ui-monospace, monospace;
|
||||||
|
--font-serif: Georgia, Times New Roman, serif;
|
||||||
|
|
||||||
|
--shadow-xs: 0 1px 2px 0 rgb(15 23 42 / 0.04);
|
||||||
|
--shadow-sm: 0 2px 4px -1px rgb(15 23 42 / 0.06);
|
||||||
|
--shadow: 0 1px 3px rgba(30, 40, 68, 0.1), 0 1px 2px rgba(30, 40, 68, 0.06);
|
||||||
|
--shadow-md: 0 4px 12px -2px rgb(15 23 42 / 0.08);
|
||||||
|
--shadow-lg: 0 12px 32px -8px rgb(15 23 42 / 0.12);
|
||||||
|
--shadow-xl: 0 20px 25px rgba(30, 40, 68, 0.1), 0 8px 10px rgba(30, 40, 68, 0.04);
|
||||||
|
--shadow-glow: 0 0 0 4px rgb(58 123 200 / 0.12);
|
||||||
|
|
||||||
|
--radius-sm: 0.375rem;
|
||||||
|
--radius: 0.375rem;
|
||||||
|
--radius-md: 0.5rem;
|
||||||
|
--radius-lg: 0.625rem;
|
||||||
|
--radius-xl: 0.875rem;
|
||||||
|
|
||||||
|
--background-image-gradient-brand: linear-gradient(135deg, #3a7bc8 0%, #2f6ab5 100%);
|
||||||
|
--background-image-gradient-brand-soft: linear-gradient(135deg, #d8e5f4 0%, #ffffff 100%);
|
||||||
|
--background-image-gradient-success: linear-gradient(135deg, #e8f5e9 0%, #ffffff 100%);
|
||||||
|
--background-image-gradient-warning: linear-gradient(135deg, #fef3c7 0%, #ffffff 100%);
|
||||||
|
|
||||||
|
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
--ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|
||||||
|
--width-sidebar: 256px;
|
||||||
|
--width-sidebar-collapsed: 64px;
|
||||||
|
|
||||||
|
--transition-duration-sidebar: 200ms;
|
||||||
|
--transition-duration-fast: 150ms;
|
||||||
|
--transition-duration-base: 200ms;
|
||||||
|
--transition-duration-slow: 300ms;
|
||||||
|
|
||||||
|
--spacing-safe: env(safe-area-inset-bottom);
|
||||||
|
--spacing-safe-top: env(safe-area-inset-top);
|
||||||
|
--spacing-safe-bottom: env(safe-area-inset-bottom);
|
||||||
|
--spacing-safe-left: env(safe-area-inset-left);
|
||||||
|
--spacing-safe-right: env(safe-area-inset-right);
|
||||||
|
|
||||||
|
--animate-accordion-down: accordion-down 0.2s ease-out;
|
||||||
|
--animate-accordion-up: accordion-up 0.2s ease-out;
|
||||||
|
--animate-badge-pop: badge-pop 0.32s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
|
||||||
|
@keyframes accordion-down {
|
||||||
|
from {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
height: var(--radix-accordion-content-height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes accordion-up {
|
||||||
|
from {
|
||||||
|
height: var(--radix-accordion-content-height);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes badge-pop {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.5);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
transform: scale(1.18);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
The default border color has changed to `currentcolor` in Tailwind CSS v4,
|
||||||
|
so we've added these compatibility styles to make sure everything still
|
||||||
|
looks the same as it did with Tailwind CSS v3.
|
||||||
|
|
||||||
|
If we ever want to remove these styles, we need to add an explicit border
|
||||||
|
color utility to any element that depends on these defaults.
|
||||||
|
*/
|
||||||
|
@layer base {
|
||||||
|
*,
|
||||||
|
::after,
|
||||||
|
::before,
|
||||||
|
::backdrop,
|
||||||
|
::file-selector-button {
|
||||||
|
border-color: var(--color-gray-200, currentcolor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ export function TemplateForm({ open, onOpenChange, template, onSuccess }: Templa
|
|||||||
validateJson(e.target.value);
|
validateJson(e.target.value);
|
||||||
}}
|
}}
|
||||||
rows={18}
|
rows={18}
|
||||||
className="w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-xs shadow-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
className="w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-xs shadow-sm focus:outline-hidden focus:ring-2 focus:ring-ring"
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
/>
|
/>
|
||||||
{jsonError && <p className="text-xs text-destructive">{jsonError}</p>}
|
{jsonError && <p className="text-xs text-destructive">{jsonError}</p>}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function ServiceHealthCard({ service }: ServiceHealthCardProps) {
|
|||||||
<CardContent className="p-4">
|
<CardContent className="p-4">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<span
|
<span
|
||||||
className={cn('h-2.5 w-2.5 rounded-full flex-shrink-0', config.dot)}
|
className={cn('h-2.5 w-2.5 rounded-full shrink-0', config.dot)}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<span className="font-medium text-sm truncate">{service.name}</span>
|
<span className="font-medium text-sm truncate">{service.name}</span>
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ export function BerthDetailHeader({ berth }: BerthDetailHeaderProps) {
|
|||||||
keeping the header lean. */}
|
keeping the header lean. */}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'inline-flex h-12 min-w-[3.25rem] items-center justify-center rounded-2xl px-3 text-lg font-bold tracking-tight text-white shadow-sm',
|
'inline-flex h-12 min-w-13 items-center justify-center rounded-2xl px-3 text-lg font-bold tracking-tight text-white shadow-sm',
|
||||||
mooringLetterDot(berth.mooringNumber) ?? 'bg-slate-400',
|
mooringLetterDot(berth.mooringNumber) ?? 'bg-slate-400',
|
||||||
)}
|
)}
|
||||||
title={berth.area ? `${berth.area} Dock` : undefined}
|
title={berth.area ? `${berth.area} Dock` : undefined}
|
||||||
@@ -368,10 +368,7 @@ function InterestLinkPicker({
|
|||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent className="w-(--radix-popper-anchor-width) min-w-[320px] p-0" align="start">
|
||||||
className="w-[var(--radix-popper-anchor-width)] min-w-[320px] p-0"
|
|
||||||
align="start"
|
|
||||||
>
|
|
||||||
<Command>
|
<Command>
|
||||||
<CommandInput placeholder="Search prospects…" />
|
<CommandInput placeholder="Search prospects…" />
|
||||||
<CommandList>
|
<CommandList>
|
||||||
|
|||||||
@@ -642,7 +642,7 @@ function EntityMultiPicker({
|
|||||||
<div className="flex flex-wrap gap-1.5">
|
<div className="flex flex-wrap gap-1.5">
|
||||||
{selectedIds.map((id) => (
|
{selectedIds.map((id) => (
|
||||||
<Badge key={id} variant="secondary" className="gap-1 pr-1">
|
<Badge key={id} variant="secondary" className="gap-1 pr-1">
|
||||||
<span className="max-w-[14rem] truncate">{labelById.get(id) ?? id.slice(0, 8)}</span>
|
<span className="max-w-56 truncate">{labelById.get(id) ?? id.slice(0, 8)}</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="rounded-full p-0.5 hover:bg-muted-foreground/20"
|
className="rounded-full p-0.5 hover:bg-muted-foreground/20"
|
||||||
@@ -672,10 +672,7 @@ function EntityMultiPicker({
|
|||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent className="w-(--radix-popper-anchor-width) min-w-[280px] p-0" align="start">
|
||||||
className="w-[var(--radix-popper-anchor-width)] min-w-[280px] p-0"
|
|
||||||
align="start"
|
|
||||||
>
|
|
||||||
<Command shouldFilter={false}>
|
<Command shouldFilter={false}>
|
||||||
<CommandInput placeholder="Search…" onValueChange={setSearch} />
|
<CommandInput placeholder="Search…" onValueChange={setSearch} />
|
||||||
<CommandList>
|
<CommandList>
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export function DateRangePicker({ value, onChange, className }: DateRangePickerP
|
|||||||
empty result, and not understand why. */
|
empty result, and not understand why. */
|
||||||
max={draftTo && draftTo < today ? draftTo : today}
|
max={draftTo && draftTo < today ? draftTo : today}
|
||||||
onChange={(e) => setDraftFrom(e.target.value)}
|
onChange={(e) => setDraftFrom(e.target.value)}
|
||||||
className="w-auto max-w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm outline-none focus:border-brand/60 focus:ring-2 focus:ring-brand/15"
|
className="w-auto max-w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm outline-hidden focus:border-brand/60 focus:ring-2 focus:ring-brand/15"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label className="block text-xs">
|
<label className="block text-xs">
|
||||||
@@ -160,7 +160,7 @@ export function DateRangePicker({ value, onChange, className }: DateRangePickerP
|
|||||||
min={draftFrom || undefined}
|
min={draftFrom || undefined}
|
||||||
max={today}
|
max={today}
|
||||||
onChange={(e) => setDraftTo(e.target.value)}
|
onChange={(e) => setDraftTo(e.target.value)}
|
||||||
className="w-auto max-w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm outline-none focus:border-brand/60 focus:ring-2 focus:ring-brand/15"
|
className="w-auto max-w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm outline-hidden focus:border-brand/60 focus:ring-2 focus:ring-brand/15"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<div className="flex items-center justify-end gap-2 pt-1">
|
<div className="flex items-center justify-end gap-2 pt-1">
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ function PreviewRow({
|
|||||||
<dt className="w-32 shrink-0 text-xs text-muted-foreground">{label}</dt>
|
<dt className="w-32 shrink-0 text-xs text-muted-foreground">{label}</dt>
|
||||||
<dd
|
<dd
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex-1 break-words inline-flex items-center gap-2',
|
'flex-1 wrap-break-word inline-flex items-center gap-2',
|
||||||
missing
|
missing
|
||||||
? 'text-rose-700 font-medium'
|
? 'text-rose-700 font-medium'
|
||||||
: value
|
: value
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export function SigningProgress({ documentId, signers }: SigningProgressProps) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{idx < sorted.length - 1 && <div className="mb-6 h-0.5 w-8 flex-shrink-0 bg-border" />}
|
{idx < sorted.length - 1 && <div className="mb-6 h-0.5 w-8 shrink-0 bg-border" />}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export function EmailDraftButton({
|
|||||||
<p
|
<p
|
||||||
contentEditable
|
contentEditable
|
||||||
suppressContentEditableWarning
|
suppressContentEditableWarning
|
||||||
className="mt-1 text-sm font-medium border rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-ring"
|
className="mt-1 text-sm font-medium border rounded-md px-3 py-2 focus:outline-hidden focus:ring-2 focus:ring-ring"
|
||||||
>
|
>
|
||||||
{draft.subject}
|
{draft.subject}
|
||||||
</p>
|
</p>
|
||||||
@@ -145,7 +145,7 @@ export function EmailDraftButton({
|
|||||||
<pre
|
<pre
|
||||||
contentEditable
|
contentEditable
|
||||||
suppressContentEditableWarning
|
suppressContentEditableWarning
|
||||||
className="mt-1 text-sm whitespace-pre-wrap font-sans border rounded-md px-3 py-2 min-h-[300px] focus:outline-none focus:ring-2 focus:ring-ring"
|
className="mt-1 text-sm whitespace-pre-wrap font-sans border rounded-md px-3 py-2 min-h-[300px] focus:outline-hidden focus:ring-2 focus:ring-ring"
|
||||||
>
|
>
|
||||||
{draft.body}
|
{draft.body}
|
||||||
</pre>
|
</pre>
|
||||||
|
|||||||
@@ -393,7 +393,7 @@ export function ExpenseFormDialog({ open, onOpenChange, expense }: ExpenseFormDi
|
|||||||
|
|
||||||
{noReceipt && (
|
{noReceipt && (
|
||||||
<div className="flex gap-2 rounded-md border border-amber-300 bg-amber-50 p-2 text-xs text-amber-900 dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-200">
|
<div className="flex gap-2 rounded-md border border-amber-300 bg-amber-50 p-2 text-xs text-amber-900 dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-200">
|
||||||
<AlertTriangle className="h-4 w-4 flex-shrink-0" />
|
<AlertTriangle className="h-4 w-4 shrink-0" />
|
||||||
<span>
|
<span>
|
||||||
Expenses without a receipt may not be reimbursed by the parent company. The PDF
|
Expenses without a receipt may not be reimbursed by the parent company. The PDF
|
||||||
export will flag this expense.
|
export will flag this expense.
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export function TripLabelCombobox({
|
|||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-[var(--radix-popper-anchor-width)] min-w-[260px] p-0">
|
<PopoverContent className="w-(--radix-popper-anchor-width) min-w-[260px] p-0">
|
||||||
<Command shouldFilter={true}>
|
<Command shouldFilter={true}>
|
||||||
<CommandInput
|
<CommandInput
|
||||||
placeholder="Type a trip name…"
|
placeholder="Type a trip name…"
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ export function InlineStagePicker({
|
|||||||
}}
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
'inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-sm font-medium',
|
'inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-sm font-medium',
|
||||||
'transition-colors hover:brightness-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
'transition-colors hover:brightness-95 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring',
|
||||||
STAGE_BADGE[stage],
|
STAGE_BADGE[stage],
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ export function InterestForm({ open, onOpenChange, defaultClientId, interest }:
|
|||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-[var(--radix-popper-anchor-width)] min-w-[280px] p-0">
|
<PopoverContent className="w-(--radix-popper-anchor-width) min-w-[280px] p-0">
|
||||||
{/* shouldFilter={false}: server-side search via setClientSearch
|
{/* shouldFilter={false}: server-side search via setClientSearch
|
||||||
drives the result set. Without this, cmdk's default filter
|
drives the result set. Without this, cmdk's default filter
|
||||||
matches the user's typed text against CommandItem.value
|
matches the user's typed text against CommandItem.value
|
||||||
@@ -339,7 +339,7 @@ export function InterestForm({ open, onOpenChange, defaultClientId, interest }:
|
|||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-[var(--radix-popper-anchor-width)] min-w-[280px] p-0">
|
<PopoverContent className="w-(--radix-popper-anchor-width) min-w-[280px] p-0">
|
||||||
<Command shouldFilter={false}>
|
<Command shouldFilter={false}>
|
||||||
<CommandInput placeholder="Search berths..." onValueChange={setBerthSearch} />
|
<CommandInput placeholder="Search berths..." onValueChange={setBerthSearch} />
|
||||||
<CommandList>
|
<CommandList>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function PipelineColumn({ stage, label, items }: PipelineColumnProps) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={setNodeRef}
|
ref={setNodeRef}
|
||||||
className={`flex flex-col gap-2 min-w-[220px] w-[220px] flex-shrink-0 bg-muted/40 rounded-lg p-3 transition-colors ${
|
className={`flex flex-col gap-2 min-w-[220px] w-[220px] shrink-0 bg-muted/40 rounded-lg p-3 transition-colors ${
|
||||||
isOver ? 'bg-muted/70 ring-2 ring-primary/30' : ''
|
isOver ? 'bg-muted/70 ring-2 ring-primary/30' : ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export function MobileTopbar() {
|
|||||||
<header
|
<header
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed top-0 inset-x-0 z-40',
|
'fixed top-0 inset-x-0 z-40',
|
||||||
'bg-gradient-to-b from-[#1e2844] to-[#171f35]',
|
'bg-linear-to-b from-[#1e2844] to-[#171f35]',
|
||||||
'shadow-[0_4px_18px_-6px_rgba(15,23,42,0.45)]',
|
'shadow-[0_4px_18px_-6px_rgba(15,23,42,0.45)]',
|
||||||
'h-[calc(56px+env(safe-area-inset-top))] pt-safe-top',
|
'h-[calc(56px+env(safe-area-inset-top))] pt-safe-top',
|
||||||
'flex items-center gap-2 px-3',
|
'flex items-center gap-2 px-3',
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ function SidebarContent({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Open user menu"
|
aria-label="Open user menu"
|
||||||
className="rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#3a7bc8] focus-visible:ring-offset-2 focus-visible:ring-offset-white"
|
className="rounded-full focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-[#3a7bc8] focus-visible:ring-offset-2 focus-visible:ring-offset-white"
|
||||||
>
|
>
|
||||||
<Avatar className="w-8 h-8 cursor-pointer">
|
<Avatar className="w-8 h-8 cursor-pointer">
|
||||||
<AvatarImage src={undefined} />
|
<AvatarImage src={undefined} />
|
||||||
@@ -396,7 +396,7 @@ function SidebarContent({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Open user menu"
|
aria-label="Open user menu"
|
||||||
className="flex w-full items-center gap-3 rounded-md p-1.5 text-left transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#3a7bc8] focus-visible:ring-offset-2 focus-visible:ring-offset-white"
|
className="flex w-full items-center gap-3 rounded-md p-1.5 text-left transition-colors hover:bg-accent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-[#3a7bc8] focus-visible:ring-offset-2 focus-visible:ring-offset-white"
|
||||||
>
|
>
|
||||||
<Avatar className="w-8 h-8 shrink-0 shadow-sm ring-2 ring-slate-200">
|
<Avatar className="w-8 h-8 shrink-0 shadow-sm ring-2 ring-slate-200">
|
||||||
<AvatarImage src={undefined} />
|
<AvatarImage src={undefined} />
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export function NotificationItem({ notification, onMarkRead }: NotificationItemP
|
|||||||
className="w-full text-left flex items-start gap-3 px-4 py-3 hover:bg-muted/50 transition-colors"
|
className="w-full text-left flex items-start gap-3 px-4 py-3 hover:bg-muted/50 transition-colors"
|
||||||
>
|
>
|
||||||
{/* Unread indicator */}
|
{/* Unread indicator */}
|
||||||
<div className="mt-1.5 flex-shrink-0">
|
<div className="mt-1.5 shrink-0">
|
||||||
{!notification.isRead ? (
|
{!notification.isRead ? (
|
||||||
<span className="block h-2 w-2 rounded-full bg-blue-500" />
|
<span className="block h-2 w-2 rounded-full bg-blue-500" />
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -493,7 +493,7 @@ export function ScanShell() {
|
|||||||
// the home indicator on iPhone 14/15 in standalone PWA mode
|
// the home indicator on iPhone 14/15 in standalone PWA mode
|
||||||
// (viewportFit:cover + statusBarStyle:default exposes the safe-
|
// (viewportFit:cover + statusBarStyle:default exposes the safe-
|
||||||
// area inset, but the original `py-6` ignored it).
|
// area inset, but the original `py-6` ignored it).
|
||||||
className="mx-auto flex min-h-[100dvh] w-full max-w-xl flex-col gap-4 px-4 py-6 pb-[max(1.5rem,env(safe-area-inset-bottom))] sm:py-10"
|
className="mx-auto flex min-h-dvh w-full max-w-xl flex-col gap-4 px-4 py-6 pb-[max(1.5rem,env(safe-area-inset-bottom))] sm:py-10"
|
||||||
>
|
>
|
||||||
{/* Brand header - logo centered, page title underneath. Establishes
|
{/* Brand header - logo centered, page title underneath. Establishes
|
||||||
the standalone identity (this is the PWA home for the scanner). */}
|
the standalone identity (this is the PWA home for the scanner). */}
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ export function CommandSearch() {
|
|||||||
// Wrapper border swap is the focus indicator; suppress the
|
// Wrapper border swap is the focus indicator; suppress the
|
||||||
// global *:focus-visible ring that would otherwise paint a
|
// global *:focus-visible ring that would otherwise paint a
|
||||||
// rectangular box clashing with the rounded wrapper.
|
// rectangular box clashing with the rounded wrapper.
|
||||||
className="h-9 flex-1 min-w-0 bg-transparent text-sm outline-none ring-0 focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-muted-foreground"
|
className="h-9 flex-1 min-w-0 bg-transparent text-sm outline-hidden ring-0 focus:outline-hidden focus:ring-0 focus-visible:outline-hidden focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-muted-foreground"
|
||||||
/>
|
/>
|
||||||
{isFetching && query.length >= 2 && (
|
{isFetching && query.length >= 2 && (
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -35,10 +35,7 @@ export function HighlightMatch({
|
|||||||
if (i % 2 === 1) {
|
if (i % 2 === 1) {
|
||||||
// The capture group lands on odd indices.
|
// The capture group lands on odd indices.
|
||||||
return (
|
return (
|
||||||
<mark
|
<mark key={i} className="bg-brand/15 text-foreground rounded-[2px] px-px font-medium">
|
||||||
key={i}
|
|
||||||
className="bg-brand/15 text-foreground rounded-[2px] px-[1px] font-medium"
|
|
||||||
>
|
|
||||||
{part}
|
{part}
|
||||||
</mark>
|
</mark>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ export function MobileSearchOverlay({ open, onOpenChange }: MobileSearchOverlayP
|
|||||||
repositionInputs={false}
|
repositionInputs={false}
|
||||||
>
|
>
|
||||||
<VaulDrawer.Portal>
|
<VaulDrawer.Portal>
|
||||||
<VaulDrawer.Overlay className="fixed inset-0 z-50 bg-black/30 backdrop-blur-sm" />
|
<VaulDrawer.Overlay className="fixed inset-0 z-50 bg-black/30 backdrop-blur-xs" />
|
||||||
<VaulDrawer.Content
|
<VaulDrawer.Content
|
||||||
// Anchor by top + explicit height (not bottom: 0). iOS treats
|
// Anchor by top + explicit height (not bottom: 0). iOS treats
|
||||||
// `bottom: 0` on position:fixed inconsistently when the
|
// `bottom: 0` on position:fixed inconsistently when the
|
||||||
@@ -252,7 +252,7 @@ export function MobileSearchOverlay({ open, onOpenChange }: MobileSearchOverlayP
|
|||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
className={cn(
|
className={cn(
|
||||||
'ml-2 h-full w-full min-w-0 bg-transparent text-base outline-none',
|
'ml-2 h-full w-full min-w-0 bg-transparent text-base outline-hidden',
|
||||||
'placeholder:text-muted-foreground',
|
'placeholder:text-muted-foreground',
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -283,7 +283,7 @@ export function MobileSearchOverlay({ open, onOpenChange }: MobileSearchOverlayP
|
|||||||
matter the phone width. "All" is sticky-left so it's always
|
matter the phone width. "All" is sticky-left so it's always
|
||||||
one tap away when the user is deep in a bucket. */}
|
one tap away when the user is deep in a bucket. */}
|
||||||
<div className="border-b pb-3">
|
<div className="border-b pb-3">
|
||||||
<div className="flex gap-1.5 overflow-x-auto px-4 [-webkit-overflow-scrolling:touch] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
|
<div className="flex gap-1.5 overflow-x-auto px-4 [-webkit-overflow-scrolling:touch] scrollbar-none [&::-webkit-scrollbar]:hidden">
|
||||||
<BucketChip
|
<BucketChip
|
||||||
label="All"
|
label="All"
|
||||||
active={activeBucket === 'all'}
|
active={activeBucket === 'all'}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export function ActionRow({ children, className }: { children: ReactNode; classN
|
|||||||
'flex gap-2',
|
'flex gap-2',
|
||||||
'overflow-x-auto snap-x snap-mandatory scroll-smooth -mx-3 px-3 sm:mx-0 sm:px-0',
|
'overflow-x-auto snap-x snap-mandatory scroll-smooth -mx-3 px-3 sm:mx-0 sm:px-0',
|
||||||
'sm:flex-wrap sm:overflow-visible',
|
'sm:flex-wrap sm:overflow-visible',
|
||||||
'[&>*]:snap-start [&>*]:shrink-0 sm:[&>*]:snap-none',
|
'*:snap-start *:shrink-0 sm:*:snap-none',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export function BrandedAuthShell({ children, branding }: BrandedAuthShellProps)
|
|||||||
const altText = branding?.appName || 'Port Nimara';
|
const altText = branding?.appName || 'Port Nimara';
|
||||||
// fixed inset-0 anchors the auth surface to the viewport directly —
|
// fixed inset-0 anchors the auth surface to the viewport directly —
|
||||||
// iOS Safari ignores overflow-hidden on inner divs for body-level
|
// iOS Safari ignores overflow-hidden on inner divs for body-level
|
||||||
// scrolling, so a regular `h-[100dvh] overflow-hidden` wrapper doesn't
|
// scrolling, so a regular `h-dvh overflow-hidden` wrapper doesn't
|
||||||
// stop the rubber-band bounce. Pinning to the viewport via position
|
// stop the rubber-band bounce. Pinning to the viewport via position
|
||||||
// fixed does. The fixed-position shell then uses flex to center the
|
// fixed does. The fixed-position shell then uses flex to center the
|
||||||
// card within the visible area.
|
// card within the visible area.
|
||||||
|
|||||||
@@ -122,10 +122,7 @@ export function CountryCombobox({
|
|||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent className="w-(--radix-popper-anchor-width) min-w-[280px] p-0" align="start">
|
||||||
className="w-[var(--radix-popper-anchor-width)] min-w-[280px] p-0"
|
|
||||||
align="start"
|
|
||||||
>
|
|
||||||
<Command>
|
<Command>
|
||||||
<CommandInput placeholder="Search country or code…" />
|
<CommandInput placeholder="Search country or code…" />
|
||||||
<CommandList>
|
<CommandList>
|
||||||
|
|||||||
@@ -408,7 +408,7 @@ export function DataTable<TData>({
|
|||||||
const next = e.target.value === 'all' ? 1000 : Number(e.target.value);
|
const next = e.target.value === 'all' ? 1000 : Number(e.target.value);
|
||||||
onPaginationChange?.(1, next);
|
onPaginationChange?.(1, next);
|
||||||
}}
|
}}
|
||||||
className="h-8 rounded-md border border-input bg-background px-2 text-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
className="h-8 rounded-md border border-input bg-background px-2 text-sm focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring"
|
||||||
>
|
>
|
||||||
<option value="25">25</option>
|
<option value="25">25</option>
|
||||||
<option value="50">50</option>
|
<option value="50">50</option>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export function DetailPageShell({
|
|||||||
return (
|
return (
|
||||||
<div className={cn('flex flex-col min-h-full', className)}>
|
<div className={cn('flex flex-col min-h-full', className)}>
|
||||||
{/* Desktop-only sticky header - mobile topbar covers this on small viewports. */}
|
{/* Desktop-only sticky header - mobile topbar covers this on small viewports. */}
|
||||||
<div className="hidden sm:block sticky top-0 z-10 bg-background/95 backdrop-blur border-b border-border px-4 py-3 sm:px-6">
|
<div className="hidden sm:block sticky top-0 z-10 bg-background/95 backdrop-blur-sm border-b border-border px-4 py-3 sm:px-6">
|
||||||
<div className="flex items-center gap-3 min-w-0">
|
<div className="flex items-center gap-3 min-w-0">
|
||||||
<h2 className="truncate text-lg font-semibold text-foreground">{entityName}</h2>
|
<h2 className="truncate text-lg font-semibold text-foreground">{entityName}</h2>
|
||||||
{status ? <div className="ml-auto shrink-0">{status}</div> : null}
|
{status ? <div className="ml-auto shrink-0">{status}</div> : null}
|
||||||
@@ -65,7 +65,7 @@ export function DetailPageShell({
|
|||||||
className={cn(
|
className={cn(
|
||||||
'sm:hidden',
|
'sm:hidden',
|
||||||
'fixed inset-x-0 bottom-[calc(56px+env(safe-area-inset-bottom))] z-30',
|
'fixed inset-x-0 bottom-[calc(56px+env(safe-area-inset-bottom))] z-30',
|
||||||
'border-t border-border bg-background/95 backdrop-blur px-4 py-3',
|
'border-t border-border bg-background/95 backdrop-blur-sm px-4 py-3',
|
||||||
'flex items-center gap-2',
|
'flex items-center gap-2',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export function EntityActivityFeed({ endpoint, emptyText = 'No activity yet.' }:
|
|||||||
const actor = row.actor?.name || row.actor?.email || 'System';
|
const actor = row.actor?.name || row.actor?.email || 'System';
|
||||||
return (
|
return (
|
||||||
<li key={row.id} className="relative">
|
<li key={row.id} className="relative">
|
||||||
<span className="absolute -left-[31px] top-1 h-2.5 w-2.5 rounded-full bg-primary/70 ring-2 ring-background" />
|
<span className="absolute left-[-31px] top-1 h-2.5 w-2.5 rounded-full bg-primary/70 ring-2 ring-background" />
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
<span className="font-medium">{summarize(row)}</span>
|
<span className="font-medium">{summarize(row)}</span>
|
||||||
<span className="text-muted-foreground"> · {actor}</span>
|
<span className="text-muted-foreground"> · {actor}</span>
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export function InlineCountryField({
|
|||||||
data-testid={testId}
|
data-testid={testId}
|
||||||
className={cn(
|
className={cn(
|
||||||
'group inline-flex items-center gap-1.5 rounded px-1 -mx-1 py-0.5 text-left text-sm',
|
'group inline-flex items-center gap-1.5 rounded px-1 -mx-1 py-0.5 text-left text-sm',
|
||||||
'hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
|
'hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring',
|
||||||
disabled && 'cursor-not-allowed opacity-60 hover:bg-transparent',
|
disabled && 'cursor-not-allowed opacity-60 hover:bg-transparent',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ function ReadButton({
|
|||||||
className={cn(
|
className={cn(
|
||||||
'group rounded px-1 -mx-1 py-0.5 text-left text-sm',
|
'group rounded px-1 -mx-1 py-0.5 text-left text-sm',
|
||||||
multiline ? 'flex w-full items-start gap-1.5' : 'inline-flex items-center gap-1.5',
|
multiline ? 'flex w-full items-start gap-1.5' : 'inline-flex items-center gap-1.5',
|
||||||
'hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
|
'hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring',
|
||||||
// Select-kind buttons get a faint border so they read as a
|
// Select-kind buttons get a faint border so they read as a
|
||||||
// distinct interactive element rather than text-with-an-icon.
|
// distinct interactive element rather than text-with-an-icon.
|
||||||
kind === 'select' && 'border border-border bg-background hover:bg-accent',
|
kind === 'select' && 'border border-border bg-background hover:bg-accent',
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ export function InlinePhoneField({
|
|||||||
data-testid={testId}
|
data-testid={testId}
|
||||||
className={cn(
|
className={cn(
|
||||||
'group inline-flex items-center gap-1.5 rounded px-1 -mx-1 py-0.5 text-left text-sm',
|
'group inline-flex items-center gap-1.5 rounded px-1 -mx-1 py-0.5 text-left text-sm',
|
||||||
'hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
|
'hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring',
|
||||||
disabled && 'cursor-not-allowed opacity-60 hover:bg-transparent',
|
disabled && 'cursor-not-allowed opacity-60 hover:bg-transparent',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export function InlineTimezoneField({
|
|||||||
data-testid={testId}
|
data-testid={testId}
|
||||||
className={cn(
|
className={cn(
|
||||||
'group inline-flex items-center gap-1.5 rounded px-1 -mx-1 py-0.5 text-left text-sm',
|
'group inline-flex items-center gap-1.5 rounded px-1 -mx-1 py-0.5 text-left text-sm',
|
||||||
'hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
|
'hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring',
|
||||||
disabled && 'cursor-not-allowed opacity-60 hover:bg-transparent',
|
disabled && 'cursor-not-allowed opacity-60 hover:bg-transparent',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export function ListCard({
|
|||||||
const innerClassName = cn(
|
const innerClassName = cn(
|
||||||
'block p-3',
|
'block p-3',
|
||||||
accentClassName && 'pl-4',
|
accentClassName && 'pl-4',
|
||||||
'rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
'rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export function ResponsiveTabs({ tabs, value, onValueChange }: ResponsiveTabsPro
|
|||||||
slides under the wrapper. */}
|
slides under the wrapper. */}
|
||||||
<div
|
<div
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
className="overflow-x-auto -mx-2 px-2 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
className="overflow-x-auto -mx-2 px-2 scrollbar-none [&::-webkit-scrollbar]:hidden"
|
||||||
>
|
>
|
||||||
<TabsList className="inline-flex w-max">
|
<TabsList className="inline-flex w-max">
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export function TagPicker({
|
|||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-[--radix-popover-trigger-width] p-0" align="start">
|
<PopoverContent className="w-(--radix-popover-trigger-width) p-0" align="start">
|
||||||
<Command>
|
<Command>
|
||||||
<CommandInput placeholder="Search tags..." />
|
<CommandInput placeholder="Search tags..." />
|
||||||
<CommandList>
|
<CommandList>
|
||||||
|
|||||||
@@ -97,10 +97,7 @@ export function TimezoneCombobox({
|
|||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent className="w-(--radix-popper-anchor-width) min-w-[360px] p-0" align="start">
|
||||||
className="w-[var(--radix-popper-anchor-width)] min-w-[360px] p-0"
|
|
||||||
align="start"
|
|
||||||
>
|
|
||||||
<Command>
|
<Command>
|
||||||
<CommandInput placeholder="Search timezones…" />
|
<CommandInput placeholder="Search timezones…" />
|
||||||
<CommandList>
|
<CommandList>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const badgeVariants = cva(
|
const badgeVariants = cva(
|
||||||
'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const BreadcrumbList = React.forwardRef<HTMLOListElement, React.ComponentPropsWi
|
|||||||
<ol
|
<ol
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5',
|
'flex flex-wrap items-center gap-1.5 wrap-break-word text-sm text-muted-foreground sm:gap-2.5',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ function Calendar({
|
|||||||
<DayPicker
|
<DayPicker
|
||||||
showOutsideDays={showOutsideDays}
|
showOutsideDays={showOutsideDays}
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-background group/calendar p-3 [--cell-size:2rem] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent',
|
'bg-background group/calendar p-3 [--cell-size:2rem] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent',
|
||||||
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
String.raw`[.rdp-button\_next>svg]:**:rtl:rotate-180`,
|
||||||
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
String.raw`[.rdp-button\_previous>svg]:**:rtl:rotate-180`,
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
captionLayout={captionLayout}
|
captionLayout={captionLayout}
|
||||||
@@ -45,20 +45,20 @@ function Calendar({
|
|||||||
),
|
),
|
||||||
button_previous: cn(
|
button_previous: cn(
|
||||||
buttonVariants({ variant: buttonVariant }),
|
buttonVariants({ variant: buttonVariant }),
|
||||||
'h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50',
|
'h-(--cell-size) w-(--cell-size) select-none p-0 aria-disabled:opacity-50',
|
||||||
defaultClassNames.button_previous,
|
defaultClassNames.button_previous,
|
||||||
),
|
),
|
||||||
button_next: cn(
|
button_next: cn(
|
||||||
buttonVariants({ variant: buttonVariant }),
|
buttonVariants({ variant: buttonVariant }),
|
||||||
'h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50',
|
'h-(--cell-size) w-(--cell-size) select-none p-0 aria-disabled:opacity-50',
|
||||||
defaultClassNames.button_next,
|
defaultClassNames.button_next,
|
||||||
),
|
),
|
||||||
month_caption: cn(
|
month_caption: cn(
|
||||||
'flex h-[--cell-size] w-full items-center justify-center px-[--cell-size]',
|
'flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)',
|
||||||
defaultClassNames.month_caption,
|
defaultClassNames.month_caption,
|
||||||
),
|
),
|
||||||
dropdowns: cn(
|
dropdowns: cn(
|
||||||
'flex h-[--cell-size] w-full items-center justify-center gap-1.5 text-sm font-medium',
|
'flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium',
|
||||||
defaultClassNames.dropdowns,
|
defaultClassNames.dropdowns,
|
||||||
),
|
),
|
||||||
dropdown_root: cn(
|
dropdown_root: cn(
|
||||||
@@ -79,7 +79,7 @@ function Calendar({
|
|||||||
defaultClassNames.weekday,
|
defaultClassNames.weekday,
|
||||||
),
|
),
|
||||||
week: cn('mt-2 flex w-full', defaultClassNames.week),
|
week: cn('mt-2 flex w-full', defaultClassNames.week),
|
||||||
week_number_header: cn('w-[--cell-size] select-none', defaultClassNames.week_number_header),
|
week_number_header: cn('w-(--cell-size) select-none', defaultClassNames.week_number_header),
|
||||||
week_number: cn(
|
week_number: cn(
|
||||||
'text-muted-foreground select-none text-[0.8rem]',
|
'text-muted-foreground select-none text-[0.8rem]',
|
||||||
defaultClassNames.week_number,
|
defaultClassNames.week_number,
|
||||||
@@ -126,7 +126,7 @@ function Calendar({
|
|||||||
WeekNumber: ({ children, ...props }) => {
|
WeekNumber: ({ children, ...props }) => {
|
||||||
return (
|
return (
|
||||||
<td {...props}>
|
<td {...props}>
|
||||||
<div className="flex size-[--cell-size] items-center justify-center text-center">
|
<div className="flex size-(--cell-size) items-center justify-center text-center">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -168,7 +168,7 @@ function CalendarDayButton({
|
|||||||
data-range-end={modifiers.range_end}
|
data-range-end={modifiers.range_end}
|
||||||
data-range-middle={modifiers.range_middle}
|
data-range-middle={modifiers.range_middle}
|
||||||
className={cn(
|
className={cn(
|
||||||
'data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 flex aspect-square h-auto w-full min-w-[--cell-size] flex-col gap-1 font-normal leading-none data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] [&>span]:text-xs [&>span]:opacity-70',
|
'data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 flex aspect-square h-auto w-full min-w-(--cell-size) flex-col gap-1 font-normal leading-none data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] [&>span]:text-xs [&>span]:opacity-70',
|
||||||
defaultClassNames.day,
|
defaultClassNames.day,
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const Checkbox = React.forwardRef<
|
|||||||
<CheckboxPrimitive.Root
|
<CheckboxPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'grid place-content-center peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
|
'grid place-content-center peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const CommandDialog = ({ children, ...props }: DialogProps) => {
|
|||||||
return (
|
return (
|
||||||
<Dialog {...props}>
|
<Dialog {...props}>
|
||||||
<DialogContent className="overflow-hidden p-0">
|
<DialogContent className="overflow-hidden p-0">
|
||||||
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
<Command className="**:[[cmdk-group-heading]]:px-2 **:[[cmdk-group-heading]]:font-medium **:[[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 **:[[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 **:[[cmdk-input]]:h-12 **:[[cmdk-item]]:px-2 **:[[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
||||||
{children}
|
{children}
|
||||||
</Command>
|
</Command>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
@@ -44,7 +44,7 @@ const CommandInput = React.forwardRef<
|
|||||||
<CommandPrimitive.Input
|
<CommandPrimitive.Input
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
|
'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -92,7 +92,7 @@ const CommandGroup = React.forwardRef<
|
|||||||
<CommandPrimitive.Group
|
<CommandPrimitive.Group
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground',
|
'overflow-hidden p-1 text-foreground **:[[cmdk-group-heading]]:px-2 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-medium **:[[cmdk-group-heading]]:text-muted-foreground',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -120,7 +120,7 @@ const CommandItem = React.forwardRef<
|
|||||||
<CommandPrimitive.Item
|
<CommandPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
'relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ const DialogContent = React.forwardRef<
|
|||||||
// Long forms get an internal scroll cap so the close button + footer
|
// Long forms get an internal scroll cap so the close button + footer
|
||||||
// stay reachable without scrolling the whole page.
|
// stay reachable without scrolling the whole page.
|
||||||
'fixed top-0 right-0 bottom-0 left-0 z-50 grid w-full gap-4 border-0 bg-background p-4 shadow-lg duration-200 sm:p-6',
|
'fixed top-0 right-0 bottom-0 left-0 z-50 grid w-full gap-4 border-0 bg-background p-4 shadow-lg duration-200 sm:p-6',
|
||||||
'max-h-[100dvh] overflow-y-auto sm:max-h-[calc(100dvh-2rem)]',
|
'max-h-dvh overflow-y-auto sm:max-h-[calc(100dvh-2rem)]',
|
||||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||||
'sm:top-[50%] sm:right-auto sm:bottom-auto sm:left-[50%] sm:max-w-lg sm:translate-x-[-50%] sm:translate-y-[-50%] sm:border sm:rounded-lg',
|
'sm:top-[50%] sm:right-auto sm:bottom-auto sm:left-[50%] sm:max-w-lg sm:translate-x-[-50%] sm:translate-y-[-50%] sm:border sm:rounded-lg',
|
||||||
'sm:data-[state=closed]:zoom-out-95 sm:data-[state=open]:zoom-in-95 sm:data-[state=closed]:slide-out-to-left-1/2 sm:data-[state=closed]:slide-out-to-top-[48%] sm:data-[state=open]:slide-in-from-left-1/2 sm:data-[state=open]:slide-in-from-top-[48%]',
|
'sm:data-[state=closed]:zoom-out-95 sm:data-[state=open]:zoom-in-95 sm:data-[state=closed]:slide-out-to-left-1/2 sm:data-[state=closed]:slide-out-to-top-[48%] sm:data-[state=open]:slide-in-from-left-1/2 sm:data-[state=open]:slide-in-from-top-[48%]',
|
||||||
@@ -57,7 +57,7 @@ const DialogContent = React.forwardRef<
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||||
<X className="h-4 w-4" />
|
<X className="h-4 w-4" />
|
||||||
<span className="sr-only">Close</span>
|
<span className="sr-only">Close</span>
|
||||||
</DialogPrimitive.Close>
|
</DialogPrimitive.Close>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.SubTrigger
|
<DropdownMenuPrimitive.SubTrigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
'flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||||
inset && 'pl-8',
|
inset && 'pl-8',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
@@ -46,7 +46,7 @@ const DropdownMenuSubContent = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.SubContent
|
<DropdownMenuPrimitive.SubContent
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]',
|
'z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-dropdown-menu-content-transform-origin)',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -63,8 +63,8 @@ const DropdownMenuContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
'z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
|
'z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-32 overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
|
||||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]',
|
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-dropdown-menu-content-transform-origin)',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -82,7 +82,7 @@ const DropdownMenuItem = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.Item
|
<DropdownMenuPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
|
'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||||
inset && 'pl-8',
|
inset && 'pl-8',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
@@ -98,7 +98,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.CheckboxItem
|
<DropdownMenuPrimitive.CheckboxItem
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
@@ -121,7 +121,7 @@ const DropdownMenuRadioItem = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.RadioItem
|
<DropdownMenuPrimitive.RadioItem
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
|
|||||||
type={type}
|
type={type}
|
||||||
inputMode={resolvedInputMode}
|
inputMode={resolvedInputMode}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex h-11 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
'flex h-11 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
|
|||||||
const NavigationMenuItem = NavigationMenuPrimitive.Item;
|
const NavigationMenuItem = NavigationMenuPrimitive.Item;
|
||||||
|
|
||||||
const navigationMenuTriggerStyle = cva(
|
const navigationMenuTriggerStyle = cva(
|
||||||
'group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:text-accent-foreground data-[state=open]:bg-accent/50 data-[state=open]:hover:bg-accent data-[state=open]:focus:bg-accent',
|
'group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-hidden disabled:pointer-events-none disabled:opacity-50 data-[state=open]:text-accent-foreground data-[state=open]:bg-accent/50 data-[state=open]:hover:bg-accent data-[state=open]:focus:bg-accent',
|
||||||
);
|
);
|
||||||
|
|
||||||
const NavigationMenuTrigger = React.forwardRef<
|
const NavigationMenuTrigger = React.forwardRef<
|
||||||
@@ -49,7 +49,7 @@ const NavigationMenuTrigger = React.forwardRef<
|
|||||||
>
|
>
|
||||||
{children}{' '}
|
{children}{' '}
|
||||||
<ChevronDown
|
<ChevronDown
|
||||||
className="relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180"
|
className="relative top-px ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</NavigationMenuPrimitive.Trigger>
|
</NavigationMenuPrimitive.Trigger>
|
||||||
@@ -80,7 +80,7 @@ const NavigationMenuViewport = React.forwardRef<
|
|||||||
<div className={cn('absolute left-0 top-full flex justify-center')}>
|
<div className={cn('absolute left-0 top-full flex justify-center')}>
|
||||||
<NavigationMenuPrimitive.Viewport
|
<NavigationMenuPrimitive.Viewport
|
||||||
className={cn(
|
className={cn(
|
||||||
'origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]',
|
'origin-top-center relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-(--radix-navigation-menu-viewport-width)',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@@ -97,7 +97,7 @@ const NavigationMenuIndicator = React.forwardRef<
|
|||||||
<NavigationMenuPrimitive.Indicator
|
<NavigationMenuPrimitive.Indicator
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in',
|
'top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const PopoverContent = React.forwardRef<
|
|||||||
// on narrow viewports the calc() ceiling kicks in.
|
// on narrow viewports the calc() ceiling kicks in.
|
||||||
collisionPadding={collisionPadding}
|
collisionPadding={collisionPadding}
|
||||||
className={cn(
|
className={cn(
|
||||||
'z-50 w-[min(calc(100vw-2rem),18rem)] rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-popover-content-transform-origin]',
|
'z-50 w-[min(calc(100vw-2rem),18rem)] rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-popover-content-transform-origin)',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const RadioGroupItem = React.forwardRef<
|
|||||||
<RadioGroupPrimitive.Item
|
<RadioGroupPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'aspect-square h-4 w-4 rounded-full border border-primary text-primary shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
'aspect-square h-4 w-4 rounded-full border border-primary text-primary shadow focus:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ const ScrollBar = React.forwardRef<
|
|||||||
orientation={orientation}
|
orientation={orientation}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex touch-none select-none transition-colors',
|
'flex touch-none select-none transition-colors',
|
||||||
orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-[1px]',
|
orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-px',
|
||||||
orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-[1px]',
|
orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-px',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const SelectTrigger = React.forwardRef<
|
|||||||
<SelectPrimitive.Trigger
|
<SelectPrimitive.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
|
'flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-placeholder:text-muted-foreground focus:outline-hidden focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -68,7 +68,7 @@ const SelectContent = React.forwardRef<
|
|||||||
<SelectPrimitive.Content
|
<SelectPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]',
|
'relative z-50 max-h-(--radix-select-content-available-height) min-w-32 overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-select-content-transform-origin)',
|
||||||
position === 'popper' &&
|
position === 'popper' &&
|
||||||
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||||
className,
|
className,
|
||||||
@@ -81,7 +81,7 @@ const SelectContent = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
'p-1',
|
'p-1',
|
||||||
position === 'popper' &&
|
position === 'popper' &&
|
||||||
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',
|
'h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -111,7 +111,7 @@ const SelectItem = React.forwardRef<
|
|||||||
<SelectPrimitive.Item
|
<SelectPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const Separator = React.forwardRef<
|
|||||||
orientation={orientation}
|
orientation={orientation}
|
||||||
className={cn(
|
className={cn(
|
||||||
'shrink-0 bg-border',
|
'shrink-0 bg-border',
|
||||||
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
|
orientation === 'horizontal' ? 'h-px w-full' : 'h-full w-px',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ const SheetContent = React.forwardRef<
|
|||||||
<SheetPortal>
|
<SheetPortal>
|
||||||
<SheetOverlay />
|
<SheetOverlay />
|
||||||
<SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
|
<SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
|
||||||
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
||||||
<X className="h-4 w-4" />
|
<X className="h-4 w-4" />
|
||||||
<span className="sr-only">Close</span>
|
<span className="sr-only">Close</span>
|
||||||
</SheetPrimitive.Close>
|
</SheetPrimitive.Close>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const Slider = React.forwardRef<
|
|||||||
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20">
|
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20">
|
||||||
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
||||||
</SliderPrimitive.Track>
|
</SliderPrimitive.Track>
|
||||||
<SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
|
<SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
|
||||||
</SliderPrimitive.Root>
|
</SliderPrimitive.Root>
|
||||||
));
|
));
|
||||||
Slider.displayName = SliderPrimitive.Root.displayName;
|
Slider.displayName = SliderPrimitive.Root.displayName;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const Switch = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<SwitchPrimitives.Root
|
<SwitchPrimitives.Root
|
||||||
className={cn(
|
className={cn(
|
||||||
'peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
|
'peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const TableFooter = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<tfoot
|
<tfoot
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn('border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', className)}
|
className={cn('border-t bg-muted/50 font-medium last:[&>tr]:border-b-0', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
@@ -64,7 +64,7 @@ const TableHead = React.forwardRef<
|
|||||||
<th
|
<th
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
'h-10 px-2 text-left align-middle font-medium text-muted-foreground has-[[role=checkbox]]:pr-0 *:[[role=checkbox]]:translate-y-[2px]',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -79,7 +79,7 @@ const TableCell = React.forwardRef<
|
|||||||
<td
|
<td
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
'p-2 align-middle has-[[role=checkbox]]:pr-0 *:[[role=checkbox]]:translate-y-[2px]',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const TabsTrigger = React.forwardRef<
|
|||||||
<TabsPrimitive.Trigger
|
<TabsPrimitive.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow',
|
'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -44,7 +44,7 @@ const TabsContent = React.forwardRef<
|
|||||||
<TabsPrimitive.Content
|
<TabsPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
'mt-2 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, React.ComponentProps<'tex
|
|||||||
return (
|
return (
|
||||||
<textarea
|
<textarea
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
'flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const TooltipContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]',
|
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-tooltip-content-transform-origin)',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export type Align = 'left' | 'right' | 'center';
|
|||||||
|
|
||||||
export interface TableColumn<Row> {
|
export interface TableColumn<Row> {
|
||||||
header: string;
|
header: string;
|
||||||
/** flex-grow weight (default 1) — controls column width proportions. */
|
/** grow weight (default 1) — controls column width proportions. */
|
||||||
flex?: number;
|
flex?: number;
|
||||||
align?: Align;
|
align?: Align;
|
||||||
render: (row: Row, rowIndex: number) => ReactNode;
|
render: (row: Row, rowIndex: number) => ReactNode;
|
||||||
|
|||||||
@@ -1,187 +0,0 @@
|
|||||||
import type { Config } from 'tailwindcss';
|
|
||||||
import tailwindcssAnimate from 'tailwindcss-animate';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
darkMode: ['class', 'class'],
|
|
||||||
content: ['./src/**/*.{ts,tsx}'],
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
border: 'hsl(var(--border))',
|
|
||||||
input: 'hsl(var(--input))',
|
|
||||||
ring: 'hsl(var(--ring))',
|
|
||||||
background: 'hsl(var(--background))',
|
|
||||||
foreground: 'hsl(var(--foreground))',
|
|
||||||
primary: {
|
|
||||||
DEFAULT: 'hsl(var(--primary))',
|
|
||||||
foreground: 'hsl(var(--primary-foreground))',
|
|
||||||
},
|
|
||||||
secondary: {
|
|
||||||
DEFAULT: 'hsl(var(--secondary))',
|
|
||||||
foreground: 'hsl(var(--secondary-foreground))',
|
|
||||||
},
|
|
||||||
destructive: {
|
|
||||||
DEFAULT: 'hsl(var(--destructive))',
|
|
||||||
foreground: 'hsl(var(--destructive-foreground))',
|
|
||||||
},
|
|
||||||
muted: {
|
|
||||||
DEFAULT: 'hsl(var(--muted))',
|
|
||||||
foreground: 'hsl(var(--muted-foreground))',
|
|
||||||
},
|
|
||||||
accent: {
|
|
||||||
DEFAULT: 'hsl(var(--accent))',
|
|
||||||
foreground: 'hsl(var(--accent-foreground))',
|
|
||||||
},
|
|
||||||
popover: {
|
|
||||||
DEFAULT: 'hsl(var(--popover))',
|
|
||||||
foreground: 'hsl(var(--popover-foreground))',
|
|
||||||
},
|
|
||||||
card: {
|
|
||||||
DEFAULT: 'hsl(var(--card))',
|
|
||||||
foreground: 'hsl(var(--card-foreground))',
|
|
||||||
},
|
|
||||||
brand: {
|
|
||||||
'50': '#d8e5f4',
|
|
||||||
'100': '#b1cbe9',
|
|
||||||
'200': '#89b0de',
|
|
||||||
'300': '#6196d3',
|
|
||||||
'400': '#3a7bc8',
|
|
||||||
'500': '#2f6ab5',
|
|
||||||
'600': '#255a9e',
|
|
||||||
'700': '#1c4a87',
|
|
||||||
DEFAULT: '#3a7bc8',
|
|
||||||
dark: '#1e2844',
|
|
||||||
},
|
|
||||||
navy: {
|
|
||||||
'50': '#cdcfd6',
|
|
||||||
'100': '#9ea1af',
|
|
||||||
'200': '#71768a',
|
|
||||||
'300': '#474e66',
|
|
||||||
'400': '#1e2844',
|
|
||||||
'500': '#171f35',
|
|
||||||
'600': '#101625',
|
|
||||||
DEFAULT: '#1e2844',
|
|
||||||
},
|
|
||||||
sage: {
|
|
||||||
DEFAULT: '#dae3c1',
|
|
||||||
light: '#edf1e2',
|
|
||||||
dark: '#b8c49e',
|
|
||||||
},
|
|
||||||
mint: {
|
|
||||||
DEFAULT: '#add5b3',
|
|
||||||
light: '#d6ead9',
|
|
||||||
dark: '#7dba85',
|
|
||||||
},
|
|
||||||
teal: {
|
|
||||||
DEFAULT: '#83aab1',
|
|
||||||
light: '#b1cdd2',
|
|
||||||
dark: '#5a8a92',
|
|
||||||
},
|
|
||||||
purple: {
|
|
||||||
DEFAULT: '#685aa3',
|
|
||||||
light: '#a49ac6',
|
|
||||||
dark: '#4d4280',
|
|
||||||
},
|
|
||||||
success: {
|
|
||||||
DEFAULT: '#2d8a4e',
|
|
||||||
bg: '#e8f5e9',
|
|
||||||
border: '#a5d6a7',
|
|
||||||
},
|
|
||||||
warning: {
|
|
||||||
DEFAULT: '#e6a817',
|
|
||||||
bg: '#fff8e1',
|
|
||||||
border: '#ffe082',
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
DEFAULT: '#d32f2f',
|
|
||||||
bg: '#ffebee',
|
|
||||||
border: '#ef9a9a',
|
|
||||||
},
|
|
||||||
sidebar: {
|
|
||||||
DEFAULT: '#1e2844',
|
|
||||||
text: '#cdcfd6',
|
|
||||||
hover: '#171f35',
|
|
||||||
active: '#3a7bc8',
|
|
||||||
divider: '#474e66',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fontFamily: {
|
|
||||||
sans: ['Inter', 'system-ui', '-apple-system', 'Arial', 'sans-serif'],
|
|
||||||
mono: ['JetBrains Mono', 'ui-monospace', 'monospace'],
|
|
||||||
serif: ['Georgia', 'Times New Roman', 'serif'],
|
|
||||||
},
|
|
||||||
boxShadow: {
|
|
||||||
xs: '0 1px 2px 0 rgb(15 23 42 / 0.04)',
|
|
||||||
sm: '0 2px 4px -1px rgb(15 23 42 / 0.06)',
|
|
||||||
DEFAULT: '0 1px 3px rgba(30, 40, 68, 0.10), 0 1px 2px rgba(30, 40, 68, 0.06)',
|
|
||||||
md: '0 4px 12px -2px rgb(15 23 42 / 0.08)',
|
|
||||||
lg: '0 12px 32px -8px rgb(15 23 42 / 0.12)',
|
|
||||||
xl: '0 20px 25px rgba(30, 40, 68, 0.10), 0 8px 10px rgba(30, 40, 68, 0.04)',
|
|
||||||
glow: '0 0 0 4px rgb(58 123 200 / 0.12)',
|
|
||||||
},
|
|
||||||
borderRadius: {
|
|
||||||
sm: '0.375rem',
|
|
||||||
DEFAULT: '0.375rem',
|
|
||||||
md: '0.5rem',
|
|
||||||
lg: '0.625rem',
|
|
||||||
xl: '0.875rem',
|
|
||||||
},
|
|
||||||
backgroundImage: {
|
|
||||||
'gradient-brand': 'linear-gradient(135deg, #3a7bc8 0%, #2f6ab5 100%)',
|
|
||||||
'gradient-brand-soft': 'linear-gradient(135deg, #d8e5f4 0%, #ffffff 100%)',
|
|
||||||
'gradient-success': 'linear-gradient(135deg, #e8f5e9 0%, #ffffff 100%)',
|
|
||||||
'gradient-warning': 'linear-gradient(135deg, #fef3c7 0%, #ffffff 100%)',
|
|
||||||
},
|
|
||||||
transitionTimingFunction: {
|
|
||||||
spring: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
|
|
||||||
smooth: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
sidebar: '256px',
|
|
||||||
'sidebar-collapsed': '64px',
|
|
||||||
},
|
|
||||||
transitionDuration: {
|
|
||||||
sidebar: '200ms',
|
|
||||||
fast: '150ms',
|
|
||||||
base: '200ms',
|
|
||||||
slow: '300ms',
|
|
||||||
},
|
|
||||||
spacing: {
|
|
||||||
safe: 'env(safe-area-inset-bottom)',
|
|
||||||
'safe-top': 'env(safe-area-inset-top)',
|
|
||||||
'safe-bottom': 'env(safe-area-inset-bottom)',
|
|
||||||
'safe-left': 'env(safe-area-inset-left)',
|
|
||||||
'safe-right': 'env(safe-area-inset-right)',
|
|
||||||
},
|
|
||||||
keyframes: {
|
|
||||||
'accordion-down': {
|
|
||||||
from: {
|
|
||||||
height: '0',
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
height: 'var(--radix-accordion-content-height)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'accordion-up': {
|
|
||||||
from: {
|
|
||||||
height: 'var(--radix-accordion-content-height)',
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
height: '0',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'badge-pop': {
|
|
||||||
'0%': { transform: 'scale(0.5)', opacity: '0' },
|
|
||||||
'60%': { transform: 'scale(1.18)', opacity: '1' },
|
|
||||||
'100%': { transform: 'scale(1)', opacity: '1' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
'accordion-down': 'accordion-down 0.2s ease-out',
|
|
||||||
'accordion-up': 'accordion-up 0.2s ease-out',
|
|
||||||
'badge-pop': 'badge-pop 0.32s cubic-bezier(0.34, 1.56, 0.64, 1)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [tailwindcssAnimate],
|
|
||||||
} satisfies Config;
|
|
||||||
Reference in New Issue
Block a user