@props([ 'name', 'id', 'options' => [], 'value' => null, 'required' => false, 'error' => false, 'emptyOption' => true, 'emptyLabel' => '', 'placeholder' => 'ابحث بالاسم أو الكود...', 'inModal' => false, /** لو true: قائمة منسدلة position:fixed لتفادي القص داخل overflow أو نافذة منبثقة */ 'fixedPanel' => false, /** عند true: لا يُصدَر input مخفي؛ ضع حقلاً مخفياً في الأب مع x-model (نماذج Alpine متداخلة) */ 'omitHidden' => false, /** عند false: إخفاء حقل البحث داخل اللوحة (قوائم قصيرة مثل الحالة) */ 'searchable' => true, ]) @php $selectedRaw = old($name, $value); $selectedStr = $selectedRaw === null || $selectedRaw === '' ? '' : (string) $selectedRaw; $id = filled($id ?? null) ? (string) $id : 'erp-ss-'.preg_replace('/[^a-zA-Z0-9_-]+/', '-', (string) $name).'-'.substr(uniqid('', true), -8); $wireAttrs = $attributes->filter(fn ($v, $k) => is_string($k) && str_starts_with($k, 'wire:model')); $outerAttrs = $attributes->except(array_keys($wireAttrs->getAttributes())); if ($omitHidden) { $outerAttrs = $outerAttrs->merge($wireAttrs->getAttributes()); } $normalized = []; foreach ($options as $row) { if (is_array($row)) { $normalized[] = [ 'v' => (string) ($row['value'] ?? ''), 'l' => (string) ($row['label'] ?? ''), ]; } elseif (is_object($row)) { $normalized[] = [ 'v' => (string) ($row->value ?? ''), 'l' => (string) ($row->label ?? ''), ]; } } if ($emptyOption) { array_unshift($normalized, [ 'v' => '', 'l' => $emptyLabel !== '' ? $emptyLabel : '—', ]); } $panelZ = $inModal ? 'z-[1060]' : 'z-40'; $useFixed = $fixedPanel || $inModal; $panelPositionClass = $useFixed ? 'fixed' : 'absolute mt-1'; $borderClass = $error ? 'border-red-500 ring-1 ring-red-200' : 'border-gray-200'; @endphp
class(['erp-searchable-select', 'relative', 'w-full']) }} x-data="{ open: false, q: '', items: @js($normalized), selected: @js($selectedStr), placeholder: @js($placeholder), useFixed: @js($useFixed), panelTop: 0, panelLeft: 0, panelWidth: 0, positionPanel() { if (!this.useFixed) return; const btn = this.$refs.triggerBtn; const panel = this.$refs.dropdownPanel; if (!btn || !panel) return; const r = btn.getBoundingClientRect(); const width = Math.max(r.width, 160); const maxLeft = Math.max(8, window.innerWidth - width - 8); this.panelWidth = width; this.panelTop = r.bottom + 4; this.panelLeft = Math.min(Math.max(8, r.left), maxLeft); }, toggle() { this.open = !this.open; if (this.open) { this.$nextTick(() => { this.positionPanel(); if (this.$refs.q) { this.$refs.q.focus(); } }); } }, close() { this.open = false; this.q = ''; }, pick(v) { this.selected = v; this.close(); const detail = { name: @js($name), value: v }; this.$dispatch('searchable-select-change', detail); this.$dispatch('custom-select-change', detail); this.$nextTick(() => { const h = this.$refs.hiddenInput; if (h) { h.dispatchEvent(new Event('input', { bubbles: true })); h.dispatchEvent(new Event('change', { bubbles: true })); } }); }, clearSel() { this.selected = ''; this.q = ''; const detail = { name: @js($name), value: '' }; this.$dispatch('searchable-select-change', detail); this.$dispatch('custom-select-change', detail); this.$nextTick(() => { const h = this.$refs.hiddenInput; if (h) { h.dispatchEvent(new Event('input', { bubbles: true })); h.dispatchEvent(new Event('change', { bubbles: true })); } }); }, display() { const it = this.items.find(i => String(i.v) === String(this.selected)); return it ? it.l : @js($emptyLabel !== '' ? $emptyLabel : '—'); }, get filtered() { const qq = (this.q || '').trim().toLowerCase(); if (!qq) { return this.items; } return this.items.filter(i => { if (i.v === '') { return true; } const lab = (i.l || '').toLowerCase(); const val = (i.v || '').toLowerCase(); return lab.includes(qq) || val.includes(qq); }); }, }" @if($useFixed) @resize.window="if (open) positionPanel()" @scroll.window.passive="if (open) positionPanel()" @endif @if(! $omitHidden) x-modelable="selected" @endif @erp-sync-searchable.window="if ($event.detail && String($event.detail.id) === @js($id)) { selected = $event.detail.value != null && $event.detail.value !== '' ? String($event.detail.value) : ''; }" @click.outside="close()" > @unless($omitHidden) @endunless
@if($searchable)
@endif
@if($emptyOption)
@endif