@props([
'name',
'id' => null,
'selected' => '',
'options' => [],
'required' => false,
'disabled' => false,
'compact' => false,
])
@php
$id = $id ?? $name;
$normalized = [];
foreach ($options as $row) {
if (! is_array($row)) {
continue;
}
$normalized[] = [
'value' => (string) ($row['value'] ?? ''),
'label' => (string) ($row['label'] ?? ''),
'disabled' => (bool) ($row['disabled'] ?? false),
];
}
$selectedStr = (string) $selected;
$isCompact = (bool) $compact;
@endphp
merge([
'class' => $isCompact
? 'relative w-max max-w-full overflow-visible'
: 'relative min-w-0 overflow-visible',
]) }}
x-data="{
open: false,
selected: @js($selectedStr),
options: @js($normalized),
disabled: @js((bool) $disabled),
init() {
this.$refs.hidden.addEventListener('styled-select:set', (event) => {
this.setValue(event.detail ?? '');
});
this.$refs.hidden.addEventListener('styled-select:set-options', (event) => {
this.setOptions(event.detail ?? []);
});
this.$refs.hidden.addEventListener('styled-select:set-disabled', (event) => {
this.setDisabled(event.detail ?? false);
});
},
normalizeOptions(rows) {
if (!Array.isArray(rows)) {
return [];
}
return rows.map((row) => ({
value: String(row?.value ?? ''),
label: String(row?.label ?? ''),
disabled: Boolean(row?.disabled ?? false),
}));
},
setOptions(rows) {
this.close();
this.options = this.normalizeOptions(rows);
const stillValid = this.options.some((o) => String(o.value) === String(this.selected));
if (!stillValid) {
this.selected = '';
this.$nextTick(() => this.emitChange());
}
},
setDisabled(value) {
this.disabled = Boolean(value);
this.close();
this.$refs.hidden.disabled = this.disabled;
},
toggle() {
if (this.disabled) return;
this.open = !this.open;
},
close() {
this.open = false;
},
emitChange() {
this.$refs.hidden.dispatchEvent(new CustomEvent('change', {
bubbles: true,
detail: { value: String(this.selected) },
}));
},
choose(opt) {
if (opt.disabled) return;
this.selected = String(opt.value);
this.open = false;
this.$nextTick(() => this.emitChange());
},
setValue(value) {
const next = String(value ?? '');
const opt = this.options.find((o) => String(o.value) === next);
if (opt) {
this.choose(opt);
return;
}
this.selected = next;
this.close();
this.$nextTick(() => this.emitChange());
},
currentLabel() {
const v = String(this.selected);
const hit = this.options.find((o) => String(o.value) === v);
return hit ? hit.label : '\u2014';
},
isSelected(opt) {
return String(this.selected) === String(opt.value);
},
}"
@keydown.escape.window="close()"
@click.outside="close()"
>
$isCompact,
'w-full' => ! $isCompact,
])
style="display: none;"
>
$isCompact,
'max-h-60 w-full overflow-auto py-1' => ! $isCompact,
])
role="listbox"
:id="'{{ $id }}_listbox'"
>