mirror of
https://github.com/pelican-dev/panel.git
synced 2025-06-13 01:11:08 +02: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;
|
namespace App\Filament\Resources\ServerResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Forms\SelectEndpoint;
|
||||||
use App\Filament\Resources\ServerResource;
|
use App\Filament\Resources\ServerResource;
|
||||||
use App\Models\Egg;
|
use App\Models\Egg;
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
@ -320,38 +321,19 @@ class CreateServer extends CreateRecord
|
|||||||
->columns(4)
|
->columns(4)
|
||||||
->schema([
|
->schema([
|
||||||
Forms\Components\TagsInput::make('ports')
|
Forms\Components\TagsInput::make('ports')
|
||||||
->columnSpanFull()
|
->columnSpan(2)
|
||||||
->hintIcon('tabler-question-mark')
|
->hintIcon('tabler-question-mark')
|
||||||
->hintIconTooltip('Ports are limited from 1025 to 65535')
|
->hintIconTooltip('Ports are limited from 1025 to 65535')
|
||||||
->placeholder('Example: 25565, 8080, 1337-1340')
|
->placeholder('Example: 25565, 8080, 1337-1340')
|
||||||
->splitKeys(['Tab', ' ', ','])
|
->splitKeys(['Tab', ' ', ','])
|
||||||
->helperText(new HtmlString('
|
->helperText(new HtmlString('
|
||||||
These are the ports that users can connect to this Server through.
|
These are the ports that users can connect to this Server through.
|
||||||
You would typically port forward these on your home network.
|
You would typically port forward these on your home network.
|
||||||
'))
|
'))
|
||||||
->label('Ports')
|
->label('Ports')
|
||||||
->afterStateUpdated(self::ports(...))
|
->afterStateUpdated(self::ports(...))
|
||||||
->live(),
|
->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')
|
Forms\Components\Repeater::make('assignments')
|
||||||
->columnSpan(2)
|
->columnSpan(2)
|
||||||
->defaultItems(fn () => count($this->eggDefaultPorts))
|
->defaultItems(fn () => count($this->eggDefaultPorts))
|
||||||
@ -372,11 +354,13 @@ class CreateServer extends CreateRecord
|
|||||||
->deletable(false)
|
->deletable(false)
|
||||||
->reorderable(false)
|
->reorderable(false)
|
||||||
->simple(
|
->simple(
|
||||||
Forms\Components\Select::make('port')
|
SelectEndpoint::make('port')
|
||||||
|
->columnSpanFull()
|
||||||
|
->label('')
|
||||||
->live()
|
->live()
|
||||||
->placeholder('Select a Port')
|
->placeholder('Select a Port')
|
||||||
->disabled(fn (Forms\Get $get) => empty($get('../../ports')) || empty($get('../../assignments')))
|
->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();
|
$key = str($component->getStatePath())->beforeLast('.')->afterLast('.')->toString();
|
||||||
|
|
||||||
return $key;
|
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