openpencil/src/components/properties/PositionSection.vue

154 lines
5.2 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import ScrubInput from '@/components/ScrubInput.vue'
import Tip from '@/components/ui/Tip.vue'
import { iconButton } from '@/components/ui/icon-button'
import { sectionWrapper } from '@/components/ui/section'
import { PositionControlsRoot } from '@open-pencil/vue'
</script>
<template>
<PositionControlsRoot
v-slot="{ active, isMulti, xValue, yValue, wValue, hValue, rotationValue, updateProp, commitProp, align, flip, rotate }"
>
<div v-if="active" data-test-id="position-section" :class="sectionWrapper()">
<label class="mb-1.5 block text-[11px] text-muted">Position</label>
<div class="mb-1.5 flex gap-2">
<div class="flex gap-0.5">
<Tip label="Align left">
<button
:class="iconButton({ size: 'md' })"
data-test-id="position-align-left"
@click="align('horizontal', 'min')"
>
<icon-lucide-align-horizontal-justify-start class="size-3.5" />
</button>
</Tip>
<Tip label="Align center horizontally">
<button
:class="iconButton({ size: 'md' })"
data-test-id="position-align-center-h"
@click="align('horizontal', 'center')"
>
<icon-lucide-align-horizontal-justify-center class="size-3.5" />
</button>
</Tip>
<Tip label="Align right">
<button
:class="iconButton({ size: 'md' })"
data-test-id="position-align-right"
@click="align('horizontal', 'max')"
>
<icon-lucide-align-horizontal-justify-end class="size-3.5" />
</button>
</Tip>
</div>
<div class="flex gap-0.5">
<Tip label="Align top">
<button
:class="iconButton({ size: 'md' })"
data-test-id="position-align-top"
@click="align('vertical', 'min')"
>
<icon-lucide-align-vertical-justify-start class="size-3.5" />
</button>
</Tip>
<Tip label="Align center vertically">
<button
:class="iconButton({ size: 'md' })"
data-test-id="position-align-center-v"
@click="align('vertical', 'center')"
>
<icon-lucide-align-vertical-justify-center class="size-3.5" />
</button>
</Tip>
<Tip label="Align bottom">
<button
:class="iconButton({ size: 'md' })"
data-test-id="position-align-bottom"
@click="align('vertical', 'max')"
>
<icon-lucide-align-vertical-justify-end class="size-3.5" />
</button>
</Tip>
</div>
</div>
<div class="flex gap-1.5">
<ScrubInput
icon="X"
:model-value="xValue"
@update:model-value="updateProp('x', $event)"
@commit="(v: number, p: number) => commitProp('x', v, p)"
/>
<ScrubInput
icon="Y"
:model-value="yValue"
@update:model-value="updateProp('y', $event)"
@commit="(v: number, p: number) => commitProp('y', v, p)"
/>
</div>
<div v-if="isMulti" class="mt-1.5 flex gap-1.5">
<ScrubInput
icon="W"
:model-value="wValue"
:min="1"
@update:model-value="updateProp('width', $event)"
@commit="(v: number, p: number) => commitProp('width', v, p)"
/>
<ScrubInput
icon="H"
:model-value="hValue"
:min="1"
@update:model-value="updateProp('height', $event)"
@commit="(v: number, p: number) => commitProp('height', v, p)"
/>
</div>
<div class="mt-1.5 flex items-center gap-1.5">
<ScrubInput
class="flex-1"
suffix="°"
:model-value="rotationValue"
:min="-360"
:max="360"
@update:model-value="updateProp('rotation', $event)"
@commit="(v: number, p: number) => commitProp('rotation', v, p)"
>
<template #icon>
<icon-lucide-rotate-ccw class="size-3" />
</template>
</ScrubInput>
<Tip label="Flip horizontal">
<button
:class="iconButton({ size: 'md', ui: { base: 'shrink-0' } })"
data-test-id="position-flip-horizontal"
@click="flip('horizontal')"
>
<icon-lucide-flip-horizontal class="size-3.5" />
</button>
</Tip>
<Tip label="Flip vertical">
<button
:class="iconButton({ size: 'md', ui: { base: 'shrink-0' } })"
data-test-id="position-flip-vertical"
@click="flip('vertical')"
>
<icon-lucide-flip-vertical class="size-3.5" />
</button>
</Tip>
<Tip label="Rotate 90°">
<button
:class="iconButton({ size: 'md', ui: { base: 'shrink-0' } })"
data-test-id="position-rotate-90"
@click="rotate(90)"
>
<icon-lucide-rotate-cw class="size-3.5" />
</button>
</Tip>
</div>
</div>
</PositionControlsRoot>
</template>