From 2156613e2a586cca6fe6b68a90a6b07878459c9b Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Thu, 2 Jul 2026 20:57:13 +0700 Subject: [PATCH 1/2] Refactor popup UI foundation --- entrypoints/popup/App.tsx | 101 ++++++++++-------------- entrypoints/popup/components/Button.tsx | 58 +------------- entrypoints/popup/components/Input.tsx | 41 +--------- entrypoints/popup/components/Toggle.tsx | 45 +---------- src/components/ui/Badge.tsx | 5 ++ src/components/ui/Button.tsx | 57 +++++++++++++ src/components/ui/Card.tsx | 5 ++ src/components/ui/EmptyState.tsx | 4 + src/components/ui/Input.tsx | 29 +++++++ src/components/ui/SectionHeader.tsx | 5 ++ src/components/ui/Select.tsx | 6 ++ src/components/ui/StatCard.tsx | 4 + src/components/ui/Tabs.tsx | 7 ++ src/components/ui/Toast.tsx | 3 + src/components/ui/ToggleSwitch.tsx | 5 ++ src/components/ui/Tooltip.tsx | 4 + src/components/ui/index.ts | 13 +++ src/components/ui/utils.ts | 6 ++ tailwind.config.ts | 5 +- 19 files changed, 204 insertions(+), 199 deletions(-) create mode 100644 src/components/ui/Badge.tsx create mode 100644 src/components/ui/Button.tsx create mode 100644 src/components/ui/Card.tsx create mode 100644 src/components/ui/EmptyState.tsx create mode 100644 src/components/ui/Input.tsx create mode 100644 src/components/ui/SectionHeader.tsx create mode 100644 src/components/ui/Select.tsx create mode 100644 src/components/ui/StatCard.tsx create mode 100644 src/components/ui/Tabs.tsx create mode 100644 src/components/ui/Toast.tsx create mode 100644 src/components/ui/ToggleSwitch.tsx create mode 100644 src/components/ui/Tooltip.tsx create mode 100644 src/components/ui/index.ts create mode 100644 src/components/ui/utils.ts diff --git a/entrypoints/popup/App.tsx b/entrypoints/popup/App.tsx index 4de933a..8c32775 100644 --- a/entrypoints/popup/App.tsx +++ b/entrypoints/popup/App.tsx @@ -1,4 +1,5 @@ import { useState, useEffect, useRef, useCallback } from "react"; +import { Button, Card, Toast } from "../../src/components/ui"; import QRCode from "qrcode"; import "./App.css"; import Settings from "./components/Settings"; @@ -709,7 +710,7 @@ function App() { // skipcq: JS-0415 return ( -
+
{/* Show Welcome Screen for first-time users */} {!hasEmailAccounts ? (
@@ -725,56 +726,40 @@ function App() { // skipcq: JS-0415 <> {/* Header */} -
-
-
- -
-

- {t("extensionName")} -

-

- {t("headerSubtitle")} -

+
+
+
+
+
+ +
+
+

+ {t("extensionName")} +

+

+ {t("headerSubtitle")} +

+
-
- + + +
{/* Main Content */} -
-
+
+ {/* Base Email Selector - Dropdown */} -
+
@@ -822,7 +807,7 @@ function App() { base_email: selectedEmail, }); }} - className="w-full pl-9 pr-10 py-2.5 border border-gray-300 dark:border-gray-600 rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent appearance-none bg-white dark:bg-gray-700 dark:text-gray-100 truncate" + className="w-full appearance-none rounded-2xl border border-gray-200/90 bg-white/80 py-2.5 pl-9 pr-10 text-sm text-gray-900 shadow-sm outline-none transition-all focus:border-blue-400 focus:ring-2 focus:ring-blue-500/20 dark:border-gray-700/80 dark:bg-gray-800/80 dark:text-gray-100 truncate" > {emailAccounts.length > 0 ? ( emailAccounts.map((account) => ( @@ -853,7 +838,7 @@ function App() {
@@ -1058,14 +1043,10 @@ function App() { {/* Statistics - Collapsible */} -
+
{/* Toast Notification */} - {toastMessage && ( -
- {toastMessage} -
- )} + {toastMessage && }
)} @@ -1077,7 +1058,7 @@ function App() { onClick={() => setQrAlias(null)} >
e.stopPropagation()} >

@@ -1090,13 +1071,13 @@ function App() {
diff --git a/entrypoints/popup/components/Button.tsx b/entrypoints/popup/components/Button.tsx index e34179a..ca3c6c8 100644 --- a/entrypoints/popup/components/Button.tsx +++ b/entrypoints/popup/components/Button.tsx @@ -1,56 +1,2 @@ -import { ReactNode } from "react"; - -interface ButtonProps { - children: ReactNode; - onClick?: () => void; - variant?: "primary" | "secondary" | "danger" | "success"; - size?: "sm" | "md" | "lg"; - disabled?: boolean; - fullWidth?: boolean; - icon?: ReactNode; -} - -/** Styled button with variant, size, and optional icon support. */ -export default function Button({ - children, - onClick, - variant = "primary", - size = "md", - disabled = false, - fullWidth = false, - icon, -}: ButtonProps) { - const baseClasses = - "font-medium rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2"; - - const variantClasses = { - primary: - "bg-gradient-to-r from-blue-600 to-purple-600 text-white hover:from-blue-700 hover:to-purple-700 focus:ring-blue-500", - secondary: - "bg-gray-100 text-gray-700 hover:bg-gray-200 focus:ring-gray-500 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600", - danger: - "bg-red-50 text-red-700 hover:bg-red-100 focus:ring-red-500 dark:bg-red-900/30 dark:text-red-400 dark:hover:bg-red-900/50", - success: - "bg-green-50 text-green-700 hover:bg-green-100 focus:ring-green-500 dark:bg-green-900/30 dark:text-green-400 dark:hover:bg-green-900/50", - }; - - const sizeClasses = { - sm: "px-3 py-1.5 text-xs", - md: "px-4 py-2 text-sm", - lg: "px-6 py-3 text-base", - }; - - const widthClass = fullWidth ? "w-full" : ""; - const disabledClass = disabled ? "opacity-50 cursor-not-allowed" : ""; - - return ( - - ); -} +export { default } from "../../../src/components/ui/Button"; +export type { ButtonProps } from "../../../src/components/ui/Button"; diff --git a/entrypoints/popup/components/Input.tsx b/entrypoints/popup/components/Input.tsx index 7e5b5b7..dbc8d89 100644 --- a/entrypoints/popup/components/Input.tsx +++ b/entrypoints/popup/components/Input.tsx @@ -1,39 +1,2 @@ -interface InputProps { - type?: "text" | "email" | "number"; - value: string | number; - onChange: (value: string) => void; - placeholder?: string; - label?: string; - disabled?: boolean; - onKeyPress?: (e: React.KeyboardEvent) => void; -} - -/** Styled text input with optional label. */ -export default function Input({ - type = "text", - value, - onChange, - placeholder, - label, - disabled = false, - onKeyPress, -}: InputProps) { - return ( -
- {label && ( - - )} - onChange(e.target.value)} - onKeyPress={onKeyPress} - placeholder={placeholder} - disabled={disabled} - className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100" - /> -
- ); -} +export { default } from "../../../src/components/ui/Input"; +export type { InputProps } from "../../../src/components/ui/Input"; diff --git a/entrypoints/popup/components/Toggle.tsx b/entrypoints/popup/components/Toggle.tsx index c73be47..7130bf6 100644 --- a/entrypoints/popup/components/Toggle.tsx +++ b/entrypoints/popup/components/Toggle.tsx @@ -1,43 +1,2 @@ -interface ToggleProps { - enabled: boolean; - onChange: (enabled: boolean) => void; - label: string; - description?: string; -} - -/** Accessible on/off switch with a label and optional description. */ -export default function Toggle({ - enabled, - onChange, - label, - description, -}: ToggleProps) { - return ( -
-
- - {description && ( -

- {description} -

- )} -
- -
- ); -} +export { default } from "../../../src/components/ui/ToggleSwitch"; +export type { ToggleSwitchProps as ToggleProps } from "../../../src/components/ui/ToggleSwitch"; diff --git a/src/components/ui/Badge.tsx b/src/components/ui/Badge.tsx new file mode 100644 index 0000000..f0deb28 --- /dev/null +++ b/src/components/ui/Badge.tsx @@ -0,0 +1,5 @@ +import type { HTMLAttributes } from "react"; +import { cn } from "./utils"; +export default function Badge({ className, ...props }: HTMLAttributes) { + return ; +} diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx new file mode 100644 index 0000000..b1540b7 --- /dev/null +++ b/src/components/ui/Button.tsx @@ -0,0 +1,57 @@ +import type { ButtonHTMLAttributes, ReactNode } from "react"; +import { cn } from "./utils"; + +export interface ButtonProps extends ButtonHTMLAttributes { + variant?: "primary" | "secondary" | "ghost" | "danger" | "success"; + size?: "sm" | "md" | "lg" | "icon"; + fullWidth?: boolean; + icon?: ReactNode; +} + +export default function Button({ + children, + className, + variant = "primary", + size = "md", + fullWidth = false, + icon, + type = "button", + ...props +}: ButtonProps) { + const variants = { + primary: + "bg-gradient-to-r from-blue-600 to-violet-600 text-white shadow-soft hover:from-blue-700 hover:to-violet-700 focus-visible:ring-blue-500", + secondary: + "border border-gray-200/80 bg-white/80 text-gray-700 shadow-sm hover:bg-gray-50 dark:border-gray-700/80 dark:bg-gray-800/80 dark:text-gray-200 dark:hover:bg-gray-700/80 focus-visible:ring-gray-400", + ghost: + "text-gray-600 hover:bg-gray-100/80 dark:text-gray-300 dark:hover:bg-gray-800/80 focus-visible:ring-gray-400", + danger: + "bg-red-50 text-red-700 hover:bg-red-100 dark:bg-red-950/40 dark:text-red-300 dark:hover:bg-red-900/50 focus-visible:ring-red-500", + success: + "bg-emerald-50 text-emerald-700 hover:bg-emerald-100 dark:bg-emerald-950/40 dark:text-emerald-300 dark:hover:bg-emerald-900/50 focus-visible:ring-emerald-500", + }; + const sizes = { + sm: "px-3 py-1.5 text-xs", + md: "px-4 py-2.5 text-sm", + lg: "px-5 py-3 text-base", + icon: "h-10 w-10 p-0", + }; + + return ( + + ); +} diff --git a/src/components/ui/Card.tsx b/src/components/ui/Card.tsx new file mode 100644 index 0000000..08f76f7 --- /dev/null +++ b/src/components/ui/Card.tsx @@ -0,0 +1,5 @@ +import type { HTMLAttributes } from "react"; +import { cn } from "./utils"; +export default function Card({ className, ...props }: HTMLAttributes) { + return
; +} diff --git a/src/components/ui/EmptyState.tsx b/src/components/ui/EmptyState.tsx new file mode 100644 index 0000000..6f433d4 --- /dev/null +++ b/src/components/ui/EmptyState.tsx @@ -0,0 +1,4 @@ +import type { ReactNode } from "react"; +export default function EmptyState({ icon, title, description }: { icon?: ReactNode; title: string; description?: string }) { + return
{icon}

{title}

{description &&

{description}

}
; +} diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx new file mode 100644 index 0000000..a18d6ad --- /dev/null +++ b/src/components/ui/Input.tsx @@ -0,0 +1,29 @@ +import type { InputHTMLAttributes, ReactNode } from "react"; +import { cn } from "./utils"; + +export interface InputProps extends Omit, "onChange"> { + label?: string; + leftIcon?: ReactNode; + onChange?: (value: string) => void; +} + +export default function Input({ label, leftIcon, className, onChange, id, ...props }: InputProps) { + return ( +
+ {label && } +
+ {leftIcon && {leftIcon}} + onChange?.(e.target.value)} + className={cn( + "w-full rounded-xl border border-gray-200/90 bg-white/80 px-3 py-2.5 text-sm text-gray-900 shadow-sm outline-none transition-all placeholder:text-gray-400 focus:border-blue-400 focus:ring-2 focus:ring-blue-500/20 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-700/80 dark:bg-gray-800/80 dark:text-gray-100 dark:focus:border-blue-500", + leftIcon && "pl-10", + className, + )} + {...props} + /> +
+
+ ); +} diff --git a/src/components/ui/SectionHeader.tsx b/src/components/ui/SectionHeader.tsx new file mode 100644 index 0000000..c24e64b --- /dev/null +++ b/src/components/ui/SectionHeader.tsx @@ -0,0 +1,5 @@ +import type { ReactNode } from "react"; +export interface SectionHeaderProps { title: string; description?: string; action?: ReactNode; } +export default function SectionHeader({ title, description, action }: SectionHeaderProps) { + return

{title}

{description &&

{description}

}
{action}
; +} diff --git a/src/components/ui/Select.tsx b/src/components/ui/Select.tsx new file mode 100644 index 0000000..241dc87 --- /dev/null +++ b/src/components/ui/Select.tsx @@ -0,0 +1,6 @@ +import type { SelectHTMLAttributes } from "react"; +import { cn } from "./utils"; +export interface SelectProps extends SelectHTMLAttributes { label?: string; } +export default function Select({ label, className, id, children, ...props }: SelectProps) { + return
{label && }
; +} diff --git a/src/components/ui/StatCard.tsx b/src/components/ui/StatCard.tsx new file mode 100644 index 0000000..fb1f0d8 --- /dev/null +++ b/src/components/ui/StatCard.tsx @@ -0,0 +1,4 @@ +import type { ReactNode } from "react"; +export default function StatCard({ icon, value, label }: { icon: ReactNode; value: ReactNode; label: string }) { + return
{icon}
{value}
{label}
; +} diff --git a/src/components/ui/Tabs.tsx b/src/components/ui/Tabs.tsx new file mode 100644 index 0000000..435d908 --- /dev/null +++ b/src/components/ui/Tabs.tsx @@ -0,0 +1,7 @@ +import type { ReactNode } from "react"; +import { cn } from "./utils"; +export interface TabItem { value: T; label: ReactNode; icon?: ReactNode; } +export interface TabsProps { items: TabItem[]; value: T; onChange: (value: T) => void; } +export default function Tabs({ items, value, onChange }: TabsProps) { + return
{items.map((item) => )}
; +} diff --git a/src/components/ui/Toast.tsx b/src/components/ui/Toast.tsx new file mode 100644 index 0000000..de5082b --- /dev/null +++ b/src/components/ui/Toast.tsx @@ -0,0 +1,3 @@ +export default function Toast({ message }: { message: string }) { + return
{message}
; +} diff --git a/src/components/ui/ToggleSwitch.tsx b/src/components/ui/ToggleSwitch.tsx new file mode 100644 index 0000000..6b62759 --- /dev/null +++ b/src/components/ui/ToggleSwitch.tsx @@ -0,0 +1,5 @@ +import { cn } from "./utils"; +export interface ToggleSwitchProps { enabled: boolean; onChange: (enabled: boolean) => void; label?: string; description?: string; } +export default function ToggleSwitch({ enabled, onChange, label, description }: ToggleSwitchProps) { + return
{label &&

{label}

}{description &&

{description}

}
; +} diff --git a/src/components/ui/Tooltip.tsx b/src/components/ui/Tooltip.tsx new file mode 100644 index 0000000..2945b08 --- /dev/null +++ b/src/components/ui/Tooltip.tsx @@ -0,0 +1,4 @@ +import type { ReactNode } from "react"; +export default function Tooltip({ children, label }: { children: ReactNode; label: string }) { + return {children}{label}; +} diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts new file mode 100644 index 0000000..abbb5ed --- /dev/null +++ b/src/components/ui/index.ts @@ -0,0 +1,13 @@ +export { default as Badge } from "./Badge"; +export { default as Button } from "./Button"; +export { default as Card } from "./Card"; +export { default as EmptyState } from "./EmptyState"; +export { default as Input } from "./Input"; +export { default as SectionHeader } from "./SectionHeader"; +export { default as Select } from "./Select"; +export { default as StatCard } from "./StatCard"; +export { default as Tabs } from "./Tabs"; +export { default as Toast } from "./Toast"; +export { default as ToggleSwitch } from "./ToggleSwitch"; +export { default as Tooltip } from "./Tooltip"; +export { cn } from "./utils"; diff --git a/src/components/ui/utils.ts b/src/components/ui/utils.ts new file mode 100644 index 0000000..3e882b1 --- /dev/null +++ b/src/components/ui/utils.ts @@ -0,0 +1,6 @@ +export type ClassValue = string | number | bigint | boolean | null | undefined; + +/** Lightweight class name joiner kept local to avoid popup bundle bloat. */ +export function cn(...classes: ClassValue[]) { + return classes.filter(Boolean).join(" "); +} diff --git a/tailwind.config.ts b/tailwind.config.ts index d50d125..65cb70c 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,10 +1,13 @@ import type { Config } from "tailwindcss"; export default { - content: ["./entrypoints/**/*.{html,tsx,ts}"], + content: ["./entrypoints/**/*.{html,tsx,ts}", "./src/**/*.{tsx,ts}"], darkMode: "class", theme: { extend: { + boxShadow: { + soft: "0 14px 35px -22px rgb(15 23 42 / 0.45)", + }, colors: { primary: { 50: "#eff6ff", From fcf9b88c396f6d4cc71733392e3a63a125955dc6 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 13:57:37 +0000 Subject: [PATCH 2/2] style: format code with Prettier This commit fixes the style issues introduced in 2156613 according to the output from Prettier. Details: https://github.com/ePlus-DEV/gmail-alias-toolkit/pull/22 --- entrypoints/popup/App.tsx | 27 ++++++++++++++-- src/components/ui/Badge.tsx | 15 +++++++-- src/components/ui/Card.tsx | 15 +++++++-- src/components/ui/EmptyState.tsx | 26 +++++++++++++-- src/components/ui/Input.tsx | 29 ++++++++++++++--- src/components/ui/SectionHeader.tsx | 28 ++++++++++++++-- src/components/ui/Select.tsx | 35 ++++++++++++++++++-- src/components/ui/StatCard.tsx | 24 ++++++++++++-- src/components/ui/Tabs.tsx | 39 +++++++++++++++++++--- src/components/ui/Toast.tsx | 6 +++- src/components/ui/ToggleSwitch.tsx | 50 +++++++++++++++++++++++++++-- src/components/ui/Tooltip.tsx | 17 ++++++++-- 12 files changed, 281 insertions(+), 30 deletions(-) diff --git a/entrypoints/popup/App.tsx b/entrypoints/popup/App.tsx index 8c32775..125791c 100644 --- a/entrypoints/popup/App.tsx +++ b/entrypoints/popup/App.tsx @@ -731,7 +731,11 @@ function App() {
- +

@@ -749,7 +753,26 @@ function App() { className="shrink-0 rounded-2xl text-white hover:bg-white/15 focus-visible:ring-white/60" title={t("settings")} > - +

diff --git a/src/components/ui/Badge.tsx b/src/components/ui/Badge.tsx index f0deb28..4b5f0fe 100644 --- a/src/components/ui/Badge.tsx +++ b/src/components/ui/Badge.tsx @@ -1,5 +1,16 @@ import type { HTMLAttributes } from "react"; import { cn } from "./utils"; -export default function Badge({ className, ...props }: HTMLAttributes) { - return ; +export default function Badge({ + className, + ...props +}: HTMLAttributes) { + return ( + + ); } diff --git a/src/components/ui/Card.tsx b/src/components/ui/Card.tsx index 08f76f7..d9b864a 100644 --- a/src/components/ui/Card.tsx +++ b/src/components/ui/Card.tsx @@ -1,5 +1,16 @@ import type { HTMLAttributes } from "react"; import { cn } from "./utils"; -export default function Card({ className, ...props }: HTMLAttributes) { - return
; +export default function Card({ + className, + ...props +}: HTMLAttributes) { + return ( +
+ ); } diff --git a/src/components/ui/EmptyState.tsx b/src/components/ui/EmptyState.tsx index 6f433d4..edf9996 100644 --- a/src/components/ui/EmptyState.tsx +++ b/src/components/ui/EmptyState.tsx @@ -1,4 +1,26 @@ import type { ReactNode } from "react"; -export default function EmptyState({ icon, title, description }: { icon?: ReactNode; title: string; description?: string }) { - return
{icon}

{title}

{description &&

{description}

}
; +export default function EmptyState({ + icon, + title, + description, +}: { + icon?: ReactNode; + title: string; + description?: string; +}) { + return ( +
+
+ {icon} +
+

+ {title} +

+ {description && ( +

+ {description} +

+ )} +
+ ); } diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx index a18d6ad..cce0e49 100644 --- a/src/components/ui/Input.tsx +++ b/src/components/ui/Input.tsx @@ -1,18 +1,39 @@ import type { InputHTMLAttributes, ReactNode } from "react"; import { cn } from "./utils"; -export interface InputProps extends Omit, "onChange"> { +export interface InputProps extends Omit< + InputHTMLAttributes, + "onChange" +> { label?: string; leftIcon?: ReactNode; onChange?: (value: string) => void; } -export default function Input({ label, leftIcon, className, onChange, id, ...props }: InputProps) { +export default function Input({ + label, + leftIcon, + className, + onChange, + id, + ...props +}: InputProps) { return (
- {label && } + {label && ( + + )}
- {leftIcon && {leftIcon}} + {leftIcon && ( + + {leftIcon} + + )} onChange?.(e.target.value)} diff --git a/src/components/ui/SectionHeader.tsx b/src/components/ui/SectionHeader.tsx index c24e64b..94f4e18 100644 --- a/src/components/ui/SectionHeader.tsx +++ b/src/components/ui/SectionHeader.tsx @@ -1,5 +1,27 @@ import type { ReactNode } from "react"; -export interface SectionHeaderProps { title: string; description?: string; action?: ReactNode; } -export default function SectionHeader({ title, description, action }: SectionHeaderProps) { - return

{title}

{description &&

{description}

}
{action}
; +export interface SectionHeaderProps { + title: string; + description?: string; + action?: ReactNode; +} +export default function SectionHeader({ + title, + description, + action, +}: SectionHeaderProps) { + return ( +
+
+

+ {title} +

+ {description && ( +

+ {description} +

+ )} +
+ {action} +
+ ); } diff --git a/src/components/ui/Select.tsx b/src/components/ui/Select.tsx index 241dc87..0cdf893 100644 --- a/src/components/ui/Select.tsx +++ b/src/components/ui/Select.tsx @@ -1,6 +1,35 @@ import type { SelectHTMLAttributes } from "react"; import { cn } from "./utils"; -export interface SelectProps extends SelectHTMLAttributes { label?: string; } -export default function Select({ label, className, id, children, ...props }: SelectProps) { - return
{label && }
; +export interface SelectProps extends SelectHTMLAttributes { + label?: string; +} +export default function Select({ + label, + className, + id, + children, + ...props +}: SelectProps) { + return ( +
+ {label && ( + + )} + +
+ ); } diff --git a/src/components/ui/StatCard.tsx b/src/components/ui/StatCard.tsx index fb1f0d8..b9746a2 100644 --- a/src/components/ui/StatCard.tsx +++ b/src/components/ui/StatCard.tsx @@ -1,4 +1,24 @@ import type { ReactNode } from "react"; -export default function StatCard({ icon, value, label }: { icon: ReactNode; value: ReactNode; label: string }) { - return
{icon}
{value}
{label}
; +export default function StatCard({ + icon, + value, + label, +}: { + icon: ReactNode; + value: ReactNode; + label: string; +}) { + return ( +
+
+ {icon} +
+
+ {value} +
+
+ {label} +
+
+ ); } diff --git a/src/components/ui/Tabs.tsx b/src/components/ui/Tabs.tsx index 435d908..c05dda9 100644 --- a/src/components/ui/Tabs.tsx +++ b/src/components/ui/Tabs.tsx @@ -1,7 +1,38 @@ import type { ReactNode } from "react"; import { cn } from "./utils"; -export interface TabItem { value: T; label: ReactNode; icon?: ReactNode; } -export interface TabsProps { items: TabItem[]; value: T; onChange: (value: T) => void; } -export default function Tabs({ items, value, onChange }: TabsProps) { - return
{items.map((item) => )}
; +export interface TabItem { + value: T; + label: ReactNode; + icon?: ReactNode; +} +export interface TabsProps { + items: TabItem[]; + value: T; + onChange: (value: T) => void; +} +export default function Tabs({ + items, + value, + onChange, +}: TabsProps) { + return ( +
+ {items.map((item) => ( + + ))} +
+ ); } diff --git a/src/components/ui/Toast.tsx b/src/components/ui/Toast.tsx index de5082b..ae99e4c 100644 --- a/src/components/ui/Toast.tsx +++ b/src/components/ui/Toast.tsx @@ -1,3 +1,7 @@ export default function Toast({ message }: { message: string }) { - return
{message}
; + return ( +
+ {message} +
+ ); } diff --git a/src/components/ui/ToggleSwitch.tsx b/src/components/ui/ToggleSwitch.tsx index 6b62759..5bde9f2 100644 --- a/src/components/ui/ToggleSwitch.tsx +++ b/src/components/ui/ToggleSwitch.tsx @@ -1,5 +1,49 @@ import { cn } from "./utils"; -export interface ToggleSwitchProps { enabled: boolean; onChange: (enabled: boolean) => void; label?: string; description?: string; } -export default function ToggleSwitch({ enabled, onChange, label, description }: ToggleSwitchProps) { - return
{label &&

{label}

}{description &&

{description}

}
; +export interface ToggleSwitchProps { + enabled: boolean; + onChange: (enabled: boolean) => void; + label?: string; + description?: string; +} +export default function ToggleSwitch({ + enabled, + onChange, + label, + description, +}: ToggleSwitchProps) { + return ( +
+
+ {label && ( +

+ {label} +

+ )} + {description && ( +

+ {description} +

+ )} +
+ +
+ ); } diff --git a/src/components/ui/Tooltip.tsx b/src/components/ui/Tooltip.tsx index 2945b08..4e9ca4f 100644 --- a/src/components/ui/Tooltip.tsx +++ b/src/components/ui/Tooltip.tsx @@ -1,4 +1,17 @@ import type { ReactNode } from "react"; -export default function Tooltip({ children, label }: { children: ReactNode; label: string }) { - return {children}{label}; +export default function Tooltip({ + children, + label, +}: { + children: ReactNode; + label: string; +}) { + return ( + + {children} + + {label} + + + ); }