Translate more editor UI surfaces
This commit is contained in:
parent
9f296b7357
commit
4b9cd54983
|
|
@ -90,6 +90,8 @@ export const panelMessages = i18n('panels', {
|
|||
page: 'Page',
|
||||
position: 'Position',
|
||||
layout: 'Layout',
|
||||
autoLayout: 'Auto layout',
|
||||
alignment: 'Alignment',
|
||||
appearance: 'Appearance',
|
||||
fill: 'Fill',
|
||||
stroke: 'Stroke',
|
||||
|
|
@ -113,6 +115,26 @@ export const panelMessages = i18n('panels', {
|
|||
noLocalVariables: 'No local variables',
|
||||
openVariables: 'Open variables',
|
||||
addPage: 'Add page',
|
||||
toggleVisibility: 'Toggle visibility',
|
||||
independentCornerRadii: 'Independent corner radii',
|
||||
detachVariable: 'Detach variable',
|
||||
applyVariable: 'Apply variable',
|
||||
noVariablesFound: 'No variables found',
|
||||
addAutoLayout: 'Add auto layout (Shift+A)',
|
||||
removeAutoLayout: 'Remove auto layout',
|
||||
alignLeft: 'Align left',
|
||||
alignCenterHorizontally: 'Align center horizontally',
|
||||
alignRight: 'Align right',
|
||||
alignTop: 'Align top',
|
||||
alignCenterVertically: 'Align center vertically',
|
||||
alignBottom: 'Align bottom',
|
||||
flipHorizontal: 'Flip horizontal',
|
||||
flipVertical: 'Flip vertical',
|
||||
rotate90: 'Rotate 90°',
|
||||
mixedFillsHelp: 'Click + to replace mixed fills',
|
||||
mixedStrokesHelp: 'Click + to replace mixed strokes',
|
||||
mixedEffectsHelp: 'Click + to replace mixed effects',
|
||||
strokeSides: 'Stroke sides',
|
||||
mixed: 'Mixed',
|
||||
layersCount: params('{count} layers'),
|
||||
goToMainComponent: 'Go to Main Component',
|
||||
|
|
@ -122,13 +144,11 @@ export const panelMessages = i18n('panels', {
|
|||
linearGradient: 'Linear',
|
||||
radialGradient: 'Radial',
|
||||
image: 'Image',
|
||||
stops: 'Stops',
|
||||
addStop: 'Add stop',
|
||||
|
||||
alignLeft: 'Align left',
|
||||
alignCenter: 'Align center',
|
||||
alignRight: 'Align right',
|
||||
alignTop: 'Align top',
|
||||
alignMiddle: 'Align middle',
|
||||
alignBottom: 'Align bottom'
|
||||
alignMiddle: 'Align middle'
|
||||
})
|
||||
|
||||
export const pageMessages = i18n('pages', {
|
||||
|
|
@ -157,6 +177,8 @@ export const dialogMessages = i18n('dialogs', {
|
|||
describeCreateOrChange: 'Describe what you want to create or change.',
|
||||
stopGenerating: 'Stop generating',
|
||||
sendMessage: 'Send message',
|
||||
baseURLPlaceholder: 'Base URL (e.g. http://localhost:11434/v1)',
|
||||
modelIDPlaceholder: 'Model ID (e.g. llama-3.3-70b)',
|
||||
search: 'Search…',
|
||||
noResults: 'No results',
|
||||
share: 'Share'
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@
|
|||
"page": "Страница",
|
||||
"position": "Позиция",
|
||||
"layout": "Раскладка",
|
||||
"autoLayout": "Автораскладка",
|
||||
"alignment": "Выравнивание",
|
||||
"appearance": "Внешний вид",
|
||||
"fill": "Заливка",
|
||||
"stroke": "Обводка",
|
||||
|
|
@ -98,6 +100,26 @@
|
|||
"noLocalVariables": "Нет локальных переменных",
|
||||
"openVariables": "Открыть переменные",
|
||||
"addPage": "Добавить страницу",
|
||||
"toggleVisibility": "Переключить видимость",
|
||||
"independentCornerRadii": "Независимые радиусы углов",
|
||||
"detachVariable": "Отвязать переменную",
|
||||
"applyVariable": "Применить переменную",
|
||||
"noVariablesFound": "Переменные не найдены",
|
||||
"addAutoLayout": "Добавить автораскладку (Shift+A)",
|
||||
"removeAutoLayout": "Убрать автораскладку",
|
||||
"alignLeft": "Выровнять по левому краю",
|
||||
"alignCenterHorizontally": "Выровнять по центру по горизонтали",
|
||||
"alignRight": "Выровнять по правому краю",
|
||||
"alignTop": "Выровнять по верхнему краю",
|
||||
"alignCenterVertically": "Выровнять по центру по вертикали",
|
||||
"alignBottom": "Выровнять по нижнему краю",
|
||||
"flipHorizontal": "Отразить по горизонтали",
|
||||
"flipVertical": "Отразить по вертикали",
|
||||
"rotate90": "Повернуть на 90°",
|
||||
"mixedFillsHelp": "Нажмите +, чтобы заменить смешанные заливки",
|
||||
"mixedStrokesHelp": "Нажмите +, чтобы заменить смешанные обводки",
|
||||
"mixedEffectsHelp": "Нажмите +, чтобы заменить смешанные эффекты",
|
||||
"strokeSides": "Стороны обводки",
|
||||
"mixed": "Смешанное",
|
||||
"layersCount": "{count} слоёв",
|
||||
"goToMainComponent": "Перейти к главному компоненту",
|
||||
|
|
@ -106,6 +128,8 @@
|
|||
"linearGradient": "Линейный",
|
||||
"radialGradient": "Радиальный",
|
||||
"image": "Изображение",
|
||||
"stops": "Точки",
|
||||
"addStop": "Добавить точку",
|
||||
"alignLeft": "По левому краю",
|
||||
"alignCenter": "По центру",
|
||||
"alignRight": "По правому краю",
|
||||
|
|
@ -138,6 +162,8 @@
|
|||
"describeCreateOrChange": "Опишите, что вы хотите создать или изменить.",
|
||||
"stopGenerating": "Остановить генерацию",
|
||||
"sendMessage": "Отправить сообщение",
|
||||
"baseURLPlaceholder": "Base URL (например, http://localhost:11434/v1)",
|
||||
"modelIDPlaceholder": "Model ID (например, llama-3.3-70b)",
|
||||
"search": "Поиск…",
|
||||
"noResults": "Ничего не найдено",
|
||||
"share": "Поделиться"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
import { FillPickerRoot } from '@open-pencil/vue'
|
||||
import { FillPickerRoot, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import GradientEditor from './GradientEditor.vue'
|
||||
import HsvColorArea from './HsvColorArea.vue'
|
||||
|
|
@ -24,6 +24,7 @@ function tabClass(active: boolean) {
|
|||
const { fill } = defineProps<{ fill: Fill }>()
|
||||
const emit = defineEmits<{ update: [fill: Fill] }>()
|
||||
const cls = usePopoverUI({ content: 'w-60 p-2' })
|
||||
const { panels } = useI18n()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -35,7 +36,7 @@ const cls = usePopoverUI({ content: 'w-60 p-2' })
|
|||
>
|
||||
<template #default="{ fill: currentFill, category, toSolid, toGradient, toImage, update }">
|
||||
<div class="mb-2 flex items-center gap-0.5">
|
||||
<Tip label="Solid">
|
||||
<Tip :label="panels.solid">
|
||||
<button
|
||||
:class="tabClass(category === 'SOLID')"
|
||||
data-test-id="fill-picker-tab-solid"
|
||||
|
|
@ -44,7 +45,7 @@ const cls = usePopoverUI({ content: 'w-60 p-2' })
|
|||
<icon-lucide-square class="size-3.5" />
|
||||
</button>
|
||||
</Tip>
|
||||
<Tip label="Gradient">
|
||||
<Tip :label="panels.linearGradient">
|
||||
<button
|
||||
:class="tabClass(category === 'GRADIENT')"
|
||||
data-test-id="fill-picker-tab-gradient"
|
||||
|
|
@ -53,7 +54,7 @@ const cls = usePopoverUI({ content: 'w-60 p-2' })
|
|||
<icon-lucide-blend class="size-3.5" />
|
||||
</button>
|
||||
</Tip>
|
||||
<Tip label="Image">
|
||||
<Tip :label="panels.image">
|
||||
<button
|
||||
:class="tabClass(category === 'IMAGE')"
|
||||
data-test-id="fill-picker-tab-image"
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ import Tip from './ui/Tip.vue'
|
|||
import HsvColorArea from './HsvColorArea.vue'
|
||||
import ScrubInput from './ScrubInput.vue'
|
||||
import { colorToCSS } from '@open-pencil/core'
|
||||
import { GradientEditorRoot, GradientEditorBar, GradientEditorStop } from '@open-pencil/vue'
|
||||
import { GradientEditorRoot, GradientEditorBar, GradientEditorStop, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import type { Fill } from '@open-pencil/core'
|
||||
|
||||
const { fill } = defineProps<{ fill: Fill }>()
|
||||
const emit = defineEmits<{ update: [fill: Fill] }>()
|
||||
const { panels } = useI18n()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -52,8 +53,8 @@ const emit = defineEmits<{ update: [fill: Fill] }>()
|
|||
|
||||
<div class="mb-2">
|
||||
<div class="mb-1 flex items-center justify-between">
|
||||
<span class="text-[11px] text-muted">Stops</span>
|
||||
<Tip label="Add stop">
|
||||
<span class="text-[11px] text-muted">{{ panels.stops }}</span>
|
||||
<Tip :label="panels.addStop">
|
||||
<button
|
||||
class="flex size-4 cursor-pointer items-center justify-center rounded border-none bg-transparent p-0 text-muted hover:text-surface"
|
||||
data-test-id="fill-picker-add-stop"
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ function save() {
|
|||
v-model="baseURLInput"
|
||||
type="text"
|
||||
data-test-id="provider-base-url"
|
||||
placeholder="Base URL (e.g. http://localhost:11434/v1)"
|
||||
:placeholder="dialogs.baseURLPlaceholder"
|
||||
:class="uiInput()"
|
||||
/>
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ function save() {
|
|||
v-model="customModelInput"
|
||||
type="text"
|
||||
data-test-id="provider-custom-model"
|
||||
placeholder="Model ID (e.g. llama-3.3-70b)"
|
||||
:placeholder="dialogs.modelIDPlaceholder"
|
||||
:class="uiInput()"
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
import { AppearanceControlsRoot } from '@open-pencil/vue'
|
||||
import { AppearanceControlsRoot, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
import Tip from '@/components/ui/Tip.vue'
|
||||
import { sectionWrapper } from '@/components/ui/section'
|
||||
|
||||
const { panels } = useI18n()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -27,8 +29,8 @@ import { sectionWrapper } from '@/components/ui/section'
|
|||
>
|
||||
<div v-if="active" data-test-id="appearance-section" :class="sectionWrapper()">
|
||||
<div class="mb-1.5 flex items-center justify-between">
|
||||
<label class="text-[11px] text-muted">Appearance</label>
|
||||
<Tip label="Toggle visibility">
|
||||
<label class="text-[11px] text-muted">{{ panels.appearance }}</label>
|
||||
<Tip :label="panels.toggleVisibility">
|
||||
<button
|
||||
data-test-id="appearance-visibility"
|
||||
class="flex cursor-pointer items-center justify-center rounded border-none bg-transparent p-0.5 text-muted hover:bg-hover hover:text-surface"
|
||||
|
|
@ -67,7 +69,7 @@ import { sectionWrapper } from '@/components/ui/section'
|
|||
<icon-lucide-radius class="size-3" />
|
||||
</template>
|
||||
</ScrubInput>
|
||||
<Tip label="Independent corner radii">
|
||||
<Tip :label="panels.independentCornerRadii">
|
||||
<button
|
||||
data-test-id="independent-corners-toggle"
|
||||
class="flex size-[26px] shrink-0 cursor-pointer items-center justify-center rounded border border-border bg-input text-muted hover:bg-hover hover:text-surface"
|
||||
|
|
|
|||
|
|
@ -4,24 +4,25 @@ import ColorInput from '@/components/ColorInput.vue'
|
|||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
import { iconButton } from '@/components/ui/icon-button'
|
||||
import { sectionLabel, sectionWrapper } from '@/components/ui/section'
|
||||
import { PropertyListRoot, useEffectsControls } from '@open-pencil/vue'
|
||||
import { PropertyListRoot, useEffectsControls, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import { colorToCSS } from '@open-pencil/core'
|
||||
|
||||
import type { Effect } from '@open-pencil/core'
|
||||
|
||||
const effectsCtx = useEffectsControls()
|
||||
const { panels } = useI18n()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PropertyListRoot
|
||||
v-slot="{ items, isMixed, activeNode, patch, add, remove, toggleVisibility }"
|
||||
prop-key="effects"
|
||||
label="Effects"
|
||||
:label="panels.effects"
|
||||
>
|
||||
<div data-test-id="effects-section" :class="sectionWrapper()">
|
||||
<div class="flex items-center justify-between">
|
||||
<label :class="sectionLabel()">Effects</label>
|
||||
<label :class="sectionLabel()">{{ panels.effects }}</label>
|
||||
<button
|
||||
data-test-id="effects-section-add"
|
||||
:class="iconButton()"
|
||||
|
|
@ -31,7 +32,7 @@ const effectsCtx = useEffectsControls()
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<p v-if="isMixed" class="text-[11px] text-muted">Click + to replace mixed effects</p>
|
||||
<p v-if="isMixed" class="text-[11px] text-muted">{{ panels.mixedEffectsHelp }}</p>
|
||||
|
||||
<div
|
||||
v-for="(effect, i) in items as Effect[]"
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ import AppSelect from '@/components/ui/AppSelect.vue'
|
|||
import { iconButton } from '@/components/ui/icon-button'
|
||||
import { sectionLabel, sectionWrapper } from '@/components/ui/section'
|
||||
import { useEditorStore } from '@/stores/editor'
|
||||
import { useExport } from '@open-pencil/vue'
|
||||
import { useExport, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import type { ExportFormat } from '@open-pencil/core'
|
||||
|
||||
const editorStore = useEditorStore()
|
||||
const { panels } = useI18n()
|
||||
const { settings, nodeName, addSetting, removeSetting, updateScale, updateFormat } = useExport()
|
||||
|
||||
const SCALE_OPTIONS = [0.5, 0.75, 1, 1.5, 2, 3, 4].map((s) => ({ value: s, label: `${s}x` }))
|
||||
|
|
@ -72,7 +73,7 @@ onScopeDispose(() => {
|
|||
<template>
|
||||
<div data-test-id="export-section" :class="sectionWrapper()">
|
||||
<div class="flex items-center justify-between">
|
||||
<label :class="sectionLabel()">Export</label>
|
||||
<label :class="sectionLabel()">{{ panels.export }}</label>
|
||||
<button data-test-id="export-section-add" :class="iconButton()" @click="addSetting">+</button>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
} from 'reka-ui'
|
||||
|
||||
import { colorToCSS, colorToHexRaw } from '@open-pencil/core'
|
||||
import { PropertyListRoot, useFillControls } from '@open-pencil/vue'
|
||||
import { PropertyListRoot, useFillControls, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import FillPicker from '@/components/FillPicker.vue'
|
||||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
|
|
@ -23,17 +23,18 @@ import { sectionLabel, sectionWrapper } from '@/components/ui/section'
|
|||
import type { Fill, Variable } from '@open-pencil/core'
|
||||
|
||||
const fillCtx = useFillControls()
|
||||
const { panels, dialogs } = useI18n()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PropertyListRoot
|
||||
v-slot="{ items, isMixed, activeNode, add, remove, update, patch, toggleVisibility }"
|
||||
prop-key="fills"
|
||||
label="Fill"
|
||||
:label="panels.fill"
|
||||
>
|
||||
<div data-test-id="fill-section" :class="sectionWrapper()">
|
||||
<div class="flex items-center justify-between">
|
||||
<label :class="sectionLabel()">Fill</label>
|
||||
<label :class="sectionLabel()">{{ panels.fill }}</label>
|
||||
<button
|
||||
data-test-id="fill-section-add"
|
||||
:class="iconButton()"
|
||||
|
|
@ -42,7 +43,7 @@ const fillCtx = useFillControls()
|
|||
+
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="isMixed" class="text-[11px] text-muted">Click + to replace mixed fills</p>
|
||||
<p v-if="isMixed" class="text-[11px] text-muted">{{ panels.mixedFillsHelp }}</p>
|
||||
<div
|
||||
v-for="(fill, i) in items as Fill[]"
|
||||
:key="i"
|
||||
|
|
@ -58,7 +59,7 @@ const fillCtx = useFillControls()
|
|||
>
|
||||
{{ fillCtx.getBoundVariable(activeNode.id, i)!.name }}
|
||||
</span>
|
||||
<Tip label="Detach variable">
|
||||
<Tip :label="panels.detachVariable">
|
||||
<button
|
||||
data-test-id="fill-unbind-variable"
|
||||
class="cursor-pointer border-none bg-transparent p-0 text-violet-400 hover:text-surface"
|
||||
|
|
@ -95,7 +96,7 @@ const fillCtx = useFillControls()
|
|||
!fillCtx.getBoundVariable(activeNode.id, i)
|
||||
"
|
||||
>
|
||||
<Tip label="Apply variable">
|
||||
<Tip :label="panels.applyVariable">
|
||||
<PopoverTrigger
|
||||
class="cursor-pointer border-none bg-transparent p-0 text-muted hover:text-surface"
|
||||
>
|
||||
|
|
@ -115,13 +116,13 @@ const fillCtx = useFillControls()
|
|||
>
|
||||
<ComboboxInput
|
||||
:model-value="fillCtx.searchTerm.value"
|
||||
placeholder="Search variables…"
|
||||
:placeholder="dialogs.search"
|
||||
class="w-full border-b border-border bg-transparent px-2 py-1.5 text-[11px] text-surface outline-none placeholder:text-muted"
|
||||
@update:model-value="fillCtx.searchTerm.value = String($event)"
|
||||
/>
|
||||
<ComboboxContent class="max-h-48 overflow-y-auto p-1">
|
||||
<ComboboxEmpty class="px-2 py-3 text-center text-[11px] text-muted"
|
||||
>No variables found</ComboboxEmpty
|
||||
>{{ panels.noVariablesFound }}</ComboboxEmpty
|
||||
>
|
||||
<ComboboxItem
|
||||
v-for="v in fillCtx.filteredVariables.value"
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
<script setup lang="ts">
|
||||
import { LayoutControlsRoot } from '@open-pencil/vue'
|
||||
import { LayoutControlsRoot, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import AppSelect from '@/components/ui/AppSelect.vue'
|
||||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
import Tip from '@/components/ui/Tip.vue'
|
||||
import { sectionWrapper } from '@/components/ui/section'
|
||||
|
||||
const { panels } = useI18n()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LayoutControlsRoot v-slot="ctx">
|
||||
<template v-if="ctx.node">
|
||||
<div data-test-id="layout-section" :class="sectionWrapper()">
|
||||
<label class="mb-1.5 block text-[11px] text-muted">Layout</label>
|
||||
<label class="mb-1.5 block text-[11px] text-muted">{{ panels.layout }}</label>
|
||||
<div class="flex gap-1.5">
|
||||
<div class="flex min-w-0 flex-1 items-center gap-1">
|
||||
<ScrubInput
|
||||
|
|
@ -49,8 +51,8 @@ import { sectionWrapper } from '@/components/ui/section'
|
|||
<template v-if="ctx.node.type === 'FRAME'">
|
||||
<div :class="sectionWrapper()">
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="mb-1.5 block text-[11px] text-muted">Auto layout</label>
|
||||
<Tip v-if="ctx.node.layoutMode === 'NONE'" label="Add auto layout (Shift+A)">
|
||||
<label class="mb-1.5 block text-[11px] text-muted">{{ panels.autoLayout }}</label>
|
||||
<Tip v-if="ctx.node.layoutMode === 'NONE'" :label="panels.addAutoLayout">
|
||||
<button
|
||||
class="cursor-pointer rounded border-none bg-transparent px-1 text-base leading-none text-muted hover:bg-hover hover:text-surface"
|
||||
data-test-id="layout-add-auto"
|
||||
|
|
@ -59,7 +61,7 @@ import { sectionWrapper } from '@/components/ui/section'
|
|||
+
|
||||
</button>
|
||||
</Tip>
|
||||
<Tip v-else label="Remove auto layout">
|
||||
<Tip v-else :label="panels.removeAutoLayout">
|
||||
<button
|
||||
class="cursor-pointer rounded border-none bg-transparent px-1 text-base leading-none text-muted hover:bg-hover hover:text-surface"
|
||||
data-test-id="layout-remove-auto"
|
||||
|
|
@ -238,7 +240,7 @@ import { sectionWrapper } from '@/components/ui/section'
|
|||
</template>
|
||||
|
||||
<div v-if="ctx.isFlex" class="mt-2">
|
||||
<label class="mb-1 block text-[11px] text-muted">Alignment</label>
|
||||
<label class="mb-1 block text-[11px] text-muted">{{ panels.alignment }}</label>
|
||||
<div data-test-id="layout-alignment-grid" class="grid w-fit grid-cols-3 gap-0.5">
|
||||
<button
|
||||
v-for="cell in ctx.alignGrid"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ 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'
|
||||
import { PositionControlsRoot, useI18n } from '@open-pencil/vue'
|
||||
|
||||
const { panels } = useI18n()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -24,11 +26,11 @@ import { PositionControlsRoot } from '@open-pencil/vue'
|
|||
}"
|
||||
>
|
||||
<div v-if="active" data-test-id="position-section" :class="sectionWrapper()">
|
||||
<label class="mb-1.5 block text-[11px] text-muted">Position</label>
|
||||
<label class="mb-1.5 block text-[11px] text-muted">{{ panels.position }}</label>
|
||||
|
||||
<div class="mb-1.5 flex gap-2">
|
||||
<div class="flex gap-0.5">
|
||||
<Tip label="Align left">
|
||||
<Tip :label="panels.alignLeft">
|
||||
<button
|
||||
:class="iconButton({ size: 'md' })"
|
||||
data-test-id="position-align-left"
|
||||
|
|
@ -37,7 +39,7 @@ import { PositionControlsRoot } from '@open-pencil/vue'
|
|||
<icon-lucide-align-horizontal-justify-start class="size-3.5" />
|
||||
</button>
|
||||
</Tip>
|
||||
<Tip label="Align center horizontally">
|
||||
<Tip :label="panels.alignCenterHorizontally">
|
||||
<button
|
||||
:class="iconButton({ size: 'md' })"
|
||||
data-test-id="position-align-center-h"
|
||||
|
|
@ -46,7 +48,7 @@ import { PositionControlsRoot } from '@open-pencil/vue'
|
|||
<icon-lucide-align-horizontal-justify-center class="size-3.5" />
|
||||
</button>
|
||||
</Tip>
|
||||
<Tip label="Align right">
|
||||
<Tip :label="panels.alignRight">
|
||||
<button
|
||||
:class="iconButton({ size: 'md' })"
|
||||
data-test-id="position-align-right"
|
||||
|
|
@ -57,7 +59,7 @@ import { PositionControlsRoot } from '@open-pencil/vue'
|
|||
</Tip>
|
||||
</div>
|
||||
<div class="flex gap-0.5">
|
||||
<Tip label="Align top">
|
||||
<Tip :label="panels.alignTop">
|
||||
<button
|
||||
:class="iconButton({ size: 'md' })"
|
||||
data-test-id="position-align-top"
|
||||
|
|
@ -66,7 +68,7 @@ import { PositionControlsRoot } from '@open-pencil/vue'
|
|||
<icon-lucide-align-vertical-justify-start class="size-3.5" />
|
||||
</button>
|
||||
</Tip>
|
||||
<Tip label="Align center vertically">
|
||||
<Tip :label="panels.alignCenterVertically">
|
||||
<button
|
||||
:class="iconButton({ size: 'md' })"
|
||||
data-test-id="position-align-center-v"
|
||||
|
|
@ -75,7 +77,7 @@ import { PositionControlsRoot } from '@open-pencil/vue'
|
|||
<icon-lucide-align-vertical-justify-center class="size-3.5" />
|
||||
</button>
|
||||
</Tip>
|
||||
<Tip label="Align bottom">
|
||||
<Tip :label="panels.alignBottom">
|
||||
<button
|
||||
:class="iconButton({ size: 'md' })"
|
||||
data-test-id="position-align-bottom"
|
||||
|
|
@ -133,7 +135,7 @@ import { PositionControlsRoot } from '@open-pencil/vue'
|
|||
<icon-lucide-rotate-ccw class="size-3" />
|
||||
</template>
|
||||
</ScrubInput>
|
||||
<Tip label="Flip horizontal">
|
||||
<Tip :label="panels.flipHorizontal">
|
||||
<button
|
||||
:class="iconButton({ size: 'md', ui: { base: 'shrink-0' } })"
|
||||
data-test-id="position-flip-horizontal"
|
||||
|
|
@ -142,7 +144,7 @@ import { PositionControlsRoot } from '@open-pencil/vue'
|
|||
<icon-lucide-flip-horizontal class="size-3.5" />
|
||||
</button>
|
||||
</Tip>
|
||||
<Tip label="Flip vertical">
|
||||
<Tip :label="panels.flipVertical">
|
||||
<button
|
||||
:class="iconButton({ size: 'md', ui: { base: 'shrink-0' } })"
|
||||
data-test-id="position-flip-vertical"
|
||||
|
|
@ -151,7 +153,7 @@ import { PositionControlsRoot } from '@open-pencil/vue'
|
|||
<icon-lucide-flip-vertical class="size-3.5" />
|
||||
</button>
|
||||
</Tip>
|
||||
<Tip label="Rotate 90°">
|
||||
<Tip :label="panels.rotate90">
|
||||
<button
|
||||
:class="iconButton({ size: 'md', ui: { base: 'shrink-0' } })"
|
||||
data-test-id="position-rotate-90"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
DropdownMenuTrigger
|
||||
} from 'reka-ui'
|
||||
|
||||
import { PropertyListRoot, useStrokeControls } from '@open-pencil/vue'
|
||||
import { PropertyListRoot, useStrokeControls, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import AppSelect from '@/components/ui/AppSelect.vue'
|
||||
import ColorInput from '@/components/ColorInput.vue'
|
||||
|
|
@ -20,6 +20,7 @@ import { sectionLabel, sectionWrapper } from '@/components/ui/section'
|
|||
import type { SceneNode, Stroke } from '@open-pencil/core'
|
||||
|
||||
const strokeCtx = useStrokeControls()
|
||||
const { panels } = useI18n()
|
||||
const sideMenuCls = useMenuUI({
|
||||
content: 'min-w-[140px] rounded-md p-0.5',
|
||||
item: 'relative px-2'
|
||||
|
|
@ -30,11 +31,11 @@ const sideMenuCls = useMenuUI({
|
|||
<PropertyListRoot
|
||||
v-slot="{ items, isMixed, activeNode, add, remove, patch, toggleVisibility }"
|
||||
prop-key="strokes"
|
||||
label="Stroke"
|
||||
:label="panels.stroke"
|
||||
>
|
||||
<div data-test-id="stroke-section" :class="sectionWrapper()">
|
||||
<div class="flex items-center justify-between">
|
||||
<label :class="sectionLabel()">Stroke</label>
|
||||
<label :class="sectionLabel()">{{ panels.stroke }}</label>
|
||||
<button
|
||||
data-test-id="stroke-section-add"
|
||||
:class="iconButton()"
|
||||
|
|
@ -44,7 +45,7 @@ const sideMenuCls = useMenuUI({
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<p v-if="isMixed" class="text-[11px] text-muted">Click + to replace mixed strokes</p>
|
||||
<p v-if="isMixed" class="text-[11px] text-muted">{{ panels.mixedStrokesHelp }}</p>
|
||||
|
||||
<div
|
||||
v-for="(stroke, i) in items as Stroke[]"
|
||||
|
|
@ -101,7 +102,7 @@ const sideMenuCls = useMenuUI({
|
|||
</template>
|
||||
</ScrubInput>
|
||||
<DropdownMenuRoot v-model:open="strokeCtx.sideMenuOpen.value">
|
||||
<Tip label="Stroke sides">
|
||||
<Tip :label="panels.strokeSides">
|
||||
<DropdownMenuTrigger as-child>
|
||||
<button
|
||||
class="flex size-[26px] shrink-0 cursor-pointer items-center justify-center rounded border border-border bg-input text-muted hover:bg-hover hover:text-surface"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ToggleGroupItem, ToggleGroupRoot } from 'reka-ui'
|
||||
|
||||
import { TypographyControlsRoot } from '@open-pencil/vue'
|
||||
import { TypographyControlsRoot, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import FontPicker from '@/components/FontPicker.vue'
|
||||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
|
|
@ -9,12 +9,14 @@ import AppSelect from '@/components/ui/AppSelect.vue'
|
|||
import Tip from '@/components/ui/Tip.vue'
|
||||
import { sectionWrapper } from '@/components/ui/section'
|
||||
import { loadFont } from '@/engine/fonts'
|
||||
|
||||
const { panels } = useI18n()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TypographyControlsRoot v-slot="ctx" :load-font="loadFont">
|
||||
<div v-if="ctx.node.value" data-test-id="typography-section" :class="sectionWrapper()">
|
||||
<label class="mb-1.5 block text-[11px] text-muted">Typography</label>
|
||||
<label class="mb-1.5 block text-[11px] text-muted">{{ panels.typography }}</label>
|
||||
|
||||
<div class="mb-1.5 flex items-center gap-1.5">
|
||||
<FontPicker
|
||||
|
|
|
|||
Loading…
Reference in a new issue