button
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { Letter, SupportedAlphabets } from "@/lib/interfaces";
|
import { Letter, SupportedAlphabets } from "@/lib/interfaces";
|
||||||
import { IconClick } from "@/components/ui/buttons";
|
import { IconClick, CircleToggleButton, CircleButton } from "@/components/ui/buttons";
|
||||||
import { IMAGES } from "@/config/images";
|
import { IMAGES } from "@/config/images";
|
||||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||||
|
|
||||||
@@ -120,51 +120,35 @@ export function AlphabetCard({ alphabet, alphabetType, onBack }: AlphabetCardPro
|
|||||||
</span>
|
</span>
|
||||||
{/* 显示选项切换按钮组 */}
|
{/* 显示选项切换按钮组 */}
|
||||||
<div className="flex gap-2 flex-wrap">
|
<div className="flex gap-2 flex-wrap">
|
||||||
<button
|
<CircleToggleButton
|
||||||
|
selected={showLetter}
|
||||||
onClick={() => setShowLetter(!showLetter)}
|
onClick={() => setShowLetter(!showLetter)}
|
||||||
className={`px-3 py-1 rounded-full text-sm transition-colors ${
|
|
||||||
showLetter
|
|
||||||
? "bg-[#35786f] text-white"
|
|
||||||
: "bg-gray-200 text-gray-600"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{t("letter")}
|
{t("letter")}
|
||||||
</button>
|
</CircleToggleButton>
|
||||||
{/* IPA 音标显示切换 */}
|
{/* IPA 音标显示切换 */}
|
||||||
<button
|
<CircleToggleButton
|
||||||
|
selected={showIPA}
|
||||||
onClick={() => setShowIPA(!showIPA)}
|
onClick={() => setShowIPA(!showIPA)}
|
||||||
className={`px-3 py-1 rounded-full text-sm transition-colors ${
|
|
||||||
showIPA
|
|
||||||
? "bg-[#35786f] text-white"
|
|
||||||
: "bg-gray-200 text-gray-600"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
IPA
|
IPA
|
||||||
</button>
|
</CircleToggleButton>
|
||||||
{/* 罗马音显示切换(仅日语显示) */}
|
{/* 罗马音显示切换(仅日语显示) */}
|
||||||
{hasRomanization && (
|
{hasRomanization && (
|
||||||
<button
|
<CircleToggleButton
|
||||||
|
selected={showRoman}
|
||||||
onClick={() => setShowRoman(!showRoman)}
|
onClick={() => setShowRoman(!showRoman)}
|
||||||
className={`px-3 py-1 rounded-full text-sm transition-colors ${
|
|
||||||
showRoman
|
|
||||||
? "bg-[#35786f] text-white"
|
|
||||||
: "bg-gray-200 text-gray-600"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{t("roman")}
|
{t("roman")}
|
||||||
</button>
|
</CircleToggleButton>
|
||||||
)}
|
)}
|
||||||
{/* 随机模式切换 */}
|
{/* 随机模式切换 */}
|
||||||
<button
|
<CircleToggleButton
|
||||||
|
selected={isRandomMode}
|
||||||
onClick={() => setIsRandomMode(!isRandomMode)}
|
onClick={() => setIsRandomMode(!isRandomMode)}
|
||||||
className={`px-3 py-1 rounded-full text-sm transition-colors ${
|
|
||||||
isRandomMode
|
|
||||||
? "bg-[#35786f] text-white"
|
|
||||||
: "bg-gray-200 text-gray-600"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{t("random")}
|
{t("random")}
|
||||||
</button>
|
</CircleToggleButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -199,13 +183,9 @@ export function AlphabetCard({ alphabet, alphabetType, onBack }: AlphabetCardPro
|
|||||||
{/* 底部导航控制区域 */}
|
{/* 底部导航控制区域 */}
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
{/* 上一个按钮 */}
|
{/* 上一个按钮 */}
|
||||||
<button
|
<CircleButton onClick={goToPrevious} aria-label="上一个字母">
|
||||||
onClick={goToPrevious}
|
|
||||||
className="p-3 rounded-full bg-gray-100 hover:bg-gray-200 transition-colors"
|
|
||||||
aria-label="上一个字母"
|
|
||||||
>
|
|
||||||
<ChevronLeft size={24} />
|
<ChevronLeft size={24} />
|
||||||
</button>
|
</CircleButton>
|
||||||
|
|
||||||
{/* 中间区域:随机按钮 */}
|
{/* 中间区域:随机按钮 */}
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
@@ -220,13 +200,9 @@ export function AlphabetCard({ alphabet, alphabetType, onBack }: AlphabetCardPro
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 下一个按钮 */}
|
{/* 下一个按钮 */}
|
||||||
<button
|
<CircleButton onClick={goToNext} aria-label="下一个字母">
|
||||||
onClick={goToNext}
|
|
||||||
className="p-3 rounded-full bg-gray-100 hover:bg-gray-200 transition-colors"
|
|
||||||
aria-label="下一个字母"
|
|
||||||
>
|
|
||||||
<ChevronRight size={24} />
|
<ChevronRight size={24} />
|
||||||
</button>
|
</CircleButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Plus, RefreshCw } from "lucide-react";
|
import { Plus, RefreshCw } from "lucide-react";
|
||||||
|
import { CircleButton, LightButton } from "@/components/ui/buttons";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { actionCreatePair } from "@/modules/folder/folder-aciton";
|
import { actionCreatePair } from "@/modules/folder/folder-aciton";
|
||||||
import { TSharedItem } from "@/shared/dictionary-type";
|
import { TSharedItem } from "@/shared/dictionary-type";
|
||||||
@@ -61,13 +62,13 @@ export function SaveButtonClient({ session, folders, searchResult, queryLang, de
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<CircleButton
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
className="hover:bg-gray-200 hover:cursor-pointer rounded-4xl border border-gray-200 w-10 h-10 flex justify-center items-center shrink-0"
|
className="w-10 h-10 shrink-0"
|
||||||
title="Save to folder"
|
title="Save to folder"
|
||||||
>
|
>
|
||||||
<Plus />
|
<Plus />
|
||||||
</button>
|
</CircleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,12 +111,12 @@ export function ReLookupButtonClient({ searchQuery, queryLang, definitionLang }:
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<LightButton
|
||||||
onClick={handleRelookup}
|
onClick={handleRelookup}
|
||||||
className="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors"
|
className="flex items-center gap-2 px-4 py-2 text-sm"
|
||||||
|
leftIcon={<RefreshCw className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<RefreshCw className="w-4 h-4" />
|
|
||||||
Re-lookup
|
Re-lookup
|
||||||
</button>
|
</LightButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { LinkButton, CircleToggleButton, LightButton } from "@/components/ui/buttons";
|
||||||
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
|
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
|
||||||
import { getTTSUrl, TTS_SUPPORTED_LANGUAGES } from "@/lib/bigmodel/tts";
|
import { getTTSUrl, TTS_SUPPORTED_LANGUAGES } from "@/lib/bigmodel/tts";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
@@ -117,12 +118,9 @@ const Memorize: React.FC<MemorizeProps> = ({ textPairs }) => {
|
|||||||
<div className="bg-white rounded-2xl shadow-xl p-6 md:p-8">
|
<div className="bg-white rounded-2xl shadow-xl p-6 md:p-8">
|
||||||
{/* 进度指示器 */}
|
{/* 进度指示器 */}
|
||||||
<div className="flex justify-center mb-4">
|
<div className="flex justify-center mb-4">
|
||||||
<button
|
<LinkButton onClick={handleIndexClick} className="text-sm">
|
||||||
onClick={handleIndexClick}
|
|
||||||
className="text-sm text-gray-500 hover:text-gray-700 transition-colors"
|
|
||||||
>
|
|
||||||
{index + 1} / {getTextPairs().length}
|
{index + 1} / {getTextPairs().length}
|
||||||
</button>
|
</LinkButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 文本显示区域 */}
|
{/* 文本显示区域 */}
|
||||||
@@ -162,45 +160,36 @@ const Memorize: React.FC<MemorizeProps> = ({ textPairs }) => {
|
|||||||
|
|
||||||
{/* 底部按钮 */}
|
{/* 底部按钮 */}
|
||||||
<div className="flex flex-row gap-2 items-center justify-center flex-wrap">
|
<div className="flex flex-row gap-2 items-center justify-center flex-wrap">
|
||||||
<button
|
<LightButton
|
||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
className="px-4 py-2 rounded-full bg-gray-200 text-gray-700 hover:bg-gray-300 transition-colors text-sm"
|
className="px-4 py-2 rounded-full text-sm"
|
||||||
>
|
>
|
||||||
{show === "question" ? t("answer") : t("next")}
|
{show === "question" ? t("answer") : t("next")}
|
||||||
</button>
|
</LightButton>
|
||||||
<button
|
<LightButton
|
||||||
onClick={handlePrevious}
|
onClick={handlePrevious}
|
||||||
className="px-4 py-2 rounded-full bg-gray-200 text-gray-700 hover:bg-gray-300 transition-colors text-sm"
|
className="px-4 py-2 rounded-full text-sm"
|
||||||
>
|
>
|
||||||
{t("previous")}
|
{t("previous")}
|
||||||
</button>
|
</LightButton>
|
||||||
<button
|
<CircleToggleButton
|
||||||
|
selected={reverse}
|
||||||
onClick={toggleReverse}
|
onClick={toggleReverse}
|
||||||
className={`px-4 py-2 rounded-full transition-colors text-sm ${reverse
|
|
||||||
? "bg-[#35786f] text-white hover:bg-[#2d5f58]"
|
|
||||||
: "bg-gray-200 text-gray-700 hover:bg-gray-300"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{t("reverse")}
|
{t("reverse")}
|
||||||
</button>
|
</CircleToggleButton>
|
||||||
<button
|
<CircleToggleButton
|
||||||
|
selected={dictation}
|
||||||
onClick={toggleDictation}
|
onClick={toggleDictation}
|
||||||
className={`px-4 py-2 rounded-full transition-colors text-sm ${dictation
|
|
||||||
? "bg-[#35786f] text-white hover:bg-[#2d5f58]"
|
|
||||||
: "bg-gray-200 text-gray-700 hover:bg-gray-300"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{t("dictation")}
|
{t("dictation")}
|
||||||
</button>
|
</CircleToggleButton>
|
||||||
<button
|
<CircleToggleButton
|
||||||
|
selected={disorder}
|
||||||
onClick={toggleDisorder}
|
onClick={toggleDisorder}
|
||||||
className={`px-4 py-2 rounded-full transition-colors text-sm ${disorder
|
|
||||||
? "bg-[#35786f] text-white hover:bg-[#2d5f58]"
|
|
||||||
: "bg-gray-200 text-gray-700 hover:bg-gray-300"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{t("disorder")}
|
{t("disorder")}
|
||||||
</button>
|
</CircleToggleButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useRef } from "react";
|
import React, { useRef } from "react";
|
||||||
|
import { Button } from "@/components/ui/Button";
|
||||||
import { FileInputProps } from "../../types/controls";
|
import { FileInputProps } from "../../types/controls";
|
||||||
|
|
||||||
interface FileInputComponentProps extends FileInputProps {
|
interface FileInputComponentProps extends FileInputProps {
|
||||||
@@ -33,13 +34,15 @@ export function FileInput({ accept, onFileSelect, disabled, className, children
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className="hidden"
|
className="hidden"
|
||||||
/>
|
/>
|
||||||
<button
|
<Button
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={`px-2 py-1 rounded shadow font-bold hover:cursor-pointer hover:bg-gray-200 text-gray-800 bg-white ${disabled ? 'opacity-50 cursor-not-allowed' : ''} ${className || ''}`}
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
className={className}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { LightButton } from "@/components/ui/buttons";
|
import { LightButton, GreenButton } from "@/components/ui/buttons";
|
||||||
import { IconClick } from "@/components/ui/buttons";
|
import { IconClick } from "@/components/ui/buttons";
|
||||||
import { IMAGES } from "@/config/images";
|
import { IMAGES } from "@/config/images";
|
||||||
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
|
import { useAudioPlayer } from "@/hooks/useAudioPlayer";
|
||||||
@@ -210,12 +210,13 @@ export default function TranslatorPage() {
|
|||||||
|
|
||||||
{/* TranslateButton Component */}
|
{/* TranslateButton Component */}
|
||||||
<div className="w-screen flex justify-center items-center">
|
<div className="w-screen flex justify-center items-center">
|
||||||
<button
|
<GreenButton
|
||||||
className={`duration-150 ease-in text-xl font-extrabold border rounded-4xl p-3 border-gray-200 h-16 ${processing ? "bg-gray-200" : "bg-white hover:bg-gray-200 hover:cursor-pointer"}`}
|
|
||||||
onClick={translate}
|
onClick={translate}
|
||||||
|
disabled={processing}
|
||||||
|
className="text-xl h-16 px-8"
|
||||||
>
|
>
|
||||||
{t("translate")}
|
{t("translate")}
|
||||||
</button>
|
</GreenButton>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useState, useActionState, startTransition } from "react";
|
|||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { Container } from "@/components/ui/Container";
|
import { Container } from "@/components/ui/Container";
|
||||||
import { Input } from "@/components/ui/Input";
|
import { Input } from "@/components/ui/Input";
|
||||||
import { LightButton } from "@/components/ui/buttons";
|
import { LightButton, LinkButton } from "@/components/ui/buttons";
|
||||||
import { authClient } from "@/lib/auth-client";
|
import { authClient } from "@/lib/auth-client";
|
||||||
import { actionSignIn, actionSignUp, ActionOutputAuth } from "@/modules/auth/auth-action";
|
import { actionSignIn, actionSignUp, ActionOutputAuth } from "@/modules/auth/auth-action";
|
||||||
|
|
||||||
@@ -262,7 +262,7 @@ export function AuthForm({ redirectTo }: AuthFormProps) {
|
|||||||
|
|
||||||
{/* 模式切换链接 */}
|
{/* 模式切换链接 */}
|
||||||
<div className="mt-6 text-center">
|
<div className="mt-6 text-center">
|
||||||
<button
|
<LinkButton
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMode(mode === 'signin' ? 'signup' : 'signin');
|
setMode(mode === 'signin' ? 'signup' : 'signin');
|
||||||
@@ -274,13 +274,12 @@ export function AuthForm({ redirectTo }: AuthFormProps) {
|
|||||||
setClearSignUp(true);
|
setClearSignUp(true);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="text-[#35786f] hover:underline"
|
|
||||||
>
|
>
|
||||||
{mode === 'signin'
|
{mode === 'signin'
|
||||||
? `${t("noAccount")} ${t("signUp")}`
|
? `${t("noAccount")} ${t("signUp")}`
|
||||||
: `${t("hasAccount")} ${t("signIn")}`
|
: `${t("hasAccount")} ${t("signIn")}`
|
||||||
}
|
}
|
||||||
</button>
|
</LinkButton>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
FolderPlus,
|
FolderPlus,
|
||||||
Trash2,
|
Trash2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import { CircleButton, DashedButton } from "@/components/ui/buttons";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
@@ -51,8 +52,8 @@ const FolderCard = ({ folder, refresh }: FolderProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<CircleButton
|
||||||
onClick={(e) => {
|
onClick={(e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const newName = prompt("Input a new name.")?.trim();
|
const newName = prompt("Input a new name.")?.trim();
|
||||||
if (newName && newName.length > 0) {
|
if (newName && newName.length > 0) {
|
||||||
@@ -67,12 +68,11 @@ const FolderCard = ({ folder, refresh }: FolderProps) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
|
|
||||||
>
|
>
|
||||||
<FolderPen size={16} />
|
<FolderPen size={16} />
|
||||||
</button>
|
</CircleButton>
|
||||||
<button
|
<CircleButton
|
||||||
onClick={(e) => {
|
onClick={(e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const confirm = prompt(t("confirmDelete", { name: folder.name }));
|
const confirm = prompt(t("confirmDelete", { name: folder.name }));
|
||||||
if (confirm === folder.name) {
|
if (confirm === folder.name) {
|
||||||
@@ -87,10 +87,10 @@ const FolderCard = ({ folder, refresh }: FolderProps) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="p-2 text-gray-400 hover:text-red-500 hover:bg-red-50 rounded-lg transition-colors"
|
className="text-gray-400 hover:text-red-500 hover:bg-red-50"
|
||||||
>
|
>
|
||||||
<Trash2 size={16} />
|
<Trash2 size={16} />
|
||||||
</button>
|
</CircleButton>
|
||||||
<ChevronRight size={18} className="text-gray-400" />
|
<ChevronRight size={18} className="text-gray-400" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -135,7 +135,7 @@ export function FoldersClient({ userId }: { userId: string; }) {
|
|||||||
<PageHeader title={t("title")} subtitle={t("subtitle")} />
|
<PageHeader title={t("title")} subtitle={t("subtitle")} />
|
||||||
|
|
||||||
{/* 新建文件夹按钮 */}
|
{/* 新建文件夹按钮 */}
|
||||||
<button
|
<DashedButton
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const folderName = prompt(t("enterFolderName"));
|
const folderName = prompt(t("enterFolderName"));
|
||||||
if (!folderName) return;
|
if (!folderName) return;
|
||||||
@@ -154,11 +154,11 @@ export function FoldersClient({ userId }: { userId: string; }) {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full p-3 border-2 border-dashed border-gray-300 rounded-xl text-gray-500 hover:border-gray-400 hover:text-gray-600 transition-colors flex items-center justify-center gap-2"
|
className="w-full"
|
||||||
>
|
>
|
||||||
<FolderPlus size={18} />
|
<FolderPlus size={18} />
|
||||||
<span>{loading ? t("creating") : t("newFolder")}</span>
|
<span>{loading ? t("creating") : t("newFolder")}</span>
|
||||||
</button>
|
</DashedButton>
|
||||||
|
|
||||||
{/* 文件夹列表 */}
|
{/* 文件夹列表 */}
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import { AddTextPairModal } from "./AddTextPairModal";
|
|||||||
import { TextPairCard } from "./TextPairCard";
|
import { TextPairCard } from "./TextPairCard";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { PageLayout } from "@/components/ui/PageLayout";
|
import { PageLayout } from "@/components/ui/PageLayout";
|
||||||
import { GreenButton } from "@/components/ui/buttons";
|
import { GreenButton, IconButton, LinkButton } from "@/components/ui/buttons";
|
||||||
import { IconButton } from "@/components/ui/buttons";
|
|
||||||
import { CardList } from "@/components/ui/CardList";
|
import { CardList } from "@/components/ui/CardList";
|
||||||
import { actionCreatePair, actionDeletePairById, actionGetPairsByFolderId } from "@/modules/folder/folder-aciton";
|
import { actionCreatePair, actionDeletePairById, actionGetPairsByFolderId } from "@/modules/folder/folder-aciton";
|
||||||
import { TSharedPair } from "@/shared/folder-type";
|
import { TSharedPair } from "@/shared/folder-type";
|
||||||
@@ -52,13 +51,13 @@ export function InFolder({ folderId, isReadOnly }: { folderId: number; isReadOnl
|
|||||||
{/* 顶部导航和标题栏 */}
|
{/* 顶部导航和标题栏 */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
{/* 返回按钮 */}
|
{/* 返回按钮 */}
|
||||||
<button
|
<LinkButton
|
||||||
onClick={router.back}
|
onClick={router.back}
|
||||||
className="flex items-center gap-2 text-gray-500 hover:text-gray-700 transition-colors mb-4"
|
className="flex items-center gap-2 mb-4"
|
||||||
>
|
>
|
||||||
<ArrowLeft size={16} />
|
<ArrowLeft size={16} />
|
||||||
<span className="text-sm">{t("back")}</span>
|
<span className="text-sm">{t("back")}</span>
|
||||||
</button>
|
</LinkButton>
|
||||||
|
|
||||||
{/* 页面标题和操作按钮 */}
|
{/* 页面标题和操作按钮 */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Edit, Trash2 } from "lucide-react";
|
import { Edit, Trash2 } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { CircleButton } from "@/components/ui/buttons";
|
||||||
import { UpdateTextPairModal } from "./UpdateTextPairModal";
|
import { UpdateTextPairModal } from "./UpdateTextPairModal";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { TSharedPair } from "@/shared/folder-type";
|
import { TSharedPair } from "@/shared/folder-type";
|
||||||
@@ -39,20 +40,20 @@ export function TextPairCard({
|
|||||||
<div className="flex items-center gap-1 opacity-50 group-hover:opacity-100 transition-opacity">
|
<div className="flex items-center gap-1 opacity-50 group-hover:opacity-100 transition-opacity">
|
||||||
{!isReadOnly && (
|
{!isReadOnly && (
|
||||||
<>
|
<>
|
||||||
<button
|
<CircleButton
|
||||||
className="p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-md transition-colors"
|
|
||||||
onClick={() => setOpenUpdateModal(true)}
|
onClick={() => setOpenUpdateModal(true)}
|
||||||
title={t("edit")}
|
title={t("edit")}
|
||||||
|
className="text-gray-400 hover:text-gray-600"
|
||||||
>
|
>
|
||||||
<Edit size={14} />
|
<Edit size={14} />
|
||||||
</button>
|
</CircleButton>
|
||||||
<button
|
<CircleButton
|
||||||
className="p-1.5 text-gray-400 hover:text-red-500 hover:bg-red-50 rounded-md transition-colors"
|
|
||||||
onClick={onDel}
|
onClick={onDel}
|
||||||
title={t("delete")}
|
title={t("delete")}
|
||||||
|
className="text-gray-400 hover:text-red-500 hover:bg-red-50"
|
||||||
>
|
>
|
||||||
<Trash2 size={14} />
|
<Trash2 size={14} />
|
||||||
</button>
|
</CircleButton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,9 +3,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { COLORS } from "@/lib/theme/colors";
|
|
||||||
|
|
||||||
export type ButtonVariant = "primary" | "secondary" | "ghost" | "icon";
|
export type ButtonVariant = "primary" | "secondary" | "ghost" | "icon" | "circle" | "dashed" | "link";
|
||||||
export type ButtonSize = "sm" | "md" | "lg";
|
export type ButtonSize = "sm" | "md" | "lg";
|
||||||
|
|
||||||
export interface ButtonProps {
|
export interface ButtonProps {
|
||||||
@@ -70,6 +69,24 @@ export function Button({
|
|||||||
icon: `
|
icon: `
|
||||||
p-2 bg-gray-200 rounded-full
|
p-2 bg-gray-200 rounded-full
|
||||||
hover:bg-gray-300
|
hover:bg-gray-300
|
||||||
|
`,
|
||||||
|
circle: `
|
||||||
|
p-2 rounded-full
|
||||||
|
hover:bg-gray-200
|
||||||
|
`,
|
||||||
|
dashed: `
|
||||||
|
border-2 border-dashed border-gray-300
|
||||||
|
text-gray-500
|
||||||
|
hover:border-gray-400
|
||||||
|
hover:text-gray-600
|
||||||
|
bg-transparent
|
||||||
|
shadow-none
|
||||||
|
`,
|
||||||
|
link: `
|
||||||
|
text-[#35786f]
|
||||||
|
hover:underline
|
||||||
|
p-0
|
||||||
|
shadow-none
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -54,3 +54,32 @@ export const IconClick = (props: any) => {
|
|||||||
|
|
||||||
// PlainButton: 基础小按钮
|
// PlainButton: 基础小按钮
|
||||||
export const PlainButton = (props: any) => <Button variant="secondary" size="sm" {...props} />;
|
export const PlainButton = (props: any) => <Button variant="secondary" size="sm" {...props} />;
|
||||||
|
|
||||||
|
// CircleButton: 圆形导航按钮
|
||||||
|
export const CircleButton = (props: any) => {
|
||||||
|
const { icon, className, ...rest } = props;
|
||||||
|
return <Button variant="circle" leftIcon={icon} className={className} {...rest} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
// DashedButton: 虚线边框按钮
|
||||||
|
export const DashedButton = (props: any) => <Button variant="dashed" {...props} />;
|
||||||
|
|
||||||
|
// LinkButton: 链接样式按钮
|
||||||
|
export const LinkButton = (props: any) => <Button variant="link" {...props} />;
|
||||||
|
|
||||||
|
// CircleToggleButton: 圆形切换按钮(支持 selected 状态)
|
||||||
|
export const CircleToggleButton = (props: any) => {
|
||||||
|
const { selected, className, children, ...rest } = props;
|
||||||
|
const selectedClass = selected
|
||||||
|
? "bg-[#35786f] text-white"
|
||||||
|
: "bg-gray-200 text-gray-600 hover:bg-gray-300";
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="circle"
|
||||||
|
className={`rounded-full px-3 py-1 text-sm transition-colors ${selectedClass} ${className || ""}`}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user