Files
thetool/public/plugins/vue/tt-components/tt-icon-select.js
2025-10-08 14:55:14 +02:00

138 lines
5.5 KiB
JavaScript

Vue.component('tt-icon-select', {
props: {
options: {type: Array, required: true},
label: {type: String, default: null},
value: {type: [String, Array, Number, null], default: null},
multiple: {type: Boolean, default: false},
sm: {type: Boolean, default: false} // Added sm prop
},
data() {
return {
isOpen: false,
};
},
computed: {
internalValue: {
get() {
return this.multiple ? (Array.isArray(this.value) ? this.value : []) : this.value;
},
set(newValue) {
if (JSON.stringify(newValue) !== JSON.stringify(this.value)) {
this.$emit('input', newValue);
}
}
},
triggerDisplay() {
if (this.multiple) {
const count = this.internalValue.length;
if (count === 0) return { text: 'Auswählen...', isPlaceholder: true };
if (count === 1) {
const opt = this.options.find(o => o.value?.toString() === this.internalValue[0]?.toString());
return opt ? { icon: opt.icon, text: opt.text, isPlaceholder: false } : { text: '1 ausgewählt', isPlaceholder: false };
}
return { text: `${count} ausgewählt`, isPlaceholder: false };
} else {
const opt = this.options.find(o => o.value?.toString() === this.internalValue?.toString());
return opt ? { icon: opt.icon, text: opt.text, isPlaceholder: false } : { text: 'Auswählen...', isPlaceholder: true };
}
}
},
mounted() {
document.addEventListener('click', this.onClickOutside);
document.addEventListener('tt-select-open', this.handleGlobalOpen);
},
beforeDestroy() {
document.removeEventListener('click', this.onClickOutside);
document.removeEventListener('tt-select-open', this.handleGlobalOpen);
},
methods: {
toggleDropdown() {
if (!this.isOpen) {
// Dispatch event that this select is opening
document.dispatchEvent(new CustomEvent('tt-select-open', { detail: { id: this._uid } }));
}
this.isOpen = !this.isOpen;
},
onClickOutside(e) {
if (this.$el && !this.$el.contains(e.target)) {
this.isOpen = false;
}
},
handleGlobalOpen(event) {
if (event.detail.id !== this._uid) {
this.isOpen = false;
}
},
selectOption(option) {
const selectedVal = option ? option.value : null;
if (this.multiple) {
let currentValues = [...this.internalValue];
if (selectedVal === null) {
currentValues = [];
} else {
const index = currentValues.findIndex(v => v?.toString() === selectedVal?.toString());
if (index > -1) {
currentValues.splice(index, 1);
} else {
currentValues.push(selectedVal);
}
}
this.internalValue = currentValues;
} else {
this.internalValue = selectedVal;
this.isOpen = false;
}
},
isSelected(option) {
const checkVal = option ? option.value : null;
if (this.multiple) {
if (checkVal === null) return this.internalValue.length === 0;
return this.internalValue.some(v => v?.toString() === checkVal?.toString());
} else {
return this.internalValue?.toString() === checkVal?.toString();
}
}
},
template: `
<div class="tt-icon-select-modern">
<button type="button" class="tt-select-trigger"
:class="{ 'open': isOpen, 'sm': sm }"
@click.stop="toggleDropdown">
<span class="trigger-text" :class="{'placeholder': triggerDisplay.isPlaceholder}">
<i v-if="triggerDisplay.icon" :class="triggerDisplay.icon"></i>
<span>{{ triggerDisplay.text }}</span>
</span>
<i class="fas fa-chevron-down trigger-arrow"></i>
</button>
<div class="tt-select-dropdown" :class="{ show: isOpen }" @click.stop>
<div class="dropdown-header">
<span>{{ label || 'Auswählen' }}</span>
<button type="button" class="close-btn" @click="toggleDropdown">&times;</button>
</div>
<ul class="options-list">
<li class="option-item"
:class="{ 'selected': isSelected(null) }"
@click="selectOption(null)">
<div class="option-label">
<i class="fas fa-ban"></i> <span>{{ multiple ? 'Alle abwählen' : 'Alle' }}</span>
</div>
<i v-if="isSelected(null)" class="fas fa-check option-checkmark"></i>
</li>
<li v-for="opt in options" :key="opt.value"
class="option-item"
:class="{ 'selected': isSelected(opt) }"
@click="selectOption(opt)">
<div class="option-label">
<i :class="opt.icon"></i>
<span>{{ opt.text }}</span>
</div>
<i v-if="isSelected(opt)" class="fas fa-check option-checkmark"></i>
</li>
</ul>
</div>
</div>
`
});