Redesign toolbar as horizontal layout above canvas

- Move DesignerToolbar from sidebar to top of canvas container
- Convert vertical toolbar to horizontal layout with grouped controls
- Add element buttons (Text, Circle, Rectangle, Image) on left
- Inline color pickers for Fill and Stroke with compact design
- Horizontal zoom controls (-, percentage, +, Reset)
- Clear Canvas button positioned on the right
- Use border separators between control groups
- Remove vertical card layout in favor of toolbar-style UI
This commit is contained in:
Frank John Begornia
2025-11-16 01:24:50 +08:00
parent bf701f8342
commit 8a9703c24a
2 changed files with 114 additions and 135 deletions

View File

@@ -102,53 +102,108 @@ const zoomLabel = computed(() => `${Math.round(props.zoom * 100)}%`);
</script>
<template>
<section class="rounded-2xl border border-slate-800/60 bg-slate-900/80 p-4 shadow-lg shadow-slate-950/30">
<div class="flex items-center justify-between">
<h3 class="text-sm font-semibold uppercase tracking-wide text-slate-400">
Canvas Tools
</h3>
<div class="flex flex-wrap items-center gap-2 border-b border-slate-800/60 bg-slate-900/50 px-4 py-3">
<!-- Add Elements Group -->
<div class="flex items-center gap-1 border-r border-slate-700/50 pr-3">
<button
type="button"
class="rounded-lg border border-red-500/40 px-3 py-1 text-xs font-medium text-red-300 transition hover:bg-red-500/10"
@click="props.onClear"
>
Clear Canvas
</button>
</div>
<div class="mt-4 grid grid-cols-2 gap-3 md:grid-cols-4">
<button
type="button"
class="flex flex-col items-center justify-center rounded-xl border border-slate-700/60 bg-slate-800/80 px-4 py-6 text-sm font-medium text-slate-100 transition hover:border-sky-500/70 hover:bg-sky-500/10 hover:text-sky-200"
class="flex h-9 w-9 items-center justify-center rounded-lg text-lg font-bold text-slate-300 transition hover:bg-slate-800"
title="Add Text"
@click="props.onAddText"
>
<span class="mb-2 text-3xl font-semibold">T</span>
<span class="text-xs uppercase tracking-wide">Add Text</span>
T
</button>
<button
type="button"
class="flex flex-col items-center justify-center rounded-xl border border-slate-700/60 bg-slate-800/80 px-4 py-6 text-sm font-medium text-slate-100 transition hover:border-sky-500/70 hover:bg-sky-500/10 hover:text-sky-200"
class="flex h-9 w-9 items-center justify-center rounded-lg text-xl font-bold text-slate-300 transition hover:bg-slate-800"
title="Add Circle"
@click="props.onAddCircle"
>
<span class="mb-2 text-3xl font-semibold"></span>
<span class="text-xs uppercase tracking-wide">Add Circle</span>
</button>
<button
type="button"
class="flex flex-col items-center justify-center rounded-xl border border-slate-700/60 bg-slate-800/80 px-4 py-6 text-sm font-medium text-slate-100 transition hover:border-sky-500/70 hover:bg-sky-500/10 hover:text-sky-200"
class="flex h-9 w-9 items-center justify-center rounded-lg text-xl font-bold text-slate-300 transition hover:bg-slate-800"
title="Add Rectangle"
@click="props.onAddRectangle"
>
<span class="mb-2 text-3xl font-semibold"></span>
<span class="text-xs uppercase tracking-wide">Add Rectangle</span>
</button>
<button
type="button"
class="flex flex-col items-center justify-center rounded-xl border border-slate-700/60 bg-slate-800/80 px-4 py-6 text-sm font-medium text-slate-100 transition hover:border-sky-500/70 hover:bg-sky-500/10 hover:text-sky-200"
class="flex h-9 w-9 items-center justify-center rounded-lg text-xl font-bold text-slate-300 transition hover:bg-slate-800"
title="Upload Image"
@click="openFilePicker"
>
<span class="mb-2 text-3xl font-semibold"></span>
<span class="text-xs uppercase tracking-wide">Upload Image</span>
🖼
</button>
</div>
<!-- Color Pickers Group -->
<div class="flex items-center gap-2 border-r border-slate-700/50 pr-3">
<label class="flex items-center gap-1.5" title="Fill Color">
<span class="text-xs text-slate-400">Fill</span>
<input
type="color"
class="h-8 w-12 cursor-pointer rounded border border-slate-700/70 bg-slate-800/80 p-0.5"
:disabled="stylingDisabled"
:value="fillValue"
@input="handleFillChange"
/>
</label>
<label class="flex items-center gap-1.5" title="Stroke Color">
<span class="text-xs text-slate-400">Stroke</span>
<input
type="color"
class="h-8 w-12 cursor-pointer rounded border border-slate-700/70 bg-slate-800/80 p-0.5"
:disabled="stylingDisabled"
:value="strokeValue"
@input="handleStrokeChange"
/>
</label>
</div>
<!-- Zoom Controls -->
<div class="flex items-center gap-2 border-r border-slate-700/50 pr-3">
<button
type="button"
class="flex h-8 w-8 items-center justify-center rounded border border-slate-700/60 bg-slate-800/80 text-sm font-bold text-slate-100 transition hover:bg-slate-700"
title="Zoom Out"
@click="props.onZoomOut"
>
</button>
<span class="min-w-12 text-center text-xs font-medium text-slate-300">
{{ zoomLabel }}
</span>
<button
type="button"
class="flex h-8 w-8 items-center justify-center rounded border border-slate-700/60 bg-slate-800/80 text-sm font-bold text-slate-100 transition hover:bg-slate-700"
title="Zoom In"
@click="props.onZoomIn"
>
+
</button>
<button
type="button"
class="rounded border border-slate-700/60 bg-slate-800/80 px-2 py-1 text-xs font-medium text-slate-200 transition hover:bg-slate-700"
title="Reset Zoom"
@click="props.onZoomReset"
>
Reset
</button>
</div>
<!-- Clear Button -->
<button
type="button"
class="ml-auto rounded border border-red-500/40 px-3 py-1.5 text-xs font-medium text-red-300 transition hover:bg-red-500/10"
@click="props.onClear"
>
Clear Canvas
</button>
<!-- Hidden File Input -->
<input
ref="fileInput"
type="file"
@@ -156,83 +211,6 @@ const zoomLabel = computed(() => `${Math.round(props.zoom * 100)}%`);
class="hidden"
@change="handleFileChange"
/>
<div class="mt-5 rounded-xl border border-slate-800/60 bg-slate-900/70 p-4">
<div class="flex items-center justify-between gap-3">
<h4 class="text-xs font-semibold uppercase tracking-wide text-slate-400">
Styling
</h4>
<span
v-if="stylingDisabled"
class="text-[11px] font-medium uppercase tracking-wide text-slate-500"
>
Select an object
</span>
</div>
<div class="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2">
<label class="flex flex-col gap-2 text-xs font-semibold uppercase tracking-wide text-slate-400">
Fill
<input
type="color"
class="h-10 w-full cursor-pointer rounded-lg border border-slate-700/70 bg-slate-800/80 p-1"
:disabled="stylingDisabled"
:value="fillValue"
@input="handleFillChange"
/>
</label>
<label class="flex flex-col gap-2 text-xs font-semibold uppercase tracking-wide text-slate-400">
Stroke
<input
type="color"
class="h-10 w-full cursor-pointer rounded-lg border border-slate-700/70 bg-slate-800/80 p-1"
:disabled="stylingDisabled"
:value="strokeValue"
@input="handleStrokeChange"
/>
</label>
</div>
</div>
<div class="mt-5 rounded-xl border border-slate-800/60 bg-slate-900/70 p-4">
<div class="flex items-center justify-between gap-3">
<h4 class="text-xs font-semibold uppercase tracking-wide text-slate-400">
View
</h4>
<span class="text-[11px] font-medium uppercase tracking-wide text-slate-300">
{{ zoomLabel }}
</span>
</div>
<div class="mt-4 flex items-center gap-3">
<button
type="button"
class="flex h-10 w-10 items-center justify-center rounded-lg border border-slate-700/60 bg-slate-800/80 text-lg font-bold text-slate-100 transition hover:border-sky-500/70 hover:bg-sky-500/10"
@click="props.onZoomOut"
>
</button>
<input
type="range"
class="flex-1 accent-sky-500"
:min="Math.round(props.minZoom * 100)"
:max="Math.round(props.maxZoom * 100)"
step="5"
:value="zoomSliderValue"
@input="handleZoomInput"
/>
<button
type="button"
class="flex h-10 w-10 items-center justify-center rounded-lg border border-slate-700/60 bg-slate-800/80 text-lg font-bold text-slate-100 transition hover:border-sky-500/70 hover:bg-sky-500/10"
@click="props.onZoomIn"
>
+
</button>
</div>
<button
type="button"
class="mt-3 w-full rounded-lg border border-slate-700/60 px-3 py-2 text-xs font-semibold uppercase tracking-wide text-slate-200 transition hover:border-sky-500/70 hover:bg-sky-500/10 hover:text-sky-200"
@click="props.onZoomReset"
>
Reset Zoom
</button>
</div>
</section>
</div>
</template>