style: finish based global them

This commit is contained in:
ghost2023
2026-05-20 11:35:00 +03:00
parent 024871c127
commit b9ef5a2b46
9 changed files with 143 additions and 95 deletions

View File

@@ -1,57 +0,0 @@
import { ButtonHTMLAttributes, forwardRef } from "react";
import clsx from "clsx";
export type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
export type ButtonSize = "sm" | "md" | "lg";
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
isLoading?: boolean;
}
const variantClasses: Record<ButtonVariant, string> = {
primary: "bg-blue-600 text-white hover:bg-blue-700 disabled:bg-blue-300",
secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200 disabled:bg-gray-50",
ghost: "bg-transparent text-gray-900 hover:bg-gray-100",
danger: "bg-red-600 text-white hover:bg-red-700 disabled:bg-red-300",
};
const sizeClasses: Record<ButtonSize, string> = {
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2 text-base",
lg: "px-6 py-3 text-lg",
};
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(
{
variant = "primary",
size = "md",
isLoading,
disabled,
className,
children,
...rest
},
ref,
) => (
<button
ref={ref}
disabled={disabled || isLoading}
className={clsx(
"inline-flex items-center justify-center rounded-md font-medium transition-colors disabled:cursor-not-allowed",
variantClasses[variant],
sizeClasses[size],
className,
)}
{...rest}
>
{isLoading ? "Loading..." : children}
</button>
),
);
Button.displayName = "Button";
export default Button;

View File

@@ -1,2 +0,0 @@
export { default } from "./Button";
export type { ButtonProps, ButtonVariant, ButtonSize } from "./Button";

View File

@@ -123,7 +123,7 @@ const DashboardLayout = ({
) : null;
return (
<div className="flex min-h-screen bg-slate-50 dark:bg-slate-950">
<div className="flex min-h-screen">
<Sidebar
title={title}
items={sidebarItems}
@@ -132,10 +132,8 @@ const DashboardLayout = ({
headerExtra={themeToggleButton}
/>
<div className="flex flex-1 flex-col">
<header className="flex h-16 items-center justify-between border-b border-slate-200 bg-white px-6 dark:border-slate-800 dark:bg-slate-900">
<div className="text-base font-medium text-slate-700 dark:text-slate-200">
{title}
</div>
<header className="flex h-16 items-center justify-between border-b px-6 ">
<div className="text-base font-medium ">{title}</div>
<div className="flex items-center gap-2">
<button
@@ -219,9 +217,7 @@ const DashboardLayout = ({
</div>
</header>
<main className="flex-1 overflow-auto bg-background p-6 ">
{children}
</main>
<main className="flex-1 overflow-auto bg-background ">{children}</main>
</div>
</div>
);

View File

@@ -22,11 +22,11 @@ const Sidebar = ({
onNavigate,
headerExtra,
}: SidebarProps) => (
<aside className="flex w-64 flex-col gap-1 border-r border-slate-200 bg-[#33578D]/10 px-3 py-5 dark:border-slate-800 dark:bg-slate-900">
<aside className="flex w-64 flex-col gap-1 border-r border-sidebar-border bg-sidebar px-3 py-5 ">
{title ? (
<div className="flex items-center justify-between gap-2 px-3 pb-4">
<div className="flex items-center gap-2">
<div className="flex h-9 w-9 items-center justify-center rounded-xl bg-[#33578D] text-sm font-bold text-white">
<div className="flex h-9 w-9 items-center justify-center rounded-xl bg-sidebar-primary text-sm font-bold text-white">
{title.charAt(0)}
</div>
<div className="text-base font-semibold text-slate-800 dark:text-slate-100">
@@ -39,8 +39,7 @@ const Sidebar = ({
<nav className="flex flex-col gap-1">
{items.map((item) => {
const isActive =
activeHref?.toLowerCase() === item.href.toLowerCase();
const isActive = activeHref?.toLowerCase() === item.href.toLowerCase();
return (
<a
@@ -54,10 +53,10 @@ const Sidebar = ({
}}
aria-current={isActive ? "page" : undefined}
className={clsx(
"group flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium transition",
"group flex items-center gap-3 rounded-md px-3 py-2.5 text-sm font-medium transition",
isActive
? "bg-[#33578D] text-white shadow-sm shadow-[#33578D]/20"
: "text-slate-700 hover:bg-[#33578D]/10 hover:text-[#33578D] dark:text-slate-300 dark:hover:bg-[#33578D]/20 dark:hover:text-white",
? "bg-sidebar-primary text-sidebar-primary-foreground shadow-sm "
: "text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
)}
>
{item.icon ? (

View File

@@ -0,0 +1,92 @@
import * as React from "react";
import { cn } from "../lib/utils";
function Card({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card"
className={cn(
"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-xs",
className,
)}
{...props}
/>
);
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
className,
)}
{...props}
/>
);
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-title"
className={cn("leading-none font-semibold", className)}
{...props}
/>
);
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-description"
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
);
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className,
)}
{...props}
/>
);
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("px-6", className)}
{...props}
/>
);
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
{...props}
/>
);
}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
};

View File

@@ -0,0 +1,21 @@
import * as React from "react"
import { cn } from "../lib/utils"
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return (
<input
type={type}
data-slot="input"
className={cn(
"h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
className
)}
{...props}
/>
)
}
export { Input }

View File

@@ -55,7 +55,7 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
<tr
data-slot="table-row"
className={cn(
"border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
"border-b transition-colors bg-background hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
className,
)}
{...props}

View File

@@ -1,9 +1,3 @@
export type {
ButtonProps,
ButtonVariant,
ButtonSize,
} from "./components/Button";
export { default as Table } from "./components/Table";
export type { TableProps, TableColumn } from "./components/Table";
@@ -24,7 +18,10 @@ export type {
DashboardLayoutProps,
} from "./components/Layout";
export { Button } from "./components/button";
export * from "./components/button";
export * from "./components/input";
export * from "./components/card";
export * from "./components/data-table";
export * from "./components/dialog";

View File

@@ -6,17 +6,17 @@
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card: oklch(0.99 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: #0d5c2c;
--primary: oklch(0.596 0.1274 163.23);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent: oklch(0.9753 0.0148 149.37);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(1 0 0);
@@ -28,9 +28,9 @@
--chart-3: oklch(0.55 0.22 263);
--chart-4: oklch(0.49 0.22 264);
--chart-5: oklch(0.42 0.18 266);
--sidebar: oklch(0.985 0 0);
--sidebar: oklch(0.986 0.0068 174.38);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary: oklch(0.596 0.1274 163.23);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
@@ -44,7 +44,6 @@
--font-mono:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
--radius: 0.625rem;
--shadow-x: 0;
--shadow-y: 1px;
--shadow-blur: 3px;
@@ -108,7 +107,6 @@
--font-mono:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
--radius: 0.625rem;
--shadow-x: 0;
--shadow-y: 1px;
--shadow-blur: 3px;
@@ -129,7 +127,7 @@
--shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25);
}
@theme {
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
@@ -167,10 +165,14 @@
--font-mono: var(--font-mono);
--font-serif: var(--font-serif);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--radius: 0.8rem;
--radius-sm: 0.48rem;
--radius-md: 0.64rem;
--radius-lg: 1rem;
--radius-xl: 1.12rem;
--radius-2xl: 1.44rem;
--radius-3xl: 1.76rem;
--radius-4xl: 2.08rem;
--shadow-2xs: var(--shadow-2xs);
--shadow-xs: var(--shadow-xs);