knxdisplay/web-interface/src/components/SidebarRight.vue
2026-02-04 18:18:07 +01:00

155 lines
6.5 KiB
Vue

<template>
<aside class="h-full overflow-y-auto p-[18px] flex flex-col gap-4 border-l border-border max-[1100px]:border-l-0 max-[1100px]:border-t">
<section class="bg-gradient-to-b from-white to-[#f6f9fc] border border-border rounded-[14px] p-3.5 shadow-[0_10px_24px_rgba(15,23,42,0.12)]" id="properties">
<div v-if="!w" class="text-muted text-center py-5 text-[13px]">
Kein Widget ausgewaehlt.<br><br>
Waehle ein Widget im Canvas oder im Baum.
</div>
<div v-else>
<!-- Layout (common for all widgets) -->
<h4 :class="headingClass">Layout</h4>
<div :class="rowClass"><label :class="labelClass">X</label><input :class="inputClass" type="number" v-model.number="w.x"></div>
<div :class="rowClass"><label :class="labelClass">Y</label><input :class="inputClass" type="number" v-model.number="w.y"></div>
<div :class="rowClass"><label :class="labelClass">Breite</label><input :class="inputClass" type="number" v-model.number="w.w"></div>
<div :class="rowClass"><label :class="labelClass">Hoehe</label><input :class="inputClass" type="number" v-model.number="w.h"></div>
<div :class="rowClass"><label :class="labelClass">Sichtbar</label><input class="accent-[var(--accent)]" type="checkbox" v-model="w.visible"></div>
<!-- Widget-specific settings -->
<component
:is="settingsComponent"
:widget="w"
@open-icon-picker="openWidgetIconPicker"
@open-condition-icon-picker="openConditionIconPicker"
@open-subbutton-icon-picker="openSubButtonIconPicker"
@open-textline-icon-picker="openTextLineIconPicker"
/>
<!-- Delete button (common for all widgets) -->
<div class="mt-4">
<button class="border border-red-200 bg-[#f7dede] text-[#b3261e] px-3.5 py-2 rounded-[10px] text-[13px] font-semibold transition hover:-translate-y-0.5 hover:bg-[#f2cfcf] active:translate-y-0.5" @click="store.deleteWidget">Widget loeschen</button>
</div>
</div>
</section>
<!-- Icon Picker Modal -->
<IconPicker
v-if="showIconPicker"
v-model="activeIconCodepoint"
@close="handleIconPickerClose"
/>
</aside>
</template>
<script setup>
import { computed, ref, markRaw } from 'vue';
import { useEditorStore } from '../stores/editor';
import { typeKeyFor } from '../utils';
import { WIDGET_TYPES } from '../constants';
import { rowClass, labelClass, inputClass, headingClass } from './widgets/shared/styles';
import IconPicker from './IconPicker.vue';
// Import all widget settings components
import LabelSettings from './widgets/settings/LabelSettings.vue';
import ButtonSettings from './widgets/settings/ButtonSettings.vue';
import LedSettings from './widgets/settings/LedSettings.vue';
import IconSettings from './widgets/settings/IconSettings.vue';
import TabViewSettings from './widgets/settings/TabViewSettings.vue';
import TabPageSettings from './widgets/settings/TabPageSettings.vue';
import PowerFlowSettings from './widgets/settings/PowerFlowSettings.vue';
import PowerNodeSettings from './widgets/settings/PowerNodeSettings.vue';
import ChartSettings from './widgets/settings/ChartSettings.vue';
import ClockSettings from './widgets/settings/ClockSettings.vue';
import RoomCardSettings from './widgets/settings/RoomCardSettings.vue';
const store = useEditorStore();
const w = computed(() => store.selectedWidget);
const key = computed(() => w.value ? typeKeyFor(w.value.type) : 'label');
const showIconPicker = ref(false);
const conditionIconPickerIdx = ref(-1);
const subButtonIconPickerIdx = ref(-1);
const textLineIconPickerIdx = ref(-1);
// Map widget types to settings components
const componentMap = {
[WIDGET_TYPES.LABEL]: markRaw(LabelSettings),
[WIDGET_TYPES.BUTTON]: markRaw(ButtonSettings),
[WIDGET_TYPES.LED]: markRaw(LedSettings),
[WIDGET_TYPES.ICON]: markRaw(IconSettings),
[WIDGET_TYPES.TABVIEW]: markRaw(TabViewSettings),
[WIDGET_TYPES.TABPAGE]: markRaw(TabPageSettings),
[WIDGET_TYPES.POWERFLOW]: markRaw(PowerFlowSettings),
[WIDGET_TYPES.POWERNODE]: markRaw(PowerNodeSettings),
[WIDGET_TYPES.CHART]: markRaw(ChartSettings),
[WIDGET_TYPES.CLOCK]: markRaw(ClockSettings),
[WIDGET_TYPES.ROOMCARD]: markRaw(RoomCardSettings)
};
const settingsComponent = computed(() => {
if (!w.value) return null;
return componentMap[w.value.type] || LabelSettings;
});
function openWidgetIconPicker() {
conditionIconPickerIdx.value = -1;
subButtonIconPickerIdx.value = -1;
textLineIconPickerIdx.value = -1;
showIconPicker.value = true;
}
function openConditionIconPicker(idx) {
conditionIconPickerIdx.value = idx;
subButtonIconPickerIdx.value = -1;
textLineIconPickerIdx.value = -1;
showIconPicker.value = true;
}
function openSubButtonIconPicker(idx) {
conditionIconPickerIdx.value = -1;
subButtonIconPickerIdx.value = idx;
textLineIconPickerIdx.value = -1;
showIconPicker.value = true;
}
function openTextLineIconPicker(idx) {
conditionIconPickerIdx.value = -1;
subButtonIconPickerIdx.value = -1;
textLineIconPickerIdx.value = idx;
showIconPicker.value = true;
}
// Dynamic icon binding for IconPicker (widget icon, condition icon, subbutton icon, or textline icon)
const activeIconCodepoint = computed({
get() {
if (conditionIconPickerIdx.value >= 0 && w.value?.conditions?.[conditionIconPickerIdx.value]) {
return w.value.conditions[conditionIconPickerIdx.value].icon || 0;
}
if (subButtonIconPickerIdx.value >= 0 && w.value?.subButtons?.[subButtonIconPickerIdx.value]) {
return w.value.subButtons[subButtonIconPickerIdx.value].icon || 0;
}
if (textLineIconPickerIdx.value >= 0 && w.value?.textLines?.[textLineIconPickerIdx.value]) {
return w.value.textLines[textLineIconPickerIdx.value].icon || 0;
}
return w.value?.iconCodepoint || 0;
},
set(value) {
if (conditionIconPickerIdx.value >= 0 && w.value?.conditions?.[conditionIconPickerIdx.value]) {
w.value.conditions[conditionIconPickerIdx.value].icon = value;
} else if (subButtonIconPickerIdx.value >= 0 && w.value?.subButtons?.[subButtonIconPickerIdx.value]) {
w.value.subButtons[subButtonIconPickerIdx.value].icon = value;
} else if (textLineIconPickerIdx.value >= 0 && w.value?.textLines?.[textLineIconPickerIdx.value]) {
w.value.textLines[textLineIconPickerIdx.value].icon = value;
} else if (w.value) {
w.value.iconCodepoint = value;
}
}
});
function handleIconPickerClose() {
showIconPicker.value = false;
conditionIconPickerIdx.value = -1;
subButtonIconPickerIdx.value = -1;
textLineIconPickerIdx.value = -1;
}
</script>