filter dan stats
This commit is contained in:
parent
0ee947f611
commit
33f1cd84c1
|
@ -33,6 +33,13 @@ class PaketFotoResource extends Resource
|
|||
protected static ?string $model = PaketFoto::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-camera';
|
||||
protected static ?string $navigationLabel = 'Paket Foto';
|
||||
public static function getPluralLabel(): string{
|
||||
return 'Paket Foto';}
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return 'Paket Foto';
|
||||
}
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
|
@ -87,9 +94,9 @@ public static function table(Table $table): Table
|
|||
])
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
// Tables\Actions\BulkActionGroup::make([
|
||||
// Tables\Actions\DeleteBulkAction::make(),
|
||||
//]),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,13 @@ class PromoResource extends Resource
|
|||
protected static ?string $model = Promo::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-currency-dollar';
|
||||
protected static ?string $navigationLabel = 'Promo';
|
||||
public static function getPluralLabel(): string{
|
||||
return 'Promo';}
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return 'Promo';
|
||||
}
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
|
|
|
@ -38,6 +38,13 @@ class ReservasiiResource extends Resource
|
|||
protected static ?string $model = Reservasii::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-squares-plus';
|
||||
protected static ?string $navigationLabel = 'Reservasi';
|
||||
public static function getPluralLabel(): string{
|
||||
return 'Reservasi';}
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return 'Reservasi';
|
||||
}
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
|
@ -214,36 +221,17 @@ public static function table(Table $table): Table
|
|||
|
||||
Tables\Columns\TextColumn::make('metode_pembayaran')
|
||||
->badge(),
|
||||
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->label('Tanggal Dibuat')
|
||||
->dateTime('d F Y - H:i')
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\SelectFilter::make('tipe_pembayaran')
|
||||
->options([
|
||||
'full' => 'Full Payment',
|
||||
'dp' => 'Down Payment',
|
||||
])
|
||||
->label('Status Pembayaran'),
|
||||
|
||||
Tables\Filters\Filter::make('tanggal')
|
||||
->form([
|
||||
Forms\Components\DatePicker::make('tanggal_from')->label('Dari Tanggal'),
|
||||
Forms\Components\DatePicker::make('tanggal_until')->label('Sampai Tanggal'),
|
||||
DatePicker::make('tanggal')->label('Tanggal'),
|
||||
])
|
||||
->query(function (Builder $query, array $data): Builder {
|
||||
return $query
|
||||
->when(
|
||||
$data['tanggal_from'],
|
||||
fn (Builder $query, $date): Builder => $query->whereDate('tanggal', '>=', $date),
|
||||
)
|
||||
->when(
|
||||
$data['tanggal_until'],
|
||||
fn (Builder $query, $date): Builder => $query->whereDate('tanggal', '<=', $date),
|
||||
);
|
||||
return $query->when(
|
||||
$data['tanggal'],
|
||||
fn (Builder $query, $date): Builder => $query->whereDate('tanggal', $date),
|
||||
);
|
||||
}),
|
||||
])
|
||||
->actions([
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
use App\Filament\Resources\ReservasiiResource;
|
||||
use App\Filament\Resources\ReservasiiResource\Widgets\ReservasiiStats;
|
||||
use Filament\Actions;
|
||||
use Filament\Forms\Components\Tabs\Tab;
|
||||
use Filament\Resources\Components\Tabs;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListReservasiis extends ListRecords
|
||||
|
@ -18,9 +20,19 @@ protected function getHeaderActions(): array
|
|||
];
|
||||
}
|
||||
|
||||
protected function getHeaderWidgets(): array{
|
||||
//protected function getHeaderWidgets(): array{
|
||||
// return[
|
||||
// ReservasiiStats::class
|
||||
//];
|
||||
//}
|
||||
|
||||
public function getTabs(): array{
|
||||
return[
|
||||
ReservasiiStats::class
|
||||
null => \Filament\Resources\Components\Tab::make('Semua'),
|
||||
'Paket Pasangan' => \Filament\Resources\Components\Tab::make()->query(fn($query) => $query->whereRelation('detail.paketFoto', 'nama_paket_foto', 'Paket Pasangan')),
|
||||
'Paket 5 orang' => \Filament\Resources\Components\Tab::make()->query(fn($query) => $query->whereRelation('detail.paketFoto', 'nama_paket_foto', 'Paket 5 Orang')),
|
||||
'Widebox Couple' => \Filament\Resources\Components\Tab::make()->query(fn($query) => $query->whereRelation('detail.paketFoto', 'nama_paket_foto', 'Widebox Couple')),
|
||||
'Widebox Group' => \Filament\Resources\Components\Tab::make()->query(fn($query) => $query->whereRelation('detail.paketFoto', 'nama_paket_foto', 'Widebox Group')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,15 @@ protected function getStats(): array{
|
|||
$todayEarnings = Reservasii::query()
|
||||
->whereDate('tanggal', Carbon::today())
|
||||
->sum('total');
|
||||
$mounthEarnings = Reservasii::whereMonth('created_at', now()->month)
|
||||
->whereYear('created_at', now()->year)
|
||||
->sum('total');
|
||||
return [
|
||||
Stat::make('Reservasi Hari Ini', Reservasii::query()->whereDate('tanggal', Carbon::today())->count()),
|
||||
Stat::make('Pendapatan Hari Ini', 'Rp ' . number_format($todayEarnings, 0, ',', '.'))
|
||||
Stat::make('Pendapatan Hari Ini', 'Rp' . number_format($todayEarnings, 0, ',', '.')),
|
||||
Stat::make('Pendapatan Per Bulan','Rp' . number_format($mounthEarnings, 0,',', '.'))
|
||||
|
||||
];
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,13 @@ class UserResource extends Resource
|
|||
{
|
||||
protected static ?string $model = User::class;
|
||||
protected static ?string $recordTitleAttribute = 'name';
|
||||
|
||||
protected static ?string $navigationLabel = 'User';
|
||||
public static function getPluralLabel(): string{
|
||||
return 'User';}
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return 'User';
|
||||
}
|
||||
protected static ?string $navigationIcon = 'heroicon-o-user-group';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
|
@ -78,9 +84,9 @@ public static function table(Table $table): Table
|
|||
])
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
// Tables\Actions\BulkActionGroup::make([
|
||||
// Tables\Actions\DeleteBulkAction::make(),
|
||||
//]),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use Filament\Widgets\Widget;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class CalendarWidget extends Widget
|
||||
{
|
||||
protected static string $view = 'filament.widgets.calendar-widget';
|
||||
|
||||
protected static ?string $heading = 'Jadwal Reservasi';
|
||||
|
||||
protected static ?int $sort = 1;
|
||||
|
||||
protected int | string | array $columnSpan = 'full';
|
||||
|
||||
public function getReservations()
|
||||
{
|
||||
$data = DB::table('reservasiis')
|
||||
->select('tanggal', 'waktu')
|
||||
->get()
|
||||
->groupBy(function($item) {
|
||||
return Carbon::parse($item->tanggal)->format('Y-m-d');
|
||||
})
|
||||
->map(function ($items) {
|
||||
return $items->pluck('waktu')->toArray();
|
||||
});
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -31,8 +31,11 @@ public function panel(Panel $panel): Panel
|
|||
->path('admin')
|
||||
->login()
|
||||
->colors([
|
||||
'primary' => Color::Amber,
|
||||
'primary' => Color::Gray,
|
||||
])
|
||||
->brandName('Ko-La Self')
|
||||
->brandLogo(asset('images/logo.png'))
|
||||
->favicon(asset('images/logo.png'))
|
||||
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
|
||||
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
|
||||
->pages([
|
||||
|
@ -41,7 +44,7 @@ public function panel(Panel $panel): Panel
|
|||
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
|
||||
->widgets([
|
||||
ReservasiiStats::class,
|
||||
CalendarWidget::class
|
||||
//CalendarWidget::class
|
||||
//Widgets\AccountWidget::class,
|
||||
//Widgets\FilamentInfoWidget::class,
|
||||
])
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
@if (isset($data))
|
||||
<script>
|
||||
window.filamentData = @js($data)
|
||||
</script>
|
||||
@endif
|
||||
|
||||
@foreach ($assets as $asset)
|
||||
@if (! $asset->isLoadedOnRequest())
|
||||
{{ $asset->getHtml() }}
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
<style>
|
||||
:root {
|
||||
@foreach ($cssVariables ?? [] as $cssVariableName => $cssVariableValue) --{{ $cssVariableName }}:{{ $cssVariableValue }}; @endforeach
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,64 @@
|
|||
@php
|
||||
use Filament\Support\Enums\Alignment;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'actions' => [],
|
||||
'alignment' => Alignment::Start,
|
||||
'fullWidth' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
if (is_array($actions)) {
|
||||
$actions = array_filter(
|
||||
$actions,
|
||||
fn ($action): bool => $action->isVisible(),
|
||||
);
|
||||
}
|
||||
|
||||
if (! $alignment instanceof Alignment) {
|
||||
$alignment = filled($alignment) ? (Alignment::tryFrom($alignment) ?? $alignment) : null;
|
||||
}
|
||||
|
||||
$hasActions = false;
|
||||
|
||||
$hasSlot = ! \Filament\Support\is_slot_empty($slot);
|
||||
$actionsAreHtmlable = $actions instanceof \Illuminate\Contracts\Support\Htmlable;
|
||||
|
||||
if ($hasSlot) {
|
||||
$hasActions = true;
|
||||
} elseif ($actionsAreHtmlable) {
|
||||
$hasActions = ! \Filament\Support\is_slot_empty($actions);
|
||||
} else {
|
||||
$hasActions = filled($actions);
|
||||
}
|
||||
@endphp
|
||||
|
||||
@if ($hasActions)
|
||||
<div
|
||||
{{
|
||||
$attributes->class([
|
||||
'fi-ac gap-3',
|
||||
'flex flex-wrap items-center' => ! $fullWidth,
|
||||
match ($alignment) {
|
||||
Alignment::Start, Alignment::Left => 'justify-start',
|
||||
Alignment::Center => 'justify-center',
|
||||
Alignment::End, Alignment::Right => 'flex-row-reverse',
|
||||
Alignment::Between, Alignment::Justify => 'justify-between',
|
||||
default => $alignment,
|
||||
} => ! $fullWidth,
|
||||
'grid grid-cols-[repeat(auto-fit,minmax(0,1fr))]' => $fullWidth,
|
||||
])
|
||||
}}
|
||||
>
|
||||
@if ($hasSlot)
|
||||
{{ $slot }}
|
||||
@elseif ($actionsAreHtmlable)
|
||||
{{ $actions }}
|
||||
@else
|
||||
@foreach ($actions as $action)
|
||||
{{ $action }}
|
||||
@endforeach
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
|
@ -0,0 +1,21 @@
|
|||
@props([
|
||||
'circular' => true,
|
||||
'size' => 'md',
|
||||
])
|
||||
|
||||
<img
|
||||
{{
|
||||
$attributes
|
||||
->class([
|
||||
'fi-avatar object-cover object-center',
|
||||
'rounded-md' => ! $circular,
|
||||
'fi-circular rounded-full' => $circular,
|
||||
match ($size) {
|
||||
'sm' => 'h-6 w-6',
|
||||
'md' => 'h-8 w-8',
|
||||
'lg' => 'h-10 w-10',
|
||||
default => $size,
|
||||
},
|
||||
])
|
||||
}}
|
||||
/>
|
|
@ -0,0 +1,238 @@
|
|||
@php
|
||||
use Filament\Support\Enums\ActionSize;
|
||||
use Filament\Support\Enums\IconPosition;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'color' => 'primary',
|
||||
'deleteButton' => null,
|
||||
'disabled' => false,
|
||||
'form' => null,
|
||||
'formId' => null,
|
||||
'href' => null,
|
||||
'icon' => null,
|
||||
'iconAlias' => null,
|
||||
'iconPosition' => IconPosition::Before,
|
||||
'iconSize' => IconSize::Small,
|
||||
'keyBindings' => null,
|
||||
'loadingIndicator' => true,
|
||||
'size' => ActionSize::Medium,
|
||||
'spaMode' => null,
|
||||
'tag' => 'span',
|
||||
'target' => null,
|
||||
'tooltip' => null,
|
||||
'type' => 'button',
|
||||
])
|
||||
|
||||
@php
|
||||
if (! $iconPosition instanceof IconPosition) {
|
||||
$iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null;
|
||||
}
|
||||
|
||||
if (! $size instanceof ActionSize) {
|
||||
$size = filled($size) ? (ActionSize::tryFrom($size) ?? $size) : null;
|
||||
}
|
||||
|
||||
if (! $iconSize instanceof IconSize) {
|
||||
$iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null;
|
||||
}
|
||||
|
||||
$isDeletable = count($deleteButton?->attributes->getAttributes() ?? []) > 0;
|
||||
|
||||
$iconClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
'fi-badge-icon h-4 w-4',
|
||||
match ($iconSize) {
|
||||
IconSize::Small => 'h-4 w-4',
|
||||
IconSize::Medium => 'h-5 w-5',
|
||||
IconSize::Large => 'h-6 w-6',
|
||||
default => $iconSize,
|
||||
},
|
||||
match ($color) {
|
||||
'gray' => 'text-gray-400 dark:text-gray-500',
|
||||
default => 'text-custom-500',
|
||||
},
|
||||
]);
|
||||
|
||||
$iconStyles = \Illuminate\Support\Arr::toCssStyles([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [500],
|
||||
alias: 'badge.icon',
|
||||
) => $color !== 'gray',
|
||||
]);
|
||||
|
||||
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
|
||||
|
||||
$hasLoadingIndicator = filled($wireTarget) || ($type === 'submit' && filled($form));
|
||||
|
||||
if ($hasLoadingIndicator) {
|
||||
$loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES);
|
||||
}
|
||||
|
||||
$hasTooltip = filled($tooltip);
|
||||
@endphp
|
||||
|
||||
<{{ $tag }}
|
||||
@if ($tag === 'a')
|
||||
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
|
||||
@endif
|
||||
@if ($keyBindings || $hasTooltip)
|
||||
x-data="{}"
|
||||
@endif
|
||||
@if ($keyBindings)
|
||||
x-bind:id="$id('key-bindings')"
|
||||
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
|
||||
@endif
|
||||
@if ($hasTooltip)
|
||||
x-tooltip="{
|
||||
content: @js($tooltip),
|
||||
theme: $store.theme,
|
||||
}"
|
||||
@endif
|
||||
{{
|
||||
$attributes
|
||||
->merge([
|
||||
'disabled' => $disabled,
|
||||
'form' => $tag === 'button' ? $formId : null,
|
||||
'type' => $tag === 'button' ? $type : null,
|
||||
'wire:loading.attr' => $tag === 'button' ? 'disabled' : null,
|
||||
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
|
||||
], escape: false)
|
||||
->class([
|
||||
'fi-badge flex items-center justify-center gap-x-1 rounded-md text-xs font-medium ring-1 ring-inset',
|
||||
'pointer-events-none opacity-70' => $disabled,
|
||||
match ($size) {
|
||||
ActionSize::ExtraSmall => 'px-0.5 min-w-[theme(spacing.4)] tracking-tighter',
|
||||
ActionSize::Small => 'px-1.5 min-w-[theme(spacing.5)] py-0.5 tracking-tight',
|
||||
ActionSize::Medium, ActionSize::Large, ActionSize::ExtraLarge => 'px-2 min-w-[theme(spacing.6)] py-1',
|
||||
default => $size,
|
||||
},
|
||||
match ($color) {
|
||||
'gray' => 'bg-gray-50 text-gray-600 ring-gray-600/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20',
|
||||
default => 'fi-color-custom bg-custom-50 text-custom-600 ring-custom-600/10 dark:bg-custom-400/10 dark:text-custom-400 dark:ring-custom-400/30',
|
||||
},
|
||||
is_string($color) ? "fi-color-{$color}" : null,
|
||||
])
|
||||
->style([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [
|
||||
50,
|
||||
400,
|
||||
600,
|
||||
],
|
||||
alias: 'badge',
|
||||
) => $color !== 'gray',
|
||||
])
|
||||
}}
|
||||
>
|
||||
@if ($iconPosition === IconPosition::Before)
|
||||
@if ($icon)
|
||||
<x-filament::icon
|
||||
:attributes="
|
||||
\Filament\Support\prepare_inherited_attributes(
|
||||
new \Illuminate\View\ComponentAttributeBag([
|
||||
'alias' => $iconAlias,
|
||||
'icon' => $icon,
|
||||
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
|
||||
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
|
||||
])
|
||||
)
|
||||
->class([$iconClasses])
|
||||
->style([$iconStyles])
|
||||
"
|
||||
/>
|
||||
@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') => '',
|
||||
'wire:target' => $loadingIndicatorTarget,
|
||||
])
|
||||
)
|
||||
->class([$iconClasses])
|
||||
->style([$iconStyles])
|
||||
"
|
||||
/>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<span class="grid">
|
||||
<span class="truncate">
|
||||
{{ $slot }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@if ($isDeletable)
|
||||
<button
|
||||
type="button"
|
||||
{{
|
||||
$deleteButton
|
||||
->attributes
|
||||
->except(['label'])
|
||||
->class([
|
||||
'fi-badge-delete-button -my-1 -me-2 -ms-1 flex items-center justify-center p-1 outline-none transition duration-75',
|
||||
match ($color) {
|
||||
'gray' => 'text-gray-700/50 hover:text-gray-700/75 focus-visible:text-gray-700/75 dark:text-gray-300/50 dark:hover:text-gray-300/75 dark:focus-visible:text-gray-300/75',
|
||||
default => 'text-custom-700/50 hover:text-custom-700/75 focus-visible:text-custom-700/75 dark:text-custom-300/50 dark:hover:text-custom-300/75 dark:focus-visible:text-custom-300/75',
|
||||
},
|
||||
])
|
||||
->style([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [300, 700],
|
||||
alias: 'badge.delete-button',
|
||||
) => $color !== 'gray',
|
||||
])
|
||||
}}
|
||||
>
|
||||
<x-filament::icon
|
||||
alias="badge.delete-button"
|
||||
icon="heroicon-m-x-mark"
|
||||
class="h-3.5 w-3.5"
|
||||
/>
|
||||
|
||||
@if (filled($label = $deleteButton->attributes->get('label')))
|
||||
<span class="sr-only">
|
||||
{{ $label }}
|
||||
</span>
|
||||
@endif
|
||||
</button>
|
||||
@elseif ($iconPosition === IconPosition::After)
|
||||
@if ($icon)
|
||||
<x-filament::icon
|
||||
:attributes="
|
||||
\Filament\Support\prepare_inherited_attributes(
|
||||
new \Illuminate\View\ComponentAttributeBag([
|
||||
'alias' => $iconAlias,
|
||||
'icon' => $icon,
|
||||
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
|
||||
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
|
||||
])
|
||||
)
|
||||
->class([$iconClasses])
|
||||
->style([$iconStyles])
|
||||
"
|
||||
/>
|
||||
@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') => '',
|
||||
'wire:target' => $loadingIndicatorTarget,
|
||||
])
|
||||
)
|
||||
->class([$iconClasses])
|
||||
->style([$iconStyles])
|
||||
"
|
||||
/>
|
||||
@endif
|
||||
@endif
|
||||
</{{ $tag }}>
|
|
@ -0,0 +1,50 @@
|
|||
@props([
|
||||
'breadcrumbs' => [],
|
||||
])
|
||||
|
||||
@php
|
||||
$iconClasses = 'fi-breadcrumbs-item-separator flex h-5 w-5 text-gray-400 dark:text-gray-500';
|
||||
$itemLabelClasses = 'fi-breadcrumbs-item-label text-sm font-medium text-gray-500 dark:text-gray-400';
|
||||
@endphp
|
||||
|
||||
<nav {{ $attributes->class(['fi-breadcrumbs']) }}>
|
||||
<ol class="fi-breadcrumbs-list flex flex-wrap items-center gap-x-2">
|
||||
@foreach ($breadcrumbs as $url => $label)
|
||||
<li class="fi-breadcrumbs-item flex items-center gap-x-2">
|
||||
@if (! $loop->first)
|
||||
<x-filament::icon
|
||||
alias="breadcrumbs.separator"
|
||||
icon="heroicon-m-chevron-right"
|
||||
@class([
|
||||
$iconClasses,
|
||||
'rtl:hidden',
|
||||
])
|
||||
/>
|
||||
|
||||
<x-filament::icon
|
||||
{{-- @deprecated Use `breadcrubs.separator.rtl` instead of `breadcrumbs.separator` for RTL. --}}
|
||||
:alias="['breadcrumbs.separator.rtl', 'breadcrumbs.separator']"
|
||||
icon="heroicon-m-chevron-left"
|
||||
@class([
|
||||
$iconClasses,
|
||||
'ltr:hidden',
|
||||
])
|
||||
/>
|
||||
@endif
|
||||
|
||||
@if (is_int($url))
|
||||
<span class="{{ $itemLabelClasses }}">
|
||||
{{ $label }}
|
||||
</span>
|
||||
@else
|
||||
<a
|
||||
{{ \Filament\Support\generate_href_html($url) }}
|
||||
class="{{ $itemLabelClasses }} transition duration-75 hover:text-gray-700 dark:hover:text-gray-200"
|
||||
>
|
||||
{{ $label }}
|
||||
</a>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ol>
|
||||
</nav>
|
|
@ -0,0 +1,9 @@
|
|||
<div
|
||||
{{
|
||||
$attributes->class([
|
||||
'fi-btn-group grid grid-flow-col rounded-lg shadow-sm ring-1 ring-gray-950/10 dark:ring-white/20',
|
||||
])
|
||||
}}
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
|
@ -0,0 +1,332 @@
|
|||
@php
|
||||
use Filament\Support\Enums\ActionSize;
|
||||
use Filament\Support\Enums\IconPosition;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'badge' => null,
|
||||
'badgeColor' => 'primary',
|
||||
'badgeSize' => 'xs',
|
||||
'color' => 'primary',
|
||||
'disabled' => false,
|
||||
'form' => null,
|
||||
'formId' => null,
|
||||
'grouped' => false,
|
||||
'href' => null,
|
||||
'icon' => null,
|
||||
'iconAlias' => null,
|
||||
'iconPosition' => IconPosition::Before,
|
||||
'iconSize' => null,
|
||||
'keyBindings' => null,
|
||||
'labeledFrom' => null,
|
||||
'labelSrOnly' => false,
|
||||
'loadingIndicator' => true,
|
||||
'outlined' => false,
|
||||
'size' => ActionSize::Medium,
|
||||
'spaMode' => null,
|
||||
'tag' => 'button',
|
||||
'target' => null,
|
||||
'tooltip' => null,
|
||||
'type' => 'button',
|
||||
])
|
||||
|
||||
@php
|
||||
if (! $iconPosition instanceof IconPosition) {
|
||||
$iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null;
|
||||
}
|
||||
|
||||
if (! $size instanceof ActionSize) {
|
||||
$size = filled($size) ? (ActionSize::tryFrom($size) ?? $size) : null;
|
||||
}
|
||||
|
||||
$iconSize ??= match ($size) {
|
||||
ActionSize::ExtraSmall, ActionSize::Small => IconSize::Small,
|
||||
default => IconSize::Medium,
|
||||
};
|
||||
|
||||
if (! $iconSize instanceof IconSize) {
|
||||
$iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null;
|
||||
}
|
||||
|
||||
$buttonClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
...[
|
||||
'fi-btn relative grid-flow-col items-center justify-center font-semibold outline-none transition duration-75 focus-visible:ring-2',
|
||||
'pointer-events-none opacity-70' => $disabled,
|
||||
'rounded-lg' => ! $grouped,
|
||||
'flex-1 [&:nth-child(1_of_.fi-btn)]:rounded-s-lg [&:nth-last-child(1_of_.fi-btn)]:rounded-e-lg [&:not(:nth-child(1_of_.fi-btn))]:shadow-[-1px_0_0_0_theme(colors.gray.200)] [&:not(:nth-last-child(1_of_.fi-btn))]:me-px dark:[&:not(:nth-child(1_of_.fi-btn))]:shadow-[-1px_0_0_0_theme(colors.white/20%)]' => $grouped,
|
||||
'cursor-pointer' => $tag === 'label',
|
||||
match ($color) {
|
||||
'gray' => null,
|
||||
default => 'fi-color-custom',
|
||||
},
|
||||
// @deprecated `fi-btn-color-*` has been replaced by `fi-color-*` and `fi-color-custom`.
|
||||
is_string($color) ? "fi-btn-color-{$color}" : null,
|
||||
is_string($color) ? "fi-color-{$color}" : null,
|
||||
($size instanceof ActionSize) ? "fi-size-{$size->value}" : null,
|
||||
// @deprecated `fi-btn-size-*` has been replaced by `fi-size-*`.
|
||||
($size instanceof ActionSize) ? "fi-btn-size-{$size->value}" : null,
|
||||
match ($size) {
|
||||
ActionSize::ExtraSmall => 'gap-1 px-2 py-1.5 text-xs',
|
||||
ActionSize::Small => 'gap-1 px-2.5 py-1.5 text-sm',
|
||||
ActionSize::Medium => 'gap-1.5 px-3 py-2 text-sm',
|
||||
ActionSize::Large => 'gap-1.5 px-3.5 py-2.5 text-sm',
|
||||
ActionSize::ExtraLarge => 'gap-1.5 px-4 py-3 text-sm',
|
||||
default => $size,
|
||||
},
|
||||
'hidden' => $labeledFrom,
|
||||
match ($labeledFrom) {
|
||||
'sm' => 'sm:inline-grid',
|
||||
'md' => 'md:inline-grid',
|
||||
'lg' => 'lg:inline-grid',
|
||||
'xl' => 'xl:inline-grid',
|
||||
'2xl' => '2xl:inline-grid',
|
||||
default => 'inline-grid',
|
||||
},
|
||||
],
|
||||
...(
|
||||
$outlined ?
|
||||
[
|
||||
'fi-btn-outlined ring-1',
|
||||
match ($color) {
|
||||
'gray' => 'text-gray-950 ring-gray-300 hover:bg-gray-400/10 focus-visible:ring-gray-400/40 dark:text-white dark:ring-gray-700',
|
||||
default => 'text-custom-600 ring-custom-600 hover:bg-custom-400/10 dark:text-custom-400 dark:ring-custom-500',
|
||||
},
|
||||
] :
|
||||
[
|
||||
'shadow-sm' => ! $grouped,
|
||||
'bg-white text-gray-950 hover:bg-gray-50 dark:bg-white/5 dark:text-white dark:hover:bg-white/10' => ($color === 'gray') || ($tag === 'label'),
|
||||
'ring-1 ring-gray-950/10 dark:ring-white/20' => (($color === 'gray') || ($tag === 'label')) && (! $grouped),
|
||||
'bg-custom-600 text-white hover:bg-custom-500 focus-visible:ring-custom-500/50 dark:bg-custom-500 dark:hover:bg-custom-400 dark:focus-visible:ring-custom-400/50' => ($color !== 'gray') && ($tag !== 'label'),
|
||||
'[input:checked+&]:bg-custom-600 [input:checked+&]:text-white [input:checked+&]:ring-0 [input:checked+&]:hover:bg-custom-500 dark:[input:checked+&]:bg-custom-500 dark:[input:checked+&]:hover:bg-custom-400 [input:checked:focus-visible+&]:ring-custom-500/50 dark:[input:checked:focus-visible+&]:ring-custom-400/50 [input:focus-visible+&]:z-10 [input:focus-visible+&]:ring-2 [input:focus-visible+&]:ring-gray-950/10 dark:[input:focus-visible+&]:ring-white/20' => ($color !== 'gray') && ($tag === 'label'),
|
||||
'[input:checked+&]:bg-gray-400 [input:checked+&]:text-white [input:checked+&]:ring-0 [input:checked+&]:hover:bg-gray-300 dark:[input:checked+&]:bg-gray-600 dark:[input:checked+&]:hover:bg-gray-500' => ($color === 'gray'),
|
||||
]
|
||||
),
|
||||
]);
|
||||
|
||||
$buttonStyles = \Illuminate\Support\Arr::toCssStyles([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [400, 500, 600],
|
||||
alias: 'button',
|
||||
) => $color !== 'gray',
|
||||
]);
|
||||
|
||||
$iconClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
'fi-btn-icon transition duration-75',
|
||||
match ($iconSize) {
|
||||
IconSize::Small => 'h-4 w-4',
|
||||
IconSize::Medium => 'h-5 w-5',
|
||||
IconSize::Large => 'h-6 w-6',
|
||||
default => $iconSize,
|
||||
},
|
||||
'text-gray-400 dark:text-gray-500' => ($color === 'gray') || ($tag === 'label'),
|
||||
'text-white' => ($color !== 'gray') && ($tag !== 'label') && (! $outlined),
|
||||
'[:checked+*>&]:text-white' => $tag === 'label',
|
||||
]);
|
||||
|
||||
$badgeContainerClasses = 'fi-btn-badge-ctn absolute start-full top-0 z-[1] w-max -translate-x-1/2 -translate-y-1/2 rounded-md bg-white dark:bg-gray-900 rtl:translate-x-1/2';
|
||||
|
||||
$labelClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
'fi-btn-label',
|
||||
'sr-only' => $labelSrOnly,
|
||||
]);
|
||||
|
||||
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
|
||||
|
||||
$hasFormProcessingLoadingIndicator = $type === 'submit' && filled($form);
|
||||
$hasLoadingIndicator = filled($wireTarget) || $hasFormProcessingLoadingIndicator;
|
||||
|
||||
if ($hasLoadingIndicator) {
|
||||
$loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES);
|
||||
}
|
||||
|
||||
$hasTooltip = filled($tooltip);
|
||||
@endphp
|
||||
|
||||
@if ($labeledFrom)
|
||||
<x-filament::icon-button
|
||||
:badge="$badge"
|
||||
:badge-color="$badgeColor"
|
||||
:color="$color"
|
||||
:disabled="$disabled"
|
||||
:form="$form"
|
||||
:form-id="$formId"
|
||||
:href="$href"
|
||||
:icon="$icon"
|
||||
:icon-alias="$iconAlias"
|
||||
:icon-size="$iconSize"
|
||||
:key-bindings="$keyBindings"
|
||||
:label="$slot"
|
||||
:size="$size"
|
||||
:tag="$tag"
|
||||
:target="$target"
|
||||
:tooltip="$tooltip"
|
||||
:type="$type"
|
||||
:class="
|
||||
match ($labeledFrom) {
|
||||
'sm' => 'sm:hidden',
|
||||
'md' => 'md:hidden',
|
||||
'lg' => 'lg:hidden',
|
||||
'xl' => 'xl:hidden',
|
||||
'2xl' => '2xl:hidden',
|
||||
default => 'hidden',
|
||||
}
|
||||
"
|
||||
:attributes="\Filament\Support\prepare_inherited_attributes($attributes)"
|
||||
/>
|
||||
@endif
|
||||
|
||||
<{{ $tag }}
|
||||
@if ($tag === 'a')
|
||||
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
|
||||
@endif
|
||||
@if (($keyBindings || $hasTooltip) && (! $hasFormProcessingLoadingIndicator))
|
||||
x-data="{}"
|
||||
@endif
|
||||
@if ($keyBindings)
|
||||
x-bind:id="$id('key-bindings')"
|
||||
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
|
||||
@endif
|
||||
@if ($hasTooltip)
|
||||
x-tooltip="{
|
||||
content: @js($tooltip),
|
||||
theme: $store.theme,
|
||||
}"
|
||||
@endif
|
||||
@if ($hasFormProcessingLoadingIndicator)
|
||||
x-data="{
|
||||
form: null,
|
||||
isProcessing: false,
|
||||
processingMessage: null,
|
||||
}"
|
||||
x-init="
|
||||
form = $el.closest('form')
|
||||
|
||||
form?.addEventListener('form-processing-started', (event) => {
|
||||
isProcessing = true
|
||||
processingMessage = event.detail.message
|
||||
})
|
||||
|
||||
form?.addEventListener('form-processing-finished', () => {
|
||||
isProcessing = false
|
||||
})
|
||||
"
|
||||
x-bind:class="{ 'enabled:opacity-70 enabled:cursor-wait': isProcessing }"
|
||||
@endif
|
||||
{{
|
||||
$attributes
|
||||
->merge([
|
||||
'disabled' => $disabled,
|
||||
'form' => $formId,
|
||||
'type' => $tag === 'button' ? $type : null,
|
||||
'wire:loading.attr' => $tag === 'button' ? 'disabled' : null,
|
||||
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
|
||||
'x-bind:disabled' => $hasFormProcessingLoadingIndicator ? 'isProcessing' : null,
|
||||
], escape: false)
|
||||
->class([$buttonClasses])
|
||||
->style([$buttonStyles])
|
||||
}}
|
||||
>
|
||||
@if ($iconPosition === IconPosition::Before)
|
||||
@if ($icon)
|
||||
<x-filament::icon
|
||||
:attributes="
|
||||
\Filament\Support\prepare_inherited_attributes(
|
||||
new \Illuminate\View\ComponentAttributeBag([
|
||||
'alias' => $iconAlias,
|
||||
'icon' => $icon,
|
||||
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
|
||||
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
|
||||
])
|
||||
)->class([$iconClasses])
|
||||
"
|
||||
/>
|
||||
@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') => '',
|
||||
'wire:target' => $loadingIndicatorTarget,
|
||||
])
|
||||
)->class([$iconClasses])
|
||||
"
|
||||
/>
|
||||
@endif
|
||||
|
||||
@if ($hasFormProcessingLoadingIndicator)
|
||||
<x-filament::loading-indicator
|
||||
x-cloak="x-cloak"
|
||||
x-show="isProcessing"
|
||||
:class="$iconClasses"
|
||||
/>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<span
|
||||
@if ($hasFormProcessingLoadingIndicator)
|
||||
x-show="! isProcessing"
|
||||
@endif
|
||||
class="{{ $labelClasses }}"
|
||||
>
|
||||
{{ $slot }}
|
||||
</span>
|
||||
|
||||
@if ($hasFormProcessingLoadingIndicator)
|
||||
<span
|
||||
x-cloak
|
||||
x-show="isProcessing"
|
||||
x-text="processingMessage"
|
||||
class="{{ $labelClasses }}"
|
||||
></span>
|
||||
@endif
|
||||
|
||||
@if ($iconPosition === IconPosition::After)
|
||||
@if ($icon)
|
||||
<x-filament::icon
|
||||
:attributes="
|
||||
\Filament\Support\prepare_inherited_attributes(
|
||||
new \Illuminate\View\ComponentAttributeBag([
|
||||
'alias' => $iconAlias,
|
||||
'icon' => $icon,
|
||||
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
|
||||
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
|
||||
])
|
||||
)->class([$iconClasses])
|
||||
"
|
||||
/>
|
||||
@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') => '',
|
||||
'wire:target' => $loadingIndicatorTarget,
|
||||
])
|
||||
)->class([$iconClasses])
|
||||
"
|
||||
/>
|
||||
@endif
|
||||
|
||||
@if ($hasFormProcessingLoadingIndicator)
|
||||
<x-filament::loading-indicator
|
||||
x-cloak="x-cloak"
|
||||
x-show="isProcessing"
|
||||
:class="$iconClasses"
|
||||
/>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if (filled($badge))
|
||||
<div class="{{ $badgeContainerClasses }}">
|
||||
<x-filament::badge :color="$badgeColor" :size="$badgeSize">
|
||||
{{ $badge }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
@endif
|
||||
</{{ $tag }}>
|
|
@ -0,0 +1,3 @@
|
|||
<x-filament::section>
|
||||
{{ $slot }}
|
||||
</x-filament::section>
|
|
@ -0,0 +1,71 @@
|
|||
@php
|
||||
use Filament\Support\Enums\IconSize;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'color' => 'gray',
|
||||
'icon' => null,
|
||||
'iconSize' => IconSize::Medium,
|
||||
'tag' => 'div',
|
||||
])
|
||||
|
||||
<{{ $tag }}
|
||||
{{
|
||||
$attributes
|
||||
->class([
|
||||
'fi-dropdown-header flex w-full gap-2 p-3 text-sm',
|
||||
match ($color) {
|
||||
'gray' => null,
|
||||
default => 'fi-color-custom',
|
||||
},
|
||||
// @deprecated `fi-dropdown-header-color-*` has been replaced by `fi-color-*` and `fi-color-custom`.
|
||||
is_string($color) ? "fi-dropdown-header-color-{$color}" : null,
|
||||
is_string($color) ? "fi-color-{$color}" : null,
|
||||
])
|
||||
}}
|
||||
>
|
||||
@if (filled($icon))
|
||||
<x-filament::icon
|
||||
:icon="$icon"
|
||||
@class([
|
||||
'fi-dropdown-header-icon',
|
||||
match ($iconSize) {
|
||||
IconSize::Small, 'sm' => 'h-4 w-4',
|
||||
IconSize::Medium, 'md' => 'h-5 w-5',
|
||||
IconSize::Large, 'lg' => 'h-6 w-6',
|
||||
default => $iconSize,
|
||||
},
|
||||
match ($color) {
|
||||
'gray' => 'text-gray-400 dark:text-gray-500',
|
||||
default => 'text-custom-500 dark:text-custom-400',
|
||||
},
|
||||
])
|
||||
@style([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [400, 500],
|
||||
alias: 'dropdown.header.icon',
|
||||
) => $color !== 'gray',
|
||||
])
|
||||
/>
|
||||
@endif
|
||||
|
||||
<span
|
||||
@class([
|
||||
'fi-dropdown-header-label flex-1 truncate text-start',
|
||||
match ($color) {
|
||||
'gray' => 'text-gray-700 dark:text-gray-200',
|
||||
default => 'text-custom-600 dark:text-custom-400',
|
||||
},
|
||||
])
|
||||
@style([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [400, 600],
|
||||
alias: 'dropdown.header.label',
|
||||
) => $color !== 'gray',
|
||||
])
|
||||
>
|
||||
{{ $slot }}
|
||||
</span>
|
||||
</{{ $tag }}>
|
|
@ -0,0 +1,87 @@
|
|||
@props([
|
||||
'availableHeight' => null,
|
||||
'availableWidth' => null,
|
||||
'flip' => true,
|
||||
'maxHeight' => null,
|
||||
'offset' => 8,
|
||||
'placement' => null,
|
||||
'shift' => false,
|
||||
'size' => false,
|
||||
'sizePadding' => 16,
|
||||
'teleport' => false,
|
||||
'trigger' => null,
|
||||
'width' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
|
||||
$sizeConfig = collect([
|
||||
'availableHeight' => $availableHeight,
|
||||
'availableWidth' => $availableWidth,
|
||||
'padding' => $sizePadding,
|
||||
])->filter()->toJson();
|
||||
@endphp
|
||||
|
||||
<div
|
||||
x-data="{
|
||||
toggle: function (event) {
|
||||
$refs.panel.toggle(event)
|
||||
},
|
||||
|
||||
open: function (event) {
|
||||
$refs.panel.open(event)
|
||||
},
|
||||
|
||||
close: function (event) {
|
||||
$refs.panel.close(event)
|
||||
},
|
||||
}"
|
||||
{{ $attributes->class(['fi-dropdown']) }}
|
||||
>
|
||||
<div
|
||||
x-on:click="toggle"
|
||||
{{ $trigger->attributes->class(['fi-dropdown-trigger flex cursor-pointer']) }}
|
||||
>
|
||||
{{ $trigger }}
|
||||
</div>
|
||||
|
||||
@if (! \Filament\Support\is_slot_empty($slot))
|
||||
<div
|
||||
x-cloak
|
||||
x-float{{ $placement ? ".placement.{$placement}" : '' }}{{ $size ? '.size' : '' }}{{ $flip ? '.flip' : '' }}{{ $shift ? '.shift' : '' }}{{ $teleport ? '.teleport' : '' }}{{ $offset ? '.offset' : '' }}="{ offset: {{ $offset }}, {{ $size ? ('size: ' . $sizeConfig) : '' }} }"
|
||||
x-ref="panel"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:leave-end="opacity-0"
|
||||
@if ($attributes->has('wire:key'))
|
||||
wire:ignore.self
|
||||
wire:key="{{ $attributes->get('wire:key') }}.panel"
|
||||
@endif
|
||||
@class([
|
||||
'fi-dropdown-panel absolute z-10 w-screen divide-y divide-gray-100 rounded-lg bg-white shadow-lg ring-1 ring-gray-950/5 transition dark:divide-white/5 dark:bg-gray-900 dark:ring-white/10',
|
||||
match ($width) {
|
||||
// These max width classes need to be `!important` otherwise they will be usurped by the Floating UI "size" middleware.
|
||||
MaxWidth::ExtraSmall, 'xs' => '!max-w-xs',
|
||||
MaxWidth::Small, 'sm' => '!max-w-sm',
|
||||
MaxWidth::Medium, 'md' => '!max-w-md',
|
||||
MaxWidth::Large, 'lg' => '!max-w-lg',
|
||||
MaxWidth::ExtraLarge, 'xl' => '!max-w-xl',
|
||||
MaxWidth::TwoExtraLarge, '2xl' => '!max-w-2xl',
|
||||
MaxWidth::ThreeExtraLarge, '3xl' => '!max-w-3xl',
|
||||
MaxWidth::FourExtraLarge, '4xl' => '!max-w-4xl',
|
||||
MaxWidth::FiveExtraLarge, '5xl' => '!max-w-5xl',
|
||||
MaxWidth::SixExtraLarge, '6xl' => '!max-w-6xl',
|
||||
MaxWidth::SevenExtraLarge, '7xl' => '!max-w-7xl',
|
||||
null => '!max-w-[14rem]',
|
||||
default => $width,
|
||||
},
|
||||
'overflow-y-auto' => $maxHeight || $size,
|
||||
])
|
||||
@style([
|
||||
"max-height: {$maxHeight}" => $maxHeight,
|
||||
])
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
<div {{ $attributes->class(['fi-dropdown-list p-1']) }}>
|
||||
{{ $slot }}
|
||||
</div>
|
|
@ -0,0 +1,275 @@
|
|||
@php
|
||||
use Filament\Support\Enums\IconSize;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'badge' => null,
|
||||
'badgeColor' => null,
|
||||
'badgeTooltip' => null,
|
||||
'color' => 'gray',
|
||||
'disabled' => false,
|
||||
'href' => null,
|
||||
'icon' => null,
|
||||
'iconAlias' => null,
|
||||
'iconColor' => null,
|
||||
'iconSize' => IconSize::Medium,
|
||||
'image' => null,
|
||||
'keyBindings' => null,
|
||||
'loadingIndicator' => true,
|
||||
'spaMode' => null,
|
||||
'tag' => 'button',
|
||||
'target' => null,
|
||||
'tooltip' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
$buttonClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
'fi-dropdown-list-item flex w-full items-center gap-2 whitespace-nowrap rounded-md p-2 text-sm transition-colors duration-75 outline-none disabled:pointer-events-none disabled:opacity-70',
|
||||
'pointer-events-none opacity-70' => $disabled,
|
||||
match ($color) {
|
||||
'gray' => 'hover:bg-gray-50 focus-visible:bg-gray-50 dark:hover:bg-white/5 dark:focus-visible:bg-white/5',
|
||||
default => 'fi-color-custom hover:bg-custom-50 focus-visible:bg-custom-50 dark:hover:bg-custom-400/10 dark:focus-visible:bg-custom-400/10',
|
||||
},
|
||||
// @deprecated `fi-dropdown-list-item-color-*` has been replaced by `fi-color-*` and `fi-color-custom`.
|
||||
is_string($color) ? "fi-dropdown-list-item-color-{$color}" : null,
|
||||
is_string($color) ? "fi-color-{$color}" : null,
|
||||
]);
|
||||
|
||||
$buttonStyles = \Illuminate\Support\Arr::toCssStyles([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [50, 400],
|
||||
alias: 'dropdown.list.item',
|
||||
) => $color !== 'gray',
|
||||
]);
|
||||
|
||||
$iconColor ??= $color;
|
||||
|
||||
$iconClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
'fi-dropdown-list-item-icon',
|
||||
match ($iconSize) {
|
||||
IconSize::Small, 'sm' => 'h-4 w-4',
|
||||
IconSize::Medium, 'md' => 'h-5 w-5',
|
||||
IconSize::Large, 'lg' => 'h-6 w-6',
|
||||
default => $iconSize,
|
||||
},
|
||||
match ($iconColor) {
|
||||
'gray' => 'text-gray-400 dark:text-gray-500',
|
||||
default => 'text-custom-500 dark:text-custom-400',
|
||||
},
|
||||
]);
|
||||
|
||||
$iconStyles = \Illuminate\Support\Arr::toCssStyles([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$iconColor,
|
||||
shades: [400, 500],
|
||||
alias: 'dropdown.list.item.icon',
|
||||
) => $iconColor !== 'gray',
|
||||
]);
|
||||
|
||||
$imageClasses = 'fi-dropdown-list-item-image h-5 w-5 rounded-full bg-cover bg-center';
|
||||
|
||||
$labelClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
'fi-dropdown-list-item-label flex-1 truncate text-start',
|
||||
match ($color) {
|
||||
'gray' => 'text-gray-700 dark:text-gray-200',
|
||||
default => 'text-custom-600 dark:text-custom-400 ',
|
||||
},
|
||||
]);
|
||||
|
||||
$labelStyles = \Illuminate\Support\Arr::toCssStyles([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [400, 600],
|
||||
alias: 'dropdown.list.item.label',
|
||||
) => $color !== 'gray',
|
||||
]);
|
||||
|
||||
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
|
||||
|
||||
$hasLoadingIndicator = filled($wireTarget);
|
||||
|
||||
if ($hasLoadingIndicator) {
|
||||
$loadingIndicatorTarget = html_entity_decode($wireTarget, ENT_QUOTES);
|
||||
}
|
||||
|
||||
$hasTooltip = filled($tooltip);
|
||||
@endphp
|
||||
|
||||
@if ($tag === 'button')
|
||||
<button
|
||||
@if ($keyBindings || $hasTooltip)
|
||||
x-data="{}"
|
||||
@endif
|
||||
@if ($keyBindings)
|
||||
x-bind:id="$id('key-bindings')"
|
||||
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
|
||||
@endif
|
||||
@if ($hasTooltip)
|
||||
x-tooltip="{
|
||||
content: @js($tooltip),
|
||||
theme: $store.theme,
|
||||
}"
|
||||
@endif
|
||||
{{
|
||||
$attributes
|
||||
->merge([
|
||||
'disabled' => $disabled,
|
||||
'type' => 'button',
|
||||
'wire:loading.attr' => 'disabled',
|
||||
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
|
||||
], escape: false)
|
||||
->class([$buttonClasses])
|
||||
->style([$buttonStyles])
|
||||
}}
|
||||
>
|
||||
@if ($icon)
|
||||
<x-filament::icon
|
||||
:attributes="
|
||||
\Filament\Support\prepare_inherited_attributes(
|
||||
new \Illuminate\View\ComponentAttributeBag([
|
||||
'alias' => $iconAlias,
|
||||
'icon' => $icon,
|
||||
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
|
||||
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
|
||||
])
|
||||
)
|
||||
->class([$iconClasses])
|
||||
->style([$iconStyles])
|
||||
"
|
||||
/>
|
||||
@endif
|
||||
|
||||
@if ($image)
|
||||
<div
|
||||
class="{{ $imageClasses }}"
|
||||
style="background-image: url('{{ $image }}')"
|
||||
></div>
|
||||
@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') => '',
|
||||
'wire:target' => $loadingIndicatorTarget,
|
||||
])
|
||||
)
|
||||
->class([$iconClasses])
|
||||
->style([$iconStyles])
|
||||
"
|
||||
/>
|
||||
@endif
|
||||
|
||||
<span class="{{ $labelClasses }}" style="{{ $labelStyles }}">
|
||||
{{ $slot }}
|
||||
</span>
|
||||
|
||||
@if (filled($badge))
|
||||
<x-filament::badge
|
||||
:color="$badgeColor"
|
||||
size="sm"
|
||||
:tooltip="$badgeTooltip"
|
||||
>
|
||||
{{ $badge }}
|
||||
</x-filament::badge>
|
||||
@endif
|
||||
</button>
|
||||
@elseif ($tag === 'a')
|
||||
<a
|
||||
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
|
||||
@if ($keyBindings || $hasTooltip)
|
||||
x-data="{}"
|
||||
@endif
|
||||
@if ($keyBindings)
|
||||
x-bind:id="$id('key-bindings')"
|
||||
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
|
||||
@endif
|
||||
@if ($hasTooltip)
|
||||
x-tooltip="{
|
||||
content: @js($tooltip),
|
||||
theme: $store.theme,
|
||||
}"
|
||||
@endif
|
||||
{{
|
||||
$attributes
|
||||
->class([$buttonClasses])
|
||||
->style([$buttonStyles])
|
||||
}}
|
||||
>
|
||||
@if ($icon)
|
||||
<x-filament::icon
|
||||
:alias="$iconAlias"
|
||||
:icon="$icon"
|
||||
:class="$iconClasses"
|
||||
:style="$iconStyles"
|
||||
/>
|
||||
@endif
|
||||
|
||||
@if ($image)
|
||||
<div
|
||||
class="{{ $imageClasses }}"
|
||||
style="background-image: url('{{ $image }}')"
|
||||
></div>
|
||||
@endif
|
||||
|
||||
<span class="{{ $labelClasses }}" style="{{ $labelStyles }}">
|
||||
{{ $slot }}
|
||||
</span>
|
||||
|
||||
@if (filled($badge))
|
||||
<x-filament::badge :color="$badgeColor" size="sm">
|
||||
{{ $badge }}
|
||||
</x-filament::badge>
|
||||
@endif
|
||||
</a>
|
||||
@elseif ($tag === 'form')
|
||||
<form
|
||||
{{ $attributes->only(['action', 'class', 'method', 'wire:submit']) }}
|
||||
>
|
||||
@csrf
|
||||
|
||||
<button
|
||||
@if ($keyBindings || $hasTooltip)
|
||||
x-data="{}"
|
||||
@endif
|
||||
@if ($keyBindings)
|
||||
x-bind:id="$id('key-bindings')"
|
||||
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
|
||||
@endif
|
||||
@if ($hasTooltip)
|
||||
x-tooltip="{
|
||||
content: @js($tooltip),
|
||||
theme: $store.theme,
|
||||
}"
|
||||
@endif
|
||||
type="submit"
|
||||
{{
|
||||
$attributes
|
||||
->except(['action', 'class', 'method', 'wire:submit'])
|
||||
->class([$buttonClasses])
|
||||
->style([$buttonStyles])
|
||||
}}
|
||||
>
|
||||
@if ($icon)
|
||||
<x-filament::icon
|
||||
:alias="$iconAlias"
|
||||
:icon="$icon"
|
||||
:class="$iconClasses"
|
||||
:style="$iconStyles"
|
||||
/>
|
||||
@endif
|
||||
|
||||
<span class="{{ $labelClasses }}" style="{{ $labelStyles }}">
|
||||
{{ $slot }}
|
||||
</span>
|
||||
|
||||
@if (filled($badge))
|
||||
<x-filament::badge :color="$badgeColor" size="sm">
|
||||
{{ $badge }}
|
||||
</x-filament::badge>
|
||||
@endif
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
|
@ -0,0 +1,25 @@
|
|||
@props([
|
||||
'label' => null,
|
||||
'labelHidden' => false,
|
||||
])
|
||||
|
||||
<fieldset
|
||||
{{
|
||||
$attributes->class([
|
||||
'fi-fieldset rounded-xl border border-gray-200 p-6 dark:border-white/10',
|
||||
])
|
||||
}}
|
||||
>
|
||||
@if (filled($label))
|
||||
<legend
|
||||
@class([
|
||||
'-ms-2 px-2 text-sm font-medium leading-6 text-gray-950 dark:text-white',
|
||||
'sr-only' => $labelHidden,
|
||||
])
|
||||
>
|
||||
{{ $label }}
|
||||
</legend>
|
||||
@endif
|
||||
|
||||
{{ $slot }}
|
||||
</fieldset>
|
|
@ -0,0 +1,62 @@
|
|||
@props([
|
||||
'default' => 1,
|
||||
'sm' => null,
|
||||
'md' => null,
|
||||
'lg' => null,
|
||||
'xl' => null,
|
||||
'twoXl' => null,
|
||||
'defaultStart' => null,
|
||||
'smStart' => null,
|
||||
'mdStart' => null,
|
||||
'lgStart' => null,
|
||||
'xlStart' => null,
|
||||
'twoXlStart' => null,
|
||||
'hidden' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
$getSpanValue = function ($span): string {
|
||||
if ($span === 'full') {
|
||||
return '1 / -1';
|
||||
}
|
||||
|
||||
return "span {$span} / span {$span}";
|
||||
};
|
||||
@endphp
|
||||
|
||||
<div
|
||||
{{
|
||||
$attributes
|
||||
->class([
|
||||
'hidden' => $hidden || $default === 'hidden',
|
||||
'col-[--col-span-default]' => $default && (! $hidden),
|
||||
'sm:col-[--col-span-sm]' => $sm && (! $hidden),
|
||||
'md:col-[--col-span-md]' => $md && (! $hidden),
|
||||
'lg:col-[--col-span-lg]' => $lg && (! $hidden),
|
||||
'xl:col-[--col-span-xl]' => $xl && (! $hidden),
|
||||
'2xl:col-[--col-span-2xl]' => $twoXl && (! $hidden),
|
||||
'col-start-[--col-start-default]' => $defaultStart && (! $hidden),
|
||||
'sm:col-start-[--col-start-sm]' => $smStart && (! $hidden),
|
||||
'md:col-start-[--col-start-md]' => $mdStart && (! $hidden),
|
||||
'lg:col-start-[--col-start-lg]' => $lgStart && (! $hidden),
|
||||
'xl:col-start-[--col-start-xl]' => $xlStart && (! $hidden),
|
||||
'2xl:col-start-[--col-start-2xl]' => $twoXlStart && (! $hidden),
|
||||
])
|
||||
->style([
|
||||
"--col-span-default: {$getSpanValue($default)}" => $default,
|
||||
"--col-span-sm: {$getSpanValue($sm)}" => $sm,
|
||||
"--col-span-md: {$getSpanValue($md)}" => $md,
|
||||
"--col-span-lg: {$getSpanValue($lg)}" => $lg,
|
||||
"--col-span-xl: {$getSpanValue($xl)}" => $xl,
|
||||
"--col-span-2xl: {$getSpanValue($twoXl)}" => $twoXl,
|
||||
"--col-start-default: {$defaultStart}" => $defaultStart,
|
||||
"--col-start-sm: {$smStart}" => $smStart,
|
||||
"--col-start-md: {$mdStart}" => $mdStart,
|
||||
"--col-start-lg: {$lgStart}" => $lgStart,
|
||||
"--col-start-xl: {$xlStart}" => $xlStart,
|
||||
"--col-start-2xl: {$twoXlStart}" => $twoXlStart,
|
||||
])
|
||||
}}
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
|
@ -0,0 +1,53 @@
|
|||
@props([
|
||||
'isGrid' => true,
|
||||
'default' => 1,
|
||||
'direction' => 'row',
|
||||
'sm' => null,
|
||||
'md' => null,
|
||||
'lg' => null,
|
||||
'xl' => null,
|
||||
'twoXl' => null,
|
||||
])
|
||||
|
||||
<div
|
||||
{{
|
||||
$attributes
|
||||
->class([
|
||||
'grid' => $isGrid && $direction === 'row',
|
||||
'grid-cols-[--cols-default]' => $default && ($direction === 'row'),
|
||||
'columns-[--cols-default]' => $default && ($direction === 'column'),
|
||||
'sm:grid-cols-[--cols-sm]' => $sm && ($direction === 'row'),
|
||||
'sm:columns-[--cols-sm]' => $sm && ($direction === 'column'),
|
||||
'md:grid-cols-[--cols-md]' => $md && ($direction === 'row'),
|
||||
'md:columns-[--cols-md]' => $md && ($direction === 'column'),
|
||||
'lg:grid-cols-[--cols-lg]' => $lg && ($direction === 'row'),
|
||||
'lg:columns-[--cols-lg]' => $lg && ($direction === 'column'),
|
||||
'xl:grid-cols-[--cols-xl]' => $xl && ($direction === 'row'),
|
||||
'xl:columns-[--cols-xl]' => $xl && ($direction === 'column'),
|
||||
'2xl:grid-cols-[--cols-2xl]' => $twoXl && ($direction === 'row'),
|
||||
'2xl:columns-[--cols-2xl]' => $twoXl && ($direction === 'column'),
|
||||
])
|
||||
->style(
|
||||
match ($direction) {
|
||||
'column' => [
|
||||
"--cols-default: {$default}" => $default,
|
||||
"--cols-sm: {$sm}" => $sm,
|
||||
"--cols-md: {$md}" => $md,
|
||||
"--cols-lg: {$lg}" => $lg,
|
||||
"--cols-xl: {$xl}" => $xl,
|
||||
"--cols-2xl: {$twoXl}" => $twoXl,
|
||||
],
|
||||
'row' => [
|
||||
"--cols-default: repeat({$default}, minmax(0, 1fr))" => $default,
|
||||
"--cols-sm: repeat({$sm}, minmax(0, 1fr))" => $sm,
|
||||
"--cols-md: repeat({$md}, minmax(0, 1fr))" => $md,
|
||||
"--cols-lg: repeat({$lg}, minmax(0, 1fr))" => $lg,
|
||||
"--cols-xl: repeat({$xl}, minmax(0, 1fr))" => $xl,
|
||||
"--cols-2xl: repeat({$twoXl}, minmax(0, 1fr))" => $twoXl,
|
||||
],
|
||||
},
|
||||
)
|
||||
}}
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
|
@ -0,0 +1,242 @@
|
|||
@php
|
||||
use Filament\Support\Enums\ActionSize;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'badge' => null,
|
||||
'badgeColor' => 'primary',
|
||||
'badgeSize' => 'xs',
|
||||
'color' => 'primary',
|
||||
'disabled' => false,
|
||||
'form' => null,
|
||||
'formId' => null,
|
||||
'href' => null,
|
||||
'icon' => null,
|
||||
'iconAlias' => null,
|
||||
'iconSize' => null,
|
||||
'keyBindings' => null,
|
||||
'label' => null,
|
||||
'loadingIndicator' => true,
|
||||
'size' => ActionSize::Medium,
|
||||
'spaMode' => null,
|
||||
'tag' => 'button',
|
||||
'target' => null,
|
||||
'tooltip' => null,
|
||||
'type' => 'button',
|
||||
])
|
||||
|
||||
@php
|
||||
if (! $size instanceof ActionSize) {
|
||||
$size = filled($size) ? (ActionSize::tryFrom($size) ?? $size) : null;
|
||||
}
|
||||
|
||||
$iconSize ??= match ($size) {
|
||||
ActionSize::ExtraSmall => IconSize::Small,
|
||||
ActionSize::Small, ActionSize::Medium => IconSize::Medium,
|
||||
ActionSize::Large, ActionSize::ExtraLarge => IconSize::Large,
|
||||
default => IconSize::Medium,
|
||||
};
|
||||
|
||||
if (! $iconSize instanceof IconSize) {
|
||||
$iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null;
|
||||
}
|
||||
|
||||
$buttonClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
'fi-icon-btn relative flex items-center justify-center rounded-lg outline-none transition duration-75 focus-visible:ring-2',
|
||||
'pointer-events-none opacity-70' => $disabled,
|
||||
...match ($size) {
|
||||
ActionSize::ExtraSmall => [
|
||||
match ($iconSize) {
|
||||
IconSize::Small => '-m-1.5',
|
||||
IconSize::Medium => '-m-1',
|
||||
IconSize::Large => '-m-0.5',
|
||||
},
|
||||
'h-7 w-7',
|
||||
],
|
||||
ActionSize::Small => [
|
||||
match ($iconSize) {
|
||||
IconSize::Small => '-m-2',
|
||||
IconSize::Medium => '-m-1.5',
|
||||
IconSize::Large => '-m-1',
|
||||
},
|
||||
'h-8 w-8',
|
||||
],
|
||||
ActionSize::Medium => [
|
||||
match ($iconSize) {
|
||||
IconSize::Small => '-m-2.5',
|
||||
IconSize::Medium => '-m-2',
|
||||
IconSize::Large => '-m-1.5',
|
||||
},
|
||||
'h-9 w-9',
|
||||
],
|
||||
ActionSize::Large => [
|
||||
match ($iconSize) {
|
||||
IconSize::Small => '-m-3',
|
||||
IconSize::Medium => '-m-2.5',
|
||||
IconSize::Large => '-m-2',
|
||||
},
|
||||
'h-10 w-10',
|
||||
],
|
||||
ActionSize::ExtraLarge => [
|
||||
match ($iconSize) {
|
||||
IconSize::Small => '-m-3.5',
|
||||
IconSize::Medium => '-m-3',
|
||||
IconSize::Large => '-m-2.5',
|
||||
},
|
||||
'h-11 w-11',
|
||||
],
|
||||
},
|
||||
match ($color) {
|
||||
'gray' => 'text-gray-400 hover:text-gray-500 focus-visible:ring-primary-600 dark:text-gray-500 dark:hover:text-gray-400 dark:focus-visible:ring-primary-500',
|
||||
default => 'fi-color-custom text-custom-500 hover:text-custom-600 focus-visible:ring-custom-600 dark:text-custom-400 dark:hover:text-custom-300 dark:focus-visible:ring-custom-500',
|
||||
},
|
||||
is_string($color) ? "fi-color-{$color}" : null,
|
||||
]);
|
||||
|
||||
$buttonStyles = \Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [300, 400, 500, 600],
|
||||
alias: 'icon-button',
|
||||
);
|
||||
|
||||
$iconClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
'fi-icon-btn-icon',
|
||||
match ($iconSize) {
|
||||
IconSize::Small => 'h-4 w-4',
|
||||
IconSize::Medium => 'h-5 w-5',
|
||||
IconSize::Large => 'h-6 w-6',
|
||||
default => $iconSize,
|
||||
},
|
||||
]);
|
||||
|
||||
$badgeContainerClasses = 'fi-icon-btn-badge-ctn absolute start-full top-1 z-[1] w-max -translate-x-1/2 -translate-y-1/2 rounded-md bg-white dark:bg-gray-900 rtl:translate-x-1/2';
|
||||
|
||||
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
|
||||
|
||||
$hasLoadingIndicator = filled($wireTarget) || ($type === 'submit' && filled($form));
|
||||
|
||||
if ($hasLoadingIndicator) {
|
||||
$loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES);
|
||||
}
|
||||
|
||||
$hasTooltip = filled($tooltip);
|
||||
@endphp
|
||||
|
||||
@if ($tag === 'button')
|
||||
<button
|
||||
@if ($keyBindings || $hasTooltip)
|
||||
x-data="{}"
|
||||
@endif
|
||||
@if ($keyBindings)
|
||||
x-bind:id="$id('key-bindings')"
|
||||
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
|
||||
@endif
|
||||
@if ($hasTooltip)
|
||||
x-tooltip="{
|
||||
content: @js($tooltip),
|
||||
theme: $store.theme,
|
||||
}"
|
||||
@endif
|
||||
{{
|
||||
$attributes
|
||||
->merge([
|
||||
'disabled' => $disabled,
|
||||
'form' => $formId,
|
||||
'type' => $type,
|
||||
'wire:loading.attr' => 'disabled',
|
||||
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
|
||||
], escape: false)
|
||||
->merge([
|
||||
'title' => $hasTooltip ? null : $label,
|
||||
], escape: true)
|
||||
->class([$buttonClasses])
|
||||
->style([$buttonStyles])
|
||||
}}
|
||||
>
|
||||
@if ($label)
|
||||
<span class="sr-only">
|
||||
{{ $label }}
|
||||
</span>
|
||||
@endif
|
||||
|
||||
<x-filament::icon
|
||||
:attributes="
|
||||
\Filament\Support\prepare_inherited_attributes(
|
||||
new \Illuminate\View\ComponentAttributeBag([
|
||||
'alias' => $iconAlias,
|
||||
'icon' => $icon,
|
||||
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
|
||||
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
|
||||
])
|
||||
)->class([$iconClasses])
|
||||
"
|
||||
/>
|
||||
|
||||
@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') => '',
|
||||
'wire:target' => $loadingIndicatorTarget,
|
||||
])
|
||||
)->class([$iconClasses])
|
||||
"
|
||||
/>
|
||||
@endif
|
||||
|
||||
@if (filled($badge))
|
||||
<div class="{{ $badgeContainerClasses }}">
|
||||
<x-filament::badge :color="$badgeColor" :size="$badgeSize">
|
||||
{{ $badge }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
@endif
|
||||
</button>
|
||||
@elseif ($tag === 'a')
|
||||
<a
|
||||
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
|
||||
@if ($keyBindings || $hasTooltip)
|
||||
x-data="{}"
|
||||
@endif
|
||||
@if ($keyBindings)
|
||||
x-bind:id="$id('key-bindings')"
|
||||
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
|
||||
@endif
|
||||
@if ($hasTooltip)
|
||||
x-tooltip="{
|
||||
content: @js($tooltip),
|
||||
theme: $store.theme,
|
||||
}"
|
||||
@endif
|
||||
{{
|
||||
$attributes
|
||||
->merge([
|
||||
'title' => $hasTooltip ? null : $label,
|
||||
], escape: true)
|
||||
->class([$buttonClasses])
|
||||
->style([$buttonStyles])
|
||||
}}
|
||||
>
|
||||
@if ($label)
|
||||
<span class="sr-only">
|
||||
{{ $label }}
|
||||
</span>
|
||||
@endif
|
||||
|
||||
<x-filament::icon
|
||||
:alias="$iconAlias"
|
||||
:icon="$icon"
|
||||
:class="$iconClasses"
|
||||
/>
|
||||
|
||||
@if (filled($badge))
|
||||
<div class="{{ $badgeContainerClasses }}">
|
||||
<x-filament::badge :color="$badgeColor" size="xs">
|
||||
{{ $badge }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
@endif
|
||||
</a>
|
||||
@endif
|
|
@ -0,0 +1,29 @@
|
|||
@props([
|
||||
'alias' => null,
|
||||
'class' => '',
|
||||
'icon' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
$icon = ($alias ? \Filament\Support\Facades\FilamentIcon::resolve($alias) : null) ?: ($icon ?? $slot);
|
||||
@endphp
|
||||
|
||||
@if ($icon instanceof \Illuminate\Contracts\Support\Htmlable)
|
||||
<span {{ $attributes->class($class) }}>
|
||||
{{ $icon }}
|
||||
</span>
|
||||
@elseif (str_contains($icon, '/'))
|
||||
<img
|
||||
{{
|
||||
$attributes
|
||||
->merge(['src' => $icon])
|
||||
->class($class)
|
||||
}}
|
||||
/>
|
||||
@else
|
||||
@svg(
|
||||
$icon,
|
||||
$class,
|
||||
array_filter($attributes->getAttributes()),
|
||||
)
|
||||
@endif
|
|
@ -0,0 +1,29 @@
|
|||
@props([
|
||||
'alpineValid' => null,
|
||||
'valid' => true,
|
||||
])
|
||||
|
||||
@php
|
||||
$hasAlpineValidClasses = filled($alpineValid);
|
||||
|
||||
$validInputClasses = 'text-primary-600 ring-gray-950/10 focus:ring-primary-600 checked:focus:ring-primary-500/50 dark:text-primary-500 dark:ring-white/20 dark:checked:bg-primary-500 dark:focus:ring-primary-500 dark:checked:focus:ring-primary-400/50 dark:disabled:ring-white/10';
|
||||
$invalidInputClasses = 'fi-invalid text-danger-600 ring-danger-600 focus:ring-danger-600 checked:focus:ring-danger-500/50 dark:text-danger-500 dark:ring-danger-500 dark:checked:bg-danger-500 dark:focus:ring-danger-500 dark:checked:focus:ring-danger-400/50';
|
||||
@endphp
|
||||
|
||||
<input
|
||||
type="checkbox"
|
||||
@if ($hasAlpineValidClasses)
|
||||
x-bind:class="{
|
||||
@js($validInputClasses): {{ $alpineValid }},
|
||||
@js($invalidInputClasses): {{ "(! {$alpineValid})" }},
|
||||
}"
|
||||
@endif
|
||||
{{
|
||||
$attributes
|
||||
->class([
|
||||
'fi-checkbox-input rounded border-none bg-white shadow-sm ring-1 transition duration-75 checked:ring-0 focus:ring-2 focus:ring-offset-0 disabled:pointer-events-none disabled:bg-gray-50 disabled:text-gray-50 disabled:checked:bg-gray-400 disabled:checked:text-gray-400 dark:bg-white/5 dark:disabled:bg-transparent dark:disabled:checked:bg-gray-600',
|
||||
$validInputClasses => (! $hasAlpineValidClasses) && $valid,
|
||||
$invalidInputClasses => (! $hasAlpineValidClasses) && (! $valid),
|
||||
])
|
||||
}}
|
||||
/>
|
|
@ -0,0 +1,22 @@
|
|||
@props([
|
||||
'inlinePrefix' => false,
|
||||
'inlineSuffix' => false,
|
||||
])
|
||||
|
||||
<input
|
||||
{{
|
||||
$attributes->class([
|
||||
'fi-input block w-full border-none py-1.5 text-base text-gray-950 transition duration-75 placeholder:text-gray-400 focus:ring-0 disabled:text-gray-500 disabled:[-webkit-text-fill-color:theme(colors.gray.500)] disabled:placeholder:[-webkit-text-fill-color:theme(colors.gray.400)] dark:text-white dark:placeholder:text-gray-500 dark:disabled:text-gray-400 dark:disabled:[-webkit-text-fill-color:theme(colors.gray.400)] dark:disabled:placeholder:[-webkit-text-fill-color:theme(colors.gray.500)] sm:text-sm sm:leading-6',
|
||||
// A fully transparent white background color is used
|
||||
// instead of transparent to fix a Safari bug
|
||||
// where the date/time input "placeholder" colors too dark.
|
||||
//
|
||||
// https://github.com/filamentphp/filament/issues/7087
|
||||
'bg-white/0',
|
||||
'ps-0' => $inlinePrefix,
|
||||
'ps-3' => ! $inlinePrefix,
|
||||
'pe-0' => $inlineSuffix,
|
||||
'pe-3' => ! $inlineSuffix,
|
||||
])
|
||||
}}
|
||||
/>
|
|
@ -0,0 +1,15 @@
|
|||
@props([
|
||||
'valid' => true,
|
||||
])
|
||||
|
||||
<input
|
||||
type="radio"
|
||||
{{
|
||||
$attributes
|
||||
->class([
|
||||
'fi-radio-input border-none bg-white shadow-sm ring-1 transition duration-75 checked:ring-0 focus:ring-2 focus:ring-offset-0 disabled:bg-gray-50 disabled:text-gray-50 disabled:checked:bg-gray-400 disabled:checked:text-gray-400 dark:bg-white/5 dark:disabled:bg-transparent dark:disabled:checked:bg-gray-600',
|
||||
'text-primary-600 ring-gray-950/10 focus:ring-primary-600 checked:focus:ring-primary-500/50 dark:text-primary-500 dark:ring-white/20 dark:checked:bg-primary-500 dark:focus:ring-primary-500 dark:checked:focus:ring-primary-400/50 dark:disabled:ring-white/10' => $valid,
|
||||
'fi-invalid text-danger-600 ring-danger-600 focus:ring-danger-600 checked:focus:ring-danger-500/50 dark:text-danger-500 dark:ring-danger-500 dark:checked:bg-danger-500 dark:focus:ring-danger-500 dark:checked:focus:ring-danger-400/50' => ! $valid,
|
||||
])
|
||||
}}
|
||||
/>
|
|
@ -0,0 +1,16 @@
|
|||
@props([
|
||||
'inlinePrefix' => false,
|
||||
'inlineSuffix' => false,
|
||||
])
|
||||
|
||||
<select
|
||||
{{
|
||||
$attributes->class([
|
||||
'fi-select-input block w-full border-none bg-transparent py-1.5 pe-8 text-base text-gray-950 transition duration-75 focus:ring-0 disabled:text-gray-500 disabled:[-webkit-text-fill-color:theme(colors.gray.500)] dark:text-white dark:disabled:text-gray-400 dark:disabled:[-webkit-text-fill-color:theme(colors.gray.400)] sm:text-sm sm:leading-6 [&_optgroup]:bg-white [&_optgroup]:dark:bg-gray-900 [&_option]:bg-white [&_option]:dark:bg-gray-900',
|
||||
'ps-0' => $inlinePrefix,
|
||||
'ps-3' => ! $inlinePrefix,
|
||||
])
|
||||
}}
|
||||
>
|
||||
{{ $slot }}
|
||||
</select>
|
|
@ -0,0 +1,212 @@
|
|||
@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', 'tabindex'])
|
||||
->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,
|
||||
])
|
||||
}}
|
||||
>
|
||||
@if ($hasPrefix || $hasLoadingIndicator)
|
||||
<div
|
||||
@if (! $hasPrefix)
|
||||
wire:loading.delay.{{ config('filament.livewire_loading_delay', 'default') }}.flex
|
||||
wire:target="{{ $loadingIndicatorTarget }}"
|
||||
wire:key="{{ \Illuminate\Support\Str::random() }}" {{-- Makes sure the loading indicator gets hidden again. --}}
|
||||
@endif
|
||||
@class([
|
||||
'fi-input-wrp-prefix 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
|
||||
|
||||
@if (filled($prefix))
|
||||
<span class="{{ $labelClasses }}">
|
||||
{{ $prefix }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<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([
|
||||
'fi-input-wrp-input min-w-0 flex-1',
|
||||
'ps-3' => $hasLoadingIndicator && (! $hasPrefix) && $inlinePrefix,
|
||||
])
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
@if ($hasSuffix)
|
||||
<div
|
||||
@class([
|
||||
'fi-input-wrp-suffix 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>
|
|
@ -0,0 +1,304 @@
|
|||
@php
|
||||
use Filament\Support\Enums\ActionSize;
|
||||
use Filament\Support\Enums\FontWeight;
|
||||
use Filament\Support\Enums\IconPosition;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'badge' => null,
|
||||
'badgeColor' => 'primary',
|
||||
'badgeSize' => 'xs',
|
||||
'color' => 'primary',
|
||||
'disabled' => false,
|
||||
'form' => null,
|
||||
'formId' => null,
|
||||
'href' => null,
|
||||
'icon' => null,
|
||||
'iconAlias' => null,
|
||||
'iconPosition' => IconPosition::Before,
|
||||
'iconSize' => null,
|
||||
'keyBindings' => null,
|
||||
'labelSrOnly' => false,
|
||||
'loadingIndicator' => true,
|
||||
'size' => ActionSize::Medium,
|
||||
'spaMode' => null,
|
||||
'tag' => 'a',
|
||||
'target' => null,
|
||||
'tooltip' => null,
|
||||
'type' => 'button',
|
||||
'weight' => FontWeight::SemiBold,
|
||||
])
|
||||
|
||||
@php
|
||||
if (! $iconPosition instanceof IconPosition) {
|
||||
$iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null;
|
||||
}
|
||||
|
||||
if (! $size instanceof ActionSize) {
|
||||
$size = filled($size) ? (ActionSize::tryFrom($size) ?? $size) : null;
|
||||
}
|
||||
|
||||
$iconSize ??= match ($size) {
|
||||
ActionSize::ExtraSmall, ActionSize::Small => IconSize::Small,
|
||||
default => IconSize::Medium,
|
||||
};
|
||||
|
||||
if (! $iconSize instanceof IconSize) {
|
||||
$iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null;
|
||||
}
|
||||
|
||||
$linkClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
'fi-link group/link relative inline-flex items-center justify-center outline-none',
|
||||
'pointer-events-none opacity-70' => $disabled,
|
||||
($size instanceof ActionSize) ? "fi-size-{$size->value}" : null,
|
||||
// @deprecated `fi-link-size-*` has been replaced by `fi-size-*`.
|
||||
($size instanceof ActionSize) ? "fi-link-size-{$size->value}" : null,
|
||||
match ($size) {
|
||||
ActionSize::ExtraSmall => 'gap-1',
|
||||
ActionSize::Small => 'gap-1',
|
||||
ActionSize::Medium => 'gap-1.5',
|
||||
ActionSize::Large => 'gap-1.5',
|
||||
ActionSize::ExtraLarge => 'gap-1.5',
|
||||
default => $size,
|
||||
},
|
||||
match ($color) {
|
||||
'gray' => null,
|
||||
default => 'fi-color-custom',
|
||||
},
|
||||
is_string($color) ? "fi-color-{$color}" : null,
|
||||
]);
|
||||
|
||||
if (! $labelSrOnly) {
|
||||
$labelClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
match ($weight) {
|
||||
FontWeight::Thin, 'thin' => 'font-thin',
|
||||
FontWeight::ExtraLight, 'extralight' => 'font-extralight',
|
||||
FontWeight::Light, 'light' => 'font-light',
|
||||
FontWeight::Medium, 'medium' => 'font-medium',
|
||||
FontWeight::Normal, 'normal' => 'font-normal',
|
||||
FontWeight::SemiBold, 'semibold' => 'font-semibold',
|
||||
FontWeight::Bold, 'bold' => 'font-bold',
|
||||
FontWeight::ExtraBold, 'extrabold' => 'font-extrabold',
|
||||
FontWeight::Black, 'black' => 'font-black',
|
||||
default => $weight,
|
||||
},
|
||||
match ($size) {
|
||||
ActionSize::ExtraSmall => 'text-xs',
|
||||
ActionSize::Small => 'text-sm',
|
||||
ActionSize::Medium => 'text-sm',
|
||||
ActionSize::Large => 'text-sm',
|
||||
ActionSize::ExtraLarge => 'text-sm',
|
||||
default => null,
|
||||
},
|
||||
match ($color) {
|
||||
'gray' => 'text-gray-700 dark:text-gray-200',
|
||||
default => 'text-custom-600 dark:text-custom-400',
|
||||
},
|
||||
'group-hover/link:underline group-focus-visible/link:underline',
|
||||
]);
|
||||
} else {
|
||||
$labelClasses = 'sr-only';
|
||||
}
|
||||
|
||||
$labelStyles = \Illuminate\Support\Arr::toCssStyles([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [400, 600],
|
||||
alias: 'link.label',
|
||||
) => $color !== 'gray',
|
||||
]);
|
||||
|
||||
$iconClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
'fi-link-icon',
|
||||
match ($iconSize) {
|
||||
IconSize::Small => 'h-4 w-4',
|
||||
IconSize::Medium => 'h-5 w-5',
|
||||
IconSize::Large => 'h-6 w-6',
|
||||
default => $iconSize,
|
||||
},
|
||||
match ($color) {
|
||||
'gray' => 'text-gray-400 dark:text-gray-500',
|
||||
default => 'text-custom-600 dark:text-custom-400',
|
||||
},
|
||||
]);
|
||||
|
||||
$iconStyles = \Illuminate\Support\Arr::toCssStyles([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [400, 600],
|
||||
alias: 'link.icon',
|
||||
) => $color !== 'gray',
|
||||
]);
|
||||
|
||||
$badgeContainerClasses = 'fi-link-badge-ctn absolute start-full top-0 z-[1] w-max -translate-x-1/4 -translate-y-3/4 rounded-md bg-white dark:bg-gray-900 rtl:translate-x-1/4';
|
||||
|
||||
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
|
||||
|
||||
$hasLoadingIndicator = filled($wireTarget) || ($type === 'submit' && filled($form));
|
||||
|
||||
if ($hasLoadingIndicator) {
|
||||
$loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES);
|
||||
}
|
||||
|
||||
$hasTooltip = filled($tooltip);
|
||||
@endphp
|
||||
|
||||
@if ($tag === 'a')
|
||||
<a
|
||||
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
|
||||
@if ($keyBindings || $hasTooltip)
|
||||
x-data="{}"
|
||||
@endif
|
||||
@if ($keyBindings)
|
||||
x-bind:id="$id('key-bindings')"
|
||||
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
|
||||
@endif
|
||||
@if ($hasTooltip)
|
||||
x-tooltip="{
|
||||
content: @js($tooltip),
|
||||
theme: $store.theme,
|
||||
}"
|
||||
@endif
|
||||
{{ $attributes->class([$linkClasses]) }}
|
||||
>
|
||||
@if ($icon && $iconPosition === IconPosition::Before)
|
||||
<x-filament::icon
|
||||
:alias="$iconAlias"
|
||||
:icon="$icon"
|
||||
:class="$iconClasses"
|
||||
:style="$iconStyles"
|
||||
/>
|
||||
@endif
|
||||
|
||||
<span class="{{ $labelClasses }}" style="{{ $labelStyles }}">
|
||||
{{ $slot }}
|
||||
</span>
|
||||
|
||||
@if ($icon && $iconPosition === IconPosition::After)
|
||||
<x-filament::icon
|
||||
:alias="$iconAlias"
|
||||
:icon="$icon"
|
||||
:class="$iconClasses"
|
||||
:style="$iconStyles"
|
||||
/>
|
||||
@endif
|
||||
|
||||
@if (filled($badge))
|
||||
<div class="{{ $badgeContainerClasses }}">
|
||||
<x-filament::badge :color="$badgeColor" :size="$badgeSize">
|
||||
{{ $badge }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
@endif
|
||||
</a>
|
||||
@trim
|
||||
@elseif ($tag === 'button')
|
||||
<button
|
||||
@if ($keyBindings || $hasTooltip)
|
||||
x-data="{}"
|
||||
@endif
|
||||
@if ($keyBindings)
|
||||
x-bind:id="$id('key-bindings')"
|
||||
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
|
||||
@endif
|
||||
@if ($hasTooltip)
|
||||
x-tooltip="{
|
||||
content: @js($tooltip),
|
||||
theme: $store.theme,
|
||||
}"
|
||||
@endif
|
||||
{{
|
||||
$attributes
|
||||
->merge([
|
||||
'disabled' => $disabled,
|
||||
'form' => $formId,
|
||||
'type' => $type,
|
||||
'wire:loading.attr' => 'disabled',
|
||||
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
|
||||
], escape: false)
|
||||
->class([$linkClasses])
|
||||
}}
|
||||
>
|
||||
@if ($iconPosition === IconPosition::Before)
|
||||
@if ($icon)
|
||||
<x-filament::icon
|
||||
:attributes="
|
||||
\Filament\Support\prepare_inherited_attributes(
|
||||
new \Illuminate\View\ComponentAttributeBag([
|
||||
'alias' => $iconAlias,
|
||||
'icon' => $icon,
|
||||
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
|
||||
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
|
||||
])
|
||||
)
|
||||
->class([$iconClasses])
|
||||
->style([$iconStyles])
|
||||
"
|
||||
/>
|
||||
@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') => '',
|
||||
'wire:target' => $loadingIndicatorTarget,
|
||||
])
|
||||
)
|
||||
->class([$iconClasses])
|
||||
->style([$iconStyles])
|
||||
"
|
||||
/>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<span class="{{ $labelClasses }}" style="{{ $labelStyles }}">
|
||||
{{ $slot }}
|
||||
</span>
|
||||
|
||||
@if ($iconPosition === IconPosition::After)
|
||||
@if ($icon)
|
||||
<x-filament::icon
|
||||
:attributes="
|
||||
\Filament\Support\prepare_inherited_attributes(
|
||||
new \Illuminate\View\ComponentAttributeBag([
|
||||
'alias' => $iconAlias,
|
||||
'icon' => $icon,
|
||||
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
|
||||
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
|
||||
])
|
||||
)
|
||||
->class([$iconClasses])
|
||||
->style([$iconStyles])
|
||||
"
|
||||
/>
|
||||
@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') => '',
|
||||
'wire:target' => $loadingIndicatorTarget,
|
||||
])
|
||||
)
|
||||
->class([$iconClasses])
|
||||
->style([$iconStyles])
|
||||
"
|
||||
/>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if (filled($badge))
|
||||
<div class="{{ $badgeContainerClasses }}">
|
||||
<x-filament::badge :color="$badgeColor" :size="$badgeSize">
|
||||
{{ $badge }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
@endif
|
||||
</button>
|
||||
@trim
|
||||
@endif
|
|
@ -0,0 +1,18 @@
|
|||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{{ $attributes->class(['animate-spin']) }}
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M12 19C15.866 19 19 15.866 19 12C19 8.13401 15.866 5 12 5C8.13401 5 5 8.13401 5 12C5 15.866 8.13401 19 12 19ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z"
|
||||
fill-rule="evenodd"
|
||||
fill="currentColor"
|
||||
opacity="0.2"
|
||||
></path>
|
||||
<path
|
||||
d="M2 12C2 6.47715 6.47715 2 12 2V5C8.13401 5 5 8.13401 5 12H2Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
After Width: | Height: | Size: 628 B |
|
@ -0,0 +1,33 @@
|
|||
@php
|
||||
if ((! isset($columnSpan)) || (! is_array($columnSpan))) {
|
||||
$columnSpan = [
|
||||
'default' => $columnSpan ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
if ((! isset($columnStart)) || (! is_array($columnStart))) {
|
||||
$columnStart = [
|
||||
'default' => $columnStart ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
$height ??= '8rem';
|
||||
@endphp
|
||||
|
||||
<x-filament::grid.column
|
||||
:default="$columnSpan['default'] ?? 1"
|
||||
:sm="$columnSpan['sm'] ?? null"
|
||||
:md="$columnSpan['md'] ?? null"
|
||||
:lg="$columnSpan['lg'] ?? null"
|
||||
:xl="$columnSpan['xl'] ?? null"
|
||||
:twoXl="$columnSpan['2xl'] ?? null"
|
||||
:defaultStart="$columnStart['default'] ?? null"
|
||||
:smStart="$columnStart['sm'] ?? null"
|
||||
:mdStart="$columnStart['md'] ?? null"
|
||||
:lgStart="$columnStart['lg'] ?? null"
|
||||
:xlStart="$columnStart['xl'] ?? null"
|
||||
:twoXlStart="$columnStart['2xl'] ?? null"
|
||||
class="fi-loading-section"
|
||||
>
|
||||
<x-filament::section class="animate-pulse" style="height: {{ $height }}" />
|
||||
</x-filament::grid.column>
|
|
@ -0,0 +1,5 @@
|
|||
<p
|
||||
{{ $attributes->class(['fi-modal-description text-sm text-gray-500 dark:text-gray-400']) }}
|
||||
>
|
||||
{{ $slot }}
|
||||
</p>
|
|
@ -0,0 +1,5 @@
|
|||
<h2
|
||||
{{ $attributes->class(['fi-modal-heading text-base font-semibold leading-6 text-gray-950 dark:text-white']) }}
|
||||
>
|
||||
{{ $slot }}
|
||||
</h2>
|
|
@ -0,0 +1,376 @@
|
|||
@php
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
use Filament\Support\Facades\FilamentView;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'alignment' => Alignment::Start,
|
||||
'ariaLabelledby' => null,
|
||||
'autofocus' => \Filament\Support\View\Components\Modal::$isAutofocused,
|
||||
'closeButton' => \Filament\Support\View\Components\Modal::$hasCloseButton,
|
||||
'closeByClickingAway' => \Filament\Support\View\Components\Modal::$isClosedByClickingAway,
|
||||
'closeByEscaping' => \Filament\Support\View\Components\Modal::$isClosedByEscaping,
|
||||
'closeEventName' => 'close-modal',
|
||||
'description' => null,
|
||||
'displayClasses' => 'inline-block',
|
||||
'extraModalWindowAttributeBag' => null,
|
||||
'footer' => null,
|
||||
'footerActions' => [],
|
||||
'footerActionsAlignment' => Alignment::Start,
|
||||
'header' => null,
|
||||
'heading' => null,
|
||||
'icon' => null,
|
||||
'iconAlias' => null,
|
||||
'iconColor' => 'primary',
|
||||
'id' => null,
|
||||
'openEventName' => 'open-modal',
|
||||
'slideOver' => false,
|
||||
'stickyFooter' => false,
|
||||
'stickyHeader' => false,
|
||||
'trigger' => null,
|
||||
'visible' => true,
|
||||
'width' => 'sm',
|
||||
])
|
||||
|
||||
@php
|
||||
$hasDescription = filled($description);
|
||||
$hasFooter = (! \Filament\Support\is_slot_empty($footer)) || (is_array($footerActions) && count($footerActions)) || (! is_array($footerActions) && (! \Filament\Support\is_slot_empty($footerActions)));
|
||||
$hasHeading = filled($heading);
|
||||
$hasIcon = filled($icon);
|
||||
$hasSlot = ! \Filament\Support\is_slot_empty($slot);
|
||||
|
||||
if (! $alignment instanceof Alignment) {
|
||||
$alignment = filled($alignment) ? (Alignment::tryFrom($alignment) ?? $alignment) : null;
|
||||
}
|
||||
|
||||
if (! $footerActionsAlignment instanceof Alignment) {
|
||||
$footerActionsAlignment = filled($footerActionsAlignment) ? (Alignment::tryFrom($footerActionsAlignment) ?? $footerActionsAlignment) : null;
|
||||
}
|
||||
|
||||
if (! $width instanceof MaxWidth) {
|
||||
$width = filled($width) ? (MaxWidth::tryFrom($width) ?? $width) : null;
|
||||
}
|
||||
|
||||
$closeEventHandler = filled($id) ? '$dispatch(' . \Illuminate\Support\Js::from($closeEventName) . ', { id: ' . \Illuminate\Support\Js::from($id) . ' })' : 'close()';
|
||||
@endphp
|
||||
|
||||
<div
|
||||
@if ($ariaLabelledby)
|
||||
aria-labelledby="{{ $ariaLabelledby }}"
|
||||
@elseif ($heading)
|
||||
aria-labelledby="{{ "{$id}.heading" }}"
|
||||
@endif
|
||||
aria-modal="true"
|
||||
role="dialog"
|
||||
x-data="{
|
||||
isOpen: false,
|
||||
|
||||
livewire: null,
|
||||
|
||||
close: function () {
|
||||
this.isOpen = false
|
||||
|
||||
this.$refs.modalContainer.dispatchEvent(
|
||||
new CustomEvent('modal-closed', { id: '{{ $id }}' }),
|
||||
)
|
||||
},
|
||||
|
||||
open: function () {
|
||||
this.$nextTick(() => {
|
||||
this.isOpen = true
|
||||
|
||||
@if (FilamentView::hasSpaMode())
|
||||
this.$dispatch('ax-modal-opened')
|
||||
@endif
|
||||
|
||||
this.$refs.modalContainer.dispatchEvent(
|
||||
new CustomEvent('modal-opened', { id: '{{ $id }}' }),
|
||||
)
|
||||
})
|
||||
},
|
||||
}"
|
||||
@if ($id)
|
||||
x-on:{{ $closeEventName }}.window="if ($event.detail.id === '{{ $id }}') close()"
|
||||
x-on:{{ $openEventName }}.window="if ($event.detail.id === '{{ $id }}') open()"
|
||||
@endif
|
||||
x-trap.noscroll{{ $autofocus ? '' : '.noautofocus' }}="isOpen"
|
||||
x-bind:class="{
|
||||
'fi-modal-open': isOpen,
|
||||
}"
|
||||
@class([
|
||||
'fi-modal',
|
||||
'fi-width-screen' => $width === MaxWidth::Screen,
|
||||
$displayClasses,
|
||||
])
|
||||
>
|
||||
@if ($trigger)
|
||||
<div
|
||||
x-on:click="open"
|
||||
{{ $trigger->attributes->class(['fi-modal-trigger flex cursor-pointer']) }}
|
||||
>
|
||||
{{ $trigger }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div x-cloak x-show="isOpen">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
x-show="isOpen"
|
||||
x-transition.duration.300ms.opacity
|
||||
@class([
|
||||
'fi-modal-close-overlay fixed inset-0 z-40 bg-gray-950/50 dark:bg-gray-950/75',
|
||||
])
|
||||
></div>
|
||||
|
||||
<div
|
||||
@class([
|
||||
'fixed inset-0 z-40',
|
||||
'overflow-y-auto' => ! ($slideOver || ($width === MaxWidth::Screen)),
|
||||
'cursor-pointer' => $closeByClickingAway,
|
||||
])
|
||||
>
|
||||
<div
|
||||
x-ref="modalContainer"
|
||||
@if ($closeByClickingAway)
|
||||
{{-- Ensure that the click element is not triggered from a user selecting text inside an input. --}}
|
||||
x-on:click.self="
|
||||
document.activeElement.selectionStart === undefined &&
|
||||
document.activeElement.selectionEnd === undefined &&
|
||||
{{ $closeEventHandler }}
|
||||
"
|
||||
@endif
|
||||
{{
|
||||
$attributes->class([
|
||||
'relative grid min-h-full grid-rows-[1fr_auto_1fr] justify-items-center sm:grid-rows-[1fr_auto_3fr]',
|
||||
'p-4' => ! ($slideOver || ($width === MaxWidth::Screen)),
|
||||
])
|
||||
}}
|
||||
>
|
||||
<div
|
||||
x-data="{ isShown: false }"
|
||||
x-init="
|
||||
$nextTick(() => {
|
||||
isShown = isOpen
|
||||
$watch('isOpen', () => (isShown = isOpen))
|
||||
})
|
||||
"
|
||||
@if ($closeByEscaping)
|
||||
x-on:keydown.window.escape="{{ $closeEventHandler }}"
|
||||
@endif
|
||||
x-show="isShown"
|
||||
x-transition:enter="duration-300"
|
||||
x-transition:leave="duration-300"
|
||||
@if ($width === MaxWidth::Screen)
|
||||
@elseif ($slideOver)
|
||||
x-transition:enter-start="translate-x-full rtl:-translate-x-full"
|
||||
x-transition:enter-end="translate-x-0"
|
||||
x-transition:leave-start="translate-x-0"
|
||||
x-transition:leave-end="translate-x-full rtl:-translate-x-full"
|
||||
@else
|
||||
x-transition:enter-start="scale-95 opacity-0"
|
||||
x-transition:enter-end="scale-100 opacity-100"
|
||||
x-transition:leave-start="scale-100 opacity-100"
|
||||
x-transition:leave-end="scale-95 opacity-0"
|
||||
@endif
|
||||
{{
|
||||
($extraModalWindowAttributeBag ?? new \Illuminate\View\ComponentAttributeBag)->class([
|
||||
'fi-modal-window pointer-events-auto relative row-start-2 flex w-full cursor-default flex-col bg-white shadow-xl ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10',
|
||||
'fi-modal-slide-over-window ms-auto overflow-y-auto' => $slideOver,
|
||||
// Using an arbitrary value instead of the h-dvh class that was added in Tailwind CSS v3.4.0
|
||||
// to ensure compatibility with custom themes that may use an older version of Tailwind CSS.
|
||||
'h-[100dvh]' => $slideOver || ($width === MaxWidth::Screen),
|
||||
'mx-auto rounded-xl' => ! ($slideOver || ($width === MaxWidth::Screen)),
|
||||
'hidden' => ! $visible,
|
||||
match ($width) {
|
||||
MaxWidth::ExtraSmall => 'max-w-xs',
|
||||
MaxWidth::Small => 'max-w-sm',
|
||||
MaxWidth::Medium => 'max-w-md',
|
||||
MaxWidth::Large => 'max-w-lg',
|
||||
MaxWidth::ExtraLarge => 'max-w-xl',
|
||||
MaxWidth::TwoExtraLarge => 'max-w-2xl',
|
||||
MaxWidth::ThreeExtraLarge => 'max-w-3xl',
|
||||
MaxWidth::FourExtraLarge => 'max-w-4xl',
|
||||
MaxWidth::FiveExtraLarge => 'max-w-5xl',
|
||||
MaxWidth::SixExtraLarge => 'max-w-6xl',
|
||||
MaxWidth::SevenExtraLarge => 'max-w-7xl',
|
||||
MaxWidth::Full => 'max-w-full',
|
||||
MaxWidth::MinContent => 'max-w-min',
|
||||
MaxWidth::MaxContent => 'max-w-max',
|
||||
MaxWidth::FitContent => 'max-w-fit',
|
||||
MaxWidth::Prose => 'max-w-prose',
|
||||
MaxWidth::ScreenSmall => 'max-w-screen-sm',
|
||||
MaxWidth::ScreenMedium => 'max-w-screen-md',
|
||||
MaxWidth::ScreenLarge => 'max-w-screen-lg',
|
||||
MaxWidth::ScreenExtraLarge => 'max-w-screen-xl',
|
||||
MaxWidth::ScreenTwoExtraLarge => 'max-w-screen-2xl',
|
||||
MaxWidth::Screen => 'fixed inset-0',
|
||||
default => $width,
|
||||
},
|
||||
])
|
||||
}}
|
||||
>
|
||||
@if ($heading || $header)
|
||||
<div
|
||||
@class([
|
||||
'fi-modal-header flex px-6 pt-6',
|
||||
'pb-6' => (! $hasSlot) && (! $hasFooter),
|
||||
'fi-sticky sticky top-0 z-10 border-b border-gray-200 bg-white pb-6 dark:border-white/10 dark:bg-gray-900' => $stickyHeader,
|
||||
'rounded-t-xl' => $stickyHeader && ! ($slideOver || ($width === MaxWidth::Screen)),
|
||||
match ($alignment) {
|
||||
Alignment::Start, Alignment::Left => 'gap-x-5',
|
||||
Alignment::Center => 'flex-col',
|
||||
default => null,
|
||||
},
|
||||
'items-center' => $hasIcon && $hasHeading && (! $hasDescription) && in_array($alignment, [Alignment::Start, Alignment::Left]),
|
||||
])
|
||||
>
|
||||
@if ($closeButton)
|
||||
<div
|
||||
@class([
|
||||
'absolute',
|
||||
'end-4 top-4' => ! $slideOver,
|
||||
'end-6 top-6' => $slideOver,
|
||||
])
|
||||
>
|
||||
<x-filament::icon-button
|
||||
color="gray"
|
||||
icon="heroicon-o-x-mark"
|
||||
icon-alias="modal.close-button"
|
||||
icon-size="lg"
|
||||
:label="__('filament::components/modal.actions.close.label')"
|
||||
tabindex="-1"
|
||||
:x-on:click="$closeEventHandler"
|
||||
class="fi-modal-close-btn"
|
||||
/>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($header)
|
||||
{{ $header }}
|
||||
@else
|
||||
@if ($hasIcon)
|
||||
<div
|
||||
@class([
|
||||
'mb-5 flex items-center justify-center' => $alignment === Alignment::Center,
|
||||
])
|
||||
>
|
||||
<div
|
||||
@class([
|
||||
'rounded-full',
|
||||
match ($iconColor) {
|
||||
'gray' => 'bg-gray-100 dark:bg-gray-500/20',
|
||||
default => 'fi-color-custom bg-custom-100 dark:bg-custom-500/20',
|
||||
},
|
||||
is_string($iconColor) ? "fi-color-{$iconColor}" : null,
|
||||
match ($alignment) {
|
||||
Alignment::Start, Alignment::Left => 'p-2',
|
||||
Alignment::Center => 'p-3',
|
||||
default => null,
|
||||
},
|
||||
])
|
||||
@style([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$iconColor,
|
||||
shades: [100, 400, 500, 600],
|
||||
alias: 'modal.icon',
|
||||
) => $iconColor !== 'gray',
|
||||
])
|
||||
>
|
||||
<x-filament::icon
|
||||
:alias="$iconAlias"
|
||||
:icon="$icon"
|
||||
@class([
|
||||
'fi-modal-icon h-6 w-6',
|
||||
match ($iconColor) {
|
||||
'gray' => 'text-gray-500 dark:text-gray-400',
|
||||
default => 'text-custom-600 dark:text-custom-400',
|
||||
},
|
||||
])
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div
|
||||
@class([
|
||||
'text-center' => $alignment === Alignment::Center,
|
||||
])
|
||||
>
|
||||
<x-filament::modal.heading
|
||||
@class([
|
||||
'me-6' => $closeButton && ((! $hasIcon) || in_array($alignment, [Alignment::Start, Alignment::Left])),
|
||||
'ms-6' => $closeButton && (! $hasIcon) && ($alignment === Alignment::Center),
|
||||
])
|
||||
>
|
||||
{{ $heading }}
|
||||
</x-filament::modal.heading>
|
||||
|
||||
@if ($hasDescription)
|
||||
<x-filament::modal.description
|
||||
class="mt-2"
|
||||
>
|
||||
{{ $description }}
|
||||
</x-filament::modal.description>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($hasSlot)
|
||||
<div
|
||||
@class([
|
||||
'fi-modal-content flex flex-col gap-y-4 py-6',
|
||||
'flex-1' => ($width === MaxWidth::Screen) || $slideOver,
|
||||
'pe-6 ps-[5.25rem]' => $hasIcon && ($alignment === Alignment::Start) && (! $stickyHeader),
|
||||
'px-6' => ! ($hasIcon && ($alignment === Alignment::Start) && (! $stickyHeader)),
|
||||
])
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($hasFooter)
|
||||
<div
|
||||
@class([
|
||||
'fi-modal-footer w-full',
|
||||
'pe-6 ps-[5.25rem]' => $hasIcon && ($alignment === Alignment::Start) && ($footerActionsAlignment !== Alignment::Center) && (! $stickyFooter),
|
||||
'px-6' => ! ($hasIcon && ($alignment === Alignment::Start) && ($footerActionsAlignment !== Alignment::Center) && (! $stickyFooter)),
|
||||
'fi-sticky sticky bottom-0 border-t border-gray-200 bg-white py-5 dark:border-white/10 dark:bg-gray-900' => $stickyFooter,
|
||||
'rounded-b-xl' => $stickyFooter && ! ($slideOver || ($width === MaxWidth::Screen)),
|
||||
'pb-6' => ! $stickyFooter,
|
||||
'mt-6' => (! $stickyFooter) && (! $hasSlot),
|
||||
'mt-auto' => $slideOver,
|
||||
])
|
||||
>
|
||||
@if (! \Filament\Support\is_slot_empty($footer))
|
||||
{{ $footer }}
|
||||
@else
|
||||
<div
|
||||
@class([
|
||||
'fi-modal-footer-actions gap-3',
|
||||
match ($footerActionsAlignment) {
|
||||
Alignment::Start, Alignment::Left => 'flex flex-wrap items-center',
|
||||
Alignment::Center => 'flex flex-col-reverse sm:grid sm:grid-cols-[repeat(auto-fit,minmax(0,1fr))]',
|
||||
Alignment::End, Alignment::Right => 'flex flex-row-reverse flex-wrap items-center',
|
||||
default => null,
|
||||
},
|
||||
])
|
||||
>
|
||||
@if (is_array($footerActions))
|
||||
@foreach ($footerActions as $action)
|
||||
{{ $action }}
|
||||
@endforeach
|
||||
@else
|
||||
{{ $footerActions }}
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,190 @@
|
|||
@props([
|
||||
'currentPageOptionProperty' => 'tableRecordsPerPage',
|
||||
'extremeLinks' => false,
|
||||
'paginator',
|
||||
'pageOptions' => [],
|
||||
])
|
||||
|
||||
@php
|
||||
use Illuminate\Contracts\Pagination\CursorPaginator;
|
||||
|
||||
$isRtl = __('filament-panels::layout.direction') === 'rtl';
|
||||
$isSimple = ! $paginator instanceof \Illuminate\Pagination\LengthAwarePaginator;
|
||||
@endphp
|
||||
|
||||
<nav
|
||||
aria-label="{{ __('filament::components/pagination.label') }}"
|
||||
role="navigation"
|
||||
{{
|
||||
$attributes->class([
|
||||
'fi-pagination grid grid-cols-[1fr_auto_1fr] items-center gap-x-3',
|
||||
'fi-simple' => $isSimple,
|
||||
])
|
||||
}}
|
||||
>
|
||||
@if (! $paginator->onFirstPage())
|
||||
@php
|
||||
if ($paginator instanceof CursorPaginator) {
|
||||
$wireClickAction = "setPage('{$paginator->previousCursor()->encode()}', '{$paginator->getCursorName()}')";
|
||||
} else {
|
||||
$wireClickAction = "previousPage('{$paginator->getPageName()}')";
|
||||
}
|
||||
@endphp
|
||||
|
||||
<x-filament::button
|
||||
color="gray"
|
||||
rel="prev"
|
||||
:wire:click="$wireClickAction"
|
||||
:wire:key="$this->getId() . '.pagination.previous'"
|
||||
class="fi-pagination-previous-btn justify-self-start"
|
||||
>
|
||||
{{ __('filament::components/pagination.actions.previous.label') }}
|
||||
</x-filament::button>
|
||||
@endif
|
||||
|
||||
@if (! $isSimple)
|
||||
<span
|
||||
class="fi-pagination-overview text-sm font-medium text-gray-700 dark:text-gray-200"
|
||||
>
|
||||
{{
|
||||
trans_choice(
|
||||
'filament::components/pagination.overview',
|
||||
$paginator->total(),
|
||||
[
|
||||
'first' => \Illuminate\Support\Number::format($paginator->firstItem() ?? 0),
|
||||
'last' => \Illuminate\Support\Number::format($paginator->lastItem() ?? 0),
|
||||
'total' => \Illuminate\Support\Number::format($paginator->total()),
|
||||
],
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
@endif
|
||||
|
||||
@if (count($pageOptions) > 1)
|
||||
<div class="col-start-2 justify-self-center">
|
||||
<label class="fi-pagination-records-per-page-select fi-compact">
|
||||
<x-filament::input.wrapper>
|
||||
<x-filament::input.select
|
||||
:wire:model.live="$currentPageOptionProperty"
|
||||
>
|
||||
@foreach ($pageOptions as $option)
|
||||
<option value="{{ $option }}">
|
||||
{{ $option === 'all' ? __('filament::components/pagination.fields.records_per_page.options.all') : $option }}
|
||||
</option>
|
||||
@endforeach
|
||||
</x-filament::input.select>
|
||||
</x-filament::input.wrapper>
|
||||
|
||||
<span class="sr-only">
|
||||
{{ __('filament::components/pagination.fields.records_per_page.label') }}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label class="fi-pagination-records-per-page-select">
|
||||
<x-filament::input.wrapper
|
||||
:prefix="__('filament::components/pagination.fields.records_per_page.label')"
|
||||
>
|
||||
<x-filament::input.select
|
||||
:wire:model.live="$currentPageOptionProperty"
|
||||
>
|
||||
@foreach ($pageOptions as $option)
|
||||
<option value="{{ $option }}">
|
||||
{{ $option === 'all' ? __('filament::components/pagination.fields.records_per_page.options.all') : $option }}
|
||||
</option>
|
||||
@endforeach
|
||||
</x-filament::input.select>
|
||||
</x-filament::input.wrapper>
|
||||
</label>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($paginator->hasMorePages())
|
||||
@php
|
||||
if ($paginator instanceof CursorPaginator) {
|
||||
$wireClickAction = "setPage('{$paginator->nextCursor()->encode()}', '{$paginator->getCursorName()}')";
|
||||
} else {
|
||||
$wireClickAction = "nextPage('{$paginator->getPageName()}')";
|
||||
}
|
||||
@endphp
|
||||
|
||||
<x-filament::button
|
||||
color="gray"
|
||||
rel="next"
|
||||
:wire:click="$wireClickAction"
|
||||
:wire:key="$this->getId() . '.pagination.next'"
|
||||
class="fi-pagination-next-btn col-start-3 justify-self-end"
|
||||
>
|
||||
{{ __('filament::components/pagination.actions.next.label') }}
|
||||
</x-filament::button>
|
||||
@endif
|
||||
|
||||
@if ((! $isSimple) && $paginator->hasPages())
|
||||
<ol
|
||||
class="fi-pagination-items justify-self-end rounded-lg bg-white shadow-sm ring-1 ring-gray-950/10 dark:bg-white/5 dark:ring-white/20"
|
||||
>
|
||||
@if (! $paginator->onFirstPage())
|
||||
@if ($extremeLinks)
|
||||
<x-filament::pagination.item
|
||||
:aria-label="__('filament::components/pagination.actions.first.label')"
|
||||
:icon="$isRtl ? 'heroicon-m-chevron-double-right' : 'heroicon-m-chevron-double-left'"
|
||||
:icon-alias="$isRtl ? 'pagination.first-button.rtl' : 'pagination.first-button'"
|
||||
rel="first"
|
||||
:wire:click="'gotoPage(1, \'' . $paginator->getPageName() . '\')'"
|
||||
:wire:key="$this->getId() . '.pagination.first'"
|
||||
/>
|
||||
@endif
|
||||
|
||||
<x-filament::pagination.item
|
||||
:aria-label="__('filament::components/pagination.actions.previous.label')"
|
||||
:icon="$isRtl ? 'heroicon-m-chevron-right' : 'heroicon-m-chevron-left'"
|
||||
{{-- @deprecated Use `pagination.previous-button.rtl` instead of `pagination.previous-button` for RTL. --}}
|
||||
:icon-alias="$isRtl ? ['pagination.previous-button.rtl', 'pagination.previous-button'] : 'pagination.previous-button'"
|
||||
rel="prev"
|
||||
:wire:click="'previousPage(\'' . $paginator->getPageName() . '\')'"
|
||||
:wire:key="$this->getId() . '.pagination.previous'"
|
||||
/>
|
||||
@endif
|
||||
|
||||
@foreach ($paginator->render()->offsetGet('elements') as $element)
|
||||
@if (is_string($element))
|
||||
<x-filament::pagination.item disabled :label="$element" />
|
||||
@endif
|
||||
|
||||
@if (is_array($element))
|
||||
@foreach ($element as $page => $url)
|
||||
<x-filament::pagination.item
|
||||
:active="$page === $paginator->currentPage()"
|
||||
:aria-label="trans_choice('filament::components/pagination.actions.go_to_page.label', $page, ['page' => $page])"
|
||||
:label="$page"
|
||||
:wire:click="'gotoPage(' . $page . ', \'' . $paginator->getPageName() . '\')'"
|
||||
:wire:key="$this->getId() . '.pagination.' . $paginator->getPageName() . '.' . $page"
|
||||
/>
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
@if ($paginator->hasMorePages())
|
||||
<x-filament::pagination.item
|
||||
:aria-label="__('filament::components/pagination.actions.next.label')"
|
||||
:icon="$isRtl ? 'heroicon-m-chevron-left' : 'heroicon-m-chevron-right'"
|
||||
{{-- @deprecated Use `pagination.next-button.rtl` instead of `pagination.next-button` for RTL. --}}
|
||||
:icon-alias="$isRtl ? ['pagination.next-button.rtl', 'pagination.next-button'] : 'pagination.next-button'"
|
||||
rel="next"
|
||||
:wire:click="'nextPage(\'' . $paginator->getPageName() . '\')'"
|
||||
:wire:key="$this->getId() . '.pagination.next'"
|
||||
/>
|
||||
|
||||
@if ($extremeLinks)
|
||||
<x-filament::pagination.item
|
||||
:aria-label="__('filament::components/pagination.actions.last.label')"
|
||||
:icon="$isRtl ? 'heroicon-m-chevron-double-left' : 'heroicon-m-chevron-double-right'"
|
||||
:icon-alias="$isRtl ? 'pagination.last-button.rtl' : 'pagination.last-button'"
|
||||
rel="last"
|
||||
:wire:click="'gotoPage(' . $paginator->lastPage() . ', \'' . $paginator->getPageName() . '\')'"
|
||||
:wire:key="$this->getId() . '.pagination.last'"
|
||||
/>
|
||||
@endif
|
||||
@endif
|
||||
</ol>
|
||||
@endif
|
||||
</nav>
|
|
@ -0,0 +1,50 @@
|
|||
@props([
|
||||
'active' => false,
|
||||
'ariaLabel' => null,
|
||||
'disabled' => false,
|
||||
'icon' => null,
|
||||
'iconAlias' => null,
|
||||
'label' => null,
|
||||
])
|
||||
|
||||
<li
|
||||
{{
|
||||
$attributes->class([
|
||||
'fi-pagination-item group/item border-x-[0.5px] border-gray-200 first:border-s-0 last:border-e-0 dark:border-white/10',
|
||||
'fi-disabled' => $disabled,
|
||||
'fi-active' => $active,
|
||||
])
|
||||
}}
|
||||
>
|
||||
<button
|
||||
aria-label="{{ $ariaLabel }}"
|
||||
@disabled($disabled)
|
||||
type="button"
|
||||
@class([
|
||||
'fi-pagination-item-button group/button relative flex overflow-hidden p-2 outline-none transition duration-75 group-first/item:rounded-s-lg group-last/item:rounded-e-lg',
|
||||
'hover:bg-gray-50 focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-primary-600 dark:hover:bg-white/5 dark:focus-visible:ring-primary-500' => ! $disabled,
|
||||
'bg-gray-50 dark:bg-white/5' => $active,
|
||||
])
|
||||
>
|
||||
@if (filled($icon))
|
||||
<x-filament::icon
|
||||
:alias="$iconAlias"
|
||||
:icon="$icon"
|
||||
class="fi-pagination-item-icon h-5 w-5 text-gray-400 transition duration-75 group-hover/button:text-gray-500 dark:text-gray-500 dark:group-hover/button:text-gray-400"
|
||||
/>
|
||||
@endif
|
||||
|
||||
@if (filled($label))
|
||||
<span
|
||||
@class([
|
||||
'fi-pagination-item-label px-1.5 text-sm font-semibold',
|
||||
'text-gray-700 dark:text-gray-200' => ! ($disabled || $active),
|
||||
'text-gray-500 dark:text-gray-400' => $disabled,
|
||||
'text-primary-600 dark:text-primary-400' => $active,
|
||||
])
|
||||
>
|
||||
{{ $label ?? '...' }}
|
||||
</span>
|
||||
@endif
|
||||
</button>
|
||||
</li>
|
|
@ -0,0 +1,5 @@
|
|||
<p
|
||||
{{ $attributes->class(['fi-section-header-description overflow-hidden break-words text-sm text-gray-500 dark:text-gray-400']) }}
|
||||
>
|
||||
{{ $slot }}
|
||||
</p>
|
|
@ -0,0 +1,5 @@
|
|||
<h3
|
||||
{{ $attributes->class(['fi-section-header-heading text-base font-semibold leading-6 text-gray-950 dark:text-white']) }}
|
||||
>
|
||||
{{ $slot }}
|
||||
</h3>
|
|
@ -0,0 +1,213 @@
|
|||
@php
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Support\Enums\IconSize;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'aside' => false,
|
||||
'collapsed' => false,
|
||||
'collapsible' => false,
|
||||
'compact' => false,
|
||||
'contentBefore' => false,
|
||||
'description' => null,
|
||||
'footerActions' => [],
|
||||
'footerActionsAlignment' => Alignment::Start,
|
||||
'headerActions' => [],
|
||||
'headerEnd' => null,
|
||||
'heading' => null,
|
||||
'icon' => null,
|
||||
'iconColor' => 'gray',
|
||||
'iconSize' => IconSize::Large,
|
||||
'persistCollapsed' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
$hasDescription = filled((string) $description);
|
||||
$hasHeading = filled($heading);
|
||||
$hasIcon = filled($icon);
|
||||
|
||||
if (is_array($headerActions)) {
|
||||
$headerActions = array_filter(
|
||||
$headerActions,
|
||||
fn ($headerAction): bool => $headerAction->isVisible(),
|
||||
);
|
||||
}
|
||||
|
||||
if (is_array($footerActions)) {
|
||||
$footerActions = array_filter(
|
||||
$footerActions,
|
||||
fn ($footerAction): bool => $footerAction->isVisible(),
|
||||
);
|
||||
}
|
||||
|
||||
$hasHeaderActions = $headerActions instanceof \Illuminate\Contracts\Support\Htmlable
|
||||
? ! \Filament\Support\is_slot_empty($headerActions)
|
||||
: filled($headerActions);
|
||||
|
||||
$hasFooterActions = $footerActions instanceof \Illuminate\Contracts\Support\Htmlable
|
||||
? ! \Filament\Support\is_slot_empty($footerActions)
|
||||
: filled($footerActions);
|
||||
|
||||
$hasHeader = $hasIcon || $hasHeading || $hasDescription || $collapsible || $hasHeaderActions || filled((string) $headerEnd);
|
||||
@endphp
|
||||
|
||||
<section
|
||||
{{-- TODO: Investigate Livewire bug - https://github.com/filamentphp/filament/pull/8511 --}}
|
||||
x-data="{
|
||||
isCollapsed: @if ($persistCollapsed) $persist(@js($collapsed)).as(`section-${$el.id}-isCollapsed`) @else @js($collapsed) @endif,
|
||||
}"
|
||||
@if ($collapsible)
|
||||
x-on:collapse-section.window="if ($event.detail.id == $el.id) isCollapsed = true"
|
||||
x-on:expand="isCollapsed = false"
|
||||
x-on:open-section.window="if ($event.detail.id == $el.id) isCollapsed = false"
|
||||
x-on:toggle-section.window="if ($event.detail.id == $el.id) isCollapsed = ! isCollapsed"
|
||||
x-bind:class="isCollapsed && 'fi-collapsed'"
|
||||
@endif
|
||||
{{
|
||||
$attributes->class([
|
||||
'fi-section',
|
||||
match ($aside) {
|
||||
true => 'fi-aside grid grid-cols-1 items-start gap-x-6 gap-y-4 md:grid-cols-3',
|
||||
false => 'rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10',
|
||||
},
|
||||
])
|
||||
}}
|
||||
>
|
||||
@if ($hasHeader)
|
||||
<header
|
||||
@if ($collapsible)
|
||||
x-on:click="isCollapsed = ! isCollapsed"
|
||||
@endif
|
||||
@class([
|
||||
'fi-section-header flex flex-col gap-3',
|
||||
'cursor-pointer' => $collapsible,
|
||||
match ($compact) {
|
||||
true => 'px-4 py-2.5',
|
||||
false => 'px-6 py-4',
|
||||
} => ! $aside,
|
||||
])
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
@if ($hasIcon)
|
||||
<x-filament::icon
|
||||
:icon="$icon"
|
||||
@class([
|
||||
'fi-section-header-icon self-start',
|
||||
match ($iconColor) {
|
||||
'gray' => 'text-gray-400 dark:text-gray-500',
|
||||
default => 'fi-color-custom text-custom-500 dark:text-custom-400',
|
||||
},
|
||||
is_string($iconColor) ? "fi-color-{$iconColor}" : null,
|
||||
match ($iconSize) {
|
||||
IconSize::Small, 'sm' => 'h-4 w-4 mt-1',
|
||||
IconSize::Medium, 'md' => 'h-5 w-5 mt-0.5',
|
||||
IconSize::Large, 'lg' => 'h-6 w-6',
|
||||
default => $iconSize,
|
||||
},
|
||||
])
|
||||
@style([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$iconColor,
|
||||
shades: [400, 500],
|
||||
alias: 'section.header.icon',
|
||||
) => $iconColor !== 'gray',
|
||||
])
|
||||
/>
|
||||
@endif
|
||||
|
||||
@if ($hasHeading || $hasDescription)
|
||||
<div class="grid flex-1 gap-y-1">
|
||||
@if ($hasHeading)
|
||||
<x-filament::section.heading>
|
||||
{{ $heading }}
|
||||
</x-filament::section.heading>
|
||||
@endif
|
||||
|
||||
@if ($hasDescription)
|
||||
<x-filament::section.description>
|
||||
{{ $description }}
|
||||
</x-filament::section.description>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($hasHeaderActions)
|
||||
<div class="hidden sm:block">
|
||||
<x-filament::actions
|
||||
:actions="$headerActions"
|
||||
:alignment="\Filament\Support\Enums\Alignment::Start"
|
||||
x-on:click.stop=""
|
||||
/>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{ $headerEnd }}
|
||||
|
||||
@if ($collapsible)
|
||||
<x-filament::icon-button
|
||||
color="gray"
|
||||
icon="heroicon-m-chevron-down"
|
||||
icon-alias="section.collapse-button"
|
||||
x-on:click.stop="isCollapsed = ! isCollapsed"
|
||||
x-bind:class="{ 'rotate-180': ! isCollapsed }"
|
||||
/>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if ($hasHeaderActions)
|
||||
<div class="sm:hidden">
|
||||
<x-filament::actions
|
||||
:actions="$headerActions"
|
||||
:alignment="\Filament\Support\Enums\Alignment::Start"
|
||||
x-on:click.stop=""
|
||||
/>
|
||||
</div>
|
||||
@endif
|
||||
</header>
|
||||
@endif
|
||||
|
||||
<div
|
||||
@if ($collapsible)
|
||||
x-bind:aria-expanded="(! isCollapsed).toString()"
|
||||
@if ($collapsed || $persistCollapsed)
|
||||
x-cloak
|
||||
@endif
|
||||
x-bind:class="{
|
||||
'invisible absolute h-0 overflow-hidden border-none': isCollapsed,
|
||||
}"
|
||||
@endif
|
||||
@class([
|
||||
'fi-section-content-ctn',
|
||||
'border-t border-gray-200 dark:border-white/10' => $hasHeader && (! $aside),
|
||||
'rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10 md:col-span-2' => $aside,
|
||||
'md:order-first' => $contentBefore,
|
||||
])
|
||||
>
|
||||
<div
|
||||
@class([
|
||||
'fi-section-content',
|
||||
match ($compact) {
|
||||
true => 'p-4',
|
||||
false => 'p-6',
|
||||
},
|
||||
])
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
@if ($hasFooterActions)
|
||||
<footer
|
||||
@class([
|
||||
'fi-section-footer border-t border-gray-200 dark:border-white/10',
|
||||
'px-6 py-4' => ! $compact,
|
||||
'px-4 py-2.5' => $compact,
|
||||
])
|
||||
>
|
||||
<x-filament::actions
|
||||
:actions="$footerActions"
|
||||
:alignment="$footerActionsAlignment"
|
||||
/>
|
||||
</footer>
|
||||
@endif
|
||||
</div>
|
||||
</section>
|
|
@ -0,0 +1,21 @@
|
|||
@props([
|
||||
'contained' => false,
|
||||
'label' => null,
|
||||
])
|
||||
|
||||
<nav
|
||||
{{
|
||||
$attributes
|
||||
->merge([
|
||||
'aria-label' => $label,
|
||||
'role' => 'tablist',
|
||||
])
|
||||
->class([
|
||||
'fi-tabs flex max-w-full gap-x-1 overflow-x-auto',
|
||||
'fi-contained border-b border-gray-200 px-3 py-2.5 dark:border-white/10' => $contained,
|
||||
'mx-auto rounded-xl bg-white p-2 shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10' => ! $contained,
|
||||
])
|
||||
}}
|
||||
>
|
||||
{{ $slot }}
|
||||
</nav>
|
|
@ -0,0 +1,123 @@
|
|||
@php
|
||||
use Filament\Support\Enums\IconPosition;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'active' => false,
|
||||
'alpineActive' => null,
|
||||
'badge' => null,
|
||||
'badgeColor' => null,
|
||||
'badgeTooltip' => null,
|
||||
'badgeIcon' => null,
|
||||
'badgeIconPosition' => IconPosition::Before,
|
||||
'href' => null,
|
||||
'icon' => null,
|
||||
'iconColor' => 'gray',
|
||||
'iconPosition' => IconPosition::Before,
|
||||
'spaMode' => null,
|
||||
'tag' => 'button',
|
||||
'target' => null,
|
||||
'type' => 'button',
|
||||
])
|
||||
|
||||
@php
|
||||
if (! $iconPosition instanceof IconPosition) {
|
||||
$iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null;
|
||||
}
|
||||
|
||||
$hasAlpineActiveClasses = filled($alpineActive);
|
||||
|
||||
$inactiveItemClasses = 'hover:bg-gray-50 focus-visible:bg-gray-50 dark:hover:bg-white/5 dark:focus-visible:bg-white/5';
|
||||
|
||||
// @deprecated `fi-tabs-item-active` has been replaced by `fi-active`.
|
||||
$activeItemClasses = 'fi-active fi-tabs-item-active bg-gray-50 dark:bg-white/5';
|
||||
|
||||
$inactiveLabelClasses = 'text-gray-500 group-hover:text-gray-700 group-focus-visible:text-gray-700 dark:text-gray-400 dark:group-hover:text-gray-200 dark:group-focus-visible:text-gray-200';
|
||||
|
||||
$activeLabelClasses = 'text-primary-600 dark:text-primary-400';
|
||||
|
||||
$iconClasses = 'fi-tabs-item-icon h-5 w-5 shrink-0 transition duration-75';
|
||||
|
||||
$inactiveIconClasses = 'text-gray-400 dark:text-gray-500';
|
||||
|
||||
$activeIconClasses = 'text-primary-600 dark:text-primary-400';
|
||||
@endphp
|
||||
|
||||
<{{ $tag }}
|
||||
@if ($tag === 'button')
|
||||
type="{{ $type }}"
|
||||
@elseif ($tag === 'a')
|
||||
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
|
||||
@endif
|
||||
@if ($hasAlpineActiveClasses)
|
||||
x-bind:class="{
|
||||
@js($inactiveItemClasses): {{-- format-ignore-start --}} ! ({{ $alpineActive }}) {{-- format-ignore-end --}},
|
||||
@js($activeItemClasses): {{ $alpineActive }},
|
||||
}"
|
||||
@endif
|
||||
{{
|
||||
$attributes
|
||||
->merge([
|
||||
'aria-selected' => $active,
|
||||
'role' => 'tab',
|
||||
])
|
||||
->class([
|
||||
'fi-tabs-item group flex items-center justify-center gap-x-2 whitespace-nowrap rounded-lg px-3 py-2 text-sm font-medium outline-none transition duration-75',
|
||||
$inactiveItemClasses => (! $hasAlpineActiveClasses) && (! $active),
|
||||
$activeItemClasses => (! $hasAlpineActiveClasses) && $active,
|
||||
])
|
||||
}}
|
||||
>
|
||||
@if ($icon && $iconPosition === IconPosition::Before)
|
||||
<x-filament::icon
|
||||
:icon="$icon"
|
||||
:x-bind:class="$hasAlpineActiveClasses ? '{ ' . \Illuminate\Support\Js::from($inactiveIconClasses) . ': ! (' . $alpineActive . '), ' . \Illuminate\Support\Js::from($activeIconClasses) . ': ' . $alpineActive . ' }' : null"
|
||||
@class([
|
||||
$iconClasses,
|
||||
$inactiveIconClasses => (! $hasAlpineActiveClasses) && (! $active),
|
||||
$activeIconClasses => (! $hasAlpineActiveClasses) && $active,
|
||||
])
|
||||
/>
|
||||
@endif
|
||||
|
||||
<span
|
||||
@if ($hasAlpineActiveClasses)
|
||||
x-bind:class="{
|
||||
@js($inactiveLabelClasses): {{-- format-ignore-start --}} ! ({{ $alpineActive }}) {{-- format-ignore-end --}},
|
||||
@js($activeLabelClasses): {{ $alpineActive }},
|
||||
}"
|
||||
@endif
|
||||
@class([
|
||||
'fi-tabs-item-label transition duration-75',
|
||||
$inactiveLabelClasses => (! $hasAlpineActiveClasses) && (! $active),
|
||||
$activeLabelClasses => (! $hasAlpineActiveClasses) && $active,
|
||||
])
|
||||
>
|
||||
{{ $slot }}
|
||||
</span>
|
||||
|
||||
@if ($icon && $iconPosition === IconPosition::After)
|
||||
<x-filament::icon
|
||||
:icon="$icon"
|
||||
:x-bind:class="$hasAlpineActiveClasses ? '{ ' . \Illuminate\Support\Js::from($inactiveIconClasses) . ': ! (' . $alpineActive . '), ' . \Illuminate\Support\Js::from($activeIconClasses) . ': ' . $alpineActive . ' }' : null"
|
||||
@class([
|
||||
$iconClasses,
|
||||
$inactiveIconClasses => (! $hasAlpineActiveClasses) && (! $active),
|
||||
$activeIconClasses => (! $hasAlpineActiveClasses) && $active,
|
||||
])
|
||||
/>
|
||||
@endif
|
||||
|
||||
@if (filled($badge))
|
||||
<x-filament::badge
|
||||
:color="$badgeColor"
|
||||
:icon="$badgeIcon"
|
||||
:icon-position="$badgeIconPosition"
|
||||
size="sm"
|
||||
:tooltip="$badgeTooltip"
|
||||
class="w-max"
|
||||
>
|
||||
{{ $badge }}
|
||||
</x-filament::badge>
|
||||
@endif
|
||||
</{{ $tag }}>
|
Loading…
Reference in New Issue