From de6b315c42d9a137a7d57783195cd921a5ec1d54 Mon Sep 17 00:00:00 2001 From: yashyrs027 Date: Wed, 6 May 2026 22:08:52 +0530 Subject: [PATCH 1/2] feat: implement centralized dual-theme system (light/dark) --- index.html | 14 + src/App.css | 488 +++++++++++++++--- src/Component/ui/Button.tsx | 50 +- src/Component/ui/DropDown.tsx | 99 ++-- src/Component/ui/Input.tsx | 49 +- src/Component/ui/TextArea.tsx | 29 +- src/Component/ui/ThemeToggle.tsx | 27 + src/config/them.config.ts | 163 +----- .../v1/Component/AddMemberHeader.tsx | 22 +- .../v1/Component/Administrative_MetaData.tsx | 16 +- .../v1/Component/Community_Involment.tsx | 19 +- .../AddMember/v1/Page/AddMemberPage.tsx | 11 +- .../v1/Sections/PersonalInfoCard.tsx | 62 ++- .../v1/Sections/ProfessionalDetails.tsx | 53 +- .../v1/Components/InternalDashboardSearch.tsx | 21 +- .../v1/Components/InternalSupport_Table.tsx | 218 ++++---- .../v1/Components/Support.tsx | 141 +++-- .../Contact_And_Support/v1/Pages/Contact.tsx | 9 +- .../v1/Section/ContactHeader.tsx | 15 +- .../v1/Section/InternalDirectoryTable.tsx | 11 +- .../Dashboard/components/AISuggestions.tsx | 38 +- .../Dashboard/components/Achievements.tsx | 71 +-- .../Dashboard/components/ActivityFeed.tsx | 71 ++- .../Dashboard/components/BudgetCard.tsx | 103 ++-- .../Dashboard/components/CalendarWidget.tsx | 154 +++--- .../Dashboard/components/CommunityStats.tsx | 67 ++- .../Dashboard/components/IssuesPanel.tsx | 90 ++-- .../Dashboard/components/PerformanceStats.tsx | 62 ++- .../components/ProductivityScore.tsx | 68 +-- .../Dashboard/components/RecentTasks.tsx | 132 +++-- .../Dashboard/components/SmartReminders.tsx | 75 +-- .../Dashboard/components/SummaryCard.tsx | 62 +-- .../Dashboard/components/TaskOverview.tsx | 33 +- src/features/Dashboard/components/TaskRow.tsx | 45 +- .../components/UpcomingUrgentTasks.tsx | 42 +- .../Dashboard/layouts/DashboardLayout.tsx | 12 +- .../Dashboard/v1/Pages/DashboardPage.tsx | 24 +- .../Events/v1/Components/EventTable.tsx | 169 +++--- src/features/Events/v1/Components/Judge.tsx | 105 ++-- src/features/Events/v1/Components/Mentors.tsx | 105 ++-- .../v1/Components/Partners_And_Sponsors.tsx | 74 +-- .../Events/v1/Components/Speakers.tsx | 109 ++-- .../Events/v1/Pages/CreateNewEvent.tsx | 23 +- src/features/Events/v1/Pages/ViewEvent.tsx | 83 +-- .../Events/v1/Sections/Event_Basic_Info.tsx | 98 ++-- .../Events/v1/Sections/Event_Header.tsx | 26 +- .../Events/v1/Sections/Event_View_Header.tsx | 35 +- .../Member/v1/Components/MemberHeader.tsx | 36 +- .../Member/v1/Components/MemberTable.tsx | 67 ++- .../Member/v1/Components/RoleChip.tsx | 23 +- .../Member/v1/Components/SearchMember.tsx | 30 +- .../Member/v1/Components/StatusChip.tsx | 40 +- src/features/Member/v1/Pages/MemberPage.tsx | 10 +- .../SideBar/v1/Components/SideBarLink.tsx | 54 +- .../SideBar/v1/Section/BotamNavBar.tsx | 51 +- src/features/SideBar/v1/Section/SideBar.tsx | 69 +-- src/features/template/LoginUserTemplate.tsx | 15 +- src/main.tsx | 9 +- src/theme/colors.ts | 182 +++++++ src/theme/hooks/useTheme.ts | 8 + src/theme/index.ts | 7 + src/theme/provider.tsx | 56 ++ src/theme/shadows.ts | 24 + src/theme/spacing.ts | 26 + src/theme/theme.config.ts | 46 ++ src/theme/typography.ts | 36 ++ 66 files changed, 2384 insertions(+), 1898 deletions(-) create mode 100644 src/Component/ui/ThemeToggle.tsx create mode 100644 src/theme/colors.ts create mode 100644 src/theme/hooks/useTheme.ts create mode 100644 src/theme/index.ts create mode 100644 src/theme/provider.tsx create mode 100644 src/theme/shadows.ts create mode 100644 src/theme/spacing.ts create mode 100644 src/theme/theme.config.ts create mode 100644 src/theme/typography.ts diff --git a/index.html b/index.html index e6fe822..689270a 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,20 @@ CommDesk + + diff --git a/src/App.css b/src/App.css index b8822c2..44fe5c5 100644 --- a/src/App.css +++ b/src/App.css @@ -7,6 +7,14 @@ @custom-variant dark (&:is(.dark *)); @plugin "@tailwindcss/typography"; +/* ─── Prevent flash on load ─────────────────────────────────────────────── */ +html { + color-scheme: light; +} +html.dark { + color-scheme: dark; +} + html, body { margin: 0; @@ -14,6 +22,7 @@ body { width: 100%; height: 100%; overflow-x: hidden; + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; } #root { @@ -21,6 +30,7 @@ body { height: 100%; } +/* ─── Tailwind radius tokens ─────────────────────────────────────────────── */ @theme inline { --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); @@ -60,119 +70,435 @@ body { --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); + + /* CommDesk design tokens */ + --color-cd-bg: var(--cd-bg); + --color-cd-surface: var(--cd-surface); + --color-cd-surface-2: var(--cd-surface-2); + --color-cd-surface-3: var(--cd-surface-3); + --color-cd-border: var(--cd-border); + --color-cd-border-subtle: var(--cd-border-subtle); + --color-cd-text: var(--cd-text); + --color-cd-text-2: var(--cd-text-2); + --color-cd-text-muted: var(--cd-text-muted); + --color-cd-primary: var(--cd-primary); + --color-cd-primary-hover: var(--cd-primary-hover); + --color-cd-primary-subtle: var(--cd-primary-subtle); + --color-cd-primary-text: var(--cd-primary-text); + --color-cd-secondary: var(--cd-secondary); + --color-cd-accent: var(--cd-accent); + --color-cd-success: var(--cd-success); + --color-cd-success-subtle: var(--cd-success-subtle); + --color-cd-warning: var(--cd-warning); + --color-cd-warning-subtle: var(--cd-warning-subtle); + --color-cd-danger: var(--cd-danger); + --color-cd-danger-subtle: var(--cd-danger-subtle); + --color-cd-hover: var(--cd-hover); } +/* ─── Light theme (default) ──────────────────────────────────────────────── */ :root { --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.13 0.028 261.692); - --card: oklch(1 0 0); - --card-foreground: oklch(0.13 0.028 261.692); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.13 0.028 261.692); - --primary: oklch(0.21 0.034 264.665); - --primary-foreground: oklch(0.985 0.002 247.839); - --secondary: oklch(0.967 0.003 264.542); - --secondary-foreground: oklch(0.21 0.034 264.665); - --muted: oklch(0.967 0.003 264.542); - --muted-foreground: oklch(0.551 0.027 264.364); - --accent: oklch(0.967 0.003 264.542); - --accent-foreground: oklch(0.21 0.034 264.665); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.928 0.006 264.531); - --input: oklch(0.928 0.006 264.531); - --ring: oklch(0.707 0.022 261.325); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0.002 247.839); - --sidebar-foreground: oklch(0.13 0.028 261.692); - --sidebar-primary: oklch(0.21 0.034 264.665); - --sidebar-primary-foreground: oklch(0.985 0.002 247.839); - --sidebar-accent: oklch(0.967 0.003 264.542); - --sidebar-accent-foreground: oklch(0.21 0.034 264.665); - --sidebar-border: oklch(0.928 0.006 264.531); - --sidebar-ring: oklch(0.707 0.022 261.325); + + /* Shadcn tokens */ + --background: #f8fafc; + --foreground: #1e293b; + --card: #ffffff; + --card-foreground: #1e293b; + --popover: #ffffff; + --popover-foreground: #1e293b; + --primary: #2563eb; + --primary-foreground: #ffffff; + --secondary: #f1f5f9; + --secondary-foreground: #1e293b; + --muted: #f1f5f9; + --muted-foreground: #64748b; + --accent: #f1f5f9; + --accent-foreground: #1e293b; + --destructive: #dc2626; + --border: #e2e8f0; + --input: #e2e8f0; + --ring: #3b82f6; + --chart-1: #3b82f6; + --chart-2: #22c55e; + --chart-3: #a855f7; + --chart-4: #f59e0b; + --chart-5: #ef4444; + --sidebar: #ffffff; + --sidebar-foreground: #1e293b; + --sidebar-primary: #2563eb; + --sidebar-primary-foreground: #ffffff; + --sidebar-accent: #f1f5f9; + --sidebar-accent-foreground: #1e293b; + --sidebar-border: #e2e8f0; + --sidebar-ring: #3b82f6; + + /* CommDesk design tokens – light */ + --cd-bg: #f8fafc; + --cd-surface: #ffffff; + --cd-surface-2: #f1f5f9; + --cd-surface-3: #eef2f7; + --cd-border: #e2e8f0; + --cd-border-subtle: #f1f5f9; + --cd-text: #1e293b; + --cd-text-2: #64748b; + --cd-text-muted: #94a3b8; + --cd-primary: #2563eb; + --cd-primary-hover: #1d4ed8; + --cd-primary-subtle: #eff6ff; + --cd-primary-text: #1d4ed8; + --cd-secondary: #9333ea; + --cd-accent: #06b6d4; + --cd-success: #16a34a; + --cd-success-subtle: #f0fdf4; + --cd-warning: #ca8a04; + --cd-warning-subtle: #fefce8; + --cd-danger: #dc2626; + --cd-danger-subtle: #fef2f2; + --cd-hover: #f1f5f9; + --cd-shadow: rgba(15, 23, 42, 0.08); + --cd-shadow-md: rgba(15, 23, 42, 0.12); } +/* ─── Dark theme ─────────────────────────────────────────────────────────── */ .dark { - --background: oklch(0.13 0.028 261.692); - --foreground: oklch(0.985 0.002 247.839); - --card: oklch(0.21 0.034 264.665); - --card-foreground: oklch(0.985 0.002 247.839); - --popover: oklch(0.21 0.034 264.665); - --popover-foreground: oklch(0.985 0.002 247.839); - --primary: oklch(0.928 0.006 264.531); - --primary-foreground: oklch(0.21 0.034 264.665); - --secondary: oklch(0.278 0.033 256.848); - --secondary-foreground: oklch(0.985 0.002 247.839); - --muted: oklch(0.278 0.033 256.848); - --muted-foreground: oklch(0.707 0.022 261.325); - --accent: oklch(0.278 0.033 256.848); - --accent-foreground: oklch(0.985 0.002 247.839); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.551 0.027 264.364); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.21 0.034 264.665); - --sidebar-foreground: oklch(0.985 0.002 247.839); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0.002 247.839); - --sidebar-accent: oklch(0.278 0.033 256.848); - --sidebar-accent-foreground: oklch(0.985 0.002 247.839); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.551 0.027 264.364); + /* Shadcn tokens */ + --background: #0f1320; + --foreground: #f8fafc; + --card: #1a1f2e; + --card-foreground: #f8fafc; + --popover: #1a1f2e; + --popover-foreground: #f8fafc; + --primary: #3b82f6; + --primary-foreground: #ffffff; + --secondary: #1e293b; + --secondary-foreground: #f8fafc; + --muted: #1e293b; + --muted-foreground: #94a3b8; + --accent: #1e293b; + --accent-foreground: #f8fafc; + --destructive: #f87171; + --border: rgba(255, 255, 255, 0.08); + --input: rgba(255, 255, 255, 0.08); + --ring: #3b82f6; + --chart-1: #60a5fa; + --chart-2: #4ade80; + --chart-3: #c084fc; + --chart-4: #facc15; + --chart-5: #f87171; + --sidebar: #1a1f2e; + --sidebar-foreground: #f8fafc; + --sidebar-primary: #3b82f6; + --sidebar-primary-foreground: #ffffff; + --sidebar-accent: #1e293b; + --sidebar-accent-foreground: #f8fafc; + --sidebar-border: rgba(255, 255, 255, 0.08); + --sidebar-ring: #3b82f6; + + /* CommDesk design tokens – dark */ + --cd-bg: #0f1320; + --cd-surface: #1a1f2e; + --cd-surface-2: #151929; + --cd-surface-3: #1e293b; + --cd-border: rgba(255, 255, 255, 0.08); + --cd-border-subtle: rgba(255, 255, 255, 0.04); + --cd-text: #f8fafc; + --cd-text-2: #94a3b8; + --cd-text-muted: #64748b; + --cd-primary: #3b82f6; + --cd-primary-hover: #60a5fa; + --cd-primary-subtle: rgba(59, 130, 246, 0.12); + --cd-primary-text: #93c5fd; + --cd-secondary: #a855f7; + --cd-accent: #22d3ee; + --cd-success: #4ade80; + --cd-success-subtle: rgba(74, 222, 128, 0.10); + --cd-warning: #facc15; + --cd-warning-subtle: rgba(250, 204, 21, 0.10); + --cd-danger: #f87171; + --cd-danger-subtle: rgba(248, 113, 113, 0.10); + --cd-hover: rgba(255, 255, 255, 0.05); + --cd-shadow: rgba(0, 0, 0, 0.3); + --cd-shadow-md: rgba(0, 0, 0, 0.4); } +/* ─── Base layer ─────────────────────────────────────────────────────────── */ @layer base { * { @apply border-border outline-ring/50; + transition: background-color 0.2s ease, border-color 0.2s ease, color 0.15s ease; } body { @apply bg-background text-foreground; } } -.inter { - font-family: "Inter", sans-serif; - font-optical-sizing: auto; +/* ─── CommDesk utility classes ───────────────────────────────────────────── */ +@layer components { + /* Cards */ + .cd-card { + background-color: var(--cd-surface); + border: 1px solid var(--cd-border); + border-radius: 1rem; + padding: 1.25rem; + box-shadow: 0 1px 3px var(--cd-shadow); + transition: box-shadow 0.2s ease, transform 0.2s ease; + } + .cd-card:hover { + box-shadow: 0 4px 12px var(--cd-shadow-md); + } + .cd-card-hover { + @apply hover:-translate-y-[2px]; + } - font-style: normal; -} + /* Section title */ + .cd-section-title { + font-size: 1rem; + font-weight: 600; + color: var(--cd-text); + margin-bottom: 1rem; + } -@layer components { - .card { - @apply bg-white/80 backdrop-blur-xl p-4 sm:p-5 rounded-2xl border border-gray-100 shadow-sm; + /* Muted text */ + .cd-text-muted { + font-size: 0.875rem; + color: var(--cd-text-2); } - .card-hover { - @apply hover:shadow-md hover:-translate-y-[2px] transition-all duration-200; + /* Badges */ + .cd-badge { + display: inline-flex; + align-items: center; + padding: 0.2rem 0.65rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 500; + line-height: 1.4; + } + .cd-badge-success { + background-color: var(--cd-success-subtle); + color: var(--cd-success); + } + .cd-badge-warning { + background-color: var(--cd-warning-subtle); + color: var(--cd-warning); + } + .cd-badge-danger { + background-color: var(--cd-danger-subtle); + color: var(--cd-danger); + } + .cd-badge-primary { + background-color: var(--cd-primary-subtle); + color: var(--cd-primary-text); + } + .cd-badge-neutral { + background-color: var(--cd-surface-2); + color: var(--cd-text-2); } - .section-title { - @apply text-base sm:text-lg font-semibold text-gray-800; + /* Buttons */ + .cd-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.5rem 1.25rem; + border-radius: 0.5rem; + font-size: 0.875rem; + font-weight: 500; + cursor: pointer; + transition: all 0.15s ease; + border: 1px solid transparent; + } + .cd-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + } + .cd-btn-primary { + background-color: var(--cd-primary); + color: #ffffff; + border-color: var(--cd-primary); + } + .cd-btn-primary:hover:not(:disabled) { + background-color: var(--cd-primary-hover); + border-color: var(--cd-primary-hover); + } + .cd-btn-secondary { + background-color: var(--cd-surface-2); + color: var(--cd-text); + border-color: var(--cd-border); + } + .cd-btn-secondary:hover:not(:disabled) { + background-color: var(--cd-hover); + } + .cd-btn-ghost { + background-color: transparent; + color: var(--cd-text-2); + border-color: transparent; + } + .cd-btn-ghost:hover:not(:disabled) { + background-color: var(--cd-hover); + color: var(--cd-text); + } + .cd-btn-danger { + background-color: var(--cd-danger-subtle); + color: var(--cd-danger); + border-color: var(--cd-danger-subtle); + } + .cd-btn-danger:hover:not(:disabled) { + background-color: var(--cd-danger); + color: #ffffff; } - .text-muted { - @apply text-xs sm:text-sm text-gray-500; + /* Inputs */ + .cd-input { + width: 100%; + background-color: var(--cd-surface); + border: 1px solid var(--cd-border); + border-radius: 0.5rem; + padding: 0.5rem 0.75rem; + font-size: 0.875rem; + color: var(--cd-text); + outline: none; + transition: border-color 0.15s ease, box-shadow 0.15s ease; + } + .cd-input::placeholder { + color: var(--cd-text-muted); + } + .cd-input:focus { + border-color: var(--cd-primary); + box-shadow: 0 0 0 3px var(--cd-primary-subtle); + } + .cd-input:disabled { + opacity: 0.5; + cursor: not-allowed; } - .badge-success { - @apply bg-green-100 text-green-600 px-2 py-1 rounded-full text-xs; + /* Table */ + .cd-table { + width: 100%; + border-collapse: collapse; + } + .cd-table thead { + background-color: var(--cd-surface-2); + } + .cd-table th { + padding: 0.75rem 1.25rem; + text-align: left; + font-size: 0.75rem; + font-weight: 600; + color: var(--cd-text-2); + text-transform: uppercase; + letter-spacing: 0.05em; + border-bottom: 1px solid var(--cd-border); + } + .cd-table td { + padding: 0.875rem 1.25rem; + font-size: 0.875rem; + color: var(--cd-text); + border-bottom: 1px solid var(--cd-border-subtle); + } + .cd-table tbody tr:hover { + background-color: var(--cd-hover); + } + .cd-table tbody tr:last-child td { + border-bottom: none; } - .badge-warning { - @apply bg-yellow-100 text-yellow-600 px-2 py-1 rounded-full text-xs; + /* Sidebar */ + .cd-sidebar { + background-color: var(--cd-surface); + border-right: 1px solid var(--cd-border); + } + + /* Nav link */ + .cd-nav-link { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + color: var(--cd-text-2); + font-size: 0.9375rem; + font-weight: 500; + transition: all 0.15s ease; + text-decoration: none; + } + .cd-nav-link:hover { + background-color: var(--cd-hover); + color: var(--cd-text); + } + .cd-nav-link.active { + background-color: var(--cd-primary-subtle); + color: var(--cd-primary-text); } + /* Page background */ + .cd-page { + background-color: var(--cd-bg); + min-height: 100vh; + } + + /* Stat metric box */ + .cd-metric { + background-color: var(--cd-surface-2); + border-radius: 0.75rem; + padding: 0.75rem; + } + + /* Glassmorphism surface */ + .cd-glass { + background-color: color-mix(in srgb, var(--cd-surface) 80%, transparent); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid var(--cd-border); + } + + /* Legacy aliases (keep old .card / .section-title working) */ + .card { + @apply cd-card; + } + .section-title { + @apply cd-section-title; + } + .text-muted { + @apply cd-text-muted; + } + .badge-success { + @apply cd-badge cd-badge-success; + } + .badge-warning { + @apply cd-badge cd-badge-warning; + } .badge-default { - @apply bg-gray-100 text-gray-600 px-2 py-1 rounded-full text-xs; + @apply cd-badge cd-badge-neutral; } } + +/* ─── Smooth theme transition ────────────────────────────────────────────── */ +html { + transition: background-color 0.25s ease; +} + +/* ─── Inter font helper ──────────────────────────────────────────────────── */ +.inter { + font-family: "Inter", sans-serif; + font-optical-sizing: auto; + font-style: normal; +} + +/* ─── Scrollbar styling ──────────────────────────────────────────────────── */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-thumb { + background-color: var(--cd-border); + border-radius: 9999px; +} +::-webkit-scrollbar-thumb:hover { + background-color: var(--cd-text-muted); +} diff --git a/src/Component/ui/Button.tsx b/src/Component/ui/Button.tsx index 25fbd84..96d83f9 100644 --- a/src/Component/ui/Button.tsx +++ b/src/Component/ui/Button.tsx @@ -6,38 +6,32 @@ type ButtonProps = { width?: string; height?: string; onClick: () => void; - textColor?: string; + variant?: "primary" | "secondary" | "ghost" | "danger"; disabled?: boolean; - backgroundColor?: string; + className?: string; }; -const Button = ({ - text, - textColor, - icon, - onClick, - disabled, - width, - backgroundColor, - height, -}: ButtonProps) => { +const Button = ({ text, icon, onClick, disabled, width, height, variant = "primary", className = "" }: ButtonProps) => { + const variantClass = disabled + ? "bg-[var(--cd-surface-2)] text-[var(--cd-text-muted)] cursor-not-allowed" + : variant === "secondary" + ? "cd-btn-secondary" + : variant === "ghost" + ? "cd-btn-ghost" + : variant === "danger" + ? "cd-btn-danger" + : "cd-btn-primary"; + return ( -
- -
+ ); }; diff --git a/src/Component/ui/DropDown.tsx b/src/Component/ui/DropDown.tsx index a1578a2..3f6efb0 100644 --- a/src/Component/ui/DropDown.tsx +++ b/src/Component/ui/DropDown.tsx @@ -1,14 +1,11 @@ import React, { useEffect, useRef, useState } from "react"; -import { getTheme, ThemeMode } from "../../config/them.config"; import { FaChevronDown } from "react-icons/fa"; type DropDownProps = { options: string[]; - label?: string; onSelect: (option: string) => void; className?: string; - themeMode?: ThemeMode; value?: string; placeholder?: string; disabled?: boolean; @@ -19,40 +16,26 @@ const DropDown: React.FC = ({ onSelect, className, label, - themeMode = "light", value, placeholder = "Select an option", disabled = false, }) => { const [open, setOpen] = useState(false); const [selected, setSelected] = useState(value ?? options[0] ?? ""); - const [hoveredOption, setHoveredOption] = useState(null); const containerRef = useRef(null); - const theme = getTheme(themeMode); - useEffect(() => { if (value !== undefined) { setSelected(value); return; } - - setSelected((previous) => { - if (previous && options.includes(previous)) { - return previous; - } - return options[0] ?? ""; - }); + setSelected((prev) => (prev && options.includes(prev) ? prev : (options[0] ?? ""))); }, [options, value]); useEffect(() => { - const onClickOutside = (event: MouseEvent) => { - if (!containerRef.current) return; - if (!containerRef.current.contains(event.target as Node)) { - setOpen(false); - } + const onClickOutside = (e: MouseEvent) => { + if (!containerRef.current?.contains(e.target as Node)) setOpen(false); }; - document.addEventListener("mousedown", onClickOutside); return () => document.removeEventListener("mousedown", onClickOutside); }, []); @@ -65,17 +48,13 @@ const DropDown: React.FC = ({ }; const isDisabled = disabled || options.length === 0; - const displayValue = selected || placeholder; return ( -
+
{label && (

{label}

@@ -87,52 +66,54 @@ const DropDown: React.FC = ({ disabled={isDisabled} aria-haspopup="listbox" aria-expanded={open} - className="w-full border rounded-lg px-3 py-2 text-left flex justify-between items-center text-sm md:text-base transition-colors duration-150 disabled:cursor-not-allowed disabled:opacity-70" + className="w-full rounded-lg px-3 py-2 text-left flex justify-between items-center text-sm transition-all duration-150 disabled:cursor-not-allowed disabled:opacity-60" style={{ - borderColor: theme.borderColor.primary, - backgroundColor: theme.background.secondary, - color: selected ? theme.textColor.primary : theme.textColor.muted, - fontFamily: theme.fontFamily.primary, + backgroundColor: "var(--cd-surface)", + borderColor: "var(--cd-border)", + color: selected ? "var(--cd-text)" : "var(--cd-text-muted)", + border: "1px solid var(--cd-border)", }} > - {displayValue} + {selected || placeholder} {open && options.length > 0 && (
- {options.map((opt) => { - const isHighlighted = hoveredOption === opt || selected === opt; - - return ( - - ); - })} + {options.map((opt) => ( + + ))}
)}
diff --git a/src/Component/ui/Input.tsx b/src/Component/ui/Input.tsx index 3243c28..b0a3798 100644 --- a/src/Component/ui/Input.tsx +++ b/src/Component/ui/Input.tsx @@ -1,5 +1,4 @@ import React, { forwardRef } from "react"; -import { getTheme } from "../../config/them.config"; type InputType = "text" | "email" | "password" | "number" | "url" | "tel" | "time"; @@ -9,18 +8,13 @@ type InputProps = { placeholder?: string; value?: string | number; type?: InputType; - onChange?: (name: string, value: string) => void; onKeyDown?: React.KeyboardEventHandler; - error?: string; - leftIcon?: React.ReactNode; rightIcon?: React.ReactNode; - className?: string; inputClassName?: string; - disabled?: boolean; required?: boolean; }; @@ -45,28 +39,32 @@ export const Input = forwardRef( }, ref, ) => { - const theme = getTheme("light"); - return ( -
+
{label && ( -
); }, diff --git a/src/Component/ui/TextArea.tsx b/src/Component/ui/TextArea.tsx index 0f8eab8..4df50d1 100644 --- a/src/Component/ui/TextArea.tsx +++ b/src/Component/ui/TextArea.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { getTheme } from "../../config/them.config"; type TextAreaProps = { label?: string; @@ -18,13 +17,17 @@ type TextAreaProps = { }; export const TextArea = (props: TextAreaProps) => { - const theme = getTheme("light"); - return ( -
- +
+ {props.label && ( + + )}