mirror of
https://github.com/YuzuZensai/Pien-Studio.git
synced 2026-09-02 14:18:35 +00:00
♻️ refactor!: massive refactor
This commit is contained in:
@@ -70,17 +70,27 @@ export function EditorHeader({
|
||||
onSetPointerTool,
|
||||
}: EditorHeaderProps) {
|
||||
const menuClass = `absolute left-0 top-full z-30 min-w-[180px] rounded border p-1 text-[11px] opacity-0 shadow-xl transition-opacity pointer-events-none group-hover:pointer-events-auto group-hover:opacity-100 hover:pointer-events-auto hover:opacity-100 ${
|
||||
isDark ? "border-white/10 bg-[#2a2c31] text-[#e2e5ea]" : "border-black/10 bg-white text-[#1f2430]"
|
||||
isDark
|
||||
? "border-white/10 bg-[#2a2c31] text-[#e2e5ea]"
|
||||
: "border-black/10 bg-white text-[#1f2430]"
|
||||
}`;
|
||||
|
||||
return (
|
||||
<header className={`flex h-14 items-center justify-between border-b px-4 ${isDark ? "border-white/10 bg-[#2b2d31]" : "border-black/10 bg-white"}`}>
|
||||
<header
|
||||
className={`flex h-14 items-center justify-between border-b px-4 ${isDark ? "border-white/10 bg-[#2b2d31]" : "border-black/10 bg-white"}`}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href="/" className="group">
|
||||
<p className={`text-[10px] uppercase tracking-[0.2em] group-hover:opacity-80 ${isDark ? "text-[#a8abb2]" : "text-[#6c7382]"}`}>
|
||||
<p
|
||||
className={`text-[10px] uppercase tracking-[0.2em] group-hover:opacity-80 ${isDark ? "text-[#a8abb2]" : "text-[#6c7382]"}`}
|
||||
>
|
||||
pien.studio
|
||||
</p>
|
||||
<h1 className={`text-sm font-semibold group-hover:opacity-80 ${isDark ? "text-[#f5f7fa]" : "text-[#1f2430]"}`}>{projectTitle}</h1>
|
||||
<h1
|
||||
className={`text-sm font-semibold group-hover:opacity-80 ${isDark ? "text-[#f5f7fa]" : "text-[#1f2430]"}`}
|
||||
>
|
||||
{projectTitle}
|
||||
</h1>
|
||||
</Link>
|
||||
|
||||
<nav className="flex items-center gap-2 text-xs font-medium">
|
||||
@@ -98,12 +108,31 @@ export function EditorHeader({
|
||||
/>
|
||||
</MenuShell>
|
||||
<MenuShell isDark={isDark} label={labels.edit} menuClass={menuClass}>
|
||||
<EditMenu isDark={isDark} labels={labels} canUndo={canUndo} canRedo={canRedo} onUndo={onUndo} onRedo={onRedo} onCopy={onCopy} onCut={onCut} onPaste={onPaste} />
|
||||
<EditMenu
|
||||
isDark={isDark}
|
||||
labels={labels}
|
||||
canUndo={canUndo}
|
||||
canRedo={canRedo}
|
||||
onUndo={onUndo}
|
||||
onRedo={onRedo}
|
||||
onCopy={onCopy}
|
||||
onCut={onCut}
|
||||
onPaste={onPaste}
|
||||
/>
|
||||
</MenuShell>
|
||||
<MenuShell isDark={isDark} label={labels.view} menuClass={menuClass}>
|
||||
<ViewMenu isDark={isDark} labels={labels} onSetHandTool={onSetHandTool} onSetPointerTool={onSetPointerTool} />
|
||||
<ViewMenu
|
||||
isDark={isDark}
|
||||
labels={labels}
|
||||
onSetHandTool={onSetHandTool}
|
||||
onSetPointerTool={onSetPointerTool}
|
||||
/>
|
||||
</MenuShell>
|
||||
<MenuShell isDark={isDark} label={labels.settings} menuClass={menuClass}>
|
||||
<MenuShell
|
||||
isDark={isDark}
|
||||
label={labels.settings}
|
||||
menuClass={menuClass}
|
||||
>
|
||||
<SettingsMenu isDark={isDark} preferences={labels.preferences} />
|
||||
</MenuShell>
|
||||
</nav>
|
||||
@@ -116,7 +145,9 @@ export function EditorHeader({
|
||||
disabled={!canUndo}
|
||||
title={labels.undo}
|
||||
className={`rounded border px-2.5 py-1.5 text-xs font-medium ${
|
||||
isDark ? "border-white/15 bg-[#25272b] text-[#d7dae0]" : "border-black/15 bg-[#f2f4f8] text-[#1f2430]"
|
||||
isDark
|
||||
? "border-white/15 bg-[#25272b] text-[#d7dae0]"
|
||||
: "border-black/15 bg-[#f2f4f8] text-[#1f2430]"
|
||||
} ${!canUndo ? "opacity-50" : "hover:opacity-90"}`}
|
||||
>
|
||||
<Undo2 className="h-3.5 w-3.5" />
|
||||
@@ -127,12 +158,16 @@ export function EditorHeader({
|
||||
disabled={!canRedo}
|
||||
title={labels.redo}
|
||||
className={`rounded border px-2.5 py-1.5 text-xs font-medium ${
|
||||
isDark ? "border-white/15 bg-[#25272b] text-[#d7dae0]" : "border-black/15 bg-[#f2f4f8] text-[#1f2430]"
|
||||
isDark
|
||||
? "border-white/15 bg-[#25272b] text-[#d7dae0]"
|
||||
: "border-black/15 bg-[#f2f4f8] text-[#1f2430]"
|
||||
} ${!canRedo ? "opacity-50" : "hover:opacity-90"}`}
|
||||
>
|
||||
<Redo2 className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
<span className={`text-[10px] font-semibold uppercase tracking-[0.2em] ${isDark ? "text-[#a8abb2]" : "text-[#6c7382]"}`}>
|
||||
<span
|
||||
className={`text-[10px] font-semibold uppercase tracking-[0.2em] ${isDark ? "text-[#a8abb2]" : "text-[#6c7382]"}`}
|
||||
>
|
||||
{isDirty ? labels.unsavedChanges : labels.saved}
|
||||
</span>
|
||||
</div>
|
||||
@@ -140,10 +175,23 @@ export function EditorHeader({
|
||||
);
|
||||
}
|
||||
|
||||
function MenuShell({ isDark, label, menuClass, children }: { isDark: boolean; label: string; menuClass: string; children: React.ReactNode }) {
|
||||
function MenuShell({
|
||||
isDark,
|
||||
label,
|
||||
menuClass,
|
||||
children,
|
||||
}: {
|
||||
isDark: boolean;
|
||||
label: string;
|
||||
menuClass: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="relative group">
|
||||
<button type="button" className={`rounded px-2 py-1 ${isDark ? "text-[#d7dae0] hover:bg-white/10" : "text-[#1f2430] hover:bg-black/5"}`}>
|
||||
<button
|
||||
type="button"
|
||||
className={`rounded px-2 py-1 ${isDark ? "text-[#d7dae0] hover:bg-white/10" : "text-[#1f2430] hover:bg-black/5"}`}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
<div className={menuClass} onClick={(event) => event.stopPropagation()}>
|
||||
@@ -176,13 +224,43 @@ function FileMenu({
|
||||
}) {
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<button type="button" onClick={onImportImage} className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}>{labels.importImage}</button>
|
||||
<button type="button" onClick={onSave} className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}>{labels.save}</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onImportImage}
|
||||
className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}
|
||||
>
|
||||
{labels.importImage}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSave}
|
||||
className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}
|
||||
>
|
||||
{labels.save}
|
||||
</button>
|
||||
<div className={`my-1 h-px ${dividerClass(isDark)}`} />
|
||||
<button type="button" onClick={onExportPng} className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}>{labels.exportPng}</button>
|
||||
<button type="button" onClick={onExportProjectFile} className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}>{labels.exportProjectFile}</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onExportPng}
|
||||
className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}
|
||||
>
|
||||
{labels.exportPng}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onExportProjectFile}
|
||||
className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}
|
||||
>
|
||||
{labels.exportProjectFile}
|
||||
</button>
|
||||
<div className={`my-1 h-px ${dividerClass(isDark)}`} />
|
||||
<button type="button" onClick={onOpenCanvasSize} className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}>{labels.canvasSize} ({canvasWidth} x {canvasHeight})</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onOpenCanvasSize}
|
||||
className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}
|
||||
>
|
||||
{labels.canvasSize} ({canvasWidth} x {canvasHeight})
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -210,29 +288,93 @@ function EditMenu({
|
||||
}) {
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<button type="button" onClick={onUndo} disabled={!canUndo} className={`w-full rounded px-2 py-1 text-left ${!canUndo ? "opacity-50" : hoverSubtleClass(isDark)}`}>{labels.undo}</button>
|
||||
<button type="button" onClick={onRedo} disabled={!canRedo} className={`w-full rounded px-2 py-1 text-left ${!canRedo ? "opacity-50" : hoverSubtleClass(isDark)}`}>{labels.redo}</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onUndo}
|
||||
disabled={!canUndo}
|
||||
className={`w-full rounded px-2 py-1 text-left ${!canUndo ? "opacity-50" : hoverSubtleClass(isDark)}`}
|
||||
>
|
||||
{labels.undo}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onRedo}
|
||||
disabled={!canRedo}
|
||||
className={`w-full rounded px-2 py-1 text-left ${!canRedo ? "opacity-50" : hoverSubtleClass(isDark)}`}
|
||||
>
|
||||
{labels.redo}
|
||||
</button>
|
||||
<div className={`my-1 h-px ${dividerClass(isDark)}`} />
|
||||
<button type="button" onClick={onCopy} className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}>{labels.copy}</button>
|
||||
<button type="button" onClick={onCut} className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}>{labels.cut}</button>
|
||||
<button type="button" onClick={onPaste} className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}>{labels.paste}</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCopy}
|
||||
className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}
|
||||
>
|
||||
{labels.copy}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCut}
|
||||
className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}
|
||||
>
|
||||
{labels.cut}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onPaste}
|
||||
className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}
|
||||
>
|
||||
{labels.paste}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ViewMenu({ isDark, labels, onSetHandTool, onSetPointerTool }: { isDark: boolean; labels: EditorHeaderProps["labels"]; onSetHandTool: () => void; onSetPointerTool: () => void }) {
|
||||
function ViewMenu({
|
||||
isDark,
|
||||
labels,
|
||||
onSetHandTool,
|
||||
onSetPointerTool,
|
||||
}: {
|
||||
isDark: boolean;
|
||||
labels: EditorHeaderProps["labels"];
|
||||
onSetHandTool: () => void;
|
||||
onSetPointerTool: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<button type="button" onClick={onSetHandTool} className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}>{labels.panTool}</button>
|
||||
<button type="button" onClick={onSetPointerTool} className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}>{labels.pointerTool}</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSetHandTool}
|
||||
className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}
|
||||
>
|
||||
{labels.panTool}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSetPointerTool}
|
||||
className={`w-full rounded px-2 py-1 text-left ${hoverSubtleClass(isDark)}`}
|
||||
>
|
||||
{labels.pointerTool}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SettingsMenu({ isDark, preferences }: { isDark: boolean; preferences: string }) {
|
||||
function SettingsMenu({
|
||||
isDark,
|
||||
preferences,
|
||||
}: {
|
||||
isDark: boolean;
|
||||
preferences: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="space-y-2 p-1">
|
||||
<p className={`px-2 text-[10px] font-semibold uppercase tracking-wide ${isDark ? "text-[#9aa1ad]" : "text-[#6c7382]"}`}>{preferences}</p>
|
||||
<p
|
||||
className={`px-2 text-[10px] font-semibold uppercase tracking-wide ${isDark ? "text-[#9aa1ad]" : "text-[#6c7382]"}`}
|
||||
>
|
||||
{preferences}
|
||||
</p>
|
||||
<UiPreferences />
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user