openpencil/src/components/properties/StrokeSection.vue
Danila Poyarkov 8d35d4dbaf Deduplicate hex formatting: colorToHexRaw() in engine/color.ts
Removes duplicate hexString() wrappers from FillSection, StrokeSection,
and ColorPicker — all now use culori-based colorToHexRaw().
2026-02-28 09:08:40 +03:00

95 lines
4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import ColorPicker from '../ColorPicker.vue'
import { useEditorStore } from '../../stores/editor'
import { colorToHexRaw, parseColor } from '../../engine/color'
import type { Color, Stroke } from '../../engine/scene-graph'
const props = defineProps<{ nodeId: string; strokes: Stroke[] }>()
const store = useEditorStore()
function updateColor(index: number, color: Color) {
const strokes = [...props.strokes]
strokes[index] = { ...strokes[index], color }
store.updateNodeWithUndo(props.nodeId, { strokes }, 'Change stroke')
}
function updateHex(index: number, hex: string) {
const color = parseColor(hex.startsWith('#') ? hex : `#${hex}`)
if (!color) return
updateColor(index, color)
}
function updateWeight(index: number, weight: number) {
const strokes = [...props.strokes]
strokes[index] = { ...strokes[index], weight }
store.updateNodeWithUndo(props.nodeId, { strokes }, 'Change stroke')
}
function updateOpacity(index: number, opacity: number) {
const strokes = [...props.strokes]
strokes[index] = { ...strokes[index], opacity: Math.max(0, Math.min(1, opacity / 100)) }
store.updateNodeWithUndo(props.nodeId, { strokes }, 'Change stroke')
}
function toggleVisibility(index: number) {
const strokes = [...props.strokes]
strokes[index] = { ...strokes[index], visible: !strokes[index].visible }
store.updateNodeWithUndo(props.nodeId, { strokes }, 'Change stroke')
}
function add() {
const stroke: Stroke = { color: { r: 0, g: 0, b: 0, a: 1 }, weight: 1, opacity: 1, visible: true, align: 'CENTER' }
store.updateNodeWithUndo(props.nodeId, { strokes: [...props.strokes, stroke] }, 'Add stroke')
}
function remove(index: number) {
store.updateNodeWithUndo(props.nodeId, { strokes: props.strokes.filter((_, i) => i !== index) }, 'Remove stroke')
}
</script>
<template>
<div class="border-b border-border px-3 py-2">
<div class="flex items-center justify-between">
<label class="mb-1 block text-[11px] text-muted">Stroke</label>
<button class="cursor-pointer rounded border-none bg-transparent px-1 text-base leading-none text-muted hover:bg-hover hover:text-surface" @click="add">+</button>
</div>
<div v-for="(stroke, i) in strokes" :key="i" class="group flex items-center gap-1.5 py-0.5">
<ColorPicker :color="stroke.color" @update="updateColor(i, $event)" />
<input
class="min-w-0 flex-1 border-none bg-transparent font-mono text-xs text-surface outline-none"
:value="colorToHexRaw(stroke.color)"
@change="updateHex(i, ($event.target as HTMLInputElement).value)"
/>
<input
type="number"
class="w-10 rounded border border-border bg-input px-1 py-0.5 text-right text-[11px] text-surface [&::-webkit-inner-spin-button]:hidden"
:value="Math.round(stroke.opacity * 100)"
min="0"
max="100"
@change="updateOpacity(i, +($event.target as HTMLInputElement).value)"
/>
<span class="text-[11px] text-muted">%</span>
<button
class="cursor-pointer border-none bg-transparent p-0 text-muted opacity-0 transition-opacity group-hover:opacity-100 hover:text-surface"
@click="toggleVisibility(i)"
>
<icon-lucide-eye v-if="stroke.visible" class="size-3.5" />
<icon-lucide-eye-off v-else class="size-3.5" />
</button>
<button class="cursor-pointer border-none bg-transparent p-0 text-sm leading-none text-muted hover:text-surface" @click="remove(i)">−</button>
</div>
<!-- Stroke weight (shown when any strokes exist) -->
<div v-if="strokes.length > 0" class="mt-1 flex items-center gap-1.5">
<span class="text-[11px] text-muted">Weight</span>
<input
type="number"
class="w-12 rounded border border-border bg-input px-1.5 py-0.5 text-center text-[11px] text-surface [&::-webkit-inner-spin-button]:hidden"
:value="strokes[0].weight"
min="0"
@change="updateWeight(0, +($event.target as HTMLInputElement).value)"
/>
</div>
</div>
</template>