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 $model = PaketFoto::class;
|
||||||
|
|
||||||
protected static ?string $navigationIcon = 'heroicon-o-camera';
|
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
|
public static function form(Form $form): Form
|
||||||
{
|
{
|
||||||
|
@ -87,9 +94,9 @@ public static function table(Table $table): Table
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
->bulkActions([
|
->bulkActions([
|
||||||
Tables\Actions\BulkActionGroup::make([
|
// Tables\Actions\BulkActionGroup::make([
|
||||||
Tables\Actions\DeleteBulkAction::make(),
|
// Tables\Actions\DeleteBulkAction::make(),
|
||||||
]),
|
//]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,13 @@ class PromoResource extends Resource
|
||||||
protected static ?string $model = Promo::class;
|
protected static ?string $model = Promo::class;
|
||||||
|
|
||||||
protected static ?string $navigationIcon = 'heroicon-o-currency-dollar';
|
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
|
public static function form(Form $form): Form
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,6 +38,13 @@ class ReservasiiResource extends Resource
|
||||||
protected static ?string $model = Reservasii::class;
|
protected static ?string $model = Reservasii::class;
|
||||||
|
|
||||||
protected static ?string $navigationIcon = 'heroicon-o-squares-plus';
|
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
|
public static function form(Form $form): Form
|
||||||
{
|
{
|
||||||
|
@ -214,36 +221,17 @@ public static function table(Table $table): Table
|
||||||
|
|
||||||
Tables\Columns\TextColumn::make('metode_pembayaran')
|
Tables\Columns\TextColumn::make('metode_pembayaran')
|
||||||
->badge(),
|
->badge(),
|
||||||
|
|
||||||
Tables\Columns\TextColumn::make('created_at')
|
|
||||||
->label('Tanggal Dibuat')
|
|
||||||
->dateTime('d F Y - H:i')
|
|
||||||
->sortable()
|
|
||||||
->toggleable(isToggledHiddenByDefault: true),
|
|
||||||
])
|
])
|
||||||
->filters([
|
->filters([
|
||||||
Tables\Filters\SelectFilter::make('tipe_pembayaran')
|
|
||||||
->options([
|
|
||||||
'full' => 'Full Payment',
|
|
||||||
'dp' => 'Down Payment',
|
|
||||||
])
|
|
||||||
->label('Status Pembayaran'),
|
|
||||||
|
|
||||||
Tables\Filters\Filter::make('tanggal')
|
Tables\Filters\Filter::make('tanggal')
|
||||||
->form([
|
->form([
|
||||||
Forms\Components\DatePicker::make('tanggal_from')->label('Dari Tanggal'),
|
DatePicker::make('tanggal')->label('Tanggal'),
|
||||||
Forms\Components\DatePicker::make('tanggal_until')->label('Sampai Tanggal'),
|
|
||||||
])
|
])
|
||||||
->query(function (Builder $query, array $data): Builder {
|
->query(function (Builder $query, array $data): Builder {
|
||||||
return $query
|
return $query->when(
|
||||||
->when(
|
$data['tanggal'],
|
||||||
$data['tanggal_from'],
|
fn (Builder $query, $date): Builder => $query->whereDate('tanggal', $date),
|
||||||
fn (Builder $query, $date): Builder => $query->whereDate('tanggal', '>=', $date),
|
);
|
||||||
)
|
|
||||||
->when(
|
|
||||||
$data['tanggal_until'],
|
|
||||||
fn (Builder $query, $date): Builder => $query->whereDate('tanggal', '<=', $date),
|
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
->actions([
|
->actions([
|
||||||
|
|
|
@ -9,4 +9,4 @@
|
||||||
class CreateReservasii extends CreateRecord
|
class CreateReservasii extends CreateRecord
|
||||||
{
|
{
|
||||||
protected static string $resource = ReservasiiResource::class;
|
protected static string $resource = ReservasiiResource::class;
|
||||||
}
|
}
|
|
@ -5,6 +5,8 @@
|
||||||
use App\Filament\Resources\ReservasiiResource;
|
use App\Filament\Resources\ReservasiiResource;
|
||||||
use App\Filament\Resources\ReservasiiResource\Widgets\ReservasiiStats;
|
use App\Filament\Resources\ReservasiiResource\Widgets\ReservasiiStats;
|
||||||
use Filament\Actions;
|
use Filament\Actions;
|
||||||
|
use Filament\Forms\Components\Tabs\Tab;
|
||||||
|
use Filament\Resources\Components\Tabs;
|
||||||
use Filament\Resources\Pages\ListRecords;
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
class ListReservasiis extends 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[
|
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()
|
$todayEarnings = Reservasii::query()
|
||||||
->whereDate('tanggal', Carbon::today())
|
->whereDate('tanggal', Carbon::today())
|
||||||
->sum('total');
|
->sum('total');
|
||||||
|
$mounthEarnings = Reservasii::whereMonth('created_at', now()->month)
|
||||||
|
->whereYear('created_at', now()->year)
|
||||||
|
->sum('total');
|
||||||
return [
|
return [
|
||||||
Stat::make('Reservasi Hari Ini', Reservasii::query()->whereDate('tanggal', Carbon::today())->count()),
|
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 $model = User::class;
|
||||||
protected static ?string $recordTitleAttribute = 'name';
|
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';
|
protected static ?string $navigationIcon = 'heroicon-o-user-group';
|
||||||
|
|
||||||
public static function form(Form $form): Form
|
public static function form(Form $form): Form
|
||||||
|
@ -78,9 +84,9 @@ public static function table(Table $table): Table
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
->bulkActions([
|
->bulkActions([
|
||||||
Tables\Actions\BulkActionGroup::make([
|
// Tables\Actions\BulkActionGroup::make([
|
||||||
Tables\Actions\DeleteBulkAction::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')
|
->path('admin')
|
||||||
->login()
|
->login()
|
||||||
->colors([
|
->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')
|
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
|
||||||
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
|
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
|
||||||
->pages([
|
->pages([
|
||||||
|
@ -41,7 +44,7 @@ public function panel(Panel $panel): Panel
|
||||||
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
|
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
|
||||||
->widgets([
|
->widgets([
|
||||||
ReservasiiStats::class,
|
ReservasiiStats::class,
|
||||||
CalendarWidget::class
|
//CalendarWidget::class
|
||||||
//Widgets\AccountWidget::class,
|
//Widgets\AccountWidget::class,
|
||||||
//Widgets\FilamentInfoWidget::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