mirror of
				https://github.com/pelican-dev/panel.git
				synced 2025-10-31 20:26:53 +01:00 
			
		
		
		
	Add custom component
This commit is contained in:
		
							parent
							
								
									a9c7eeddde
								
							
						
					
					
						commit
						0f798e5edb
					
				
							
								
								
									
										10
									
								
								app/Filament/Forms/SelectEndpoint.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/Filament/Forms/SelectEndpoint.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Filament\Forms; | ||||
| 
 | ||||
| use Filament\Forms\Components\Select; | ||||
| 
 | ||||
| class SelectEndpoint extends Select | ||||
| { | ||||
|     protected string $view = 'filament.components.select'; | ||||
| } | ||||
| @ -2,6 +2,7 @@ | ||||
| 
 | ||||
| namespace App\Filament\Resources\ServerResource\Pages; | ||||
| 
 | ||||
| use App\Filament\Forms\SelectEndpoint; | ||||
| use App\Filament\Resources\ServerResource; | ||||
| use App\Models\Egg; | ||||
| use App\Models\Node; | ||||
| @ -320,38 +321,19 @@ class CreateServer extends CreateRecord | ||||
|                         ->columns(4) | ||||
|                         ->schema([ | ||||
|                             Forms\Components\TagsInput::make('ports') | ||||
|                                 ->columnSpanFull() | ||||
|                                 ->columnSpan(2) | ||||
|                                 ->hintIcon('tabler-question-mark') | ||||
|                                 ->hintIconTooltip('Ports are limited from 1025 to 65535') | ||||
|                                 ->placeholder('Example: 25565, 8080, 1337-1340') | ||||
|                                 ->splitKeys(['Tab', ' ', ',']) | ||||
|                                 ->helperText(new HtmlString(' | ||||
|                                 These are the ports that users can connect to this Server through. | ||||
|                                 You would typically port forward these on your home network. | ||||
|                             ')) | ||||
|                                     These are the ports that users can connect to this Server through. | ||||
|                                     You would typically port forward these on your home network. | ||||
|                                 ')) | ||||
|                                 ->label('Ports') | ||||
|                                 ->afterStateUpdated(self::ports(...)) | ||||
|                                 ->live(), | ||||
| 
 | ||||
|                             Forms\Components\Repeater::make('ip') | ||||
|                                 ->columnSpan(2) | ||||
|                                 ->defaultItems(5) | ||||
|                                 ->label('IP Assignments') | ||||
|                                 ->live() | ||||
|                                 ->addable(false) | ||||
|                                 ->deletable(false) | ||||
|                                 ->reorderable(false) | ||||
|                                 ->hintIcon('tabler-question-mark') | ||||
|                                 ->hintIconTooltip('These are the IPs available on the selected Node.') | ||||
|                                 ->simple( | ||||
|                                     Forms\Components\Select::make('port') | ||||
|                                         ->live() | ||||
|                                         ->placeholder('Select an IP') | ||||
| //                                        ->afterStateUpdated()
 | ||||
|                                         ->options(fn () => $this->node?->ipAddresses()) | ||||
|                                         ->required(), | ||||
|                                 ), | ||||
| 
 | ||||
|                             Forms\Components\Repeater::make('assignments') | ||||
|                                 ->columnSpan(2) | ||||
|                                 ->defaultItems(fn () => count($this->eggDefaultPorts)) | ||||
| @ -372,11 +354,13 @@ class CreateServer extends CreateRecord | ||||
|                                 ->deletable(false) | ||||
|                                 ->reorderable(false) | ||||
|                                 ->simple( | ||||
|                                     Forms\Components\Select::make('port') | ||||
|                                     SelectEndpoint::make('port') | ||||
|                                         ->columnSpanFull() | ||||
|                                         ->label('') | ||||
|                                         ->live() | ||||
|                                         ->placeholder('Select a Port') | ||||
|                                         ->disabled(fn (Forms\Get $get) => empty($get('../../ports')) || empty($get('../../assignments'))) | ||||
|                                         ->prefix(function (Forms\Components\Component $component) { | ||||
|                                         ->suffix(function (Forms\Components\Component $component) { | ||||
|                                             $key = str($component->getStatePath())->beforeLast('.')->afterLast('.')->toString(); | ||||
| 
 | ||||
|                                             return $key; | ||||
|  | ||||
							
								
								
									
										223
									
								
								resources/views/components/filament/wrapper.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								resources/views/components/filament/wrapper.blade.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,223 @@ | ||||
| @props([ | ||||
|     'alpineDisabled' => null, | ||||
|     'alpineValid' => null, | ||||
|     'disabled' => false, | ||||
|     'inlinePrefix' => false, | ||||
|     'inlineSuffix' => false, | ||||
|     'prefix' => null, | ||||
|     'prefixActions' => [], | ||||
|     'prefixIcon' => null, | ||||
|     'prefixIconColor' => 'gray', | ||||
|     'prefixIconAlias' => null, | ||||
|     'suffix' => null, | ||||
|     'suffixActions' => [], | ||||
|     'suffixIcon' => null, | ||||
|     'suffixIconColor' => 'gray', | ||||
|     'suffixIconAlias' => null, | ||||
|     'valid' => true, | ||||
| ]) | ||||
| 
 | ||||
| @php | ||||
|     $prefixActions = array_filter( | ||||
|         $prefixActions, | ||||
|         fn (\Filament\Forms\Components\Actions\Action $prefixAction): bool => $prefixAction->isVisible(), | ||||
|     ); | ||||
| 
 | ||||
|     $suffixActions = array_filter( | ||||
|         $suffixActions, | ||||
|         fn (\Filament\Forms\Components\Actions\Action $suffixAction): bool => $suffixAction->isVisible(), | ||||
|     ); | ||||
| 
 | ||||
|     $hasPrefix = count($prefixActions) || $prefixIcon || filled($prefix); | ||||
|     $hasSuffix = count($suffixActions) || $suffixIcon || filled($suffix); | ||||
| 
 | ||||
|     $hasAlpineDisabledClasses = filled($alpineDisabled); | ||||
|     $hasAlpineValidClasses = filled($alpineValid); | ||||
|     $hasAlpineClasses = $hasAlpineDisabledClasses || $hasAlpineValidClasses; | ||||
| 
 | ||||
|     $enabledWrapperClasses = 'bg-white dark:bg-white/5 [&:not(:has(.fi-ac-action:focus))]:focus-within:ring-2'; | ||||
|     $disabledWrapperClasses = 'fi-disabled bg-gray-50 dark:bg-transparent'; | ||||
|     $validWrapperClasses = 'ring-gray-950/10'; | ||||
|     $invalidWrapperClasses = 'fi-invalid ring-danger-600 dark:ring-danger-500'; | ||||
|     $enabledValidWrapperClasses = 'dark:ring-white/20 [&:not(:has(.fi-ac-action:focus))]:focus-within:ring-primary-600 dark:[&:not(:has(.fi-ac-action:focus))]:focus-within:ring-primary-500'; | ||||
|     $enabledInvalidWrapperClasses = '[&:not(:has(.fi-ac-action:focus))]:focus-within:ring-danger-600 dark:[&:not(:has(.fi-ac-action:focus))]:focus-within:ring-danger-500'; | ||||
|     $disabledValidWrapperClasses = 'dark:ring-white/10'; | ||||
| 
 | ||||
|     $actionsClasses = 'flex items-center gap-3'; | ||||
|     $labelClasses = 'fi-input-wrp-label whitespace-nowrap text-sm text-gray-500 dark:text-gray-400'; | ||||
| 
 | ||||
|     $getIconClasses = fn (string | array $color = 'gray'): string => \Illuminate\Support\Arr::toCssClasses([ | ||||
|         'fi-input-wrp-icon h-5 w-5', | ||||
|         match ($color) { | ||||
|             'gray' => 'text-gray-400 dark:text-gray-500', | ||||
|             default => 'text-custom-500', | ||||
|         }, | ||||
|     ]); | ||||
| 
 | ||||
|     $getIconStyles = fn (string | array $color = 'gray'): string => \Illuminate\Support\Arr::toCssStyles([ | ||||
|         \Filament\Support\get_color_css_variables( | ||||
|             $color, | ||||
|             shades: [500], | ||||
|             alias: 'input-wrapper.icon', | ||||
|         ) => $color !== 'gray', | ||||
|     ]); | ||||
| 
 | ||||
|     $wireTarget = $attributes->whereStartsWith(['wire:target'])->first(); | ||||
| 
 | ||||
|     $hasLoadingIndicator = filled($wireTarget); | ||||
| 
 | ||||
|     if ($hasLoadingIndicator) { | ||||
|         $loadingIndicatorTarget = html_entity_decode($wireTarget, ENT_QUOTES); | ||||
|     } | ||||
| @endphp | ||||
| 
 | ||||
| <div | ||||
|     @if ($hasAlpineClasses) | ||||
|         x-bind:class="{ | ||||
|             {{ $hasAlpineDisabledClasses ? "'{$enabledWrapperClasses}': ! ({$alpineDisabled})," : null }} | ||||
|             {{ $hasAlpineDisabledClasses ? "'{$disabledWrapperClasses}': {$alpineDisabled}," : null }} | ||||
|             {{ $hasAlpineValidClasses ? "'{$validWrapperClasses}': {$alpineValid}," : null }} | ||||
|             {{ $hasAlpineValidClasses ? "'{$invalidWrapperClasses}': ! ({$alpineValid})," : null }} | ||||
|             {{ ($hasAlpineDisabledClasses && $hasAlpineValidClasses) ? "'{$enabledValidWrapperClasses}': ! ({$alpineDisabled}) && {$alpineValid}," : null }} | ||||
|             {{ ($hasAlpineDisabledClasses && $hasAlpineValidClasses) ? "'{$enabledInvalidWrapperClasses}': ! ({$alpineDisabled}) && ! ({$alpineValid})," : null }} | ||||
|             {{ ($hasAlpineDisabledClasses && $hasAlpineValidClasses) ? "'{$disabledValidWrapperClasses}': {$alpineDisabled} && ! ({$alpineValid})," : null }} | ||||
|         }" | ||||
|     @endif | ||||
|     {{ | ||||
|         $attributes | ||||
|             ->except(['wire:target']) | ||||
|             ->class([ | ||||
|                 'fi-input-wrp flex rounded-lg shadow-sm ring-1 transition duration-75', | ||||
|                 $enabledWrapperClasses => (! $hasAlpineClasses) && (! $disabled), | ||||
|                 $disabledWrapperClasses => (! $hasAlpineClasses) && $disabled, | ||||
|                 $validWrapperClasses => (! $hasAlpineClasses) && $valid, | ||||
|                 $invalidWrapperClasses => (! $hasAlpineClasses) && (! $valid), | ||||
|                 $enabledValidWrapperClasses => (! $hasAlpineClasses) && (! $disabled) && $valid, | ||||
|                 $enabledInvalidWrapperClasses => (! $hasAlpineClasses) && (! $disabled) && (! $valid), | ||||
|                 $disabledValidWrapperClasses => (! $hasAlpineClasses) && $disabled && $valid, | ||||
|             ]) | ||||
|     }} | ||||
| > | ||||
| 
 | ||||
|     @php $hasPrefix = true; @endphp | ||||
| 
 | ||||
|     <div | ||||
|         @class([ | ||||
|             'items-center gap-x-3 ps-3', | ||||
|             'flex' => $hasPrefix, | ||||
|             'hidden' => ! $hasPrefix, | ||||
|             'pe-1' => $inlinePrefix && filled($prefix), | ||||
|             'pe-2' => $inlinePrefix && blank($prefix), | ||||
|             'border-e border-gray-200 pe-3 ps-3 dark:border-white/10' => ! $inlinePrefix, | ||||
|         ]) | ||||
|     > | ||||
|         @if (count($prefixActions)) | ||||
|             <div class="{{ $actionsClasses }}"> | ||||
|                 @foreach ($prefixActions as $prefixAction) | ||||
|                     {{ $prefixAction }} | ||||
|                 @endforeach | ||||
|             </div> | ||||
|         @endif | ||||
| 
 | ||||
|         @if ($prefixIcon) | ||||
|             <x-filament::icon | ||||
|                 :attributes=" | ||||
|                         \Filament\Support\prepare_inherited_attributes( | ||||
|                             new \Illuminate\View\ComponentAttributeBag([ | ||||
|                                 'alias' => $prefixIconAlias, | ||||
|                                 'icon' => $prefixIcon, | ||||
|                                 'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator, | ||||
|                                 'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null, | ||||
|                             ]) | ||||
|                         ) | ||||
|                             ->class([$getIconClasses($prefixIconColor)]) | ||||
|                             ->style([$getIconStyles($prefixIconColor)]) | ||||
|                     " | ||||
|             /> | ||||
|         @endif | ||||
| 
 | ||||
|         @if ($hasLoadingIndicator) | ||||
|             <x-filament::loading-indicator | ||||
|                 :attributes=" | ||||
|                         \Filament\Support\prepare_inherited_attributes( | ||||
|                             new \Illuminate\View\ComponentAttributeBag([ | ||||
|                                 'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => $hasPrefix, | ||||
|                                 'wire:target' => $hasPrefix ? $loadingIndicatorTarget : null, | ||||
|                             ]) | ||||
|                         )->class([$getIconClasses()]) | ||||
|                     " | ||||
|             /> | ||||
|         @endif | ||||
| 
 | ||||
|         <span class="{{ $labelClasses }}"> | ||||
|                 <x-filament::input.select> | ||||
|                     @foreach ((\App\Models\Node::find(6))->ipAddresses() as $ip) | ||||
|                         <option value="{{ $ip }}">{{ $ip }}</option> | ||||
|                     @endforeach | ||||
|                 </x-filament::input.select> | ||||
|             </span> | ||||
|     </div> | ||||
| 
 | ||||
| 
 | ||||
|     <div | ||||
|         @class([ | ||||
|             'items-center gap-x-3 ps-3', | ||||
|             'flex' => $hasPrefix, | ||||
|             'pe-1' => $inlinePrefix && filled($prefix), | ||||
|             'pe-2' => $inlinePrefix && blank($prefix), | ||||
|             'border-e border-gray-200 pe-3 ps-3 dark:border-white/10' => ! $inlinePrefix, | ||||
|         ]) | ||||
|     > | ||||
|         <span class="{{ $labelClasses }}">:</span> | ||||
|     </div> | ||||
| 
 | ||||
|     <div | ||||
|         @if ($hasLoadingIndicator && (! $hasPrefix)) | ||||
|             @if ($inlinePrefix) | ||||
|                 wire:loading.delay.{{ config('filament.livewire_loading_delay', 'default') }}.class.remove="ps-3" | ||||
|         @endif | ||||
| 
 | ||||
|         wire:target="{{ $loadingIndicatorTarget }}" | ||||
|         @endif | ||||
|         @class([ | ||||
|             'min-w-0 flex-1', | ||||
|             'ps-3' => $hasLoadingIndicator && (! $hasPrefix) && $inlinePrefix, | ||||
|         ]) | ||||
|     > | ||||
|         {{ $slot }} | ||||
|     </div> | ||||
| 
 | ||||
|     @if ($hasSuffix) | ||||
|         <div | ||||
|             @class([ | ||||
|                 'flex items-center gap-x-3 pe-3', | ||||
|                 'ps-1' => $inlineSuffix && filled($suffix), | ||||
|                 'ps-2' => $inlineSuffix && blank($suffix), | ||||
|                 'border-s border-gray-200 ps-3 dark:border-white/10' => ! $inlineSuffix, | ||||
|             ]) | ||||
|         > | ||||
|             @if (filled($suffix)) | ||||
|                 <span class="{{ $labelClasses }}"> | ||||
|                     {{ $suffix }} | ||||
|                 </span> | ||||
|             @endif | ||||
| 
 | ||||
|             @if ($suffixIcon) | ||||
|                 <x-filament::icon | ||||
|                     :alias="$suffixIconAlias" | ||||
|                     :icon="$suffixIcon" | ||||
|                     :class="$getIconClasses($suffixIconColor)" | ||||
|                     :style="$getIconStyles($suffixIconColor)" | ||||
|                 /> | ||||
|             @endif | ||||
| 
 | ||||
|             @if (count($suffixActions)) | ||||
|                 <div class="{{ $actionsClasses }}"> | ||||
|                     @foreach ($suffixActions as $suffixAction) | ||||
|                         {{ $suffixAction }} | ||||
|                     @endforeach | ||||
|                 </div> | ||||
|             @endif | ||||
|         </div> | ||||
|     @endif | ||||
| </div> | ||||
							
								
								
									
										98
									
								
								resources/views/filament/components/select.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								resources/views/filament/components/select.blade.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | ||||
| @php | ||||
|     use Filament\Support\Facades\FilamentView; | ||||
| 
 | ||||
|     $canSelectPlaceholder = $canSelectPlaceholder(); | ||||
|     $isDisabled = $isDisabled(); | ||||
|     $isPrefixInline = $isPrefixInline(); | ||||
|     $isSuffixInline = $isSuffixInline(); | ||||
|     $prefixActions = $getPrefixActions(); | ||||
|     $prefixIcon = $getPrefixIcon(); | ||||
|     $prefixLabel = $getPrefixLabel(); | ||||
|     $suffixActions = $getSuffixActions(); | ||||
|     $suffixIcon = $getSuffixIcon(); | ||||
|     $suffixLabel = $getSuffixLabel(); | ||||
|     $statePath = $getStatePath(); | ||||
| @endphp | ||||
| 
 | ||||
| <x-dynamic-component | ||||
|     :component="$getFieldWrapperView()" | ||||
|     :field="$field" | ||||
|     :inline-label-vertical-alignment="\Filament\Support\Enums\VerticalAlignment::Center" | ||||
| > | ||||
|     <x-filament.wrapper | ||||
|         :disabled="$isDisabled" | ||||
|         :inline-prefix="$isPrefixInline" | ||||
|         :inline-suffix="$isSuffixInline" | ||||
|         :prefix="$prefixLabel" | ||||
|         :prefix-actions="$prefixActions" | ||||
|         :prefix-icon="$prefixIcon" | ||||
|         :prefix-icon-color="$getPrefixIconColor()" | ||||
|         :suffix="$suffixLabel" | ||||
|         :suffix-actions="$suffixActions" | ||||
|         :suffix-icon="$suffixIcon" | ||||
|         :suffix-icon-color="$getSuffixIconColor()" | ||||
|         :valid="! $errors->has($statePath)" | ||||
|         :attributes=" | ||||
|             \Filament\Support\prepare_inherited_attributes($getExtraAttributeBag()) | ||||
|                 ->class(['fi-fo-select']) | ||||
|         " | ||||
|     > | ||||
|         <x-filament::input.select | ||||
|                 :autofocus="$isAutofocused()" | ||||
|                 :disabled="$isDisabled" | ||||
|                 :id="$getId()" | ||||
|                 :inline-prefix="$isPrefixInline && (count($prefixActions) || $prefixIcon || filled($prefixLabel))" | ||||
|                 :inline-suffix="$isSuffixInline && (count($suffixActions) || $suffixIcon || filled($suffixLabel))" | ||||
|                 :required="$isRequired() && (! $isConcealed())" | ||||
|                 :attributes=" | ||||
|                     $getExtraInputAttributeBag() | ||||
|                         ->merge([ | ||||
|                             $applyStateBindingModifiers('wire:model') => $statePath, | ||||
|                         ], escape: false) | ||||
|                 " | ||||
|             > | ||||
|                 @php | ||||
|                     $isHtmlAllowed = $isHtmlAllowed(); | ||||
|                 @endphp | ||||
| 
 | ||||
|                 @if ($canSelectPlaceholder) | ||||
|                     <option value=""> | ||||
|                         @if (! $isDisabled) | ||||
|                             {{ $getPlaceholder() }} | ||||
|                         @endif | ||||
|                     </option> | ||||
|                 @endif | ||||
| 
 | ||||
|                 @foreach ($getOptions() as $value => $label) | ||||
|                     @if (is_array($label)) | ||||
|                         <optgroup label="{{ $value }}"> | ||||
|                             @foreach ($label as $groupedValue => $groupedLabel) | ||||
|                                 <option | ||||
|                                     @disabled($isOptionDisabled($groupedValue, $groupedLabel)) | ||||
|                                     value="{{ $groupedValue }}" | ||||
|                                 > | ||||
|                                     @if ($isHtmlAllowed) | ||||
|                                         {!! $groupedLabel !!} | ||||
|                                     @else | ||||
|                                         {{ $groupedLabel }} | ||||
|                                     @endif | ||||
|                                 </option> | ||||
|                             @endforeach | ||||
|                         </optgroup> | ||||
|                     @else | ||||
|                         <option | ||||
|                             @disabled($isOptionDisabled($value, $label)) | ||||
|                             value="{{ $value }}" | ||||
|                         > | ||||
|                             @if ($isHtmlAllowed) | ||||
|                                 {!! $label !!} | ||||
|                             @else | ||||
|                                 {{ $label }} | ||||
|                             @endif | ||||
|                         </option> | ||||
|                     @endif | ||||
|                 @endforeach | ||||
|             </x-filament::input.select> | ||||
| 
 | ||||
|     </x-filament.wrapper> | ||||
| </x-dynamic-component> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Lance Pioch
						Lance Pioch