feat: 增加 shadcn 样式
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { InputOTP, InputOTPGroup, InputOTPSlot } from '@/components/ui/input-otp';
|
||||||
import { MailIcon } from './Icons';
|
import { MailIcon } from './Icons';
|
||||||
|
|
||||||
export default function LoginModal({
|
export default function LoginModal({
|
||||||
@@ -56,15 +57,21 @@ export default function LoginModal({
|
|||||||
<div className="muted" style={{ marginBottom: 8, fontSize: '0.8rem' }}>
|
<div className="muted" style={{ marginBottom: 8, fontSize: '0.8rem' }}>
|
||||||
请输入邮箱验证码以完成注册/登录
|
请输入邮箱验证码以完成注册/登录
|
||||||
</div>
|
</div>
|
||||||
<input
|
<InputOTP
|
||||||
className="input"
|
|
||||||
type="text"
|
|
||||||
placeholder="输入验证码"
|
|
||||||
value={loginOtp}
|
|
||||||
onChange={(e) => setLoginOtp(e.target.value)}
|
|
||||||
disabled={loginLoading}
|
|
||||||
maxLength={6}
|
maxLength={6}
|
||||||
/>
|
value={loginOtp}
|
||||||
|
onChange={(value) => setLoginOtp(value)}
|
||||||
|
disabled={loginLoading}
|
||||||
|
>
|
||||||
|
<InputOTPGroup>
|
||||||
|
<InputOTPSlot index={0} />
|
||||||
|
<InputOTPSlot index={1} />
|
||||||
|
<InputOTPSlot index={2} />
|
||||||
|
<InputOTPSlot index={3} />
|
||||||
|
<InputOTPSlot index={4} />
|
||||||
|
<InputOTPSlot index={5} />
|
||||||
|
</InputOTPGroup>
|
||||||
|
</InputOTP>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{loginError && (
|
{loginError && (
|
||||||
|
|||||||
@@ -3,6 +3,13 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { CloseIcon } from './Icons';
|
import { CloseIcon } from './Icons';
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select';
|
||||||
|
|
||||||
export default function ScanImportConfirmModal({
|
export default function ScanImportConfirmModal({
|
||||||
scannedFunds,
|
scannedFunds,
|
||||||
@@ -121,18 +128,18 @@ export default function ScanImportConfirmModal({
|
|||||||
</div>
|
</div>
|
||||||
<div style={{ marginTop: 12, display: 'flex', alignItems: 'center', gap: 8 }}>
|
<div style={{ marginTop: 12, display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||||
<span className="muted" style={{ fontSize: 13, whiteSpace: 'nowrap' }}>添加到分组:</span>
|
<span className="muted" style={{ fontSize: 13, whiteSpace: 'nowrap' }}>添加到分组:</span>
|
||||||
<select
|
<Select value={selectedGroupId} onValueChange={(value) => setSelectedGroupId(value)}>
|
||||||
className="select"
|
<SelectTrigger className="flex-1">
|
||||||
value={selectedGroupId}
|
<SelectValue placeholder="选择分组" />
|
||||||
onChange={(e) => setSelectedGroupId(e.target.value)}
|
</SelectTrigger>
|
||||||
style={{ flex: 1 }}
|
<SelectContent>
|
||||||
>
|
<SelectItem value="all">全部</SelectItem>
|
||||||
<option value="all">全部</option>
|
<SelectItem value="fav">自选</SelectItem>
|
||||||
<option value="fav">自选</option>
|
{groups.filter(g => g.id !== 'all' && g.id !== 'fav').map(g => (
|
||||||
{groups.filter(g => g.id !== 'all' && g.id !== 'fav').map(g => (
|
<SelectItem key={g.id} value={g.id}>{g.name}</SelectItem>
|
||||||
<option key={g.id} value={g.id}>{g.name}</option>
|
))}
|
||||||
))}
|
</SelectContent>
|
||||||
</select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
144
app/globals.css
144
app/globals.css
@@ -1,3 +1,9 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
@import "tw-animate-css";
|
||||||
|
@import "shadcn/tailwind.css";
|
||||||
|
|
||||||
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--bg: #0f172a;
|
--bg: #0f172a;
|
||||||
--card: #111827;
|
--card: #111827;
|
||||||
@@ -10,6 +16,33 @@
|
|||||||
--border: #1f2937;
|
--border: #1f2937;
|
||||||
--table-pinned-header-bg: #2a394b;
|
--table-pinned-header-bg: #2a394b;
|
||||||
--table-row-hover-bg: #2a394b;
|
--table-row-hover-bg: #2a394b;
|
||||||
|
--radius: 0.625rem;
|
||||||
|
--background: #0f172a;
|
||||||
|
--foreground: #e5e7eb;
|
||||||
|
--card-foreground: #e5e7eb;
|
||||||
|
--popover: #111827;
|
||||||
|
--popover-foreground: #e5e7eb;
|
||||||
|
--primary-foreground: #0f172a;
|
||||||
|
--secondary: #1f2937;
|
||||||
|
--secondary-foreground: #e5e7eb;
|
||||||
|
--muted-foreground: #9ca3af;
|
||||||
|
--accent-foreground: #e5e7eb;
|
||||||
|
--destructive: #f87171;
|
||||||
|
--input: #1f2937;
|
||||||
|
--ring: #22d3ee;
|
||||||
|
--chart-1: #22d3ee;
|
||||||
|
--chart-2: #60a5fa;
|
||||||
|
--chart-3: #34d399;
|
||||||
|
--chart-4: #f472b6;
|
||||||
|
--chart-5: #fbbf24;
|
||||||
|
--sidebar: #111827;
|
||||||
|
--sidebar-foreground: #e5e7eb;
|
||||||
|
--sidebar-primary: #22d3ee;
|
||||||
|
--sidebar-primary-foreground: #0f172a;
|
||||||
|
--sidebar-accent: #1f2937;
|
||||||
|
--sidebar-accent-foreground: #e5e7eb;
|
||||||
|
--sidebar-border: #1f2937;
|
||||||
|
--sidebar-ring: #22d3ee;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 亮色主题:ui-ux-pro-max 规范 - 正文 #0F172A、弱化 #475569、玻璃 bg-white/80+、边框可见 */
|
/* 亮色主题:ui-ux-pro-max 规范 - 正文 #0F172A、弱化 #475569、玻璃 bg-white/80+、边框可见 */
|
||||||
@@ -25,6 +58,32 @@
|
|||||||
--border: #e2e8f0;
|
--border: #e2e8f0;
|
||||||
--table-pinned-header-bg: #e2e8f0;
|
--table-pinned-header-bg: #e2e8f0;
|
||||||
--table-row-hover-bg: #e2e8f0;
|
--table-row-hover-bg: #e2e8f0;
|
||||||
|
--background: #ffffff;
|
||||||
|
--foreground: #0f172a;
|
||||||
|
--card-foreground: #0f172a;
|
||||||
|
--popover: #ffffff;
|
||||||
|
--popover-foreground: #0f172a;
|
||||||
|
--primary-foreground: #ffffff;
|
||||||
|
--secondary: #f1f5f9;
|
||||||
|
--secondary-foreground: #0f172a;
|
||||||
|
--muted-foreground: #475569;
|
||||||
|
--accent-foreground: #ffffff;
|
||||||
|
--destructive: #dc2626;
|
||||||
|
--input: #e2e8f0;
|
||||||
|
--ring: #0891b2;
|
||||||
|
--chart-1: #0891b2;
|
||||||
|
--chart-2: #2563eb;
|
||||||
|
--chart-3: #059669;
|
||||||
|
--chart-4: #db2777;
|
||||||
|
--chart-5: #ca8a04;
|
||||||
|
--sidebar: #f8fafc;
|
||||||
|
--sidebar-foreground: #0f172a;
|
||||||
|
--sidebar-primary: #0891b2;
|
||||||
|
--sidebar-primary-foreground: #ffffff;
|
||||||
|
--sidebar-accent: #f1f5f9;
|
||||||
|
--sidebar-accent-foreground: #0f172a;
|
||||||
|
--sidebar-border: #e2e8f0;
|
||||||
|
--sidebar-ring: #0891b2;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@@ -698,7 +757,6 @@ body::before {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.navbar-input-field {
|
.navbar-input-field {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
@@ -3205,3 +3263,87 @@ input[type="number"] {
|
|||||||
background: rgba(8, 145, 178, 0.12);
|
background: rgba(8, 145, 178, 0.12);
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
--radius-2xl: calc(var(--radius) + 8px);
|
||||||
|
--radius-3xl: calc(var(--radius) + 12px);
|
||||||
|
--radius-4xl: calc(var(--radius) + 16px);
|
||||||
|
--color-background: var(--background);
|
||||||
|
--color-foreground: var(--foreground);
|
||||||
|
--color-card: var(--card);
|
||||||
|
--color-card-foreground: var(--card-foreground);
|
||||||
|
--color-popover: var(--popover);
|
||||||
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
|
--color-primary: var(--primary);
|
||||||
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
|
--color-secondary: var(--secondary);
|
||||||
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
|
--color-muted: var(--muted);
|
||||||
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
|
--color-accent: var(--accent);
|
||||||
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
|
--color-destructive: var(--destructive);
|
||||||
|
--color-border: var(--border);
|
||||||
|
--color-input: var(--input);
|
||||||
|
--color-ring: var(--ring);
|
||||||
|
--color-chart-1: var(--chart-1);
|
||||||
|
--color-chart-2: var(--chart-2);
|
||||||
|
--color-chart-3: var(--chart-3);
|
||||||
|
--color-chart-4: var(--chart-4);
|
||||||
|
--color-chart-5: var(--chart-5);
|
||||||
|
--color-sidebar: var(--sidebar);
|
||||||
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: #020617;
|
||||||
|
--foreground: #f8fafc;
|
||||||
|
--card: #0f172a;
|
||||||
|
--card-foreground: #f8fafc;
|
||||||
|
--popover: #0f172a;
|
||||||
|
--popover-foreground: #f8fafc;
|
||||||
|
--primary: #22d3ee;
|
||||||
|
--primary-foreground: #0f172a;
|
||||||
|
--secondary: #1e293b;
|
||||||
|
--secondary-foreground: #f8fafc;
|
||||||
|
--muted: #1e293b;
|
||||||
|
--muted-foreground: #94a3af;
|
||||||
|
--accent: #60a5fa;
|
||||||
|
--accent-foreground: #f8fafc;
|
||||||
|
--destructive: #f87171;
|
||||||
|
--border: #1f2937;
|
||||||
|
--input: #1e293b;
|
||||||
|
--ring: #22d3ee;
|
||||||
|
--chart-1: #22d3ee;
|
||||||
|
--chart-2: #60a5fa;
|
||||||
|
--chart-3: #34d399;
|
||||||
|
--chart-4: #f472b6;
|
||||||
|
--chart-5: #fbbf24;
|
||||||
|
--sidebar: #0f172a;
|
||||||
|
--sidebar-foreground: #f8fafc;
|
||||||
|
--sidebar-primary: #22d3ee;
|
||||||
|
--sidebar-primary-foreground: #0f172a;
|
||||||
|
--sidebar-accent: #1e293b;
|
||||||
|
--sidebar-accent-foreground: #f8fafc;
|
||||||
|
--sidebar-border: #1f2937;
|
||||||
|
--sidebar-ring: #22d3ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border outline-ring/50;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
components.json
Normal file
23
components.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"rsc": true,
|
||||||
|
"tsx": false,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "",
|
||||||
|
"css": "app/globals.css",
|
||||||
|
"baseColor": "neutral",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide",
|
||||||
|
"rtl": false,
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"hooks": "@/hooks"
|
||||||
|
},
|
||||||
|
"registries": {}
|
||||||
|
}
|
||||||
79
components/ui/input-otp.jsx
Normal file
79
components/ui/input-otp.jsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { OTPInput, OTPInputContext } from "input-otp"
|
||||||
|
import { MinusIcon } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function InputOTP({
|
||||||
|
className,
|
||||||
|
containerClassName,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<OTPInput
|
||||||
|
data-slot="input-otp"
|
||||||
|
containerClassName={cn("flex items-center gap-2 has-disabled:opacity-50", containerClassName)}
|
||||||
|
className={cn("disabled:cursor-not-allowed disabled:opacity-50", className)}
|
||||||
|
{...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function InputOTPGroup({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="input-otp-group"
|
||||||
|
className={cn("flex items-center", className)}
|
||||||
|
{...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function InputOTPSlot({
|
||||||
|
index,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
const inputOTPContext = React.useContext(OTPInputContext)
|
||||||
|
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="input-otp-slot"
|
||||||
|
data-active={isActive}
|
||||||
|
className={cn(
|
||||||
|
"relative flex h-12 w-10 items-center justify-center rounded-md border-2 bg-background text-lg font-semibold shadow-sm transition-all duration-200",
|
||||||
|
"border-input/60 dark:border-input/80",
|
||||||
|
"text-foreground dark:text-foreground",
|
||||||
|
"first:rounded-l-md last:rounded-r-md",
|
||||||
|
"focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary",
|
||||||
|
"data-[active=true]:border-primary data-[active=true]:ring-2 data-[active=true]:ring-primary/30 dark:data-[active=true]:ring-primary/40",
|
||||||
|
"aria-invalid:border-destructive aria-invalid:text-destructive",
|
||||||
|
"dark:bg-slate-900/50 dark:data-[active=true]:bg-slate-800/50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}>
|
||||||
|
{char}
|
||||||
|
{hasFakeCaret && (
|
||||||
|
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
||||||
|
<div className="h-6 w-px animate-caret-blink bg-primary duration-1000" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function InputOTPSeparator({
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div data-slot="input-otp-separator" role="separator" className="text-muted-foreground dark:text-muted-foreground/50" {...props}>
|
||||||
|
<MinusIcon className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
|
||||||
197
components/ui/select.jsx
Normal file
197
components/ui/select.jsx
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
||||||
|
import { Select as SelectPrimitive } from "radix-ui"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Select({
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return <SelectPrimitive.Root data-slot="select" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectGroup({
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return <SelectPrimitive.Group data-slot="select-group" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectValue({
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return <SelectPrimitive.Value data-slot="select-value" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectTrigger({
|
||||||
|
className,
|
||||||
|
size = "default",
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.Trigger
|
||||||
|
data-slot="select-trigger"
|
||||||
|
data-size={size}
|
||||||
|
className={cn(
|
||||||
|
"flex w-full items-center justify-between gap-2 rounded-lg border px-3 py-2.5 text-sm font-medium whitespace-nowrap shadow-sm transition-all duration-200 outline-none",
|
||||||
|
"border-input bg-background text-foreground",
|
||||||
|
"hover:border-primary/60 hover:ring-1 hover:ring-primary/30",
|
||||||
|
"focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/50",
|
||||||
|
"disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-input disabled:hover:ring-0",
|
||||||
|
"aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
||||||
|
"data-[placeholder]:text-muted-foreground",
|
||||||
|
"data-[size=default]:h-11 data-[size=sm]:h-10",
|
||||||
|
"*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2",
|
||||||
|
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}>
|
||||||
|
{children}
|
||||||
|
<SelectPrimitive.Icon asChild>
|
||||||
|
<ChevronDownIcon className="size-4 opacity-60 transition-transform duration-200" />
|
||||||
|
</SelectPrimitive.Icon>
|
||||||
|
</SelectPrimitive.Trigger>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectContent({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
position = "item-aligned",
|
||||||
|
align = "center",
|
||||||
|
sideOffset = 4,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.Portal>
|
||||||
|
<SelectPrimitive.Content
|
||||||
|
data-slot="select-content"
|
||||||
|
className={cn(
|
||||||
|
"relative z-[100] max-h-(--radix-select-content-available-height) min-w-[var(--radix-select-trigger-width)] origin-(--radix-select-content-transform-origin) overflow-hidden rounded-xl border shadow-2xl",
|
||||||
|
"bg-popover/80 text-popover-foreground dark:bg-popover/70",
|
||||||
|
"backdrop-blur-xl backdrop-saturate-[180%]",
|
||||||
|
"border-border/60",
|
||||||
|
"ring-1 ring-black/5 dark:ring-white/10",
|
||||||
|
"shadow-black/5 dark:shadow-black/60",
|
||||||
|
"animate-in fade-in zoom-in-95 duration-200 ease-out",
|
||||||
|
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:duration-150",
|
||||||
|
"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",
|
||||||
|
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",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
position={position}
|
||||||
|
align={align}
|
||||||
|
sideOffset={sideOffset}
|
||||||
|
{...props}>
|
||||||
|
<SelectScrollUpButton className="bg-transparent text-muted-foreground/50" />
|
||||||
|
<SelectPrimitive.Viewport
|
||||||
|
className={cn("p-1.5", position === "popper" &&
|
||||||
|
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1")}>
|
||||||
|
{children}
|
||||||
|
</SelectPrimitive.Viewport>
|
||||||
|
<SelectScrollDownButton className="bg-transparent text-muted-foreground/50" />
|
||||||
|
</SelectPrimitive.Content>
|
||||||
|
</SelectPrimitive.Portal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectLabel({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.Label
|
||||||
|
data-slot="select-label"
|
||||||
|
className={cn("px-2 py-1.5 text-xs text-muted-foreground", className)}
|
||||||
|
{...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectItem({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.Item
|
||||||
|
data-slot="select-item"
|
||||||
|
className={cn(
|
||||||
|
"relative flex w-full cursor-pointer select-none items-center rounded-lg py-2.5 px-3 text-sm font-medium transition-colors duration-150 outline-none",
|
||||||
|
"text-foreground",
|
||||||
|
"hover:bg-primary/10 dark:hover:bg-primary/20",
|
||||||
|
"focus:bg-primary/10 dark:focus:bg-primary/20",
|
||||||
|
"data-[highlighted]:bg-primary/10 dark:data-[highlighted]:bg-primary/20",
|
||||||
|
"data-[state=checked]:bg-primary/10 dark:data-[state=checked]:bg-primary/20",
|
||||||
|
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed",
|
||||||
|
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
|
"*:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}>
|
||||||
|
<span
|
||||||
|
data-slot="select-item-indicator"
|
||||||
|
className="absolute right-3 flex size-4 items-center justify-center text-primary">
|
||||||
|
<SelectPrimitive.ItemIndicator>
|
||||||
|
<CheckIcon className="size-4" />
|
||||||
|
</SelectPrimitive.ItemIndicator>
|
||||||
|
</span>
|
||||||
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||||
|
</SelectPrimitive.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectSeparator({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.Separator
|
||||||
|
data-slot="select-separator"
|
||||||
|
className={cn("pointer-events-none -mx-1 my-1 h-px bg-border/60", className)}
|
||||||
|
{...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectScrollUpButton({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.ScrollUpButton
|
||||||
|
data-slot="select-scroll-up-button"
|
||||||
|
className={cn("flex cursor-default items-center justify-center py-1", className)}
|
||||||
|
{...props}>
|
||||||
|
<ChevronUpIcon className="size-4" />
|
||||||
|
</SelectPrimitive.ScrollUpButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectScrollDownButton({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.ScrollDownButton
|
||||||
|
data-slot="select-scroll-down-button"
|
||||||
|
className={cn("flex cursor-default items-center justify-center py-1", className)}
|
||||||
|
{...props}>
|
||||||
|
<ChevronDownIcon className="size-4" />
|
||||||
|
</SelectPrimitive.ScrollDownButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectLabel,
|
||||||
|
SelectScrollDownButton,
|
||||||
|
SelectScrollUpButton,
|
||||||
|
SelectSeparator,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
}
|
||||||
9
jsconfig.json
Normal file
9
jsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./*"]
|
||||||
|
},
|
||||||
|
"jsx": "react"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
lib/utils.js
Normal file
6
lib/utils.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { clsx } from "clsx";
|
||||||
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
export function cn(...inputs) {
|
||||||
|
return twMerge(clsx(inputs));
|
||||||
|
}
|
||||||
5859
package-lock.json
generated
5859
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@@ -20,14 +20,20 @@
|
|||||||
"@supabase/supabase-js": "^2.78.0",
|
"@supabase/supabase-js": "^2.78.0",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"framer-motion": "^12.29.2",
|
"framer-motion": "^12.29.2",
|
||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
|
"input-otp": "^1.4.2",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.17.23",
|
||||||
|
"lucide-react": "^0.577.0",
|
||||||
"next": "^16.1.5",
|
"next": "^16.1.5",
|
||||||
|
"radix-ui": "^1.4.3",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-chartjs-2": "^5.3.1",
|
"react-chartjs-2": "^5.3.1",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
|
"tailwind-merge": "^3.5.0",
|
||||||
"tesseract.js": "^5.1.1",
|
"tesseract.js": "^5.1.1",
|
||||||
"uuid": "^13.0.0"
|
"uuid": "^13.0.0"
|
||||||
},
|
},
|
||||||
@@ -35,11 +41,17 @@
|
|||||||
"node": ">=20.9.0"
|
"node": ">=20.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@tailwindcss/postcss": "^4.2.1",
|
||||||
|
"autoprefixer": "^10.4.27",
|
||||||
"babel-plugin-react-compiler": "^1.0.0",
|
"babel-plugin-react-compiler": "^1.0.0",
|
||||||
"eslint": "^9.0.0",
|
"eslint": "^9.0.0",
|
||||||
"eslint-config-next": "^16.1.5",
|
"eslint-config-next": "^16.1.5",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"lint-staged": "^16.2.7"
|
"lint-staged": "^16.2.7",
|
||||||
|
"postcss": "^8.5.8",
|
||||||
|
"shadcn": "^3.8.5",
|
||||||
|
"tailwindcss": "^4.2.1",
|
||||||
|
"tw-animate-css": "^1.4.0"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,jsx,ts,tsx}": [
|
"*.{js,jsx,ts,tsx}": [
|
||||||
|
|||||||
5
postcss.config.mjs
Normal file
5
postcss.config.mjs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
"@tailwindcss/postcss": {},
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user