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> </script>
<template> <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 flex-wrap items-center gap-2 border-b border-slate-800/60 bg-slate-900/50 px-4 py-3">
<div class="flex items-center justify-between"> <!-- Add Elements Group -->
<h3 class="text-sm font-semibold uppercase tracking-wide text-slate-400"> <div class="flex items-center gap-1 border-r border-slate-700/50 pr-3">
Canvas Tools
</h3>
<button <button
type="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" class="flex h-9 w-9 items-center justify-center rounded-lg text-lg font-bold text-slate-300 transition hover:bg-slate-800"
@click="props.onClear" title="Add Text"
>
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"
@click="props.onAddText" @click="props.onAddText"
> >
<span class="mb-2 text-3xl font-semibold">T</span> T
<span class="text-xs uppercase tracking-wide">Add Text</span>
</button> </button>
<button <button
type="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" @click="props.onAddCircle"
> >
<span class="mb-2 text-3xl font-semibold"></span>
<span class="text-xs uppercase tracking-wide">Add Circle</span>
</button> </button>
<button <button
type="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" @click="props.onAddRectangle"
> >
<span class="mb-2 text-3xl font-semibold"></span>
<span class="text-xs uppercase tracking-wide">Add Rectangle</span>
</button> </button>
<button <button
type="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" @click="openFilePicker"
> >
<span class="mb-2 text-3xl font-semibold"></span> 🖼
<span class="text-xs uppercase tracking-wide">Upload Image</span>
</button> </button>
</div> </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 <input
ref="fileInput" ref="fileInput"
type="file" type="file"
@@ -156,83 +211,6 @@ const zoomLabel = computed(() => `${Math.round(props.zoom * 100)}%`);
class="hidden" class="hidden"
@change="handleFileChange" @change="handleFileChange"
/> />
<div class="mt-5 rounded-xl border border-slate-800/60 bg-slate-900/70 p-4"> </div>
<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>
</template> </template>

View File

@@ -379,26 +379,6 @@ const handleCheckout = async () => {
@select="handleTemplateSelect" @select="handleTemplateSelect"
/> />
<DesignerToolbar
:on-add-text="addTextbox"
:on-add-circle="() => addShape('circle')"
:on-add-rectangle="() => addShape('rect')"
:on-clear="clearDesign"
:on-import-image="addImageFromFile"
:on-fill-change="setActiveFillColor"
:on-stroke-change="setActiveStrokeColor"
:active-fill="activeFillColor"
:active-stroke="activeStrokeColor"
:can-style-selection="canStyleSelection"
:zoom="zoomLevel"
:min-zoom="minZoom"
:max-zoom="maxZoom"
:on-zoom-change="setZoom"
:on-zoom-in="zoomIn"
:on-zoom-out="zoomOut"
:on-zoom-reset="resetZoom"
/>
<DesignerPreview <DesignerPreview
:preview-url="previewUrl" :preview-url="previewUrl"
:template-label="templateLabel" :template-label="templateLabel"
@@ -416,19 +396,40 @@ const handleCheckout = async () => {
<div class="flex flex-col gap-6"> <div class="flex flex-col gap-6">
<div <div
class="rounded-3xl border border-slate-800/60 bg-linear-to-br from-slate-900 via-slate-950 to-black p-6 shadow-2xl shadow-slate-950/60" class="rounded-3xl border border-slate-800/60 bg-linear-to-br from-slate-900 via-slate-950 to-black shadow-2xl shadow-slate-950/60"
> >
<DesignerCanvas <DesignerToolbar
:size="displaySize" :on-add-text="addTextbox"
:background-color="selectedTemplate.backgroundColor" :on-add-circle="() => addShape('circle')"
:register-canvas="registerCanvas" :on-add-rectangle="() => addShape('rect')"
:unregister-canvas="unregisterCanvas" :on-clear="clearDesign"
:on-import-image="addImageFromFile"
:on-fill-change="setActiveFillColor"
:on-stroke-change="setActiveStrokeColor"
:active-fill="activeFillColor"
:active-stroke="activeStrokeColor"
:can-style-selection="canStyleSelection"
:zoom="zoomLevel"
:min-zoom="minZoom"
:max-zoom="maxZoom"
:on-zoom-change="setZoom"
:on-zoom-in="zoomIn"
:on-zoom-out="zoomOut"
:on-zoom-reset="resetZoom"
/> />
<p class="mt-4 text-sm text-slate-400"> <div class="p-6">
Safe zone and bleed guides update automatically when you switch <DesignerCanvas
templates. Use the toolbar to layer text, shapes, and imagery :size="displaySize"
inside the circular boundary. :background-color="selectedTemplate.backgroundColor"
</p> :register-canvas="registerCanvas"
:unregister-canvas="unregisterCanvas"
/>
<p class="mt-4 text-sm text-slate-400">
Safe zone and bleed guides update automatically when you switch
templates. Use the toolbar to layer text, shapes, and imagery
inside the circular boundary.
</p>
</div>
</div> </div>
</div> </div>
</section> </section>