diff --git a/src/app/(features)/alphabet/AlphabetCard.tsx b/src/app/(features)/alphabet/AlphabetCard.tsx
index e985c64..38b4036 100644
--- a/src/app/(features)/alphabet/AlphabetCard.tsx
+++ b/src/app/(features)/alphabet/AlphabetCard.tsx
@@ -3,7 +3,7 @@
import { useState, useEffect, useCallback } from "react";
import { useTranslations } from "next-intl";
import { Letter, SupportedAlphabets } from "@/lib/interfaces";
-import IconClick from "@/components/ui/buttons/IconClick";
+import { IconClick } from "@/components/ui/buttons";
import IMAGES from "@/config/images";
import { ChevronLeft, ChevronRight } from "lucide-react";
diff --git a/src/app/(features)/alphabet/MemoryCard.tsx b/src/app/(features)/alphabet/MemoryCard.tsx
index ecb8d0b..a14bf8a 100644
--- a/src/app/(features)/alphabet/MemoryCard.tsx
+++ b/src/app/(features)/alphabet/MemoryCard.tsx
@@ -1,5 +1,5 @@
-import LightButton from "@/components/ui/buttons/LightButton";
-import IconClick from "@/components/ui/buttons/IconClick";
+import { LightButton } from "@/components/ui/buttons";
+import { IconClick } from "@/components/ui/buttons";
import IMAGES from "@/config/images";
import { Letter, SupportedAlphabets } from "@/lib/interfaces";
import {
diff --git a/src/app/(features)/alphabet/page.tsx b/src/app/(features)/alphabet/page.tsx
index b341799..b38131f 100644
--- a/src/app/(features)/alphabet/page.tsx
+++ b/src/app/(features)/alphabet/page.tsx
@@ -4,7 +4,7 @@ import { useState, useEffect } from "react";
import { useTranslations } from "next-intl";
import { Letter, SupportedAlphabets } from "@/lib/interfaces";
import Container from "@/components/ui/Container";
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
import AlphabetCard from "./AlphabetCard";
export default function Alphabet() {
diff --git a/src/app/(features)/srt-player/VideoPlayer/VideoPanel.tsx b/src/app/(features)/srt-player/VideoPlayer/VideoPanel.tsx
index 37e53e9..5c1e469 100644
--- a/src/app/(features)/srt-player/VideoPlayer/VideoPanel.tsx
+++ b/src/app/(features)/srt-player/VideoPlayer/VideoPanel.tsx
@@ -1,6 +1,6 @@
import { useState, useRef, forwardRef, useEffect, useCallback } from "react";
import SubtitleDisplay from "./SubtitleDisplay";
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
import { getIndex, parseSrt, getNearistIndex } from "../subtitle";
import { useTranslations } from "next-intl";
diff --git a/src/app/(features)/srt-player/components/atoms/PlayButton.tsx b/src/app/(features)/srt-player/components/atoms/PlayButton.tsx
index d3d7332..c9bb4d3 100644
--- a/src/app/(features)/srt-player/components/atoms/PlayButton.tsx
+++ b/src/app/(features)/srt-player/components/atoms/PlayButton.tsx
@@ -2,7 +2,7 @@
import React from "react";
import { useTranslations } from "next-intl";
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
import { PlayButtonProps } from "../../types/player";
export default function PlayButton({ isPlaying, onToggle, disabled, className }: PlayButtonProps) {
diff --git a/src/app/(features)/srt-player/components/atoms/SpeedControl.tsx b/src/app/(features)/srt-player/components/atoms/SpeedControl.tsx
index 0ef233e..36644f1 100644
--- a/src/app/(features)/srt-player/components/atoms/SpeedControl.tsx
+++ b/src/app/(features)/srt-player/components/atoms/SpeedControl.tsx
@@ -1,7 +1,7 @@
"use client";
import React from "react";
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
import { SpeedControlProps } from "../../types/player";
import { getPlaybackRateOptions, getPlaybackRateLabel } from "../../utils/timeUtils";
diff --git a/src/app/(features)/srt-player/components/compounds/ControlBar.tsx b/src/app/(features)/srt-player/components/compounds/ControlBar.tsx
index 1c4a3cd..91f946d 100644
--- a/src/app/(features)/srt-player/components/compounds/ControlBar.tsx
+++ b/src/app/(features)/srt-player/components/compounds/ControlBar.tsx
@@ -3,7 +3,7 @@
import React from "react";
import { useTranslations } from "next-intl";
import { ChevronLeft, ChevronRight, RotateCcw, Pause } from "lucide-react";
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
import { ControlBarProps } from "../../types/controls";
import PlayButton from "../atoms/PlayButton";
import SpeedControl from "../atoms/SpeedControl";
diff --git a/src/app/(features)/srt-player/components/compounds/UploadZone.tsx b/src/app/(features)/srt-player/components/compounds/UploadZone.tsx
index fb9a694..7f7d9b7 100644
--- a/src/app/(features)/srt-player/components/compounds/UploadZone.tsx
+++ b/src/app/(features)/srt-player/components/compounds/UploadZone.tsx
@@ -4,7 +4,7 @@ import React from "react";
import { useTranslations } from "next-intl";
import { toast } from "sonner";
import { Video, FileText } from "lucide-react";
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
import { FileUploadProps } from "../../types/controls";
import { useFileUpload } from "../../hooks/useFileUpload";
diff --git a/src/app/(features)/srt-player/page.tsx b/src/app/(features)/srt-player/page.tsx
index 9f82083..bc1add3 100644
--- a/src/app/(features)/srt-player/page.tsx
+++ b/src/app/(features)/srt-player/page.tsx
@@ -14,7 +14,7 @@ import SubtitleArea from "./components/compounds/SubtitleArea";
import ControlBar from "./components/compounds/ControlBar";
import UploadZone from "./components/compounds/UploadZone";
import SeekBar from "./components/atoms/SeekBar";
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
export default function SrtPlayerPage() {
const t = useTranslations("home");
diff --git a/src/app/(features)/text-speaker/SaveList.tsx b/src/app/(features)/text-speaker/SaveList.tsx
index 25d81e0..e67d065 100644
--- a/src/app/(features)/text-speaker/SaveList.tsx
+++ b/src/app/(features)/text-speaker/SaveList.tsx
@@ -6,7 +6,7 @@ import {
TextSpeakerArraySchema,
TextSpeakerItemSchema,
} from "@/lib/interfaces";
-import IconClick from "@/components/ui/buttons/IconClick";
+import { IconClick } from "@/components/ui/buttons";
import IMAGES from "@/config/images";
import { useTranslations } from "next-intl";
import { getLocalStorageOperator } from "@/lib/browser/localStorageOperators";
diff --git a/src/app/(features)/text-speaker/page.tsx b/src/app/(features)/text-speaker/page.tsx
index 6ac9e94..7bc6529 100644
--- a/src/app/(features)/text-speaker/page.tsx
+++ b/src/app/(features)/text-speaker/page.tsx
@@ -1,7 +1,7 @@
"use client";
-import LightButton from "@/components/ui/buttons/LightButton";
-import IconClick from "@/components/ui/buttons/IconClick";
+import { LightButton } from "@/components/ui/buttons";
+import { IconClick } from "@/components/ui/buttons";
import IMAGES from "@/config/images";
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
import {
diff --git a/src/app/(features)/translator/AddToFolder.tsx b/src/app/(features)/translator/AddToFolder.tsx
index bd47cfc..382f263 100644
--- a/src/app/(features)/translator/AddToFolder.tsx
+++ b/src/app/(features)/translator/AddToFolder.tsx
@@ -1,6 +1,6 @@
"use client";
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
import Container from "@/components/ui/Container";
import { TranslationHistorySchema } from "@/lib/interfaces";
import { Dispatch, useEffect, useState } from "react";
diff --git a/src/app/(features)/translator/FolderSelector.tsx b/src/app/(features)/translator/FolderSelector.tsx
index b7a6388..5bb696c 100644
--- a/src/app/(features)/translator/FolderSelector.tsx
+++ b/src/app/(features)/translator/FolderSelector.tsx
@@ -2,7 +2,7 @@ import Container from "@/components/ui/Container";
import { useEffect, useState } from "react";
import { Folder } from "../../../../generated/prisma/browser";
import { getFoldersByUserId } from "@/lib/server/services/folderService";
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
import { Folder as Fd } from "lucide-react";
interface FolderSelectorProps {
diff --git a/src/app/(features)/translator/page.tsx b/src/app/(features)/translator/page.tsx
index b257ecb..4a0f501 100644
--- a/src/app/(features)/translator/page.tsx
+++ b/src/app/(features)/translator/page.tsx
@@ -1,7 +1,7 @@
"use client";
-import LightButton from "@/components/ui/buttons/LightButton";
-import IconClick from "@/components/ui/buttons/IconClick";
+import { LightButton } from "@/components/ui/buttons";
+import { IconClick } from "@/components/ui/buttons";
import IMAGES from "@/config/images";
import { VOICES } from "@/config/locales";
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
diff --git a/src/app/auth/AuthForm.tsx b/src/app/auth/AuthForm.tsx
index 030a44d..0e7f2a7 100644
--- a/src/app/auth/AuthForm.tsx
+++ b/src/app/auth/AuthForm.tsx
@@ -5,7 +5,7 @@ import { useTranslations } from "next-intl";
import { signInAction, signUpAction, SignUpState } from "@/lib/actions/auth";
import Container from "@/components/ui/Container";
import Input from "@/components/ui/Input";
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
import { authClient } from "@/lib/auth-client";
interface AuthFormProps {
diff --git a/src/app/folders/[folder_id]/AddTextPairModal.tsx b/src/app/folders/[folder_id]/AddTextPairModal.tsx
index 431b60f..1c0a39b 100644
--- a/src/app/folders/[folder_id]/AddTextPairModal.tsx
+++ b/src/app/folders/[folder_id]/AddTextPairModal.tsx
@@ -1,8 +1,9 @@
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
import Input from "@/components/ui/Input";
import { X } from "lucide-react";
-import { useRef } from "react";
+import { useRef, useState } from "react";
import { useTranslations } from "next-intl";
+import { LOCALES } from "@/config/locales";
interface AddTextPairModalProps {
isOpen: boolean;
@@ -15,6 +16,53 @@ interface AddTextPairModalProps {
) => void;
}
+const COMMON_LOCALES = [
+ { label: "中文", value: "zh-CN" },
+ { label: "英文", value: "en-US" },
+ { label: "意大利语", value: "it-IT" },
+ { label: "日语", value: "ja-JP" },
+ { label: "其他", value: "other" },
+];
+
+interface LocaleSelectorProps {
+ value: string;
+ onChange: (val: string) => void;
+}
+
+function LocaleSelector({ value, onChange }: LocaleSelectorProps) {
+ const isCommonLocale = COMMON_LOCALES.some((l) => l.value === value && l.value !== "other");
+ const showFullList = value === "other" || !isCommonLocale;
+
+ return (
+
+
+ {showFullList && (
+
+ )}
+
+ );
+}
+
export default function AddTextPairModal({
isOpen,
onClose,
@@ -23,23 +71,23 @@ export default function AddTextPairModal({
const t = useTranslations("folder_id");
const input1Ref = useRef(null);
const input2Ref = useRef(null);
- const input3Ref = useRef(null);
- const input4Ref = useRef(null);
+ const [locale1, setLocale1] = useState("en-US");
+ const [locale2, setLocale2] = useState("zh-CN");
+
if (!isOpen) return null;
const handleAdd = () => {
if (
!input1Ref.current?.value ||
!input2Ref.current?.value ||
- !input3Ref.current?.value ||
- !input4Ref.current?.value
+ !locale1 ||
+ !locale2
)
return;
const text1 = input1Ref.current.value;
const text2 = input2Ref.current.value;
- const locale1 = input3Ref.current.value;
- const locale2 = input4Ref.current.value;
+
if (
typeof text1 === "string" &&
typeof text2 === "string" &&
@@ -55,6 +103,7 @@ export default function AddTextPairModal({
input2Ref.current.value = "";
}
};
+
return (
{t("add")}
diff --git a/src/app/folders/[folder_id]/InFolder.tsx b/src/app/folders/[folder_id]/InFolder.tsx
index fa5b9ce..2c1dc40 100644
--- a/src/app/folders/[folder_id]/InFolder.tsx
+++ b/src/app/folders/[folder_id]/InFolder.tsx
@@ -12,8 +12,8 @@ import AddTextPairModal from "./AddTextPairModal";
import TextPairCard from "./TextPairCard";
import { useTranslations } from "next-intl";
import PageLayout from "@/components/ui/PageLayout";
-import GreenButton from "@/components/ui/buttons/GreenButton";
-import IconButton from "@/components/ui/buttons/IconButton";
+import { GreenButton } from "@/components/ui/buttons";
+import { IconButton } from "@/components/ui/buttons";
import CardList from "@/components/ui/CardList";
export interface TextPair {
diff --git a/src/app/folders/[folder_id]/UpdateTextPairModal.tsx b/src/app/folders/[folder_id]/UpdateTextPairModal.tsx
index 533e18e..9ee3a57 100644
--- a/src/app/folders/[folder_id]/UpdateTextPairModal.tsx
+++ b/src/app/folders/[folder_id]/UpdateTextPairModal.tsx
@@ -1,4 +1,4 @@
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
import Input from "@/components/ui/Input";
import { X } from "lucide-react";
import { useRef } from "react";
diff --git a/src/app/profile/LogoutButton.tsx b/src/app/profile/LogoutButton.tsx
index ad553b0..02511df 100644
--- a/src/app/profile/LogoutButton.tsx
+++ b/src/app/profile/LogoutButton.tsx
@@ -1,6 +1,6 @@
"use client";
-import LightButton from "@/components/ui/buttons/LightButton";
+import { LightButton } from "@/components/ui/buttons";
import { authClient } from "@/lib/auth-client";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
diff --git a/src/components/LanguageSettings.tsx b/src/components/LanguageSettings.tsx
index fb68c8a..0d4c259 100644
--- a/src/components/LanguageSettings.tsx
+++ b/src/components/LanguageSettings.tsx
@@ -1,9 +1,8 @@
"use client";
import IMAGES from "@/config/images";
-import IconClick from "./ui/buttons/IconClick";
+import { IconClick, GhostButton } from "./ui/buttons";
import { useState } from "react";
-import GhostButton from "./ui/buttons/GhostButton";
export default function LanguageSettings() {
const [showLanguageMenu, setShowLanguageMenu] = useState(false);
@@ -21,6 +20,7 @@ export default function LanguageSettings() {
alt="language"
disableOnHoverBgChange={true}
onClick={handleLanguageClick}
+ size={40}
>
{showLanguageMenu && (
diff --git a/src/components/layout/Navbar.tsx b/src/components/layout/Navbar.tsx
index 3f91b70..05781ca 100644
--- a/src/components/layout/Navbar.tsx
+++ b/src/components/layout/Navbar.tsx
@@ -1,11 +1,11 @@
import Image from "next/image";
import IMAGES from "@/config/images";
-import { Folder, Home } from "lucide-react";
+import { Folder, Home, User } from "lucide-react";
import LanguageSettings from "../LanguageSettings";
import { auth } from "@/auth";
import { headers } from "next/headers";
import { getTranslations } from "next-intl/server";
-import GhostButton from "../ui/buttons/GhostButton";
+import { GhostButton } from "../ui/buttons";
export async function Navbar() {
const t = await getTranslations("navbar");
@@ -14,46 +14,56 @@ export async function Navbar() {
});
return (
-
-
+
+
{t("title")}
-
-
+
+
-
+
+
-
-
+
{t("folders")}
-
-
+
+
- {
- (() => {
- return session &&
- {t("profile")}
- || {t("sign_in")};
-
- })()
- }
{t("sourceCode")}
+ {
+ (() => {
+ return session &&
+ <>
+ {t("profile")}
+
+
+
+ >
+ || <>
+ {t("sign_in")}
+
+
+
+ >;
+
+ })()
+ }
);
diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx
new file mode 100644
index 0000000..267f47a
--- /dev/null
+++ b/src/components/ui/Button.tsx
@@ -0,0 +1,163 @@
+"use client";
+
+import React from "react";
+import Link from "next/link";
+import Image from "next/image";
+import { COLORS } from "@/lib/theme/colors";
+
+export type ButtonVariant = "primary" | "secondary" | "ghost" | "icon";
+export type ButtonSize = "sm" | "md" | "lg";
+
+export interface ButtonProps {
+ // Content
+ children?: React.ReactNode;
+
+ // Behavior
+ onClick?: () => void;
+ disabled?: boolean;
+ type?: "button" | "submit" | "reset";
+
+ // Styling
+ variant?: ButtonVariant;
+ size?: ButtonSize;
+ className?: string;
+ selected?: boolean;
+ style?: React.CSSProperties;
+
+ // Icons
+ leftIcon?: React.ReactNode;
+ rightIcon?: React.ReactNode;
+ iconSrc?: string; // For Next.js Image icons
+ iconAlt?: string;
+
+ // Navigation
+ href?: string;
+}
+
+export default function Button({
+ variant = "secondary",
+ size = "md",
+ selected = false,
+ href,
+ iconSrc,
+ iconAlt,
+ leftIcon,
+ rightIcon,
+ children,
+ className = "",
+ style,
+ type = "button",
+ disabled = false,
+ ...props
+}: ButtonProps) {
+ // Base classes
+ const baseClasses = "inline-flex items-center justify-center gap-2 rounded font-bold shadow hover:cursor-pointer transition-colors";
+
+ // Variant-specific classes
+ const variantStyles: Record = {
+ primary: `
+ text-white
+ hover:opacity-90
+ `,
+ secondary: `
+ text-black
+ hover:bg-gray-100
+ `,
+ ghost: `
+ hover:bg-black/30
+ p-2
+ `,
+ icon: `
+ p-2 bg-gray-200 rounded-full
+ hover:bg-gray-300
+ `
+ };
+
+ // Size-specific classes
+ const sizeStyles: Record = {
+ sm: "px-3 py-1 text-sm",
+ md: "px-4 py-2",
+ lg: "px-6 py-3 text-lg"
+ };
+
+ const variantClass = variantStyles[variant];
+ const sizeClass = sizeStyles[size];
+
+ // Selected state for secondary variant
+ const selectedClass = variant === "secondary" && selected ? "bg-gray-100" : "";
+
+ // Background color for primary variant
+ const backgroundColor = variant === "primary" ? COLORS.primary : undefined;
+
+ // Combine all classes
+ const combinedClasses = `
+ ${baseClasses}
+ ${variantClass}
+ ${sizeClass}
+ ${selectedClass}
+ ${disabled ? 'opacity-50 cursor-not-allowed' : ''}
+ ${className}
+ `.trim().replace(/\s+/g, " ");
+
+ // Icon rendering helper for SVG icons
+ const renderSvgIcon = (icon: React.ReactNode, position: "left" | "right") => {
+ if (!icon) return null;
+ return (
+
+ {icon}
+
+ );
+ };
+
+ // Image icon rendering for Next.js Image
+ const renderImageIcon = () => {
+ if (!iconSrc) return null;
+ const sizeMap = { sm: 16, md: 20, lg: 24 };
+ const imgSize = sizeMap[size] || 20;
+
+ return (
+
+ );
+ };
+
+ // Content assembly
+ const content = (
+ <>
+ {renderImageIcon()}
+ {renderSvgIcon(leftIcon, "left")}
+ {children}
+ {renderSvgIcon(rightIcon, "right")}
+ >
+ );
+
+ // If href is provided, render as Link
+ if (href) {
+ return (
+
+ {content}
+
+ );
+ }
+
+ // Otherwise render as button
+ return (
+
+ );
+}
diff --git a/src/components/ui/buttons/GhostButton.tsx b/src/components/ui/buttons/GhostButton.tsx
deleted file mode 100644
index a6ef6b7..0000000
--- a/src/components/ui/buttons/GhostButton.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import Link from "next/link";
-
-export type ButtonType = "button" | "submit" | "reset" | undefined;
-
-export default function GhostButton({
- onClick,
- className,
- children,
- type = "button",
- href
-}: {
- onClick?: () => void;
- className?: string;
- children?: React.ReactNode;
- type?: ButtonType;
- href?: string;
-}) {
- return (
-
- );
-}
diff --git a/src/components/ui/buttons/GreenButton.tsx b/src/components/ui/buttons/GreenButton.tsx
deleted file mode 100644
index 818909f..0000000
--- a/src/components/ui/buttons/GreenButton.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import PlainButton, { ButtonType } from "./PlainButton";
-import { COLORS } from "@/lib/theme/colors";
-
-interface GreenButtonProps {
- onClick?: () => void;
- children: React.ReactNode;
- className?: string;
- type?: ButtonType;
- disabled?: boolean;
-}
-
-export default function GreenButton({
- onClick,
- children,
- className = "",
- type = "button",
- disabled = false,
-}: GreenButtonProps) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/components/ui/buttons/IconButton.tsx b/src/components/ui/buttons/IconButton.tsx
deleted file mode 100644
index 26d809c..0000000
--- a/src/components/ui/buttons/IconButton.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import PlainButton, { ButtonType } from "./PlainButton";
-
-interface IconButtonProps {
- onClick?: () => void;
- icon: React.ReactNode;
- className?: string;
- type?: ButtonType;
- disabled?: boolean;
-}
-
-export default function IconButton({
- onClick,
- icon,
- className = "",
- type = "button",
- disabled = false,
-}: IconButtonProps) {
- return (
-
- {icon}
-
- );
-}
diff --git a/src/components/ui/buttons/IconClick.tsx b/src/components/ui/buttons/IconClick.tsx
deleted file mode 100644
index 1e21a14..0000000
--- a/src/components/ui/buttons/IconClick.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import Image from "next/image";
-
-interface IconClickProps {
- src: string;
- alt: string;
- onClick?: () => void;
- className?: string;
- size?: number;
- disableOnHoverBgChange?: boolean;
-}
-export default function IconClick({
- src,
- alt,
- onClick = () => {},
- className = "",
- size = 32,
- disableOnHoverBgChange = false,
-}: IconClickProps) {
- return (
- <>
-
-
-
- >
- );
-}
diff --git a/src/components/ui/buttons/LightButton.tsx b/src/components/ui/buttons/LightButton.tsx
deleted file mode 100644
index 0e4de45..0000000
--- a/src/components/ui/buttons/LightButton.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import PlainButton, { ButtonType } from "../buttons/PlainButton";
-
-export default function LightButton({
- onClick,
- className,
- selected,
- children,
- type = "button",
- disabled
-}: {
- onClick?: (() => void) | undefined;
- className?: string;
- selected?: boolean;
- children?: React.ReactNode;
- type?: ButtonType;
- disabled?: boolean;
-}) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/components/ui/buttons/PlainButton.tsx b/src/components/ui/buttons/PlainButton.tsx
deleted file mode 100644
index c4e2d59..0000000
--- a/src/components/ui/buttons/PlainButton.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-export type ButtonType = "button" | "submit" | "reset" | undefined;
-
-export default function PlainButton({
- onClick,
- className,
- children,
- type = "button",
- disabled,
- style
-}: {
- onClick?: () => void;
- className?: string;
- children?: React.ReactNode;
- type?: ButtonType;
- disabled?: boolean;
- style?: React.CSSProperties;
-}) {
- return (
-
- );
-}
diff --git a/src/components/ui/buttons/index.tsx b/src/components/ui/buttons/index.tsx
new file mode 100644
index 0000000..fee0a4a
--- /dev/null
+++ b/src/components/ui/buttons/index.tsx
@@ -0,0 +1,56 @@
+// 向后兼容的按钮组件包装器
+// 这些组件将新 Button 组件包装,以保持向后兼容
+
+import Button from "../Button";
+
+// LightButton: 次要按钮,支持 selected 状态
+export const LightButton = (props: any) => ;
+
+// GreenButton: 主题色主要按钮
+export const GreenButton = (props: any) => ;
+
+// IconButton: SVG 图标按钮
+export const IconButton = (props: any) => {
+ const { icon, ...rest } = props;
+ return ;
+};
+
+// GhostButton: 透明导航按钮
+export const GhostButton = (props: any) => {
+ const { className, children, ...rest } = props;
+ return (
+
+ );
+};
+
+// IconClick: 图片图标按钮
+export const IconClick = (props: any) => {
+ // IconClick 使用 src/alt 属性,需要映射到 Button 的 iconSrc/iconAlt
+ const { src, alt, size, disableOnHoverBgChange, className, ...rest } = props;
+ let buttonSize: "sm" | "md" | "lg" = "md";
+ if (typeof size === "number") {
+ if (size <= 20) buttonSize = "sm";
+ else if (size >= 32) buttonSize = "lg";
+ } else if (typeof size === "string") {
+ buttonSize = (size === "sm" || size === "md" || size === "lg") ? size : "md";
+ }
+
+ // 如果禁用悬停背景变化,通过 className 覆盖
+ const hoverClass = disableOnHoverBgChange ? "hover:bg-black/30 hover:cursor-pointer border-0 bg-transparent shadow-none" : "";
+
+ return (
+
+ );
+};
+
+// PlainButton: 基础小按钮
+export const PlainButton = (props: any) => ;