mirror of
https://github.com/tiennm99/coolify.git
synced 2026-08-20 20:23:23 +00:00
Use the current request scheme/host when no FQDN or public IP is set, skipping loopback hosts. Polish boarding/server create cards, optional listbox tooltips, and cover the layout and Sentinel URL behavior in tests.
84 lines
4.0 KiB
PHP
84 lines
4.0 KiB
PHP
@props([
|
|
'id' => null,
|
|
'label' => null,
|
|
'helper' => null,
|
|
'required' => false,
|
|
'options' => [], // list of ['value' => ..., 'label' => ..., 'disabled' => bool]
|
|
'placeholder' => 'Select…',
|
|
'emptyText' => 'No options available.',
|
|
'live' => false,
|
|
'onChange' => null, // optional $wire method to call after a selection
|
|
'wire' => true, // false = purely client-side value (no Livewire binding)
|
|
'value' => null, // initial value when wire=false
|
|
'disabled' => false,
|
|
'tooltip' => true,
|
|
])
|
|
|
|
<div class="w-full min-w-0">
|
|
@if ($label)
|
|
{{--
|
|
Keep helper outside the label so taps do not open the listbox trigger.
|
|
Margin lives only on this wrapper (mb-0 on the label) so spacing matches
|
|
plain text labels and is not doubled by .application-settings-form label rules.
|
|
--}}
|
|
{{-- Fixed h-4 matches the helper icon so side-by-side fields align with or without a helper. --}}
|
|
<div class="mb-1.5 flex h-4 w-full items-center gap-1.5">
|
|
<label for="{{ $id }}-trigger" class="mb-0! flex items-center gap-1.5 leading-4">
|
|
{{ $label }}
|
|
@if ($required)
|
|
<x-highlighted text="*" />
|
|
@endif
|
|
</label>
|
|
@if ($helper)
|
|
<x-helper :helper="$helper" />
|
|
@endif
|
|
</div>
|
|
@endif
|
|
<div class="relative min-w-0" x-data="{
|
|
open: false,
|
|
options: @js(array_values($options)),
|
|
value: @if (!$wire) @js($value) @elseif ($live) @entangle($id).live @else @entangle($id) @endif,
|
|
get current() {
|
|
const found = this.options.find((option) => String(option.value) === String(this.value));
|
|
return found ? found.label : @js($placeholder);
|
|
},
|
|
choose(option) {
|
|
if (option.disabled) return;
|
|
this.open = false;
|
|
if (String(option.value) === String(this.value)) return;
|
|
this.value = option.value;
|
|
@if ($onChange) this.$nextTick(() => this.$wire.{{ $onChange }}()); @endif
|
|
}
|
|
}" x-modelable="value" {{ $attributes->whereStartsWith('x-model') }}
|
|
{{ $attributes->whereStartsWith('x-effect') }}
|
|
@click.outside="open = false" @keydown.escape="open = false">
|
|
<button id="{{ $id }}-trigger" type="button" class="listbox-trigger" @click="open = !open"
|
|
@disabled($disabled) {{ $attributes->whereStartsWith('x-bind:disabled') }} aria-haspopup="listbox"
|
|
:aria-expanded="open" @if ($tooltip) :title="current" @endif>
|
|
<span class="listbox-trigger-label" x-text="current"></span>
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
|
stroke="currentColor" class="size-3.5 shrink-0 opacity-60">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="m8 9 4-4 4 4m0 6-4 4-4-4" />
|
|
</svg>
|
|
</button>
|
|
<div class="listbox-panel" x-show="open" x-cloak role="listbox">
|
|
<div x-show="options.length === 0"
|
|
class="px-3 py-2 text-[13px] text-neutral-500 dark:text-fg-dim">
|
|
{{ $emptyText }}
|
|
</div>
|
|
<template x-for="option in options" :key="String(option.value)">
|
|
<button type="button" class="listbox-option" role="option"
|
|
:class="{ 'listbox-option-disabled': option.disabled }"
|
|
:aria-selected="String(option.value) === String(value)" @click="choose(option)">
|
|
<span class="truncate" x-text="option.label"></span>
|
|
<svg x-show="String(option.value) === String(value)" xmlns="http://www.w3.org/2000/svg"
|
|
fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor"
|
|
class="size-3.5 shrink-0">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
|
|
</svg>
|
|
</button>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|