154 lines
4.2 KiB
Vue
154 lines
4.2 KiB
Vue
|
<template>
|
||
|
<PlannerSidebar title="Properties">
|
||
|
<div
|
||
|
class="bg-white-50 flex flex-col"
|
||
|
v-if="selectedObject && applicableProperties"
|
||
|
>
|
||
|
<div
|
||
|
class="grid grid-cols-2 px-2 py-2"
|
||
|
v-for="prop of applicableProperties.properties"
|
||
|
>
|
||
|
<label :for="`${prop.key}-${selectedObject.id}`">{{
|
||
|
prop.title
|
||
|
}}</label>
|
||
|
<input
|
||
|
v-if="prop.type === 'string'"
|
||
|
:id="`${prop.key}-${selectedObject.id}`"
|
||
|
type="text"
|
||
|
:value="selectedObject[prop.key as keyof typeof selectedObject]"
|
||
|
@input="updateProp(prop, ($event.target as HTMLInputElement)?.value)"
|
||
|
/>
|
||
|
<input
|
||
|
v-else-if="prop.type === 'color'"
|
||
|
:id="`${prop.key}-${selectedObject.id}`"
|
||
|
type="color"
|
||
|
:value="selectedObject[prop.key as keyof typeof selectedObject]"
|
||
|
@change="updateProp(prop, ($event.target as HTMLInputElement)?.value)"
|
||
|
/>
|
||
|
<input
|
||
|
v-else-if="prop.type === 'number'"
|
||
|
:id="`${prop.key}-${selectedObject.id}`"
|
||
|
type="number"
|
||
|
:value="selectedObject[prop.key as keyof typeof selectedObject]"
|
||
|
@input="updateProp(prop, ($event.target as HTMLInputElement)?.value)"
|
||
|
/>
|
||
|
<input
|
||
|
v-else-if="prop.type === 'boolean'"
|
||
|
:id="`${prop.key}-${selectedObject.id}`"
|
||
|
type="checkbox"
|
||
|
:checked="(selectedObject[prop.key as keyof typeof selectedObject] as boolean)"
|
||
|
@input="
|
||
|
updateProp(prop, ($event.target as HTMLInputElement)?.checked)
|
||
|
"
|
||
|
/>
|
||
|
<select
|
||
|
v-else-if="prop.type === 'select'"
|
||
|
:id="`${prop.key}-${selectedObject.id}`"
|
||
|
:value="selectedObject[prop.key as keyof typeof selectedObject]"
|
||
|
@input="updateProp(prop, ($event.target as HTMLInputElement)?.value)"
|
||
|
>
|
||
|
<option v-for="option of prop.options" :value="option.value">
|
||
|
{{ option.title }}
|
||
|
</option>
|
||
|
</select>
|
||
|
</div>
|
||
|
</div>
|
||
|
</PlannerSidebar>
|
||
|
</template>
|
||
|
|
||
|
<script setup lang="ts">
|
||
|
import PlannerSidebar from './PlannerSidebar.vue';
|
||
|
import { Layer } from '../../modules/house-planner/interfaces';
|
||
|
import {
|
||
|
ObjectProperties,
|
||
|
ObjectProperty,
|
||
|
} from './interfaces/properties.interfaces';
|
||
|
import { computed } from 'vue';
|
||
|
|
||
|
const props = defineProps<{
|
||
|
layers: Layer[];
|
||
|
}>();
|
||
|
|
||
|
const emit = defineEmits<{
|
||
|
(
|
||
|
e: 'update',
|
||
|
layerId: number,
|
||
|
objectId: number,
|
||
|
key: string,
|
||
|
value: unknown
|
||
|
): void;
|
||
|
}>();
|
||
|
|
||
|
const commonProps: ObjectProperty[] = [
|
||
|
{ key: 'name', title: 'Name', type: 'string' },
|
||
|
{ key: 'visible', title: 'Visible', type: 'boolean', groupable: true },
|
||
|
];
|
||
|
|
||
|
const lineProps: ObjectProperty[] = [
|
||
|
...commonProps,
|
||
|
{ key: 'width', title: 'Line Width', type: 'number', groupable: true },
|
||
|
{ key: 'color', title: 'Color', type: 'color', groupable: true },
|
||
|
{
|
||
|
key: 'lineCap',
|
||
|
title: 'Line Cap',
|
||
|
type: 'select',
|
||
|
groupable: true,
|
||
|
options: [
|
||
|
{ value: undefined, title: '' },
|
||
|
{ value: 'butt', title: 'Butt' },
|
||
|
{ value: 'round', title: 'Round' },
|
||
|
{ value: 'square', title: 'Square' },
|
||
|
],
|
||
|
},
|
||
|
{ key: 'closed', title: 'Closed', type: 'boolean', groupable: true },
|
||
|
];
|
||
|
|
||
|
const objectTypeProperties: ObjectProperties[] = [
|
||
|
{
|
||
|
type: 'line',
|
||
|
properties: lineProps,
|
||
|
},
|
||
|
{
|
||
|
type: 'curve',
|
||
|
properties: lineProps,
|
||
|
},
|
||
|
{
|
||
|
type: 'room',
|
||
|
properties: lineProps,
|
||
|
},
|
||
|
{
|
||
|
type: 'object',
|
||
|
properties: commonProps,
|
||
|
},
|
||
|
];
|
||
|
|
||
|
const currentLayer = computed(() =>
|
||
|
props.layers.find((layer) => layer.active && layer.visible)
|
||
|
);
|
||
|
|
||
|
// TODO multi edit
|
||
|
const selectedObject = computed(
|
||
|
() => currentLayer.value?.contents?.filter((obj) => obj.selected)[0]
|
||
|
);
|
||
|
|
||
|
const applicableProperties = computed(
|
||
|
() =>
|
||
|
selectedObject.value &&
|
||
|
objectTypeProperties.find(
|
||
|
(prop) => prop.type === selectedObject.value?.type
|
||
|
)
|
||
|
);
|
||
|
|
||
|
const updateProp = (prop: ObjectProperty, value: unknown) => {
|
||
|
if (!currentLayer.value || !selectedObject.value) return;
|
||
|
if (prop.type === 'number') value = parseFloat(value as string);
|
||
|
emit(
|
||
|
'update',
|
||
|
currentLayer.value!.id,
|
||
|
selectedObject.value!.id,
|
||
|
prop.key,
|
||
|
value
|
||
|
);
|
||
|
};
|
||
|
</script>
|