Repo rebuild
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
<div class="d-flex align-items-center" x-data>
|
||||
<form class="mr-2"
|
||||
x-on:submit.prevent="
|
||||
$refs.exportBtn.disabled = true;
|
||||
var oTable = LaravelDataTables['{{ $tableId }}'];
|
||||
var baseUrl = oTable.ajax.url() === '' ? window.location.toString() : oTable.ajax.url();
|
||||
|
||||
var url = new URL(baseUrl);
|
||||
var searchParams = new URLSearchParams(url.search);
|
||||
searchParams.set('action', 'exportQueue');
|
||||
searchParams.set('exportType', '{{ $fileType }}');
|
||||
searchParams.set('sheetName', '{{ $sheetName }}');
|
||||
searchParams.set('buttonName', '{{ $buttonName }}');
|
||||
searchParams.set('emailTo', '{{ urlencode($emailTo) }}');
|
||||
|
||||
var tableParams = $.param(oTable.ajax.params());
|
||||
if (tableParams) {
|
||||
var tableSearchParams = new URLSearchParams(tableParams);
|
||||
tableSearchParams.forEach((value, key) => {
|
||||
searchParams.append(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
url.search = searchParams.toString();
|
||||
|
||||
$.get(url.toString()).then(function(exportId) {
|
||||
$wire.export(exportId);
|
||||
}).catch(function(error) {
|
||||
$wire.exportFinished = true;
|
||||
$wire.exporting = false;
|
||||
$wire.exportFailed = true;
|
||||
});
|
||||
">
|
||||
<button type="submit" x-ref="exportBtn" :disabled="$wire.exporting"
|
||||
class="{{ $class }}">{{ $buttonName }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@if ($exporting && $emailTo)
|
||||
<div class="d-inline">Export will be emailed to {{ $emailTo }}.</div>
|
||||
@endif
|
||||
|
||||
@if ($exporting && !$exportFinished)
|
||||
<div class="d-inline" wire:poll="updateExportProgress">Exporting...please wait.</div>
|
||||
@endif
|
||||
|
||||
@if ($exportFinished && !$exportFailed && !$autoDownload)
|
||||
<span>Done. Download file <a href="#" class="text-primary"
|
||||
wire:click.prevent="downloadExport">here</a></span>
|
||||
@endif
|
||||
|
||||
@if ($exportFinished && !$exportFailed && $autoDownload && $downloaded)
|
||||
<span>Done. File has been downloaded.</span>
|
||||
@endif
|
||||
|
||||
@if ($exportFailed)
|
||||
<span>Export failed, please try again later.</span>
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
Attached you will find requested report.
|
||||
@@ -0,0 +1,18 @@
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
window.{{ config('datatables-html.namespace', 'LaravelDataTables') }} =
|
||||
window.{{ config('datatables-html.namespace', 'LaravelDataTables') }} || {};
|
||||
$.ajaxSetup({headers: {'X-CSRF-TOKEN': '{{ csrf_token() }}'}});
|
||||
@foreach ($editors as $editor)
|
||||
var {{ $editor->instance }} =
|
||||
window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}["%1$s-{{ $editor->instance }}"] = new
|
||||
$.fn.dataTable.Editor({!! $editor->toJson() !!});
|
||||
{!! $editor->scripts !!}
|
||||
@foreach ((array) $editor->events as $event)
|
||||
{{ $editor->instance }}.on('{!! $event['event'] !!}', {!! $event['script'] !!});
|
||||
@endforeach
|
||||
@endforeach
|
||||
window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}["%1$s"] = $("#%1$s").DataTable(%2$s);
|
||||
});
|
||||
@foreach ($scripts as $script)
|
||||
@include($script)
|
||||
@endforeach
|
||||
@@ -0,0 +1,18 @@
|
||||
window.dtx = window.dtx || {};
|
||||
window.dtx["%1$s"] = function(opts) {
|
||||
window.{{ config('datatables-html.namespace', 'LaravelDataTables') }} =
|
||||
window.{{ config('datatables-html.namespace', 'LaravelDataTables') }} || {};
|
||||
@if (isset($editors))
|
||||
@foreach ($editors as $editor)
|
||||
var {{ $editor->instance }} =
|
||||
window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}["%1$s-{{ $editor->instance }}"] = new
|
||||
$.fn.dataTable.Editor({!! $editor->toJson() !!});
|
||||
{!! $editor->scripts !!}
|
||||
@foreach ((array) $editor->events as $event)
|
||||
{{ $editor->instance }}.on('{!! $event['event'] !!}', {!! $event['script'] !!});
|
||||
@endforeach
|
||||
@endforeach
|
||||
@endif
|
||||
return window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}["%1$s"] =
|
||||
$("#%1$s").DataTable($.extend(%2$s, opts));
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
$(function(){
|
||||
@foreach ($editors as $editor)
|
||||
{{ config('datatables-html.namespace', 'LaravelDataTables') }}["%1$s-{{ $editor->instance }}"].on('preSubmit',
|
||||
function(e, data, action) {
|
||||
if (action !== 'remove') return;
|
||||
|
||||
for (let row_id of Object.keys(data.data))
|
||||
{
|
||||
data.data[row_id] = {
|
||||
DT_RowId: data.data[row_id].DT_RowId
|
||||
};
|
||||
}
|
||||
});
|
||||
@endforeach
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
window.{{ config('datatables-html.namespace', 'LaravelDataTables') }} =
|
||||
window.{{ config('datatables-html.namespace', 'LaravelDataTables') }} || {};
|
||||
window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}.options = %2$s
|
||||
window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}.editors = [];
|
||||
@foreach ($editors as $editor)
|
||||
window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}.editors["{{ $editor->instance }}"] =
|
||||
{!! $editor->toJson() !!}
|
||||
@endforeach
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Print Table</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name=description content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
|
||||
<style>
|
||||
body {
|
||||
margin: 20px
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
|
||||
aria-hidden="true"></div>
|
||||
<table class="table table-bordered table-condensed table-striped">
|
||||
@foreach ($data as $row)
|
||||
@if ($loop->first)
|
||||
<tr>
|
||||
@foreach ($row as $key => $value)
|
||||
<th>{!! $key !!}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
@endif
|
||||
<tr>
|
||||
@foreach ($row as $key => $value)
|
||||
@if (is_string($value) || is_numeric($value))
|
||||
<td>{!! $value !!}</td>
|
||||
@else
|
||||
<td></td>
|
||||
@endif
|
||||
@endforeach
|
||||
</tr>
|
||||
@endforeach
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,23 @@
|
||||
$(function(){
|
||||
$('#%1$s').on('xhr.dt', function (e, settings, json, xhr) {
|
||||
if (json == null || !('disableOrdering' in json)) return;
|
||||
|
||||
let table = {{ config('datatables-html.namespace', 'LaravelDataTables') }}[$(this).attr('id')];
|
||||
if (json.disableOrdering) {
|
||||
table.settings()[0].aoColumns.forEach(function(column) {
|
||||
column.bSortable = false;
|
||||
$(column.nTh).removeClass('sorting_asc sorting_desc sorting').addClass('sorting_disabled');
|
||||
});
|
||||
} else {
|
||||
let changed = false;
|
||||
table.settings()[0].aoColumns.forEach(function(column) {
|
||||
if (column.bSortable) return;
|
||||
column.bSortable = true;
|
||||
changed = true;
|
||||
});
|
||||
if (changed) {
|
||||
table.draw();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
document.addEventListener("DOMContentLoaded",function(){window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}=window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}||{};window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}["%1$s"]=$("#%1$s").DataTable(%2$s);});
|
||||
@foreach ($scripts as $script)
|
||||
@include($script)
|
||||
@endforeach
|
||||
@@ -0,0 +1,55 @@
|
||||
@php
|
||||
use Filament\Actions\View\ActionsRenderHook;
|
||||
use Filament\Support\Facades\FilamentView;
|
||||
|
||||
$actionModalAlignment = $action->getModalAlignment();
|
||||
$actionIsModalAutofocused = $action->isModalAutofocused();
|
||||
$actionHasModalCloseButton = $action->hasModalCloseButton();
|
||||
$actionIsModalClosedByClickingAway = $action->isModalClosedByClickingAway();
|
||||
$actionIsModalClosedByEscaping = $action->isModalClosedByEscaping();
|
||||
$actionModalDescription = $action->getModalDescription();
|
||||
$actionExtraModalWindowAttributeBag = $action->getExtraModalWindowAttributeBag();
|
||||
$actionExtraModalOverlayAttributeBag = $action->getExtraModalOverlayAttributeBag();
|
||||
$actionModalFooterActions = $action->getVisibleModalFooterActions();
|
||||
$actionModalFooterActionsAlignment = $action->getModalFooterActionsAlignment();
|
||||
$actionModalHeading = $action->getModalHeading();
|
||||
$actionModalIcon = $action->getModalIcon();
|
||||
$actionModalIconColor = $action->getModalIconColor();
|
||||
$actionModalId = "fi-{$this->getId()}-action-{$action->getNestingIndex()}";
|
||||
$actionIsModalSlideOver = $action->isModalSlideOver();
|
||||
$actionModalSlideOverPosition = $action->getModalSlideOverPosition();
|
||||
$actionIsModalFooterSticky = $action->isModalFooterSticky();
|
||||
$actionIsModalHeaderSticky = $action->isModalHeaderSticky();
|
||||
$actionModalWidth = $action->getModalWidth();
|
||||
$actionLivewireCallMountedActionName = $action->hasFormWrapper()
|
||||
? $action->getLivewireCallMountedActionName()
|
||||
: null;
|
||||
$actionModalWireKey = "{$this->getId()}.actions.{$action->getName()}.modal";
|
||||
@endphp
|
||||
|
||||
<x-filament::modal :alignment="$actionModalAlignment" :autofocus="$actionIsModalAutofocused" :close-button="$actionHasModalCloseButton" :close-by-clicking-away="$actionIsModalClosedByClickingAway" :close-by-escaping="$actionIsModalClosedByEscaping"
|
||||
:description="$actionModalDescription" :extra-modal-window-attribute-bag="$actionExtraModalWindowAttributeBag" :extra-modal-overlay-attribute-bag="$actionExtraModalOverlayAttributeBag" :footer-actions="$actionModalFooterActions" :footer-actions-alignment="$actionModalFooterActionsAlignment" :heading="$actionModalHeading"
|
||||
:icon="$actionModalIcon" :icon-color="$actionModalIconColor" :id="$actionModalId" :slide-over="$actionIsModalSlideOver" :slide-over-position="$actionModalSlideOverPosition" :sticky-footer="$actionIsModalFooterSticky"
|
||||
:sticky-header="$actionIsModalHeaderSticky" :width="$actionModalWidth" :wire:key="$actionModalWireKey"
|
||||
:wire:submit.prevent="$actionLivewireCallMountedActionName"
|
||||
:x-on:modal-closed="'if ($event.detail.id === ' . \Illuminate\Support\Js::from($actionModalId) . ') $wire.unmountAction(false)'">
|
||||
{{ FilamentView::renderHook(ActionsRenderHook::MODAL_CUSTOM_CONTENT_BEFORE, scopes: static::class, data: ['action' => $action]) }}
|
||||
|
||||
{{ $action->getModalContent() }}
|
||||
|
||||
{{ FilamentView::renderHook(ActionsRenderHook::MODAL_CUSTOM_CONTENT_AFTER, scopes: static::class, data: ['action' => $action]) }}
|
||||
|
||||
@if ($this->mountedActionHasSchema(mountedAction: $action))
|
||||
{{ FilamentView::renderHook(ActionsRenderHook::MODAL_SCHEMA_BEFORE, scopes: static::class, data: ['action' => $action]) }}
|
||||
|
||||
{{ $this->getMountedActionSchema(mountedAction: $action) }}
|
||||
|
||||
{{ FilamentView::renderHook(ActionsRenderHook::MODAL_SCHEMA_AFTER, scopes: static::class, data: ['action' => $action]) }}
|
||||
@endif
|
||||
|
||||
{{ FilamentView::renderHook(ActionsRenderHook::MODAL_CUSTOM_CONTENT_FOOTER_BEFORE, scopes: static::class, data: ['action' => $action]) }}
|
||||
|
||||
{{ $action->getModalContentFooter() }}
|
||||
|
||||
{{ FilamentView::renderHook(ActionsRenderHook::MODAL_CUSTOM_CONTENT_FOOTER_AFTER, scopes: static::class, data: ['action' => $action]) }}
|
||||
</x-filament::modal>
|
||||
@@ -0,0 +1,62 @@
|
||||
@props([
|
||||
'actions' => [],
|
||||
'badge' => null,
|
||||
'badgeColor' => null,
|
||||
'button' => false,
|
||||
'buttonGroup' => null,
|
||||
'color' => null,
|
||||
'dropdownMaxHeight' => null,
|
||||
'dropdownOffset' => null,
|
||||
'dropdownPlacement' => null,
|
||||
'dropdownWidth' => null,
|
||||
'group' => null,
|
||||
'icon' => null,
|
||||
'iconSize' => null,
|
||||
'iconButton' => false,
|
||||
'label' => null,
|
||||
'link' => false,
|
||||
'size' => null,
|
||||
'tooltip' => null,
|
||||
'triggerView' => null,
|
||||
'view' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
$group ??= \Filament\Actions\ActionGroup::make($actions)
|
||||
->badgeColor($badgeColor)
|
||||
->color($color)
|
||||
->dropdownMaxHeight($dropdownMaxHeight)
|
||||
->dropdownOffset($dropdownOffset)
|
||||
->dropdownPlacement($dropdownPlacement)
|
||||
->dropdownWidth($dropdownWidth)
|
||||
->icon($icon)
|
||||
->iconSize($iconSize)
|
||||
->label($label)
|
||||
->size($size)
|
||||
->tooltip($tooltip)
|
||||
->triggerView($triggerView)
|
||||
->view($view);
|
||||
|
||||
$badge === true ? $group->badge() : $group->badge($badge);
|
||||
|
||||
if ($button) {
|
||||
$group
|
||||
->button()
|
||||
->iconPosition($attributes->get('iconPosition') ?? $attributes->get('icon-position'))
|
||||
->outlined($attributes->get('outlined') ?? false);
|
||||
}
|
||||
|
||||
if ($buttonGroup) {
|
||||
$group->buttonGroup();
|
||||
}
|
||||
|
||||
if ($iconButton) {
|
||||
$group->iconButton();
|
||||
}
|
||||
|
||||
if ($link) {
|
||||
$group->link();
|
||||
}
|
||||
@endphp
|
||||
|
||||
{{ $group }}
|
||||
@@ -0,0 +1,15 @@
|
||||
@if ($this instanceof \Filament\Actions\Contracts\HasActions && !$this->hasActionsModalRendered)
|
||||
<div wire:partial="action-modals" x-data="filamentActionModals({
|
||||
livewireId: @js($this->getId()),
|
||||
})" style="height: 0">
|
||||
@foreach ($this->getMountedActions() as $action)
|
||||
@if (!$loop->last || $this->mountedActionShouldOpenModal())
|
||||
{{ $action->toModalHtmlable() }}
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@php
|
||||
$this->hasActionsModalRendered = true;
|
||||
@endphp
|
||||
@endif
|
||||
@@ -0,0 +1,275 @@
|
||||
@php
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$items = $getItems();
|
||||
$blockPickerBlocks = $getBlockPickerBlocks();
|
||||
$blockPickerColumns = $getBlockPickerColumns();
|
||||
$blockPickerWidth = $getBlockPickerWidth();
|
||||
$hasBlockPreviews = $hasBlockPreviews();
|
||||
$hasInteractiveBlockPreviews = $hasInteractiveBlockPreviews();
|
||||
|
||||
$addAction = $getAction($getAddActionName());
|
||||
$addActionAlignment = $getAddActionAlignment();
|
||||
$addBetweenAction = $getAction($getAddBetweenActionName());
|
||||
$cloneAction = $getAction($getCloneActionName());
|
||||
$collapseAllAction = $getAction($getCollapseAllActionName());
|
||||
$editAction = $getAction($getEditActionName());
|
||||
$expandAllAction = $getAction($getExpandAllActionName());
|
||||
$deleteAction = $getAction($getDeleteActionName());
|
||||
$moveDownAction = $getAction($getMoveDownActionName());
|
||||
$moveUpAction = $getAction($getMoveUpActionName());
|
||||
$reorderAction = $getAction($getReorderActionName());
|
||||
$extraItemActions = $getExtraItemActions();
|
||||
|
||||
$isAddable = $isAddable();
|
||||
$isCloneable = $isCloneable();
|
||||
$isCollapsible = $isCollapsible();
|
||||
$isDeletable = $isDeletable();
|
||||
$isReorderableWithButtons = $isReorderableWithButtons();
|
||||
$isReorderableWithDragAndDrop = $isReorderableWithDragAndDrop();
|
||||
|
||||
$collapseAllActionIsVisible = $isCollapsible && $collapseAllAction->isVisible();
|
||||
$expandAllActionIsVisible = $isCollapsible && $expandAllAction->isVisible();
|
||||
$persistCollapsed = $shouldPersistCollapsed();
|
||||
|
||||
$key = $getKey();
|
||||
$statePath = $getStatePath();
|
||||
|
||||
$blockLabelHeadingTag = $getHeadingTag();
|
||||
$isBlockLabelTruncated = $isBlockLabelTruncated();
|
||||
$labelBetweenItems = $getLabelBetweenItems();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
<div
|
||||
{{ $attributes->merge($getExtraAttributes(), escape: false)->class(['fi-fo-builder', 'fi-collapsible' => $isCollapsible]) }}>
|
||||
@if ($collapseAllActionIsVisible || $expandAllActionIsVisible)
|
||||
<div @class(['fi-fo-builder-actions', 'fi-hidden' => count($items) < 2])>
|
||||
@if ($collapseAllActionIsVisible)
|
||||
<span x-on:click="$dispatch('builder-collapse', '{{ $statePath }}')">
|
||||
{{ $collapseAllAction }}
|
||||
</span>
|
||||
@endif
|
||||
|
||||
@if ($expandAllActionIsVisible)
|
||||
<span x-on:click="$dispatch('builder-expand', '{{ $statePath }}')">
|
||||
{{ $expandAllAction }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (count($items))
|
||||
<ul x-sortable data-sortable-animation-duration="{{ $getReorderAnimationDuration() }}"
|
||||
x-on:end.stop="
|
||||
$wire.mountAction(
|
||||
'reorder',
|
||||
{ items: $event.target.sortable.toArray() },
|
||||
{ schemaComponent: '{{ $key }}' },
|
||||
)
|
||||
"
|
||||
class="fi-fo-builder-items">
|
||||
@php
|
||||
$hasBlockLabels = $hasBlockLabels();
|
||||
$hasBlockIcons = $hasBlockIcons();
|
||||
$hasBlockNumbers = $hasBlockNumbers();
|
||||
$hasBlockHeaders = $hasBlockHeaders();
|
||||
@endphp
|
||||
|
||||
@foreach ($items as $itemKey => $item)
|
||||
@php
|
||||
$visibleExtraItemActions = array_filter(
|
||||
$extraItemActions,
|
||||
fn(Action $action): bool => $action(['item' => $itemKey])->isVisible(),
|
||||
);
|
||||
$cloneAction = $cloneAction(['item' => $itemKey]);
|
||||
$cloneActionIsVisible = $isCloneable && $cloneAction->isVisible();
|
||||
$deleteAction = $deleteAction(['item' => $itemKey]);
|
||||
$deleteActionIsVisible = $isDeletable && $deleteAction->isVisible();
|
||||
$editAction = $editAction(['item' => $itemKey]);
|
||||
$editActionIsVisible = $hasBlockPreviews && $editAction->isVisible();
|
||||
$moveDownAction = $moveDownAction(['item' => $itemKey])->disabled($loop->last);
|
||||
$moveDownActionIsVisible = $isReorderableWithButtons && $moveDownAction->isVisible();
|
||||
$moveUpAction = $moveUpAction(['item' => $itemKey])->disabled($loop->first);
|
||||
$moveUpActionIsVisible = $isReorderableWithButtons && $moveUpAction->isVisible();
|
||||
$reorderActionIsVisible = $isReorderableWithDragAndDrop && $reorderAction->isVisible();
|
||||
$hasItemHeader =
|
||||
$hasBlockHeaders &&
|
||||
($reorderActionIsVisible ||
|
||||
$moveUpActionIsVisible ||
|
||||
$moveDownActionIsVisible ||
|
||||
$hasBlockIcons ||
|
||||
$hasBlockLabels ||
|
||||
$editActionIsVisible ||
|
||||
$cloneActionIsVisible ||
|
||||
$deleteActionIsVisible ||
|
||||
$isCollapsible ||
|
||||
$visibleExtraItemActions);
|
||||
@endphp
|
||||
|
||||
<li wire:ignore.self wire:key="{{ $item->getLivewireKey() }}.item" x-data="{
|
||||
isCollapsed: @if ($persistCollapsed) $persist(@js($isCollapsed($item))).as(`builder-${@js($key)}-${@js($itemKey)}-isCollapsed`) @else @js($isCollapsed($item)) @endif,
|
||||
}"
|
||||
x-on:builder-expand.window="$event.detail === '{{ $statePath }}' && (isCollapsed = false)"
|
||||
x-on:builder-collapse.window="$event.detail === '{{ $statePath }}' && (isCollapsed = true)"
|
||||
x-on:expand="isCollapsed = false" x-sortable-item="{{ $itemKey }}"
|
||||
{{ $item->getParentComponent()->getExtraAttributeBag()->class(['fi-fo-builder-item', 'fi-fo-builder-item-has-header' => $hasItemHeader]) }}
|
||||
x-bind:class="{ 'fi-collapsed': isCollapsed }">
|
||||
@if ($hasItemHeader)
|
||||
<div @if ($isCollapsible) x-on:click.stop="isCollapsed = !isCollapsed" @endif
|
||||
class="fi-fo-builder-item-header">
|
||||
@if ($reorderActionIsVisible || $moveUpActionIsVisible || $moveDownActionIsVisible)
|
||||
<ul class="fi-fo-builder-item-header-start-actions">
|
||||
@if ($reorderActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $reorderAction->extraAttributes(['x-sortable-handle' => true], merge: true) }}
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@if ($moveUpActionIsVisible || $moveDownActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $moveUpAction }}
|
||||
</li>
|
||||
|
||||
<li x-on:click.stop>
|
||||
{{ $moveDownAction }}
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
@php
|
||||
$blockIcon = $item->getParentComponent()->getIcon($item->getRawState(), $itemKey);
|
||||
@endphp
|
||||
|
||||
@if ($hasBlockIcons && filled($blockIcon))
|
||||
{{ \Filament\Support\generate_icon_html($blockIcon, attributes: new \Illuminate\View\ComponentAttributeBag()->class(['fi-fo-builder-item-header-icon'])) }}
|
||||
@endif
|
||||
|
||||
@if ($hasBlockLabels)
|
||||
<{{ $blockLabelHeadingTag }} @class([
|
||||
'fi-fo-builder-item-header-label',
|
||||
'fi-truncated' => $isBlockLabelTruncated,
|
||||
])>
|
||||
{{ $item->getParentComponent()->getLabel($item->getRawState(), $itemKey, $loop->index) }}
|
||||
|
||||
@if ($hasBlockNumbers)
|
||||
{{ $loop->iteration }}
|
||||
@endif
|
||||
</{{ $blockLabelHeadingTag }}>
|
||||
@endif
|
||||
|
||||
@if (
|
||||
$editActionIsVisible ||
|
||||
$cloneActionIsVisible ||
|
||||
$deleteActionIsVisible ||
|
||||
$isCollapsible ||
|
||||
$visibleExtraItemActions)
|
||||
<ul class="fi-fo-builder-item-header-end-actions">
|
||||
@foreach ($visibleExtraItemActions as $extraItemAction)
|
||||
<li x-on:click.stop>
|
||||
{{ $extraItemAction(['item' => $itemKey]) }}
|
||||
</li>
|
||||
@endforeach
|
||||
|
||||
@if ($editActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $editAction }}
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@if ($cloneActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $cloneAction }}
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@if ($deleteActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $deleteAction }}
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@if ($isCollapsible)
|
||||
<li class="fi-fo-builder-item-header-collapsible-actions"
|
||||
x-on:click.stop="isCollapsed = !isCollapsed">
|
||||
<div class="fi-fo-builder-item-header-collapse-action">
|
||||
{{ $getAction('collapse') }}
|
||||
</div>
|
||||
|
||||
<div class="fi-fo-builder-item-header-expand-action">
|
||||
{{ $getAction('expand') }}
|
||||
</div>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div x-show="! isCollapsed" @class([
|
||||
'fi-fo-builder-item-content',
|
||||
'fi-fo-builder-item-content-has-preview' =>
|
||||
$hasBlockPreviews && $item->getParentComponent()->hasPreview(),
|
||||
])>
|
||||
@if ($hasBlockPreviews && $item->getParentComponent()->hasPreview())
|
||||
<div @class([
|
||||
'fi-fo-builder-item-preview',
|
||||
'fi-interactive' => $hasInteractiveBlockPreviews,
|
||||
])>
|
||||
{{ $item->getParentComponent()->renderPreview($item->getRawState()) }}
|
||||
</div>
|
||||
|
||||
@if ($editActionIsVisible && !$hasInteractiveBlockPreviews)
|
||||
<div class="fi-fo-builder-item-preview-edit-overlay" role="button"
|
||||
x-on:click.stop="{{ '$wire.mountAction(\'edit\', { item: \'' . $itemKey . '\' }, { schemaComponent: \'' . $key . '\' })' }}">
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
{{ $item }}
|
||||
@endif
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@if (!$loop->last)
|
||||
@if ($isAddable && $addBetweenAction(['afterItem' => $itemKey])->isVisible())
|
||||
<li class="fi-fo-builder-add-between-items-ctn">
|
||||
<div class="fi-fo-builder-add-between-items">
|
||||
<div class="fi-fo-builder-block-picker-ctn">
|
||||
<x-filament-forms::builder.block-picker :action="$addBetweenAction" :after-item="$itemKey"
|
||||
:columns="$blockPickerColumns" :blocks="$blockPickerBlocks" :key="$key" :width="$blockPickerWidth">
|
||||
<x-slot name="trigger">
|
||||
{{ $addBetweenAction(['afterItem' => $itemKey]) }}
|
||||
</x-slot>
|
||||
</x-filament-forms::builder.block-picker>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@elseif (filled($labelBetweenItems))
|
||||
<li class="fi-fo-builder-label-between-items-ctn">
|
||||
<div class="fi-fo-builder-label-between-items-divider-before"></div>
|
||||
|
||||
<span class="fi-fo-builder-label-between-items">
|
||||
{{ $labelBetweenItems }}
|
||||
</span>
|
||||
|
||||
<div class="fi-fo-builder-label-between-items-divider-after"></div>
|
||||
</li>
|
||||
@endif
|
||||
@endif
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
@if ($isAddable && $addAction->isVisible())
|
||||
<x-filament-forms::builder.block-picker :action="$addAction" :action-alignment="$addActionAlignment" :blocks="$blockPickerBlocks"
|
||||
:columns="$blockPickerColumns" :key="$key" :width="$blockPickerWidth">
|
||||
<x-slot name="trigger">
|
||||
{{ $addAction }}
|
||||
</x-slot>
|
||||
</x-filament-forms::builder.block-picker>
|
||||
@endif
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
@php
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Support\Enums\GridDirection;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'action',
|
||||
'actionAlignment' => null,
|
||||
'afterItem' => null,
|
||||
'blocks',
|
||||
'columns' => null,
|
||||
'key',
|
||||
'trigger',
|
||||
'width' => null,
|
||||
])
|
||||
|
||||
<x-filament::dropdown :placement="match ($actionAlignment) {
|
||||
Alignment::Start, Alignment::Left => 'bottom-start',
|
||||
Alignment::End, Alignment::Right => 'bottom-end',
|
||||
default => null,
|
||||
}" shift :width="$width" :attributes="\Filament\Support\prepare_inherited_attributes(
|
||||
$attributes->class([
|
||||
'fi-fo-builder-block-picker',
|
||||
$actionAlignment instanceof Alignment ? 'fi-align-' . $actionAlignment->value : $actionAlignment,
|
||||
]),
|
||||
)">
|
||||
<x-slot name="trigger">
|
||||
{{ $trigger }}
|
||||
</x-slot>
|
||||
|
||||
<x-filament::dropdown.list>
|
||||
<div {{ new ComponentAttributeBag()->grid($columns, GridDirection::Column) }}>
|
||||
@foreach ($blocks as $block)
|
||||
@php
|
||||
$blockIcon = $block->getIcon();
|
||||
|
||||
$wireClickActionArguments = ['block' => $block->getName()];
|
||||
|
||||
if (filled($afterItem)) {
|
||||
$wireClickActionArguments['afterItem'] = $afterItem;
|
||||
}
|
||||
|
||||
$wireClickActionArguments = \Illuminate\Support\Js::from($wireClickActionArguments);
|
||||
|
||||
$wireClickAction = "mountAction('{$action->getName()}', {$wireClickActionArguments}, { schemaComponent: '{$key}' })";
|
||||
@endphp
|
||||
|
||||
<x-filament::dropdown.list.item :icon="$blockIcon" x-on:click="close" :wire:click="$wireClickAction">
|
||||
{{ $block->getLabel() }}
|
||||
</x-filament::dropdown.list.item>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-filament::dropdown.list>
|
||||
</x-filament::dropdown>
|
||||
@@ -0,0 +1,110 @@
|
||||
@php
|
||||
use Filament\Support\Enums\GridDirection;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$extraInputAttributeBag = $getExtraInputAttributeBag();
|
||||
$isHtmlAllowed = $isHtmlAllowed();
|
||||
$gridDirection = $getGridDirection() ?? GridDirection::Column;
|
||||
$isBulkToggleable = $isBulkToggleable();
|
||||
$isDisabled = $isDisabled();
|
||||
$isSearchable = $isSearchable();
|
||||
$statePath = $getStatePath();
|
||||
$options = $getOptions();
|
||||
$livewireKey = $getLivewireKey();
|
||||
$wireModelAttribute = $applyStateBindingModifiers('wire:model');
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
<div x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('checkbox-list', 'filament/forms') }}"
|
||||
x-data="checkboxListFormComponent({
|
||||
livewireId: @js($this->getId()),
|
||||
})" {{ $getExtraAlpineAttributeBag()->class(['fi-fo-checkbox-list']) }}>
|
||||
@if (!$isDisabled)
|
||||
@if ($isSearchable)
|
||||
<x-filament::input.wrapper inline-prefix :prefix-icon="\Filament\Support\Icons\Heroicon::MagnifyingGlass" :prefix-icon-alias="\Filament\Forms\View\FormsIconAlias::COMPONENTS_CHECKBOX_LIST_SEARCH_FIELD"
|
||||
class="fi-fo-checkbox-list-search-input-wrp">
|
||||
<input placeholder="{{ $getSearchPrompt() }}" type="search"
|
||||
x-model.debounce.{{ $getSearchDebounce() }}="search"
|
||||
class="fi-input fi-input-has-inline-prefix" />
|
||||
</x-filament::input.wrapper>
|
||||
@endif
|
||||
|
||||
@if ($isBulkToggleable && count($options))
|
||||
<div x-cloak class="fi-fo-checkbox-list-actions" wire:key="{{ $livewireKey }}.actions">
|
||||
<span x-show="! areAllCheckboxesChecked" x-on:click="toggleAllCheckboxes()"
|
||||
wire:key="{{ $livewireKey }}.actions.select-all">
|
||||
{{ $getAction('selectAll') }}
|
||||
</span>
|
||||
|
||||
<span x-show="areAllCheckboxesChecked" x-on:click="toggleAllCheckboxes()"
|
||||
wire:key="{{ $livewireKey }}.actions.deselect-all">
|
||||
{{ $getAction('deselectAll') }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<div
|
||||
{{ $getExtraAttributeBag()->grid($getColumns(), $gridDirection)->merge(
|
||||
[
|
||||
'x-show' => $isSearchable ? 'visibleCheckboxListOptions.length' : null,
|
||||
],
|
||||
escape: false,
|
||||
)->class(['fi-fo-checkbox-list-options']) }}>
|
||||
@forelse ($options as $value => $label)
|
||||
<div wire:key="{{ $livewireKey }}.options.{{ $value }}"
|
||||
@if ($isSearchable) x-show="
|
||||
$el
|
||||
.querySelector('.fi-fo-checkbox-list-option-label')
|
||||
?.innerText.toLowerCase()
|
||||
.includes(search.toLowerCase()) ||
|
||||
$el
|
||||
.querySelector('.fi-fo-checkbox-list-option-description')
|
||||
?.innerText.toLowerCase()
|
||||
.includes(search.toLowerCase())
|
||||
" @endif
|
||||
class="fi-fo-checkbox-list-option-ctn">
|
||||
<label class="fi-fo-checkbox-list-option">
|
||||
<input type="checkbox"
|
||||
{{ $extraInputAttributeBag->merge(
|
||||
[
|
||||
'disabled' => $isDisabled || $isOptionDisabled($value, $label),
|
||||
'value' => e($value),
|
||||
'wire:loading.attr' => 'disabled',
|
||||
$wireModelAttribute => $statePath,
|
||||
'x-on:change' => $isBulkToggleable ? 'checkIfAllCheckboxesAreChecked()' : null,
|
||||
],
|
||||
escape: false,
|
||||
)->class(['fi-checkbox-input', 'fi-valid' => !$errors->has($statePath), 'fi-invalid' => $errors->has($statePath)]) }} />
|
||||
|
||||
<div class="fi-fo-checkbox-list-option-text">
|
||||
<span class="fi-fo-checkbox-list-option-label">
|
||||
@if ($isHtmlAllowed)
|
||||
{!! $label !!}
|
||||
@else
|
||||
{{ $label }}
|
||||
@endif
|
||||
</span>
|
||||
|
||||
@if ($hasDescription($value))
|
||||
<p class="fi-fo-checkbox-list-option-description">
|
||||
{{ $getDescription($value) }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
@empty
|
||||
<div wire:key="{{ $livewireKey }}.empty"></div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
@if ($isSearchable)
|
||||
<div x-cloak x-show="search && ! visibleCheckboxListOptions.length"
|
||||
class="fi-fo-checkbox-list-no-search-results-message">
|
||||
{{ $getNoSearchResultsMessage() }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,33 @@
|
||||
@php
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$statePath = $getStatePath();
|
||||
$attributes = $attributes
|
||||
->merge(
|
||||
[
|
||||
'autofocus' => $isAutofocused(),
|
||||
'disabled' => $isDisabled(),
|
||||
'id' => $getId(),
|
||||
'required' => $isRequired() && !$isConcealed(),
|
||||
'wire:loading.attr' => 'disabled',
|
||||
$applyStateBindingModifiers('wire:model') => $statePath,
|
||||
],
|
||||
escape: false,
|
||||
)
|
||||
->merge($getExtraAttributes(), escape: false)
|
||||
->merge($getExtraInputAttributes(), escape: false)
|
||||
->class([
|
||||
'fi-checkbox-input',
|
||||
'fi-valid' => !$errors->has($statePath),
|
||||
'fi-invalid' => $errors->has($statePath),
|
||||
]);
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" :inline-label-vertical-alignment="\Filament\Support\Enums\VerticalAlignment::Center">
|
||||
@if ($isInline())
|
||||
<x-slot name="labelPrefix">
|
||||
<input type="checkbox" {{ $attributes }} />
|
||||
</x-slot>
|
||||
@else
|
||||
<input type="checkbox" {{ $attributes }} />
|
||||
@endif
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,34 @@
|
||||
@php
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$extraAttributeBag = $getExtraAttributeBag();
|
||||
$isDisabled = $isDisabled();
|
||||
$isLive = $isLive();
|
||||
$isLiveOnBlur = $isLiveOnBlur();
|
||||
$isLiveDebounced = $isLiveDebounced();
|
||||
$liveDebounce = $getLiveDebounce();
|
||||
$key = $getKey();
|
||||
$language = $getLanguage();
|
||||
$statePath = $getStatePath();
|
||||
$livewireKey = $getLivewireKey();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
<x-filament::input.wrapper :disabled="$isDisabled" :valid="!$errors->has($statePath)" :attributes="\Filament\Support\prepare_inherited_attributes($extraAttributeBag)->class(['fi-fo-code-editor'])">
|
||||
<div x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('code-editor', 'filament/forms') }}"
|
||||
x-data="codeEditorFormComponent({
|
||||
canWrap: @js($canWrap()),
|
||||
isDisabled: @js($isDisabled),
|
||||
isLive: @js($isLive),
|
||||
isLiveDebounced: @js($isLiveDebounced),
|
||||
isLiveOnBlur: @js($isLiveOnBlur),
|
||||
liveDebounce: @js($liveDebounce),
|
||||
language: @js($language?->value),
|
||||
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$statePath}')", isOptimisticallyLive: false) }},
|
||||
})" wire:ignore
|
||||
wire:key="{{ $livewireKey }}.{{ substr(md5(serialize([$isDisabled, $language?->value])), 0, 64) }}"
|
||||
{{ $getExtraAlpineAttributeBag() }}>
|
||||
<div x-ref="editor" x-cloak></div>
|
||||
</div>
|
||||
</x-filament::input.wrapper>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,86 @@
|
||||
@php
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$extraAttributeBag = $getExtraAttributeBag();
|
||||
$isDisabled = $isDisabled();
|
||||
$isLive = $isLive();
|
||||
$isLiveOnBlur = $isLiveOnBlur();
|
||||
$isLiveDebounced = $isLiveDebounced();
|
||||
$isPrefixInline = $isPrefixInline();
|
||||
$isSuffixInline = $isSuffixInline();
|
||||
$liveDebounce = $getLiveDebounce();
|
||||
$prefixActions = $getPrefixActions();
|
||||
$prefixIcon = $getPrefixIcon();
|
||||
$prefixIconColor = $getPrefixIconColor();
|
||||
$prefixLabel = $getPrefixLabel();
|
||||
$suffixActions = $getSuffixActions();
|
||||
$suffixIcon = $getSuffixIcon();
|
||||
$suffixIconColor = $getSuffixIconColor();
|
||||
$suffixLabel = $getSuffixLabel();
|
||||
$statePath = $getStatePath();
|
||||
$placeholder = $getPlaceholder();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" :inline-label-vertical-alignment="\Filament\Support\Enums\VerticalAlignment::Center">
|
||||
<x-filament::input.wrapper :disabled="$isDisabled" :inline-prefix="$isPrefixInline" :inline-suffix="$isSuffixInline" :prefix="$prefixLabel" :prefix-actions="$prefixActions"
|
||||
:prefix-icon="$prefixIcon" :prefix-icon-color="$prefixIconColor" :suffix="$suffixLabel" :suffix-actions="$suffixActions" :suffix-icon="$suffixIcon" :suffix-icon-color="$suffixIconColor"
|
||||
:valid="!$errors->has($statePath)" x-on:focus-input.stop="$el.querySelector('input')?.focus()" :attributes="\Filament\Support\prepare_inherited_attributes($extraAttributeBag)->class('fi-fo-color-picker')">
|
||||
<div x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('color-picker', 'filament/forms') }}"
|
||||
x-data="colorPickerFormComponent({
|
||||
isAutofocused: @js($isAutofocused()),
|
||||
isDisabled: @js($isDisabled),
|
||||
isLive: @js($isLive),
|
||||
isLiveDebounced: @js($isLiveDebounced),
|
||||
isLiveOnBlur: @js($isLiveOnBlur),
|
||||
liveDebounce: @js($liveDebounce),
|
||||
state: $wire.$entangle('{{ $statePath }}'),
|
||||
})" x-on:keydown.esc="isOpen() && $event.stopPropagation()"
|
||||
x-on:focusout="if (isOpen() && ! $el.contains($event.relatedTarget)) $refs.panel.close()"
|
||||
{{ $getExtraAlpineAttributeBag()->class(['fi-input-wrp-content']) }}>
|
||||
<input x-on:focus="$refs.panel.open($refs.input)" x-on:keydown.enter.prevent.stop="togglePanelVisibility()"
|
||||
x-ref="input"
|
||||
{{ $getExtraInputAttributeBag()->merge(
|
||||
[
|
||||
'autocomplete' => 'off',
|
||||
'disabled' => $isDisabled,
|
||||
'id' => $getId(),
|
||||
'placeholder' => filled($placeholder) ? e($placeholder) : null,
|
||||
'required' => $isRequired() && !$isConcealed(),
|
||||
'type' => 'text',
|
||||
'x-model' . ($isLiveDebounced ? '.debounce.' . $liveDebounce : null) => 'state',
|
||||
'x-on:blur' => $isLiveOnBlur ? 'isOpen() ? null : commitState()' : null,
|
||||
],
|
||||
escape: false,
|
||||
)->class([
|
||||
'fi-input',
|
||||
'fi-input-has-inline-prefix' =>
|
||||
$isPrefixInline && (count($prefixActions) || $prefixIcon || filled($prefixLabel)),
|
||||
'fi-input-has-inline-suffix' =>
|
||||
$isSuffixInline && (count($suffixActions) || $suffixIcon || filled($suffixLabel)),
|
||||
]) }} />
|
||||
|
||||
<div class="fi-fo-color-picker-preview my-auto me-3 size-5 shrink-0 rounded-full select-none"
|
||||
x-on:click="togglePanelVisibility()"
|
||||
x-bind:class="{
|
||||
'fi-empty': !state,
|
||||
}"
|
||||
x-bind:style="{ 'background-color': state }"></div>
|
||||
|
||||
<div wire:ignore.self wire:key="{{ $getLivewireKey() }}.panel" x-cloak
|
||||
x-float.placement.bottom-start.offset.flip.shift="{ offset: 8 }" x-ref="panel"
|
||||
class="fi-fo-color-picker-panel">
|
||||
@php
|
||||
$tag =
|
||||
match ($getFormat()) {
|
||||
'hsl' => 'hsl-string',
|
||||
'rgb' => 'rgb-string',
|
||||
'rgba' => 'rgba-string',
|
||||
default => 'hex',
|
||||
} . '-color-picker';
|
||||
@endphp
|
||||
|
||||
<{{ $tag }} x-ref="picker" color="{{ $getState() }}" />
|
||||
</div>
|
||||
</div>
|
||||
</x-filament::input.wrapper>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,192 @@
|
||||
@php
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$datalistOptions = $getDatalistOptions();
|
||||
$disabledDates = $getDisabledDates();
|
||||
$extraAlpineAttributes = $getExtraAlpineAttributes();
|
||||
$extraAttributeBag = $getExtraAttributeBag();
|
||||
$extraInputAttributeBag = $getExtraInputAttributeBag();
|
||||
$hasDate = $hasDate();
|
||||
$hasTime = $hasTime();
|
||||
$hasSeconds = $hasSeconds();
|
||||
$id = $getId();
|
||||
$isDisabled = $isDisabled();
|
||||
$isAutofocused = $isAutofocused();
|
||||
$isPrefixInline = $isPrefixInline();
|
||||
$isSuffixInline = $isSuffixInline();
|
||||
$maxDate = $getMaxDate();
|
||||
$minDate = $getMinDate();
|
||||
$defaultFocusedDate = $getDefaultFocusedDate();
|
||||
$prefixActions = $getPrefixActions();
|
||||
$prefixIcon = $getPrefixIcon();
|
||||
$prefixIconColor = $getPrefixIconColor();
|
||||
$prefixLabel = $getPrefixLabel();
|
||||
$suffixActions = $getSuffixActions();
|
||||
$suffixIcon = $getSuffixIcon();
|
||||
$suffixIconColor = $getSuffixIconColor();
|
||||
$suffixLabel = $getSuffixLabel();
|
||||
$statePath = $getStatePath();
|
||||
$placeholder = $getPlaceholder();
|
||||
$isReadOnly = $isReadOnly();
|
||||
$isRequired = $isRequired();
|
||||
$isConcealed = $isConcealed();
|
||||
$step = $getStep();
|
||||
$type = $getType();
|
||||
$livewireKey = $getLivewireKey();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" :inline-label-vertical-alignment="\Filament\Support\Enums\VerticalAlignment::Center">
|
||||
<x-filament::input.wrapper :disabled="$isDisabled" :inline-prefix="$isPrefixInline" :inline-suffix="$isSuffixInline" :prefix="$prefixLabel" :prefix-actions="$prefixActions"
|
||||
:prefix-icon="$prefixIcon" :prefix-icon-color="$prefixIconColor" :suffix="$suffixLabel" :suffix-actions="$suffixActions" :suffix-icon="$suffixIcon" :suffix-icon-color="$suffixIconColor"
|
||||
:valid="!$errors->has($statePath)" x-on:focus-input.stop="$el.querySelector('input:not([type=hidden])')?.focus()"
|
||||
:attributes="\Filament\Support\prepare_inherited_attributes($extraAttributeBag)->class([
|
||||
'fi-fo-date-time-picker',
|
||||
])">
|
||||
@if ($isNative())
|
||||
<input
|
||||
{{ $extraInputAttributeBag->merge($extraAlpineAttributes, escape: false)->merge(
|
||||
[
|
||||
'autofocus' => $isAutofocused,
|
||||
'disabled' => $isDisabled,
|
||||
'id' => $id,
|
||||
'list' => $datalistOptions ? $id . '-list' : null,
|
||||
'max' => $hasTime ? $maxDate : ($maxDate ? \Carbon\Carbon::parse($maxDate)->toDateString() : null),
|
||||
'min' => $hasTime ? $minDate : ($minDate ? \Carbon\Carbon::parse($minDate)->toDateString() : null),
|
||||
'placeholder' => filled($placeholder) ? e($placeholder) : null,
|
||||
'readonly' => $isReadOnly,
|
||||
'required' => $isRequired && !$isConcealed,
|
||||
'step' => $step,
|
||||
'type' => $type,
|
||||
$applyStateBindingModifiers('wire:model') => $statePath,
|
||||
'x-data' => count($extraAlpineAttributes) ? '{}' : null,
|
||||
],
|
||||
escape: false,
|
||||
)->class([
|
||||
'fi-input',
|
||||
'fi-input-has-inline-prefix' =>
|
||||
$isPrefixInline && (count($prefixActions) || $prefixIcon || filled($prefixLabel)),
|
||||
'fi-input-has-inline-suffix' =>
|
||||
$isSuffixInline && (count($suffixActions) || $suffixIcon || filled($suffixLabel)),
|
||||
]) }} />
|
||||
@else
|
||||
<div x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('date-time-picker', 'filament/forms') }}"
|
||||
x-data="dateTimePickerFormComponent({
|
||||
defaultFocusedDate: @js($defaultFocusedDate),
|
||||
displayFormat: '{{ convert_date_format($getDisplayFormat())->to('day.js') }}',
|
||||
firstDayOfWeek: {{ $getFirstDayOfWeek() }},
|
||||
isAutofocused: @js($isAutofocused),
|
||||
locale: @js($getLocale()),
|
||||
shouldCloseOnDateSelection: @js($shouldCloseOnDateSelection()),
|
||||
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$statePath}')") }},
|
||||
})" wire:ignore
|
||||
wire:key="{{ $livewireKey }}.{{ substr(
|
||||
md5(serialize([$disabledDates, $isDisabled, $isReadOnly, $maxDate, $minDate, $hasDate, $hasTime, $hasSeconds])),
|
||||
0,
|
||||
64,
|
||||
) }}"
|
||||
x-on:keydown.esc="isOpen() && $event.stopPropagation()" {{ $getExtraAlpineAttributeBag() }}>
|
||||
<input x-ref="maxDate" type="hidden" value="{{ $maxDate }}" />
|
||||
|
||||
<input x-ref="minDate" type="hidden" value="{{ $minDate }}" />
|
||||
|
||||
<input x-ref="disabledDates" type="hidden" value="{{ json_encode($disabledDates) }}" />
|
||||
|
||||
<button x-ref="button" x-on:click="togglePanelVisibility()"
|
||||
x-on:keydown.enter.prevent.stop="
|
||||
if (! $el.disabled) {
|
||||
isOpen() ? selectDate() : togglePanelVisibility()
|
||||
}
|
||||
"
|
||||
x-on:keydown.arrow-left.prevent.stop="if (! $el.disabled) focusPreviousDay()"
|
||||
x-on:keydown.arrow-right.prevent.stop="if (! $el.disabled) focusNextDay()"
|
||||
x-on:keydown.arrow-up.prevent.stop="if (! $el.disabled) focusPreviousWeek()"
|
||||
x-on:keydown.arrow-down.prevent.stop="if (! $el.disabled) focusNextWeek()"
|
||||
x-on:keydown.backspace.prevent.stop="if (! $el.disabled) clearState()"
|
||||
x-on:keydown.clear.prevent.stop="if (! $el.disabled) clearState()"
|
||||
x-on:keydown.delete.prevent.stop="if (! $el.disabled) clearState()"
|
||||
aria-label="{{ $placeholder }}" type="button" tabindex="-1" @disabled($isDisabled || $isReadOnly)
|
||||
{{ $getExtraTriggerAttributeBag()->class(['fi-fo-date-time-picker-trigger']) }}>
|
||||
<input @disabled($isDisabled) readonly placeholder="{{ $placeholder }}"
|
||||
wire:key="{{ $livewireKey }}.display-text" x-model="displayText"
|
||||
@if ($id = $getId()) id="{{ $id }}" @endif
|
||||
@class(['fi-fo-date-time-picker-display-text-input']) />
|
||||
</button>
|
||||
|
||||
<div x-ref="panel" x-cloak x-float.placement.bottom-start.offset.flip.shift="{ offset: 8 }" wire:ignore
|
||||
wire:key="{{ $livewireKey }}.panel" @class(['fi-fo-date-time-picker-panel'])>
|
||||
@if ($hasDate)
|
||||
<div class="fi-fo-date-time-picker-panel-header">
|
||||
<select x-model="focusedMonth" class="fi-fo-date-time-picker-month-select">
|
||||
<template x-for="(month, index) in months">
|
||||
<option x-bind:value="index" x-text="month"></option>
|
||||
</template>
|
||||
</select>
|
||||
|
||||
<input type="number" inputmode="numeric" x-model.debounce="focusedYear"
|
||||
class="fi-fo-date-time-picker-year-input" />
|
||||
</div>
|
||||
|
||||
<div class="fi-fo-date-time-picker-calendar-header">
|
||||
<template x-for="(day, index) in dayLabels" x-bind:key="index">
|
||||
<div x-text="day" class="fi-fo-date-time-picker-calendar-header-day"></div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div role="grid" class="fi-fo-date-time-picker-calendar">
|
||||
<template x-for="day in emptyDaysInFocusedMonth" x-bind:key="day">
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<template x-for="day in daysInFocusedMonth" x-bind:key="day">
|
||||
<div x-text="day" x-on:click="dayIsDisabled(day) || selectDate(day)"
|
||||
x-on:mouseenter="setFocusedDay(day)" role="option"
|
||||
x-bind:aria-selected="focusedDate.date() === day"
|
||||
x-bind:class="{
|
||||
'fi-fo-date-time-picker-calendar-day-today': dayIsToday(day),
|
||||
'fi-focused': focusedDate.date() === day,
|
||||
'fi-selected': dayIsSelected(day),
|
||||
'fi-disabled': dayIsDisabled(day),
|
||||
}"
|
||||
class="fi-fo-date-time-picker-calendar-day"></div>
|
||||
</template>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($hasTime)
|
||||
<div class="fi-fo-date-time-picker-time-inputs">
|
||||
<input max="23" min="0" step="{{ $getHoursStep() }}" type="number"
|
||||
inputmode="numeric" x-on:blur="checkTimeInputValidity" x-on:invalid="timeInputInvalid"
|
||||
x-model.debounce="hour" />
|
||||
|
||||
<span class="fi-fo-date-time-picker-time-input-separator">
|
||||
:
|
||||
</span>
|
||||
|
||||
<input max="59" min="0" step="{{ $getMinutesStep() }}" type="number"
|
||||
inputmode="numeric" x-on:blur="checkTimeInputValidity" x-on:invalid="timeInputInvalid"
|
||||
x-model.debounce="minute" />
|
||||
|
||||
@if ($hasSeconds)
|
||||
<span class="fi-fo-date-time-picker-time-input-separator">
|
||||
:
|
||||
</span>
|
||||
|
||||
<input max="59" min="0" step="{{ $getSecondsStep() }}" type="number"
|
||||
inputmode="numeric" x-on:blur="checkTimeInputValidity"
|
||||
x-on:invalid="timeInputInvalid" x-model.debounce="second" />
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</x-filament::input.wrapper>
|
||||
|
||||
@if ($datalistOptions)
|
||||
<datalist id="{{ $id }}-list">
|
||||
@foreach ($datalistOptions as $option)
|
||||
<option value="{{ $option }}" />
|
||||
@endforeach
|
||||
</datalist>
|
||||
@endif
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,202 @@
|
||||
@php
|
||||
use Filament\Support\Enums\VerticalAlignment;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'areHtmlErrorMessagesAllowed' => null,
|
||||
'errorMessage' => null,
|
||||
'errorMessages' => null,
|
||||
'field' => null,
|
||||
'hasErrors' => true,
|
||||
'hasInlineLabel' => null,
|
||||
'hasNestedRecursiveValidationRules' => null,
|
||||
'id' => null,
|
||||
'inlineLabelVerticalAlignment' => VerticalAlignment::Start,
|
||||
'isDisabled' => null,
|
||||
'label' => null,
|
||||
'labelPrefix' => null,
|
||||
'labelSrOnly' => null,
|
||||
'labelSuffix' => null,
|
||||
'labelTag' => 'label',
|
||||
'required' => null,
|
||||
'shouldShowAllValidationMessages' => null,
|
||||
'statePath' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
if ($field) {
|
||||
$hasInlineLabel ??= $field->hasInlineLabel();
|
||||
$hasNestedRecursiveValidationRules ??=
|
||||
$field instanceof \Filament\Forms\Components\Contracts\HasNestedRecursiveValidationRules;
|
||||
$id ??= $field->getId();
|
||||
$isDisabled ??= $field->isDisabled();
|
||||
$label ??= $field->getLabel();
|
||||
$labelSrOnly ??= $field->isLabelHidden();
|
||||
$required ??= $field->isMarkedAsRequired();
|
||||
$statePath ??= $field->getStatePath();
|
||||
$areHtmlErrorMessagesAllowed ??= $field->areHtmlValidationMessagesAllowed();
|
||||
$shouldShowAllValidationMessages ??= $field->shouldShowAllValidationMessages();
|
||||
}
|
||||
|
||||
$aboveLabelSchema = $field?->getChildSchema($field::ABOVE_LABEL_SCHEMA_KEY)?->toHtmlString();
|
||||
$belowLabelSchema = $field?->getChildSchema($field::BELOW_LABEL_SCHEMA_KEY)?->toHtmlString();
|
||||
$beforeLabelSchema = $field?->getChildSchema($field::BEFORE_LABEL_SCHEMA_KEY)?->toHtmlString();
|
||||
$afterLabelSchema = $field?->getChildSchema($field::AFTER_LABEL_SCHEMA_KEY)?->toHtmlString();
|
||||
$aboveContentSchema = $field?->getChildSchema($field::ABOVE_CONTENT_SCHEMA_KEY)?->toHtmlString();
|
||||
$belowContentSchema = $field?->getChildSchema($field::BELOW_CONTENT_SCHEMA_KEY)?->toHtmlString();
|
||||
$beforeContentSchema = $field?->getChildSchema($field::BEFORE_CONTENT_SCHEMA_KEY)?->toHtmlString();
|
||||
$afterContentSchema = $field?->getChildSchema($field::AFTER_CONTENT_SCHEMA_KEY)?->toHtmlString();
|
||||
$aboveErrorMessageSchema = $field?->getChildSchema($field::ABOVE_ERROR_MESSAGE_SCHEMA_KEY)?->toHtmlString();
|
||||
$belowErrorMessageSchema = $field?->getChildSchema($field::BELOW_ERROR_MESSAGE_SCHEMA_KEY)?->toHtmlString();
|
||||
|
||||
$hasError =
|
||||
$hasErrors &&
|
||||
(filled($errorMessage) ||
|
||||
filled($errorMessages) ||
|
||||
(filled($statePath) &&
|
||||
($errors->has($statePath) || ($hasNestedRecursiveValidationRules && $errors->has("{$statePath}.*")))));
|
||||
|
||||
if ($hasError && filled($statePath) && blank($errorMessage) && blank($errorMessages)) {
|
||||
if ($shouldShowAllValidationMessages) {
|
||||
$errorMessages = $errors->has($statePath)
|
||||
? $errors->get($statePath)
|
||||
: ($hasNestedRecursiveValidationRules
|
||||
? $errors->get("{$statePath}.*")
|
||||
: []);
|
||||
|
||||
if (count($errorMessages) === 1) {
|
||||
$errorMessage = Arr::first($errorMessages);
|
||||
$errorMessages = [];
|
||||
}
|
||||
} else {
|
||||
$errorMessage = $errors->has($statePath)
|
||||
? $errors->first($statePath)
|
||||
: ($hasNestedRecursiveValidationRules
|
||||
? $errors->first("{$statePath}.*")
|
||||
: null);
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div data-field-wrapper
|
||||
{{ $attributes->merge($field?->getExtraFieldWrapperAttributes() ?? [], escape: false)->class(['fi-fo-field', 'fi-fo-field-has-inline-label' => $hasInlineLabel]) }}>
|
||||
@if (filled($label) && $labelSrOnly)
|
||||
<{{ $labelTag }}
|
||||
@if ($labelTag === 'label') for="{{ $id }}"
|
||||
@else
|
||||
id="{{ $id }}-label" @endif
|
||||
class="fi-fo-field-label fi-sr-only">
|
||||
{{ $label }}
|
||||
</{{ $labelTag }}>
|
||||
@endif
|
||||
|
||||
@if (
|
||||
(filled($label) && !$labelSrOnly) ||
|
||||
$hasInlineLabel ||
|
||||
$aboveLabelSchema ||
|
||||
$belowLabelSchema ||
|
||||
$beforeLabelSchema ||
|
||||
$afterLabelSchema ||
|
||||
$labelPrefix ||
|
||||
$labelSuffix)
|
||||
<div @class([
|
||||
'fi-fo-field-label-col',
|
||||
"fi-vertical-align-{$inlineLabelVerticalAlignment->value}" => $hasInlineLabel,
|
||||
])>
|
||||
{{ $aboveLabelSchema }}
|
||||
|
||||
<div @class([
|
||||
'fi-fo-field-label-ctn',
|
||||
$label instanceof \Illuminate\View\ComponentSlot
|
||||
? $label->attributes->get('class')
|
||||
: null,
|
||||
])>
|
||||
{{ $beforeLabelSchema }}
|
||||
|
||||
@if ((filled($label) && !$labelSrOnly) || $labelPrefix || $labelSuffix)
|
||||
<{{ $labelTag }}
|
||||
@if ($labelTag === 'label') for="{{ $id }}"
|
||||
@else
|
||||
id="{{ $id }}-label" @endif
|
||||
class="fi-fo-field-label">
|
||||
{{ $labelPrefix }}
|
||||
|
||||
@if (filled($label) && !$labelSrOnly)
|
||||
<span class="fi-fo-field-label-content">
|
||||
{{ $label }}@if ($required && !$isDisabled)
|
||||
<sup class="fi-fo-field-label-required-mark">*</sup>
|
||||
@endif
|
||||
</span>
|
||||
@endif
|
||||
|
||||
{{ $labelSuffix }}
|
||||
</{{ $labelTag }}>
|
||||
@endif
|
||||
|
||||
{{ $afterLabelSchema }}
|
||||
</div>
|
||||
|
||||
{{ $belowLabelSchema }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (
|
||||
!\Filament\Support\is_slot_empty($slot) ||
|
||||
$hasError ||
|
||||
$aboveContentSchema ||
|
||||
$belowContentSchema ||
|
||||
$beforeContentSchema ||
|
||||
$afterContentSchema ||
|
||||
$aboveErrorMessageSchema ||
|
||||
$belowErrorMessageSchema)
|
||||
<div class="fi-fo-field-content-col">
|
||||
{{ $aboveContentSchema }}
|
||||
|
||||
@if ($beforeContentSchema || $afterContentSchema)
|
||||
<div class="fi-fo-field-content-ctn">
|
||||
{{ $beforeContentSchema }}
|
||||
|
||||
<div class="fi-fo-field-content">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
{{ $afterContentSchema }}
|
||||
</div>
|
||||
@else
|
||||
{{ $slot }}
|
||||
@endif
|
||||
|
||||
{{ $belowContentSchema }}
|
||||
|
||||
@if ($hasError)
|
||||
{{ $aboveErrorMessageSchema }}
|
||||
|
||||
@if (filled($errorMessages))
|
||||
<ul data-validation-error class="fi-fo-field-wrp-error-list">
|
||||
@foreach ($errorMessages as $errorMessage)
|
||||
<li class="fi-fo-field-wrp-error-message">
|
||||
@if ($areHtmlErrorMessagesAllowed)
|
||||
{!! $errorMessage !!}
|
||||
@else
|
||||
{{ $errorMessage }}
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@elseif ($areHtmlErrorMessagesAllowed)
|
||||
<div data-validation-error class="fi-fo-field-wrp-error-message">
|
||||
{!! $errorMessage !!}
|
||||
</div>
|
||||
@else
|
||||
<p data-validation-error class="fi-fo-field-wrp-error-message">
|
||||
{{ $errorMessage }}
|
||||
</p>
|
||||
@endif
|
||||
|
||||
{{ $belowErrorMessageSchema }}
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,292 @@
|
||||
@php
|
||||
use Filament\Support\Enums\Alignment;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$id = $getId();
|
||||
$automaticallyCropImagesAspectRatio = $getAutomaticallyCropImagesAspectRatio();
|
||||
$automaticallyResizeImagesHeight = $getAutomaticallyResizeImagesHeight();
|
||||
$automaticallyResizeImagesWidth = $getAutomaticallyResizeImagesWidth();
|
||||
$isAvatar = $isAvatar();
|
||||
$isMultiple = $isMultiple();
|
||||
$key = $getKey();
|
||||
$statePath = $getStatePath();
|
||||
$isDisabled = $isDisabled();
|
||||
$hasImageEditor = $hasImageEditor();
|
||||
$isImageEditorExplicitlyEnabled = $isImageEditorExplicitlyEnabled();
|
||||
$hasCircleCropper = $hasCircleCropper();
|
||||
$livewireKey = $getLivewireKey();
|
||||
|
||||
$alignment = $getAlignment() ?? Alignment::Start;
|
||||
|
||||
if (!$alignment instanceof Alignment) {
|
||||
$alignment = filled($alignment) ? Alignment::tryFrom($alignment) ?? $alignment : null;
|
||||
}
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" label-tag="div">
|
||||
<div x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('file-upload', 'filament/forms') }}"
|
||||
x-data="fileUploadFormComponent({
|
||||
acceptedFileTypes: @js($getAcceptedFileTypes()),
|
||||
automaticallyCropImagesAspectRatio: @js($automaticallyCropImagesAspectRatio),
|
||||
automaticallyOpenImageEditorForAspectRatio: @js($getAutomaticallyOpenImageEditorForAspectRatio()),
|
||||
automaticallyResizeImagesMode: @js($getAutomaticallyResizeImagesMode()),
|
||||
automaticallyResizeImagesHeight: @js($automaticallyResizeImagesHeight),
|
||||
automaticallyResizeImagesWidth: @js($automaticallyResizeImagesWidth),
|
||||
cancelUploadUsing: (fileKey) => {
|
||||
$wire.cancelUpload(`{{ $statePath }}.${fileKey}`)
|
||||
},
|
||||
canEditSvgs: @js($canEditSvgs()),
|
||||
confirmSvgEditingMessage: @js(__('filament-forms::components.file_upload.editor.svg.messages.confirmation')),
|
||||
deleteUploadedFileUsing: async (fileKey) => {
|
||||
return await $wire.callSchemaComponentMethod(
|
||||
@js($key),
|
||||
'deleteUploadedFile', { fileKey },
|
||||
)
|
||||
},
|
||||
disabledSvgEditingMessage: @js(__('filament-forms::components.file_upload.editor.svg.messages.disabled')),
|
||||
getUploadedFilesUsing: async () => {
|
||||
return await Livewire.fireAction(
|
||||
$wire.__instance,
|
||||
'callSchemaComponentMethod',
|
||||
[@js($key), 'getUploadedFiles'], { async: true },
|
||||
)
|
||||
},
|
||||
hasCircleCropper: @js($hasCircleCropper),
|
||||
hasImageEditor: @js($hasImageEditor),
|
||||
imageEditorEmptyFillColor: @js($getImageEditorEmptyFillColor()),
|
||||
imageEditorMode: @js($getImageEditorMode()),
|
||||
imageEditorViewportHeight: @js($getImageEditorViewportHeight()),
|
||||
imageEditorViewportWidth: @js($getImageEditorViewportWidth()),
|
||||
imagePreviewHeight: @js($getImagePreviewHeight()),
|
||||
isAvatar: @js($isAvatar),
|
||||
isDeletable: @js($isDeletable()),
|
||||
isDisabled: @js($isDisabled),
|
||||
isDownloadable: @js($isDownloadable()),
|
||||
isImageEditorExplicitlyEnabled: @js($isImageEditorExplicitlyEnabled),
|
||||
isMultiple: @js($isMultiple),
|
||||
isOpenable: @js($isOpenable()),
|
||||
isPasteable: @js($isPasteable()),
|
||||
isPreviewable: @js($isPreviewable()),
|
||||
isReorderable: @js($isReorderable()),
|
||||
isSvgEditingConfirmed: @js($isSvgEditingConfirmed()),
|
||||
itemPanelAspectRatio: @js($getItemPanelAspectRatio()),
|
||||
loadingIndicatorPosition: @js($getLoadingIndicatorPosition()),
|
||||
locale: @js(app()->getLocale()),
|
||||
maxFiles: @js($maxFiles = $getMaxFiles()),
|
||||
maxFilesValidationMessage: @js($maxFiles ? trans_choice('validation.max.array', $maxFiles, ['attribute' => $getValidationAttribute(), 'max' => $maxFiles]) : null),
|
||||
maxParallelUploads: @js($getMaxParallelUploads()),
|
||||
maxSize: @js(($size = $getMaxSize()) ? "{$size}KB" : null),
|
||||
mimeTypeMap: @js($getMimeTypeMap()),
|
||||
minSize: @js(($size = $getMinSize()) ? "{$size}KB" : null),
|
||||
panelAspectRatio: @js($getPanelAspectRatio()),
|
||||
panelLayout: @js($getPanelLayout()),
|
||||
placeholder: @js($getPlaceholder()),
|
||||
removeUploadedFileButtonPosition: @js($getRemoveUploadedFileButtonPosition()),
|
||||
removeUploadedFileUsing: async (fileKey) => {
|
||||
return await $wire.callSchemaComponentMethod(
|
||||
@js($key),
|
||||
'removeUploadedFile', { fileKey },
|
||||
)
|
||||
},
|
||||
reorderUploadedFilesUsing: async (fileKeys) => {
|
||||
return await $wire.callSchemaComponentMethod(
|
||||
@js($key),
|
||||
'reorderUploadedFiles', { fileKeys },
|
||||
)
|
||||
},
|
||||
shouldAppendFiles: @js($shouldAppendFiles()),
|
||||
shouldAutomaticallyUpscaleImagesWhenResizing: @js($shouldAutomaticallyUpscaleImagesWhenResizing()),
|
||||
shouldOrientImageFromExif: @js($shouldOrientImagesFromExif()),
|
||||
shouldTransformImage: @js($automaticallyCropImagesAspectRatio || $automaticallyResizeImagesHeight || $automaticallyResizeImagesWidth),
|
||||
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$statePath}')") }},
|
||||
uploadButtonPosition: @js($getUploadButtonPosition()),
|
||||
uploadingMessage: @js($getUploadingMessage()),
|
||||
uploadProgressIndicatorPosition: @js($getUploadProgressIndicatorPosition()),
|
||||
uploadUsing: (fileKey, file, success, error, progress) => {
|
||||
$wire.upload(
|
||||
`{{ $statePath }}.${fileKey}`,
|
||||
file,
|
||||
() => {
|
||||
success(fileKey)
|
||||
},
|
||||
error,
|
||||
(progressEvent) => {
|
||||
progress(true, progressEvent.detail.progress, 100)
|
||||
},
|
||||
)
|
||||
},
|
||||
})" wire:ignore
|
||||
wire:key="{{ $livewireKey }}.{{ substr(md5(serialize([$isDisabled])), 0, 64) }}"
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'aria-labelledby' => "{$id}-label",
|
||||
'id' => $id,
|
||||
'role' => 'group',
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->merge($getExtraAlpineAttributes(), escape: false)->class([
|
||||
'fi-fo-file-upload',
|
||||
'fi-fo-file-upload-avatar' => $isAvatar,
|
||||
$alignment instanceof Alignment ? "fi-align-{$alignment->value}" : $alignment,
|
||||
]) }}>
|
||||
<div class="fi-fo-file-upload-input-ctn">
|
||||
<input x-ref="input"
|
||||
{{ $getExtraInputAttributeBag()->merge(
|
||||
[
|
||||
'aria-labelledby' => "{$id}-label",
|
||||
'disabled' => $isDisabled,
|
||||
'multiple' => $isMultiple,
|
||||
'type' => 'file',
|
||||
],
|
||||
escape: false,
|
||||
) }} />
|
||||
</div>
|
||||
|
||||
<div x-show="error" x-text="error" x-cloak class="fi-fo-file-upload-error-message"></div>
|
||||
|
||||
@if ($hasImageEditor && !$isDisabled)
|
||||
<div x-show="isEditorOpen" x-cloak x-on:click.stop="" x-trap.noscroll="isEditorOpen"
|
||||
x-on:keydown.escape.prevent.stop="closeEditor" @class([
|
||||
'fi-fo-file-upload-editor',
|
||||
'fi-fo-file-upload-editor-circle-cropper' => $hasCircleCropper,
|
||||
'fi-fo-file-upload-editor-crop-only' => !$isImageEditorExplicitlyEnabled,
|
||||
])>
|
||||
<div aria-hidden="true" class="fi-fo-file-upload-editor-overlay"></div>
|
||||
|
||||
<div class="fi-fo-file-upload-editor-window">
|
||||
<div class="fi-fo-file-upload-editor-image-ctn">
|
||||
<img x-ref="editor" class="fi-fo-file-upload-editor-image" />
|
||||
</div>
|
||||
|
||||
<div class="fi-fo-file-upload-editor-control-panel">
|
||||
@if ($isImageEditorExplicitlyEnabled)
|
||||
<div class="fi-fo-file-upload-editor-control-panel-main">
|
||||
<div class="fi-fo-file-upload-editor-control-panel-group">
|
||||
@foreach ([
|
||||
[
|
||||
'label' => __('filament-forms::components.file_upload.editor.fields.x_position.label'),
|
||||
'ref' => 'xPositionInput',
|
||||
'unit' => __('filament-forms::components.file_upload.editor.fields.x_position.unit'),
|
||||
'alpineSaveHandler' => 'editor.setData({...editor.getData(true), x: +$el.value})',
|
||||
],
|
||||
[
|
||||
'label' => __('filament-forms::components.file_upload.editor.fields.y_position.label'),
|
||||
'ref' => 'yPositionInput',
|
||||
'unit' => __('filament-forms::components.file_upload.editor.fields.y_position.unit'),
|
||||
'alpineSaveHandler' => 'editor.setData({...editor.getData(true), y: +$el.value})',
|
||||
],
|
||||
[
|
||||
'label' => __('filament-forms::components.file_upload.editor.fields.width.label'),
|
||||
'ref' => 'widthInput',
|
||||
'unit' => __('filament-forms::components.file_upload.editor.fields.width.unit'),
|
||||
'alpineSaveHandler' => 'editor.setData({...editor.getData(true), width: +$el.value})',
|
||||
],
|
||||
[
|
||||
'label' => __('filament-forms::components.file_upload.editor.fields.height.label'),
|
||||
'ref' => 'heightInput',
|
||||
'unit' => __('filament-forms::components.file_upload.editor.fields.height.unit'),
|
||||
'alpineSaveHandler' => 'editor.setData({...editor.getData(true), height: +$el.value})',
|
||||
],
|
||||
[
|
||||
'label' => __('filament-forms::components.file_upload.editor.fields.rotation.label'),
|
||||
'ref' => 'rotationInput',
|
||||
'unit' => __('filament-forms::components.file_upload.editor.fields.rotation.unit'),
|
||||
'alpineSaveHandler' => 'editor.rotateTo(+$el.value)',
|
||||
],
|
||||
] as $input)
|
||||
<label>
|
||||
<x-filament::input.wrapper>
|
||||
<x-slot name="prefix">
|
||||
{{ $input['label'] }}
|
||||
</x-slot>
|
||||
|
||||
<input x-on:keyup.enter.prevent.stop="editor && {!! $input['alpineSaveHandler'] !!}"
|
||||
x-on:blur="editor && {!! $input['alpineSaveHandler'] !!}"
|
||||
x-ref="{{ $input['ref'] }}" x-on:keydown.enter.prevent
|
||||
type="text" class="fi-input" />
|
||||
|
||||
<x-slot name="suffix">
|
||||
{{ $input['unit'] }}
|
||||
</x-slot>
|
||||
</x-filament::input.wrapper>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="fi-fo-file-upload-editor-control-panel-group">
|
||||
@foreach ($getImageEditorActions() as $groupedActions)
|
||||
<div class="fi-btn-group">
|
||||
@foreach ($groupedActions as $action)
|
||||
<button aria-label="{{ $action['label'] }}" type="button"
|
||||
x-on:click.prevent.stop="{{ $action['alpineClickHandler'] }}"
|
||||
x-tooltip="{ content: @js($action['label']), theme: $store.theme }"
|
||||
class="fi-btn">
|
||||
{{ $action['iconHtml'] }}
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if (count($aspectRatios = $getImageEditorAspectRatioOptionsForJs()))
|
||||
<div class="fi-fo-file-upload-editor-control-panel-group">
|
||||
<div class="fi-fo-file-upload-editor-control-panel-group-title">
|
||||
{{ __('filament-forms::components.file_upload.editor.aspect_ratios.label') }}
|
||||
</div>
|
||||
|
||||
@foreach (collect($aspectRatios)->chunk(5) as $ratiosChunk)
|
||||
<div class="fi-btn-group">
|
||||
@foreach ($ratiosChunk as $label => $ratio)
|
||||
<button type="button"
|
||||
x-on:click.prevent.stop="
|
||||
currentRatio = @js($label) {!! ';' !!}
|
||||
editor.setAspectRatio(@js($ratio))
|
||||
"
|
||||
x-tooltip="{ content: @js(__('filament-forms::components.file_upload.editor.actions.set_aspect_ratio.label', ['ratio' => $label])), theme: $store.theme }"
|
||||
x-bind:class="{ 'fi-active': currentRatio === @js($label) }"
|
||||
class="fi-btn">
|
||||
{{ $label }}
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="fi-fo-file-upload-editor-control-panel-footer">
|
||||
@if ($isImageEditorExplicitlyEnabled)
|
||||
<button type="button" x-on:click.prevent="pond.imageEditEditor.oncancel"
|
||||
class="fi-btn">
|
||||
{{ __('filament-forms::components.file_upload.editor.actions.cancel.label') }}
|
||||
</button>
|
||||
|
||||
<button type="button" x-on:click.prevent.stop="editor.reset()"
|
||||
{{ new \Illuminate\View\ComponentAttributeBag()->color(\Filament\Support\View\Components\ButtonComponent::class, 'danger')->class(['fi-btn fi-fo-file-upload-editor-control-panel-reset-action']) }}>
|
||||
{{ __('filament-forms::components.file_upload.editor.actions.reset.label') }}
|
||||
</button>
|
||||
|
||||
<button type="button" x-on:click.prevent="saveEditor"
|
||||
{{ new \Illuminate\View\ComponentAttributeBag()->color(\Filament\Support\View\Components\ButtonComponent::class, 'success')->class(['fi-btn']) }}>
|
||||
{{ __('filament-forms::components.file_upload.editor.actions.save.label') }}
|
||||
</button>
|
||||
@else
|
||||
<button type="button" x-on:click.prevent="saveEditor"
|
||||
{{ new \Illuminate\View\ComponentAttributeBag()->color(\Filament\Support\View\Components\ButtonComponent::class, 'success')->class(['fi-btn']) }}>
|
||||
{{ __('filament-forms::components.file_upload.editor.actions.save.label') }}
|
||||
</button>
|
||||
|
||||
<button type="button" x-on:click.prevent="pond.imageEditEditor.oncancel"
|
||||
class="fi-btn">
|
||||
{{ __('filament-forms::components.file_upload.editor.actions.cancel.label') }}
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,9 @@
|
||||
<input
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $getId(),
|
||||
'type' => 'hidden',
|
||||
$applyStateBindingModifiers('wire:model') => $getStatePath(),
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->class(['fi-fo-hidden']) }} />
|
||||
@@ -0,0 +1,92 @@
|
||||
@php
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$extraAttributeBag = $getExtraAttributeBag();
|
||||
$canEditKeys = $canEditKeys();
|
||||
$canEditValues = $canEditValues();
|
||||
$keyPlaceholder = $getKeyPlaceholder();
|
||||
$valuePlaceholder = $getValuePlaceholder();
|
||||
$debounce = $getLiveDebounce();
|
||||
$isAddable = $isAddable();
|
||||
$isDeletable = $isDeletable();
|
||||
$isDisabled = $isDisabled();
|
||||
$isReorderable = $isReorderable();
|
||||
$statePath = $getStatePath();
|
||||
$livewireKey = $getLivewireKey();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" class="fi-fo-key-value-wrp">
|
||||
<x-filament::input.wrapper :disabled="$isDisabled" :valid="!$errors->has($statePath)" :attributes="\Filament\Support\prepare_inherited_attributes($extraAttributeBag)->class(['fi-fo-key-value'])">
|
||||
<div x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('key-value', 'filament/forms') }}"
|
||||
x-data="keyValueFormComponent({
|
||||
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$statePath}')") }},
|
||||
})" wire:ignore
|
||||
wire:key="{{ $livewireKey }}.{{ substr(md5(serialize([$isDisabled])), 0, 64) }}"
|
||||
{{ $attributes->merge($getExtraAlpineAttributes(), escape: false)->class(['fi-fo-key-value-table-ctn']) }}>
|
||||
<table class="fi-fo-key-value-table">
|
||||
<thead>
|
||||
<tr>
|
||||
@if ($isReorderable && !$isDisabled)
|
||||
<th scope="col" x-show="rows.length" class="fi-has-action"></th>
|
||||
@endif
|
||||
|
||||
<th scope="col">
|
||||
{{ $getKeyLabel() }}
|
||||
</th>
|
||||
|
||||
<th scope="col">
|
||||
{{ $getValueLabel() }}
|
||||
</th>
|
||||
|
||||
@if ($isDeletable && !$isDisabled)
|
||||
<th scope="col" x-show="rows.length" class="fi-has-action"></th>
|
||||
@endif
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody
|
||||
@if ($isReorderable) x-on:end.stop="reorderRows($event)"
|
||||
x-sortable
|
||||
data-sortable-animation-duration="{{ $getReorderAnimationDuration() }}" @endif>
|
||||
<template x-bind:key="index" x-for="(row, index) in rows">
|
||||
<tr @if ($isReorderable) x-bind:x-sortable-item="row.key" @endif>
|
||||
@if ($isReorderable && !$isDisabled)
|
||||
<td class="fi-has-action">
|
||||
<div x-sortable-handle class="fi-fo-key-value-table-row-sortable-handle">
|
||||
{{ $getAction('reorder') }}
|
||||
</div>
|
||||
</td>
|
||||
@endif
|
||||
|
||||
<td>
|
||||
<input @disabled(!$canEditKeys || $isDisabled) placeholder="{{ $keyPlaceholder }}" type="text"
|
||||
x-model="row.key" x-on:input.debounce.{{ $debounce ?? '500ms' }}="updateState"
|
||||
class="fi-input" />
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input @disabled(!$canEditValues || $isDisabled) placeholder="{{ $valuePlaceholder }}"
|
||||
type="text" x-model="row.value"
|
||||
x-on:input.debounce.{{ $debounce ?? '500ms' }}="updateState" class="fi-input" />
|
||||
</td>
|
||||
|
||||
@if ($isDeletable && !$isDisabled)
|
||||
<td class="fi-has-action">
|
||||
<div x-on:click="deleteRow(index)">
|
||||
{{ $getAction('delete') }}
|
||||
</div>
|
||||
</td>
|
||||
@endif
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@if ($isAddable && !$isDisabled)
|
||||
<div x-on:click="addRow" class="fi-fo-key-value-add-action-ctn">
|
||||
{{ $getAction('add') }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-filament::input.wrapper>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,29 @@
|
||||
@php
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$extraAttributes = $getExtraAttributes();
|
||||
$id = $getId();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
@if (filled($id) || filled($extraAttributes))
|
||||
{!! '<div' !!}
|
||||
{{-- Avoid formatting issues with unclosed elements --}}
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $id,
|
||||
],
|
||||
escape: false,
|
||||
)->merge($extraAttributes, escape: false) }}
|
||||
>
|
||||
@endif
|
||||
|
||||
@if (filled($key = $getLivewireKey()))
|
||||
@livewire($getComponent(), $getComponentProperties(), key($key))
|
||||
@else
|
||||
@livewire($getComponent(), $getComponentProperties())
|
||||
@endif
|
||||
@if (filled($id) || filled($extraAttributes))
|
||||
{!! '</div>' !!}
|
||||
{{-- Avoid formatting issues with unclosed elements --}}
|
||||
@endif
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,66 @@
|
||||
@php
|
||||
$id = $getId();
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$extraAttributeBag = $getExtraAttributeBag();
|
||||
$key = $getKey();
|
||||
$label = $getLabel();
|
||||
$statePath = $getStatePath();
|
||||
$fileAttachmentsMaxSize = $getFileAttachmentsMaxSize();
|
||||
$fileAttachmentsAcceptedFileTypes = $getFileAttachmentsAcceptedFileTypes();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
@if ($isDisabled())
|
||||
<div id="{{ $id }}" class="fi-fo-markdown-editor fi-disabled fi-prose">
|
||||
{!! str($getState())->markdown($getCommonMarkOptions(), $getCommonMarkExtensions())->sanitizeHtml() !!}
|
||||
</div>
|
||||
@else
|
||||
<x-filament::input.wrapper :valid="!$errors->has($statePath)" :attributes="\Filament\Support\prepare_inherited_attributes($extraAttributeBag)->class(['fi-fo-markdown-editor'])">
|
||||
<div aria-labelledby="{{ $id }}-label" id="{{ $id }}" role="group" x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('markdown-editor', 'filament/forms') }}"
|
||||
x-data="markdownEditorFormComponent({
|
||||
canAttachFiles: @js($hasFileAttachments()),
|
||||
isLiveDebounced: @js($isLiveDebounced()),
|
||||
isLiveOnBlur: @js($isLiveOnBlur()),
|
||||
label: @js($label),
|
||||
liveDebounce: @js($getNormalizedLiveDebounce()),
|
||||
maxHeight: @js($getMaxHeight()),
|
||||
minHeight: @js($getMinHeight()),
|
||||
placeholder: @js($getPlaceholder()),
|
||||
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$statePath}')", isOptimisticallyLive: false) }},
|
||||
toolbarButtons: @js($getToolbarButtons()),
|
||||
translations: @js(__('filament-forms::components.markdown_editor')),
|
||||
uploadFileAttachmentUsing: async (file, onSuccess, onError) => {
|
||||
const acceptedTypes = @js($fileAttachmentsAcceptedFileTypes)
|
||||
|
||||
if (acceptedTypes && !acceptedTypes.includes(file.type)) {
|
||||
return onError(@js($fileAttachmentsAcceptedFileTypes ? __('filament-forms::components.markdown_editor.file_attachments_accepted_file_types_message', ['values' => implode(', ', $fileAttachmentsAcceptedFileTypes)]) : null))
|
||||
}
|
||||
|
||||
const maxSize = @js($fileAttachmentsMaxSize)
|
||||
|
||||
if (maxSize && file.size > +maxSize * 1024) {
|
||||
return onError(@js($fileAttachmentsMaxSize ? trans_choice('filament-forms::components.markdown_editor.file_attachments_max_size_message', $fileAttachmentsMaxSize, ['max' => $fileAttachmentsMaxSize]) : null))
|
||||
}
|
||||
|
||||
$wire.upload(`componentFileAttachments.{{ $statePath }}`, file, () => {
|
||||
$wire
|
||||
.callSchemaComponentMethod(
|
||||
'{{ $key }}',
|
||||
'saveUploadedFileAttachmentAndGetUrl',
|
||||
)
|
||||
.then((url) => {
|
||||
if (!url) {
|
||||
return onError()
|
||||
}
|
||||
|
||||
onSuccess(url)
|
||||
})
|
||||
})
|
||||
},
|
||||
})" wire:ignore {{ $getExtraAlpineAttributeBag() }}>
|
||||
<textarea x-ref="editor" x-cloak></textarea>
|
||||
</div>
|
||||
</x-filament::input.wrapper>
|
||||
@endif
|
||||
</x-dynamic-component>
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
@php
|
||||
use Filament\Forms\Components\TableSelect\Livewire\TableSelectLivewireComponent;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$extraAttributes = $getExtraAttributes();
|
||||
$id = $getId();
|
||||
$isDisabled = $isDisabled();
|
||||
$isMultiple = $isMultiple();
|
||||
$hasBadges = $hasBadges();
|
||||
$badgeColor = $getBadgeColor();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
<div
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $id,
|
||||
],
|
||||
escape: false,
|
||||
)->merge($extraAttributes, escape: false)->class([
|
||||
'fi-fo-modal-table-select',
|
||||
'fi-fo-modal-table-select-disabled' => $isDisabled,
|
||||
'fi-fo-modal-table-select-multiple' => $isMultiple,
|
||||
]) }}>
|
||||
@if (
|
||||
(!$isMultiple && filled($optionLabel = $getOptionLabel())) ||
|
||||
($isMultiple && filled($optionLabels = $getOptionLabels())))
|
||||
@if ($isMultiple && $hasBadges)
|
||||
<div class="fi-fo-modal-table-select-badges-ctn">
|
||||
@foreach ($optionLabels as $optionLabel)
|
||||
@if ($hasBadges)
|
||||
<x-filament::badge :color="$badgeColor">
|
||||
{{ $optionLabel }}
|
||||
</x-filament::badge>
|
||||
@else
|
||||
{{ $optionLabel }}
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div>
|
||||
@if ($hasBadges)
|
||||
<x-filament::badge :color="$badgeColor">
|
||||
{{ $optionLabel }}
|
||||
</x-filament::badge>
|
||||
@elseif ($isMultiple)
|
||||
@foreach ($optionLabels as $optionLabel)
|
||||
{{ $optionLabel . ($loop->last ? '' : ', ') }}
|
||||
@endforeach
|
||||
@else
|
||||
{{ $optionLabel }}
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@elseif (filled($placeholder = $getPlaceholder()))
|
||||
<div class="fi-fo-modal-table-select-placeholder">
|
||||
{{ $placeholder }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (!$isDisabled)
|
||||
<div>
|
||||
{{ $getAction('select') }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
@php
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$placeholder = $getPlaceholder();
|
||||
$extraAttributes = $getExtraAttributeBag()->merge($getExtraAlpineAttributes(), escape: false);
|
||||
$extraInputAttributes = $getExtraInputAttributeBag()->merge(
|
||||
[
|
||||
'autocomplete' => false,
|
||||
'autofocus' => $isAutofocused(),
|
||||
'disabled' => $isDisabled(),
|
||||
'id' => $getId(),
|
||||
'length' => $getLength(),
|
||||
'placeholder' => filled($placeholder) ? e($placeholder) : null,
|
||||
'readonly' => $isReadOnly(),
|
||||
'required' => $isRequired() && !$isConcealed(),
|
||||
$applyStateBindingModifiers('wire:model') => $getStatePath(),
|
||||
],
|
||||
escape: false,
|
||||
);
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
<x-filament::input.one-time-code :attributes="\Filament\Support\prepare_inherited_attributes($extraAttributes)">
|
||||
<x-slot name="input" :attributes="\Filament\Support\prepare_inherited_attributes($extraInputAttributes)"></x-slot>
|
||||
</x-filament::input.one-time-code>
|
||||
</x-dynamic-component>
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
@props([
|
||||
'field' => null,
|
||||
'id' => null,
|
||||
'label' => null,
|
||||
'labelTag' => 'label',
|
||||
])
|
||||
|
||||
@php
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
if ($field) {
|
||||
$id ??= $field->getId();
|
||||
$label ??= $field->getLabel();
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div data-field-wrapper
|
||||
{{ new ComponentAttributeBag()->merge($field?->getExtraFieldWrapperAttributes() ?? [], escape: false)->class(['fi-fo-field']) }}>
|
||||
@if (filled($label))
|
||||
<{{ $labelTag }}
|
||||
@if ($labelTag === 'label') for="{{ $id }}"
|
||||
@else
|
||||
id="{{ $id }}-label" @endif
|
||||
class="fi-fo-field-label fi-sr-only">
|
||||
{{ $label }}
|
||||
</{{ $labelTag }}>
|
||||
@endif
|
||||
|
||||
{{ $slot }}
|
||||
</div>
|
||||
@@ -0,0 +1,55 @@
|
||||
@php
|
||||
use Filament\Support\Enums\GridDirection;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$extraInputAttributeBag = $getExtraInputAttributeBag();
|
||||
$gridDirection = $getGridDirection() ?? GridDirection::Column;
|
||||
$id = $getId();
|
||||
$isDisabled = $isDisabled();
|
||||
$isInline = $isInline();
|
||||
$statePath = $getStatePath();
|
||||
$wireModelAttribute = $applyStateBindingModifiers('wire:model');
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
<div
|
||||
{{ $getExtraAttributeBag()->when(!$isInline, fn(ComponentAttributeBag $attributes) => $attributes->grid($getColumns(), $gridDirection))->class(['fi-fo-radio', 'fi-inline' => $isInline]) }}>
|
||||
@foreach ($getOptions() as $value => $label)
|
||||
@php
|
||||
$inputAttributes = $extraInputAttributeBag->merge(
|
||||
[
|
||||
'autofocus' => $loop->first && $isAutofocused(),
|
||||
'disabled' => $isDisabled || $isOptionDisabled($value, $label),
|
||||
'id' => e($id . '-' . $value),
|
||||
'name' => $id,
|
||||
'value' => e($value),
|
||||
$wireModelAttribute => $statePath,
|
||||
],
|
||||
escape: false,
|
||||
);
|
||||
@endphp
|
||||
|
||||
<label class="fi-fo-radio-label">
|
||||
<input type="radio"
|
||||
{{ $inputAttributes->class([
|
||||
'fi-radio-input',
|
||||
'fi-valid' => !$errors->has($statePath),
|
||||
'fi-invalid' => $errors->has($statePath),
|
||||
]) }} />
|
||||
|
||||
<div class="fi-fo-radio-label-text">
|
||||
<p>
|
||||
{{ $label }}
|
||||
</p>
|
||||
|
||||
@if ($hasDescription($value))
|
||||
<p class="fi-fo-radio-label-description">
|
||||
{{ $getDescription($value) }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,224 @@
|
||||
@php
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
|
||||
$items = $getItems();
|
||||
|
||||
$addAction = $getAction($getAddActionName());
|
||||
$addActionAlignment = $getAddActionAlignment();
|
||||
$addBetweenAction = $getAction($getAddBetweenActionName());
|
||||
$cloneAction = $getAction($getCloneActionName());
|
||||
$collapseAllAction = $getAction($getCollapseAllActionName());
|
||||
$expandAllAction = $getAction($getExpandAllActionName());
|
||||
$deleteAction = $getAction($getDeleteActionName());
|
||||
$moveDownAction = $getAction($getMoveDownActionName());
|
||||
$moveUpAction = $getAction($getMoveUpActionName());
|
||||
$reorderAction = $getAction($getReorderActionName());
|
||||
$extraItemActions = $getExtraItemActions();
|
||||
|
||||
$hasItemNumbers = $hasItemNumbers();
|
||||
$hasItemHeaders = $hasItemHeaders();
|
||||
$isAddable = $isAddable();
|
||||
$isCloneable = $isCloneable();
|
||||
$isCollapsible = $isCollapsible();
|
||||
$isDeletable = $isDeletable();
|
||||
$isReorderableWithButtons = $isReorderableWithButtons();
|
||||
$isReorderableWithDragAndDrop = $isReorderableWithDragAndDrop();
|
||||
|
||||
$collapseAllActionIsVisible = $isCollapsible && $collapseAllAction->isVisible();
|
||||
$expandAllActionIsVisible = $isCollapsible && $expandAllAction->isVisible();
|
||||
$persistCollapsed = $shouldPersistCollapsed();
|
||||
|
||||
$key = $getKey();
|
||||
$statePath = $getStatePath();
|
||||
|
||||
$itemLabelHeadingTag = $getHeadingTag();
|
||||
$isItemLabelTruncated = $isItemLabelTruncated();
|
||||
$labelBetweenItems = $getLabelBetweenItems();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
<div
|
||||
{{ $attributes->merge($getExtraAttributes(), escape: false)->class(['fi-fo-repeater', 'fi-collapsible' => $isCollapsible]) }}>
|
||||
@if ($collapseAllActionIsVisible || $expandAllActionIsVisible)
|
||||
<div @class(['fi-fo-repeater-actions', 'fi-hidden' => count($items) < 2])>
|
||||
@if ($collapseAllActionIsVisible)
|
||||
<span x-on:click="$dispatch('repeater-collapse', '{{ $statePath }}')">
|
||||
{{ $collapseAllAction }}
|
||||
</span>
|
||||
@endif
|
||||
|
||||
@if ($expandAllActionIsVisible)
|
||||
<span x-on:click="$dispatch('repeater-expand', '{{ $statePath }}')">
|
||||
{{ $expandAllAction }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (count($items))
|
||||
<ul x-sortable
|
||||
{{ new ComponentAttributeBag()->grid($getGridColumns())->merge(
|
||||
[
|
||||
'data-sortable-animation-duration' => $getReorderAnimationDuration(),
|
||||
'x-on:end.stop' =>
|
||||
'$wire.mountAction(\'reorder\', { items: $event.target.sortable.toArray() }, { schemaComponent: \'' .
|
||||
$key .
|
||||
'\' })',
|
||||
],
|
||||
escape: false,
|
||||
)->class(['fi-fo-repeater-items']) }}>
|
||||
@foreach ($items as $itemKey => $item)
|
||||
@php
|
||||
$itemLabel = $getItemLabel($itemKey, $loop->index);
|
||||
$visibleExtraItemActions = array_filter(
|
||||
$extraItemActions,
|
||||
fn(Action $action): bool => $action(['item' => $itemKey])->isVisible(),
|
||||
);
|
||||
$cloneAction = $cloneAction(['item' => $itemKey]);
|
||||
$cloneActionIsVisible = $isCloneable && $cloneAction->isVisible();
|
||||
$deleteAction = $deleteAction(['item' => $itemKey]);
|
||||
$deleteActionIsVisible = $isDeletable && $deleteAction->isVisible();
|
||||
$moveDownAction = $moveDownAction(['item' => $itemKey])->disabled($loop->last);
|
||||
$moveDownActionIsVisible = $isReorderableWithButtons && $moveDownAction->isVisible();
|
||||
$moveUpAction = $moveUpAction(['item' => $itemKey])->disabled($loop->first);
|
||||
$moveUpActionIsVisible = $isReorderableWithButtons && $moveUpAction->isVisible();
|
||||
$reorderActionIsVisible = $isReorderableWithDragAndDrop && $reorderAction->isVisible();
|
||||
$hasItemHeader =
|
||||
$hasItemHeaders &&
|
||||
($reorderActionIsVisible ||
|
||||
$moveUpActionIsVisible ||
|
||||
$moveDownActionIsVisible ||
|
||||
filled($itemLabel) ||
|
||||
$cloneActionIsVisible ||
|
||||
$deleteActionIsVisible ||
|
||||
$isCollapsible ||
|
||||
$visibleExtraItemActions);
|
||||
@endphp
|
||||
|
||||
<li wire:ignore.self wire:key="{{ $item->getLivewireKey() }}.item" x-data="{
|
||||
isCollapsed: @if ($persistCollapsed) $persist(@js($isCollapsed($item))).as(`repeater-${@js($key)}-${@js($itemKey)}-isCollapsed`) @else @js($isCollapsed($item)) @endif,
|
||||
}"
|
||||
x-on:repeater-expand.window="$event.detail === '{{ $statePath }}' && (isCollapsed = false)"
|
||||
x-on:repeater-collapse.window="$event.detail === '{{ $statePath }}' && (isCollapsed = true)"
|
||||
x-on:expand="isCollapsed = false" x-sortable-item="{{ $itemKey }}"
|
||||
@class([
|
||||
'fi-fo-repeater-item',
|
||||
'fi-fo-repeater-item-has-header' => $hasItemHeader,
|
||||
]) x-bind:class="{ 'fi-collapsed': isCollapsed }">
|
||||
@if ($hasItemHeader)
|
||||
<div @if ($isCollapsible) x-on:click.stop="isCollapsed = !isCollapsed" @endif
|
||||
class="fi-fo-repeater-item-header">
|
||||
@if ($reorderActionIsVisible || $moveUpActionIsVisible || $moveDownActionIsVisible)
|
||||
<ul class="fi-fo-repeater-item-header-start-actions">
|
||||
@if ($reorderActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $reorderAction->extraAttributes(['x-sortable-handle' => true], merge: true) }}
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@if ($moveUpActionIsVisible || $moveDownActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $moveUpAction }}
|
||||
</li>
|
||||
|
||||
<li x-on:click.stop>
|
||||
{{ $moveDownAction }}
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
@if (filled($itemLabel))
|
||||
<{{ $itemLabelHeadingTag }} @class([
|
||||
'fi-fo-repeater-item-header-label',
|
||||
'fi-truncated' => $isItemLabelTruncated,
|
||||
])>
|
||||
{{ $itemLabel }}
|
||||
|
||||
@if ($hasItemNumbers)
|
||||
{{ $loop->iteration }}
|
||||
@endif
|
||||
</{{ $itemLabelHeadingTag }}>
|
||||
@endif
|
||||
|
||||
@if ($cloneActionIsVisible || $deleteActionIsVisible || $isCollapsible || $visibleExtraItemActions)
|
||||
<ul class="fi-fo-repeater-item-header-end-actions">
|
||||
@foreach ($visibleExtraItemActions as $extraItemAction)
|
||||
<li x-on:click.stop>
|
||||
{{ $extraItemAction(['item' => $itemKey]) }}
|
||||
</li>
|
||||
@endforeach
|
||||
|
||||
@if ($cloneActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $cloneAction }}
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@if ($deleteActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $deleteAction }}
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@if ($isCollapsible)
|
||||
<li class="fi-fo-repeater-item-header-collapsible-actions"
|
||||
x-on:click.stop="isCollapsed = !isCollapsed">
|
||||
<div class="fi-fo-repeater-item-header-collapse-action">
|
||||
{{ $getAction('collapse') }}
|
||||
</div>
|
||||
|
||||
<div class="fi-fo-repeater-item-header-expand-action">
|
||||
{{ $getAction('expand') }}
|
||||
</div>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div x-show="! isCollapsed" class="fi-fo-repeater-item-content">
|
||||
{{ $item }}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@if (!$loop->last)
|
||||
@if ($isAddable && $addBetweenAction(['afterItem' => $itemKey])->isVisible())
|
||||
<li class="fi-fo-repeater-add-between-items-ctn">
|
||||
<div class="fi-fo-repeater-add-between-items">
|
||||
{{ $addBetweenAction(['afterItem' => $itemKey]) }}
|
||||
</div>
|
||||
</li>
|
||||
@elseif (filled($labelBetweenItems))
|
||||
<li class="fi-fo-repeater-label-between-items-ctn">
|
||||
<div class="fi-fo-repeater-label-between-items-divider-before"></div>
|
||||
|
||||
<span class="fi-fo-repeater-label-between-items">
|
||||
{{ $labelBetweenItems }}
|
||||
</span>
|
||||
|
||||
<div class="fi-fo-repeater-label-between-items-divider-after"></div>
|
||||
</li>
|
||||
@endif
|
||||
@endif
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
@if ($isAddable && $addAction->isVisible())
|
||||
<div @class([
|
||||
'fi-fo-repeater-add',
|
||||
$addActionAlignment instanceof Alignment
|
||||
? 'fi-align-' . $addActionAlignment->value
|
||||
: $addActionAlignment,
|
||||
])>
|
||||
{{ $addAction }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,125 @@
|
||||
@php
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
|
||||
$items = $getItems();
|
||||
|
||||
$addAction = $getAction($getAddActionName());
|
||||
$addActionAlignment = $getAddActionAlignment();
|
||||
$cloneAction = $getAction($getCloneActionName());
|
||||
$deleteAction = $getAction($getDeleteActionName());
|
||||
$moveDownAction = $getAction($getMoveDownActionName());
|
||||
$moveUpAction = $getAction($getMoveUpActionName());
|
||||
$reorderAction = $getAction($getReorderActionName());
|
||||
$extraItemActions = $getExtraItemActions();
|
||||
|
||||
$isAddable = $isAddable();
|
||||
$isCloneable = $isCloneable();
|
||||
$isDeletable = $isDeletable();
|
||||
$isReorderableWithButtons = $isReorderableWithButtons();
|
||||
$isReorderableWithDragAndDrop = $isReorderableWithDragAndDrop();
|
||||
|
||||
$key = $getKey();
|
||||
$statePath = $getStatePath();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
<div {{ $attributes->merge($getExtraAttributes(), escape: false)->class(['fi-fo-simple-repeater']) }}>
|
||||
@if (count($items))
|
||||
<ul x-sortable
|
||||
{{ new ComponentAttributeBag()->grid($getGridColumns())->merge(
|
||||
[
|
||||
'data-sortable-animation-duration' => $getReorderAnimationDuration(),
|
||||
'x-on:end.stop' =>
|
||||
'$wire.mountAction(\'reorder\', { items: $event.target.sortable.toArray() }, { schemaComponent: \'' .
|
||||
$key .
|
||||
'\' })',
|
||||
],
|
||||
escape: false,
|
||||
)->class(['fi-fo-simple-repeater-items']) }}>
|
||||
@foreach ($items as $itemKey => $item)
|
||||
@php
|
||||
$visibleExtraItemActions = array_filter(
|
||||
$extraItemActions,
|
||||
fn(Action $action): bool => $action(['item' => $itemKey])->isVisible(),
|
||||
);
|
||||
$cloneAction = $cloneAction(['item' => $itemKey]);
|
||||
$cloneActionIsVisible = $isCloneable && $cloneAction->isVisible();
|
||||
$deleteAction = $deleteAction(['item' => $itemKey]);
|
||||
$deleteActionIsVisible = $isDeletable && $deleteAction->isVisible();
|
||||
$moveDownAction = $moveDownAction(['item' => $itemKey])->disabled($loop->last);
|
||||
$moveDownActionIsVisible = $isReorderableWithButtons && $moveDownAction->isVisible();
|
||||
$moveUpAction = $moveUpAction(['item' => $itemKey])->disabled($loop->first);
|
||||
$moveUpActionIsVisible = $isReorderableWithButtons && $moveUpAction->isVisible();
|
||||
$reorderActionIsVisible = $isReorderableWithDragAndDrop && $reorderAction->isVisible();
|
||||
@endphp
|
||||
|
||||
<li wire:key="{{ $item->getLivewireKey() }}.item" x-sortable-item="{{ $itemKey }}"
|
||||
class="fi-fo-simple-repeater-item">
|
||||
<div class="fi-fo-simple-repeater-item-content">
|
||||
{{ $item }}
|
||||
</div>
|
||||
|
||||
@if (
|
||||
$reorderActionIsVisible ||
|
||||
$moveUpActionIsVisible ||
|
||||
$moveDownActionIsVisible ||
|
||||
$cloneActionIsVisible ||
|
||||
$deleteActionIsVisible ||
|
||||
$visibleExtraItemActions)
|
||||
<ul class="fi-fo-simple-repeater-item-actions">
|
||||
@if ($reorderActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $reorderAction->extraAttributes(['x-sortable-handle' => true], merge: true) }}
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@if ($moveUpActionIsVisible || $moveDownActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $moveUpAction }}
|
||||
</li>
|
||||
|
||||
<li x-on:click.stop>
|
||||
{{ $moveDownAction }}
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@foreach ($visibleExtraItemActions as $extraItemAction)
|
||||
<li x-on:click.stop>
|
||||
{{ $extraItemAction(['item' => $itemKey]) }}
|
||||
</li>
|
||||
@endforeach
|
||||
|
||||
@if ($cloneActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $cloneAction }}
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@if ($deleteActionIsVisible)
|
||||
<li x-on:click.stop>
|
||||
{{ $deleteAction }}
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
@if ($isAddable && $addAction->isVisible())
|
||||
<div @class([
|
||||
'fi-fo-simple-repeater-add',
|
||||
$addActionAlignment instanceof Alignment
|
||||
? 'fi-align-' . $addActionAlignment->value
|
||||
: $addActionAlignment,
|
||||
])>
|
||||
{{ $addAction }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,217 @@
|
||||
@php
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Support\Enums\VerticalAlignment;
|
||||
use Illuminate\Support\Js;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
|
||||
$items = $getItems();
|
||||
|
||||
$addAction = $getAction($getAddActionName());
|
||||
$addActionAlignment = $getAddActionAlignment();
|
||||
$addBetweenAction = $getAction($getAddBetweenActionName());
|
||||
$cloneAction = $getAction($getCloneActionName());
|
||||
$deleteAction = $getAction($getDeleteActionName());
|
||||
$moveDownAction = $getAction($getMoveDownActionName());
|
||||
$moveUpAction = $getAction($getMoveUpActionName());
|
||||
$reorderAction = $getAction($getReorderActionName());
|
||||
$extraItemActions = $getExtraItemActions();
|
||||
|
||||
$isAddable = $isAddable();
|
||||
$isCloneable = $isCloneable();
|
||||
$isDeletable = $isDeletable();
|
||||
$isReorderableWithButtons = $isReorderableWithButtons();
|
||||
$isReorderableWithDragAndDrop = $isReorderableWithDragAndDrop();
|
||||
|
||||
$key = $getKey();
|
||||
$statePath = $getStatePath();
|
||||
|
||||
$tableColumns = $getTableColumns();
|
||||
|
||||
$isCompact = $isCompact();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
<div
|
||||
{{ $attributes->merge($getExtraAttributes(), escape: false)->class(['fi-fo-table-repeater', 'fi-compact' => $isCompact]) }}>
|
||||
@if (count($items))
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@if (count($items) > 1 && ($isReorderableWithButtons || $isReorderableWithDragAndDrop))
|
||||
<th class="fi-fo-table-repeater-empty-header-cell"></th>
|
||||
@endif
|
||||
|
||||
@foreach ($tableColumns as $column)
|
||||
<th @class([
|
||||
'fi-wrapped' => $column->canHeaderWrap(),
|
||||
($columnAlignment = $column->getAlignment()) instanceof Alignment
|
||||
? 'fi-align-' . $columnAlignment->value
|
||||
: $columnAlignment,
|
||||
]) @style([
|
||||
'width: ' . e($columnWidth = $column->getWidth()) => filled($columnWidth),
|
||||
])>
|
||||
@if (!$column->isHeaderLabelHidden())
|
||||
{{ $column->getLabel() }}@if ($column->isMarkedAsRequired())
|
||||
<sup class="fi-fo-table-repeater-header-required-mark">*</sup>
|
||||
@endif
|
||||
@else
|
||||
<span class="fi-sr-only">
|
||||
{{ $column->getLabel() }}
|
||||
</span>
|
||||
@endif
|
||||
</th>
|
||||
@endforeach
|
||||
|
||||
@if (count($extraItemActions) || $isCloneable || $isDeletable)
|
||||
<th class="fi-fo-table-repeater-empty-header-cell"></th>
|
||||
@endif
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody x-sortable
|
||||
{{ new ComponentAttributeBag()->merge(
|
||||
[
|
||||
'data-sortable-animation-duration' => $getReorderAnimationDuration(),
|
||||
'x-on:end.stop' =>
|
||||
'$wire.mountAction(\'reorder\', { items: $event.target.sortable.toArray() }, { schemaComponent: \'' .
|
||||
$key .
|
||||
'\' })',
|
||||
],
|
||||
escape: false,
|
||||
) }}>
|
||||
@foreach ($items as $itemKey => $item)
|
||||
@php
|
||||
$visibleExtraItemActions = array_filter(
|
||||
$extraItemActions,
|
||||
fn(Action $action): bool => $action(['item' => $itemKey])->isVisible(),
|
||||
);
|
||||
$cloneAction = $cloneAction(['item' => $itemKey]);
|
||||
$cloneActionIsVisible = $isCloneable && $cloneAction->isVisible();
|
||||
$deleteAction = $deleteAction(['item' => $itemKey]);
|
||||
$deleteActionIsVisible = $isDeletable && $deleteAction->isVisible();
|
||||
$moveDownAction = $moveDownAction(['item' => $itemKey])->disabled($loop->last);
|
||||
$moveDownActionIsVisible = $isReorderableWithButtons && $moveDownAction->isVisible();
|
||||
$moveUpAction = $moveUpAction(['item' => $itemKey])->disabled($loop->first);
|
||||
$moveUpActionIsVisible = $isReorderableWithButtons && $moveUpAction->isVisible();
|
||||
$reorderActionIsVisible = $isReorderableWithDragAndDrop && $reorderAction->isVisible();
|
||||
$itemStatePath = $item->getStatePath();
|
||||
@endphp
|
||||
|
||||
<tr wire:key="{{ $item->getLivewireKey() }}.item" x-sortable-item="{{ $itemKey }}">
|
||||
@if (count($items) > 1 && ($isReorderableWithButtons || $isReorderableWithDragAndDrop))
|
||||
<td>
|
||||
@if ($reorderActionIsVisible || $moveUpActionIsVisible || $moveDownActionIsVisible)
|
||||
<div class="fi-fo-table-repeater-actions">
|
||||
@if ($reorderActionIsVisible)
|
||||
<div x-on:click.stop>
|
||||
{{ $reorderAction->extraAttributes(['x-sortable-handle' => true], merge: true) }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($moveUpActionIsVisible || $moveDownActionIsVisible)
|
||||
<div x-on:click.stop>
|
||||
{{ $moveUpAction }}
|
||||
</div>
|
||||
|
||||
<div x-on:click.stop>
|
||||
{{ $moveDownAction }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
@endif
|
||||
|
||||
@php
|
||||
$counter = 0;
|
||||
@endphp
|
||||
|
||||
@foreach ($item->getComponents(withHidden: true) as $schemaComponent)
|
||||
@php
|
||||
throw_unless(
|
||||
$schemaComponent instanceof \Filament\Schemas\Components\Component,
|
||||
new \Exception(
|
||||
'Table repeaters must only contain schema components, but [' .
|
||||
$schemaComponent::class .
|
||||
'] was used.',
|
||||
),
|
||||
);
|
||||
@endphp
|
||||
|
||||
@if (count($tableColumns) > $counter)
|
||||
@if ($schemaComponent instanceof \Filament\Forms\Components\Hidden)
|
||||
{{ $schemaComponent }}
|
||||
@else
|
||||
@php
|
||||
$counter++;
|
||||
@endphp
|
||||
|
||||
@if ($schemaComponent->isVisible())
|
||||
@php
|
||||
$currentColumn = $tableColumns[$counter - 1] ?? null;
|
||||
$columnVerticalAlignment = $currentColumn?->getVerticalAlignment();
|
||||
@endphp
|
||||
|
||||
<td @class([
|
||||
$columnVerticalAlignment instanceof VerticalAlignment
|
||||
? 'fi-vertical-align-' . $columnVerticalAlignment->value
|
||||
: (is_string($columnVerticalAlignment)
|
||||
? $columnVerticalAlignment
|
||||
: ''),
|
||||
])>
|
||||
{!! $schemaComponent->toSchemaHtml() !!}
|
||||
</td>
|
||||
@else
|
||||
<td class="fi-hidden"></td>
|
||||
@endif
|
||||
@endif
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
@if (count($extraItemActions) || $isCloneable || $isDeletable)
|
||||
<td>
|
||||
@if ($visibleExtraItemActions || $cloneActionIsVisible || $deleteActionIsVisible)
|
||||
<div class="fi-fo-table-repeater-actions">
|
||||
@foreach ($visibleExtraItemActions as $extraItemAction)
|
||||
<div x-on:click.stop>
|
||||
{{ $extraItemAction(['item' => $itemKey]) }}
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@if ($cloneActionIsVisible)
|
||||
<div x-on:click.stop>
|
||||
{{ $cloneAction }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($deleteActionIsVisible)
|
||||
<div x-on:click.stop>
|
||||
{{ $deleteAction }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
@endif
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@endif
|
||||
|
||||
@if ($isAddable && $addAction->isVisible())
|
||||
<div @class([
|
||||
'fi-fo-table-repeater-add',
|
||||
$addActionAlignment instanceof Alignment
|
||||
? 'fi-align-' . $addActionAlignment->value
|
||||
: $addActionAlignment,
|
||||
])>
|
||||
{{ $addAction }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,204 @@
|
||||
@php
|
||||
$customBlocks = $getCustomBlocks();
|
||||
$groupedCustomBlocks = $getGroupedCustomBlocks();
|
||||
$extraAttributeBag = $getExtraAttributeBag();
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$id = $getId();
|
||||
$isDisabled = $isDisabled();
|
||||
$label = $getLabel();
|
||||
$livewireKey = $getLivewireKey();
|
||||
$key = $getKey();
|
||||
$mergeTags = $getMergeTags();
|
||||
$statePath = $getStatePath();
|
||||
$mentions = $getMentionsForJs();
|
||||
$toolbarButtons = $getToolbarButtons();
|
||||
$tools = $getTools();
|
||||
$floatingToolbars = $getFloatingToolbars();
|
||||
$linkProtocols = $getLinkProtocols();
|
||||
$fileAttachmentsMaxSize = $getFileAttachmentsMaxSize();
|
||||
$fileAttachmentsAcceptedFileTypes = $getFileAttachmentsAcceptedFileTypes();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
<x-filament::input.wrapper :disabled="$isDisabled" :valid="!$errors->has($statePath)" x-cloak :attributes="\Filament\Support\prepare_inherited_attributes($extraAttributeBag)->class(['fi-fo-rich-editor'])">
|
||||
<div x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('rich-editor', 'filament/forms') }}"
|
||||
x-data="richEditorFormComponent({
|
||||
acceptedFileTypes: @js($fileAttachmentsAcceptedFileTypes),
|
||||
acceptedFileTypesValidationMessage: @js($fileAttachmentsAcceptedFileTypes ? __('filament-forms::components.rich_editor.file_attachments_accepted_file_types_message', ['values' => implode(', ', $fileAttachmentsAcceptedFileTypes)]) : null),
|
||||
activePanel: @js($getActivePanel()),
|
||||
canAttachFiles: @js($hasFileAttachments()),
|
||||
deleteCustomBlockButtonIconHtml: @js(\Filament\Support\generate_icon_html(\Filament\Support\Icons\Heroicon::Trash, alias: \Filament\Forms\View\FormsIconAlias::COMPONENTS_RICH_EDITOR_PANELS_CUSTOM_BLOCK_DELETE_BUTTON)->toHtml()),
|
||||
editCustomBlockButtonIconHtml: @js(\Filament\Support\generate_icon_html(\Filament\Support\Icons\Heroicon::PencilSquare, alias: \Filament\Forms\View\FormsIconAlias::COMPONENTS_RICH_EDITOR_PANELS_CUSTOM_BLOCK_EDIT_BUTTON)->toHtml()),
|
||||
extensions: @js($getTipTapJsExtensions()),
|
||||
floatingToolbars: @js($floatingToolbars),
|
||||
getMentionLabelsUsing: async (mentions) => {
|
||||
return await $wire.callSchemaComponentMethod(
|
||||
@js($key),
|
||||
'getMentionLabelsForJs', { mentions },
|
||||
)
|
||||
},
|
||||
getMentionSearchResultsUsing: async (query, char) => {
|
||||
return await $wire.callSchemaComponentMethod(
|
||||
@js($key),
|
||||
'getMentionSearchResultsForJs', { search: query, char },
|
||||
)
|
||||
},
|
||||
hasResizableImages: @js($hasResizableImages()),
|
||||
isDisabled: @js($isDisabled),
|
||||
label: @js($label),
|
||||
isLiveDebounced: @js($isLiveDebounced()),
|
||||
isLiveOnBlur: @js($isLiveOnBlur()),
|
||||
key: @js($key),
|
||||
linkProtocols: @js($linkProtocols),
|
||||
liveDebounce: @js($getNormalizedLiveDebounce()),
|
||||
livewireId: @js($this->getId()),
|
||||
maxFileSize: @js($fileAttachmentsMaxSize),
|
||||
maxFileSizeValidationMessage: @js($fileAttachmentsMaxSize ? trans_choice('filament-forms::components.rich_editor.file_attachments_max_size_message', $fileAttachmentsMaxSize, ['max' => $fileAttachmentsMaxSize]) : null),
|
||||
mentions: @js($mentions),
|
||||
mergeTags: @js($mergeTags),
|
||||
noMergeTagSearchResultsMessage: @js($getNoMergeTagSearchResultsMessage()),
|
||||
placeholder: @js($getPlaceholder()),
|
||||
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$statePath}')", isOptimisticallyLive: false) }},
|
||||
statePath: @js($statePath),
|
||||
textColors: @js($getTextColorsForJs()),
|
||||
uploadingFileMessage: @js($getUploadingFileMessage()),
|
||||
})"
|
||||
x-bind:class="{
|
||||
'fi-fo-rich-editor-uploading-file': isUploadingFile,
|
||||
}"
|
||||
wire:ignore wire:key="{{ $livewireKey }}.{{ substr(md5(serialize([$isDisabled])), 0, 64) }}">
|
||||
@if (!$isDisabled && filled($toolbarButtons))
|
||||
<div class="fi-fo-rich-editor-toolbar">
|
||||
@foreach ($toolbarButtons as $button => $buttonGroup)
|
||||
<div class="fi-fo-rich-editor-toolbar-group">
|
||||
@foreach ($buttonGroup as $button)
|
||||
@if (is_string($button))
|
||||
{{ $tools[$button] ?? throw new LogicException("Toolbar button [{$button}] cannot be found.") }}
|
||||
@else
|
||||
{{ $button }}
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div x-show="isUploadingFile" x-cloak class="fi-fo-rich-editor-uploading-file-message">
|
||||
{{ \Filament\Support\generate_loading_indicator_html() }}
|
||||
|
||||
<span>
|
||||
{{ $getUploadingFileMessage() }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div x-show="! isUploadingFile && fileValidationMessage" x-cloak
|
||||
class="fi-fo-rich-editor-file-validation-message">
|
||||
<span x-text="fileValidationMessage" x-show="! isUploadingFile && fileValidationMessage"></span>
|
||||
</div>
|
||||
|
||||
<div {{ $getExtraInputAttributeBag()->class(['fi-fo-rich-editor-main']) }}>
|
||||
<div class="fi-fo-rich-editor-content fi-prose" x-ref="editor">
|
||||
@foreach ($floatingToolbars as $nodeName => $buttons)
|
||||
<div x-ref="floatingToolbar::{{ $nodeName }}"
|
||||
class="fi-fo-rich-editor-floating-toolbar fi-not-prose">
|
||||
@foreach ($buttons as $button)
|
||||
@if (is_string($button))
|
||||
{{ $tools[$button] }}
|
||||
@else
|
||||
{{ $button }}
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if (!$isDisabled)
|
||||
<div x-show="isPanelActive()" x-cloak class="fi-fo-rich-editor-panels">
|
||||
<div x-show="isPanelActive('customBlocks')" x-cloak class="fi-fo-rich-editor-panel">
|
||||
<div class="fi-fo-rich-editor-panel-header">
|
||||
<p class="fi-fo-rich-editor-panel-heading">
|
||||
{{ __('filament-forms::components.rich_editor.tools.custom_blocks') }}
|
||||
</p>
|
||||
|
||||
<div class="fi-fo-rich-editor-panel-close-btn-ctn">
|
||||
<button type="button" x-on:click="togglePanel()" class="fi-icon-btn">
|
||||
{{ \Filament\Support\generate_icon_html(\Filament\Support\Icons\Heroicon::XMark, alias: \Filament\Forms\View\FormsIconAlias::COMPONENTS_RICH_EDITOR_PANELS_CUSTOM_BLOCKS_CLOSE_BUTTON) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fi-fo-rich-editor-custom-blocks-ctn">
|
||||
@foreach ($groupedCustomBlocks as $customBlockGroupLabel => $groupBlocks)
|
||||
@if (filled($customBlockGroupLabel))
|
||||
<h4 class="fi-fo-rich-editor-custom-blocks-group-header">
|
||||
{{ $customBlockGroupLabel }}
|
||||
</h4>
|
||||
@endif
|
||||
|
||||
<div class="fi-fo-rich-editor-custom-blocks-list">
|
||||
@foreach ($groupBlocks as $block)
|
||||
@php
|
||||
$blockId = $block::getId();
|
||||
@endphp
|
||||
|
||||
<button draggable="true" type="button" x-data="{ isLoading: false }"
|
||||
x-on:click="
|
||||
isLoading = true
|
||||
|
||||
$wire.mountAction(
|
||||
'customBlock',
|
||||
{ editorSelection, id: @js($blockId), mode: 'insert' },
|
||||
{ schemaComponent: @js($key) },
|
||||
)
|
||||
"
|
||||
x-on:dragstart="$event.dataTransfer.setData('customBlock', @js($blockId))"
|
||||
x-on:open-modal.window="isLoading = false"
|
||||
x-on:run-rich-editor-commands.window="isLoading = false"
|
||||
class="fi-fo-rich-editor-custom-block-btn">
|
||||
{{ \Filament\Support\generate_loading_indicator_html(
|
||||
new \Illuminate\View\ComponentAttributeBag([
|
||||
'x-show' => 'isLoading',
|
||||
]),
|
||||
) }}
|
||||
|
||||
{{ $block::getLabel() }}
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div x-show="isPanelActive('mergeTags')" x-cloak class="fi-fo-rich-editor-panel">
|
||||
<div class="fi-fo-rich-editor-panel-header">
|
||||
<p class="fi-fo-rich-editor-panel-heading">
|
||||
{{ __('filament-forms::components.rich_editor.tools.merge_tags') }}
|
||||
</p>
|
||||
|
||||
<div class="fi-fo-rich-editor-panel-close-btn-ctn">
|
||||
<button type="button" x-on:click="togglePanel()" class="fi-icon-btn">
|
||||
{{ \Filament\Support\generate_icon_html(\Filament\Support\Icons\Heroicon::XMark, alias: \Filament\Forms\View\FormsIconAlias::COMPONENTS_RICH_EDITOR_PANELS_MERGE_TAGS_CLOSE_BUTTON) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fi-fo-rich-editor-merge-tags-list">
|
||||
@foreach ($mergeTags as $tagId => $tagLabel)
|
||||
<button draggable="true" type="button"
|
||||
x-on:click="insertMergeTag(@js($tagId))"
|
||||
x-on:dragstart="$event.dataTransfer.setData('mergeTag', @js($tagId))"
|
||||
class="fi-fo-rich-editor-merge-tag-btn">
|
||||
<span data-type="mergeTag" data-id="{{ $tagId }}">
|
||||
{{ $tagLabel }}
|
||||
</span>
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-filament::input.wrapper>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,180 @@
|
||||
@php
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$extraInputAttributeBag = $getExtraInputAttributeBag();
|
||||
$canSelectPlaceholder = $canSelectPlaceholder();
|
||||
$isAutofocused = $isAutofocused();
|
||||
$isDisabled = $isDisabled();
|
||||
$isMultiple = $isMultiple();
|
||||
$isReorderable = $isReorderable();
|
||||
$isSearchable = $isSearchable();
|
||||
$hasInitialNoOptionsMessage = $hasInitialNoOptionsMessage();
|
||||
$hasDynamicOptions = $hasDynamicOptions();
|
||||
$canOptionLabelsWrap = $canOptionLabelsWrap();
|
||||
$isRequired = $isRequired();
|
||||
$isConcealed = $isConcealed();
|
||||
$isHtmlAllowed = $isHtmlAllowed();
|
||||
$isNative = !($isSearchable || $isMultiple || $isHtmlAllowed) && $isNative();
|
||||
$isPrefixInline = $isPrefixInline();
|
||||
$isSuffixInline = $isSuffixInline();
|
||||
$key = $getKey();
|
||||
$id = $getId();
|
||||
$prefixActions = $getPrefixActions();
|
||||
$prefixIcon = $getPrefixIcon();
|
||||
$prefixIconColor = $getPrefixIconColor();
|
||||
$prefixLabel = $getPrefixLabel();
|
||||
$suffixActions = $getSuffixActions();
|
||||
$suffixIcon = $getSuffixIcon();
|
||||
$suffixIconColor = $getSuffixIconColor();
|
||||
$suffixLabel = $getSuffixLabel();
|
||||
$statePath = $getStatePath();
|
||||
$state = $getRawState();
|
||||
$livewireKey = $getLivewireKey();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" class="fi-fo-select-wrp">
|
||||
<x-filament::input.wrapper :disabled="$isDisabled" :inline-prefix="$isPrefixInline" :inline-suffix="$isSuffixInline" :prefix="$prefixLabel" :prefix-actions="$prefixActions"
|
||||
:prefix-icon="$prefixIcon" :prefix-icon-color="$prefixIconColor" :suffix="$suffixLabel" :suffix-actions="$suffixActions" :suffix-icon="$suffixIcon" :suffix-icon-color="$suffixIconColor"
|
||||
:valid="!$errors->has($statePath)"
|
||||
:x-on:focus-input.stop="$isNative ? '$el.querySelector(\'select\')?.focus()' : '$el.querySelector(\'.fi-select-input-btn\')?.focus()'"
|
||||
:attributes="\Filament\Support\prepare_inherited_attributes($getExtraAttributeBag())->class([
|
||||
'fi-fo-select',
|
||||
'fi-fo-select-has-inline-prefix' =>
|
||||
$isPrefixInline && (count($prefixActions) || $prefixIcon || filled($prefixLabel)),
|
||||
'fi-fo-select-native' => $isNative,
|
||||
])">
|
||||
@if ($isNative)
|
||||
@php
|
||||
$options = $getOptions();
|
||||
@endphp
|
||||
|
||||
<select
|
||||
{{ $extraInputAttributeBag->merge(
|
||||
[
|
||||
'autofocus' => $isAutofocused,
|
||||
'disabled' => $isDisabled,
|
||||
'id' => $id,
|
||||
'required' => $isRequired && !$isConcealed,
|
||||
'wire:key' => $hasDynamicOptions ? $livewireKey . '.' . substr(md5(serialize($options)), 0, 64) : null,
|
||||
$applyStateBindingModifiers('wire:model') => $statePath,
|
||||
],
|
||||
escape: false,
|
||||
)->class([
|
||||
'fi-select-input',
|
||||
'fi-select-input-has-inline-prefix' =>
|
||||
$isPrefixInline && (count($prefixActions) || $prefixIcon || filled($prefixLabel)),
|
||||
]) }}>
|
||||
@if ($canSelectPlaceholder)
|
||||
<option value="">
|
||||
@if (!$isDisabled)
|
||||
{{ $getPlaceholder() }}
|
||||
@endif
|
||||
</option>
|
||||
@endif
|
||||
|
||||
@foreach ($options as $value => $label)
|
||||
@if (is_array($label))
|
||||
<optgroup label="{{ $value }}">
|
||||
@foreach ($label as $groupedValue => $groupedLabel)
|
||||
<option @disabled($isOptionDisabled($groupedValue, $groupedLabel)) value="{{ $groupedValue }}">
|
||||
@if ($isHtmlAllowed)
|
||||
{!! $groupedLabel !!}
|
||||
@else
|
||||
{{ $groupedLabel }}
|
||||
@endif
|
||||
</option>
|
||||
@endforeach
|
||||
</optgroup>
|
||||
@else
|
||||
<option @disabled($isOptionDisabled($value, $label)) value="{{ $value }}">
|
||||
@if ($isHtmlAllowed)
|
||||
{!! $label !!}
|
||||
@else
|
||||
{{ $label }}
|
||||
@endif
|
||||
</option>
|
||||
@endif
|
||||
@endforeach
|
||||
</select>
|
||||
@else
|
||||
<div class="fi-hidden" x-data="{
|
||||
isDisabled: @js($isDisabled),
|
||||
init() {
|
||||
const container = $el.nextElementSibling
|
||||
container.dispatchEvent(
|
||||
new CustomEvent('set-select-property', {
|
||||
detail: { isDisabled: this.isDisabled },
|
||||
}),
|
||||
)
|
||||
},
|
||||
}"></div>
|
||||
<div x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('select', 'filament/forms') }}"
|
||||
x-data="selectFormComponent({
|
||||
canOptionLabelsWrap: @js($canOptionLabelsWrap),
|
||||
canSelectPlaceholder: @js($canSelectPlaceholder),
|
||||
getOptionLabelUsing: async () => {
|
||||
return await Livewire.fireAction(
|
||||
$wire.__instance,
|
||||
'callSchemaComponentMethod',
|
||||
[@js($key), 'getOptionLabel'], { async: true },
|
||||
)
|
||||
},
|
||||
getOptionLabelsUsing: async () => {
|
||||
return await Livewire.fireAction(
|
||||
$wire.__instance,
|
||||
'callSchemaComponentMethod',
|
||||
[@js($key), 'getOptionLabelsForJs'], { async: true },
|
||||
)
|
||||
},
|
||||
getOptionsUsing: async () => {
|
||||
return await Livewire.fireAction(
|
||||
$wire.__instance,
|
||||
'callSchemaComponentMethod',
|
||||
[@js($key), 'getOptionsForJs'], { async: true },
|
||||
)
|
||||
},
|
||||
getSearchResultsUsing: async (search) => {
|
||||
return await Livewire.fireAction(
|
||||
$wire.__instance,
|
||||
'callSchemaComponentMethod',
|
||||
[@js($key), 'getSearchResultsForJs', { search }], { async: true },
|
||||
)
|
||||
},
|
||||
hasDynamicOptions: @js($hasDynamicOptions),
|
||||
hasDynamicSearchResults: @js($hasDynamicSearchResults()),
|
||||
hasInitialNoOptionsMessage: @js($hasInitialNoOptionsMessage),
|
||||
initialOptionLabel: @js(blank($state) || $isMultiple ? null : $getOptionLabel()),
|
||||
initialOptionLabels: @js(filled($state) && $isMultiple ? $getOptionLabelsForJs() : []),
|
||||
initialState: @js($state),
|
||||
isAutofocused: @js($isAutofocused),
|
||||
isDisabled: @js($isDisabled),
|
||||
isHtmlAllowed: @js($isHtmlAllowed),
|
||||
isMultiple: @js($isMultiple),
|
||||
isReorderable: @js($isReorderable),
|
||||
isSearchable: @js($isSearchable),
|
||||
livewireId: @js($this->getId()),
|
||||
loadingMessage: @js($getLoadingMessage()),
|
||||
maxItems: @js($getMaxItems()),
|
||||
maxItemsMessage: @js($getMaxItemsMessage()),
|
||||
noOptionsMessage: @js($getNoOptionsMessage()),
|
||||
noSearchResultsMessage: @js($getNoSearchResultsMessage()),
|
||||
options: @js($getOptionsForJs()),
|
||||
optionsLimit: @js($getOptionsLimit()),
|
||||
placeholder: @js($getPlaceholder()),
|
||||
position: @js($getPosition()),
|
||||
searchDebounce: @js($getSearchDebounce()),
|
||||
searchingMessage: @js($getSearchingMessage()),
|
||||
searchPrompt: @js($getSearchPrompt()),
|
||||
searchableOptionFields: @js($getSearchableOptionFields()),
|
||||
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$statePath}')") }},
|
||||
statePath: @js($statePath),
|
||||
})" wire:ignore
|
||||
wire:key="{{ $livewireKey }}.{{ substr(md5(serialize([$isDisabled, $isReorderable])), 0, 64) }}"
|
||||
x-on:keydown.esc="select.dropdown.isActive && $event.stopPropagation()"
|
||||
x-on:set-select-property="$event.detail.isDisabled ? select.disable() : select.enable()"
|
||||
{{ $attributes->merge($getExtraAlpineAttributes(), escape: false)->class(['fi-select-input']) }}>
|
||||
<div x-ref="select"></div>
|
||||
</div>
|
||||
@endif
|
||||
</x-filament::input.wrapper>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,48 @@
|
||||
@php
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$isVertical = $isVertical();
|
||||
$pipsMode = $getPipsMode();
|
||||
$livewireKey = $getLivewireKey();
|
||||
$isDisabled = $isDisabled();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" :inline-label-vertical-alignment="\Filament\Support\Enums\VerticalAlignment::Center">
|
||||
<div x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('slider', 'filament/forms') }}"
|
||||
x-data="sliderFormComponent({
|
||||
arePipsStepped: @js($arePipsStepped()),
|
||||
behavior: @js($getBehaviorForJs()),
|
||||
decimalPlaces: @js($getDecimalPlaces()),
|
||||
fillTrack: @js($getFillTrack()),
|
||||
isDisabled: @js($isDisabled),
|
||||
isRtl: @js($isRtl()),
|
||||
isVertical: @js($isVertical),
|
||||
maxDifference: @js($getMaxDifference()),
|
||||
minDifference: @js($getMinDifference()),
|
||||
maxValue: @js($getMaxValue()),
|
||||
minValue: @js($getMinValue()),
|
||||
nonLinearPoints: @js($getNonLinearPoints()),
|
||||
pipsDensity: @js($getPipsDensity()),
|
||||
pipsFilter: @js($getPipsFilterForJs()),
|
||||
pipsFormatter: @js($getPipsFormatterForJs()),
|
||||
pipsMode: @js($pipsMode),
|
||||
pipsValues: @js($getPipsValues()),
|
||||
rangePadding: @js($getRangePadding()),
|
||||
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$getStatePath()}')") }},
|
||||
step: @js($getStep()),
|
||||
tooltips: @js($getTooltipsForJs()),
|
||||
})" wire:ignore
|
||||
wire:key="{{ $livewireKey }}.{{ substr(md5(serialize([$isDisabled])), 0, 64) }}"
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $getId(),
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->merge($getExtraAlpineAttributes(), escape: false)->class([
|
||||
'fi-fo-slider',
|
||||
'fi-fo-slider-has-pips' => $pipsMode,
|
||||
'fi-fo-slider-has-tooltips' => $hasTooltips(),
|
||||
'fi-fo-slider-vertical' => $isVertical,
|
||||
]) }}>
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,33 @@
|
||||
@php
|
||||
use Filament\Forms\Components\TableSelect\Livewire\TableSelectLivewireComponent;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$extraAttributes = $getExtraAttributes();
|
||||
$id = $getId();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
<div
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $id,
|
||||
],
|
||||
escape: false,
|
||||
)->merge($extraAttributes, escape: false) }}>
|
||||
@livewire(
|
||||
TableSelectLivewireComponent::class,
|
||||
[
|
||||
'isDisabled' => $isDisabled(),
|
||||
'maxSelectableRecords' => $getMaxItems(),
|
||||
'model' => $getModel(),
|
||||
'record' => $getRecord(),
|
||||
'relationshipName' => $getRelationshipName(),
|
||||
'shouldIgnoreRelatedRecords' => $shouldIgnoreRelatedRecords(),
|
||||
'tableConfiguration' => base64_encode($getTableConfiguration()),
|
||||
'tableArguments' => $getTableArguments(),
|
||||
$applyStateBindingModifiers('wire:model') => $getStatePath(),
|
||||
],
|
||||
key($getLivewireKey())
|
||||
)
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,94 @@
|
||||
@php
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$extraAttributes = $getExtraAttributes();
|
||||
$extraInputAttributeBag = $getExtraInputAttributeBag();
|
||||
$color = $getColor() ?? 'primary';
|
||||
$id = $getId();
|
||||
$isAutofocused = $isAutofocused();
|
||||
$isDisabled = $isDisabled();
|
||||
$isPrefixInline = $isPrefixInline();
|
||||
$isReorderable = !$isDisabled && $isReorderable();
|
||||
$isSuffixInline = $isSuffixInline();
|
||||
$placeholder = $getPlaceholder();
|
||||
$prefixActions = $getPrefixActions();
|
||||
$prefixIcon = $getPrefixIcon();
|
||||
$prefixIconColor = $getPrefixIconColor();
|
||||
$prefixLabel = $getPrefixLabel();
|
||||
$statePath = $getStatePath();
|
||||
$suffixActions = $getSuffixActions();
|
||||
$suffixIcon = $getSuffixIcon();
|
||||
$suffixIconColor = $getSuffixIconColor();
|
||||
$suffixLabel = $getSuffixLabel();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" class="fi-fo-tags-input-wrp">
|
||||
<x-filament::input.wrapper :disabled="$isDisabled" :inline-prefix="$isPrefixInline" :inline-suffix="$isSuffixInline" :prefix="$prefixLabel" :prefix-actions="$prefixActions"
|
||||
:prefix-icon="$prefixIcon" :prefix-icon-color="$prefixIconColor" :suffix="$suffixLabel" :suffix-actions="$suffixActions" :suffix-icon="$suffixIcon" :suffix-icon-color="$suffixIconColor"
|
||||
:valid="!$errors->has($statePath)" x-on:focus-input.stop="$el.querySelector('input')?.focus()" :attributes="\Filament\Support\prepare_inherited_attributes($attributes)
|
||||
->merge($extraAttributes, escape: false)
|
||||
->class(['fi-fo-tags-input', 'fi-disabled' => $isDisabled])">
|
||||
<div x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('tags-input', 'filament/forms') }}"
|
||||
x-data="tagsInputFormComponent({
|
||||
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$statePath}')") }},
|
||||
splitKeys: @js($getSplitKeys()),
|
||||
})" {{ $getExtraAlpineAttributeBag() }}>
|
||||
<input
|
||||
{{ $extraInputAttributeBag->merge(
|
||||
[
|
||||
'autocomplete' => 'off',
|
||||
'autofocus' => $isAutofocused,
|
||||
'disabled' => $isDisabled,
|
||||
'id' => $id,
|
||||
'list' => $id . '-suggestions',
|
||||
'placeholder' => filled($placeholder) ? e($placeholder) : null,
|
||||
'type' => 'text',
|
||||
'x-bind' => 'input',
|
||||
],
|
||||
escape: false,
|
||||
)->class([
|
||||
'fi-input',
|
||||
'fi-input-has-inline-prefix' =>
|
||||
$isPrefixInline && (count($prefixActions) || $prefixIcon || filled($prefixLabel)),
|
||||
'fi-input-has-inline-suffix' =>
|
||||
$isSuffixInline && (count($suffixActions) || $suffixIcon || filled($suffixLabel)),
|
||||
]) }} />
|
||||
|
||||
<datalist id="{{ $id }}-suggestions">
|
||||
@foreach ($getSuggestions() as $suggestion)
|
||||
<template x-bind:key="@js($suggestion)"
|
||||
x-if="! (state?.includes(@js($suggestion)) ?? true)">
|
||||
<option value="{{ $suggestion }}" />
|
||||
</template>
|
||||
@endforeach
|
||||
</datalist>
|
||||
|
||||
<div wire:ignore>
|
||||
<template x-cloak x-if="state?.length">
|
||||
<div @if ($isReorderable) x-on:end.stop="reorderTags($event)"
|
||||
x-sortable
|
||||
data-sortable-animation-duration="{{ $getReorderAnimationDuration() }}" @endif
|
||||
class="fi-fo-tags-input-tags-ctn">
|
||||
<template x-for="(tag, index) in state" x-bind:key="`${tag}-${index}`">
|
||||
<x-filament::badge :color="$color"
|
||||
:x-bind:x-sortable-item="$isReorderable ? 'index' : null" :x-sortable-handle="$isReorderable ? '' : null"
|
||||
@class([
|
||||
'fi-reorderable' => $isReorderable,
|
||||
])>
|
||||
{{ $getTagPrefix() }}
|
||||
|
||||
<span x-text="tag"></span>
|
||||
|
||||
{{ $getTagSuffix() }}
|
||||
|
||||
<x-slot name="deleteButton" x-on:click.stop="deleteTag(tag)"
|
||||
:x-bind:aria-label="'\''.__('filament-forms::components.tags_input.actions.delete.label').
|
||||
': \' + tag'"></x-slot>
|
||||
</x-filament::badge>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</x-filament::input.wrapper>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,96 @@
|
||||
@php
|
||||
use Filament\Forms\Components\TextInput\Actions\HidePasswordAction;
|
||||
use Filament\Forms\Components\TextInput\Actions\ShowPasswordAction;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$datalistOptions = $getDatalistOptions();
|
||||
$extraAlpineAttributes = $getExtraAlpineAttributes();
|
||||
$extraAttributeBag = $getExtraAttributeBag();
|
||||
$id = $getId();
|
||||
$isConcealed = $isConcealed();
|
||||
$isDisabled = $isDisabled();
|
||||
$isPasswordRevealable = $isPasswordRevealable();
|
||||
$isPrefixInline = $isPrefixInline();
|
||||
$isSuffixInline = $isSuffixInline();
|
||||
$mask = $getMask();
|
||||
$prefixActions = $getPrefixActions();
|
||||
$prefixIcon = $getPrefixIcon();
|
||||
$prefixIconColor = $getPrefixIconColor();
|
||||
$prefixLabel = $getPrefixLabel();
|
||||
$suffixActions = $getSuffixActions();
|
||||
$suffixIcon = $getSuffixIcon();
|
||||
$suffixIconColor = $getSuffixIconColor();
|
||||
$suffixLabel = $getSuffixLabel();
|
||||
$statePath = $getStatePath();
|
||||
$placeholder = $getPlaceholder();
|
||||
|
||||
if ($isPasswordRevealable) {
|
||||
$xData = '{ isPasswordRevealed: false }';
|
||||
} elseif (count($extraAlpineAttributes) || filled($mask)) {
|
||||
$xData = '{}';
|
||||
} else {
|
||||
$xData = null;
|
||||
}
|
||||
|
||||
if ($isPasswordRevealable) {
|
||||
$type = null;
|
||||
} elseif (filled($mask)) {
|
||||
$type = 'text';
|
||||
} else {
|
||||
$type = $getType();
|
||||
}
|
||||
|
||||
$inputAttributes = $getExtraInputAttributeBag()
|
||||
->merge($extraAlpineAttributes, escape: false)
|
||||
->merge(
|
||||
[
|
||||
'autocapitalize' => $getAutocapitalize(),
|
||||
'autocomplete' => $getAutocomplete(),
|
||||
'autofocus' => $isAutofocused(),
|
||||
'disabled' => $isDisabled,
|
||||
'id' => $id,
|
||||
'inlinePrefix' => $isPrefixInline && (count($prefixActions) || $prefixIcon || filled($prefixLabel)),
|
||||
'inlineSuffix' => $isSuffixInline && (count($suffixActions) || $suffixIcon || filled($suffixLabel)),
|
||||
'inputmode' => $getInputMode(),
|
||||
'list' => $datalistOptions ? $id . '-list' : null,
|
||||
'max' => !$isConcealed ? $getMaxValue() : null,
|
||||
'maxlength' => !$isConcealed ? $getMaxLength() : null,
|
||||
'min' => !$isConcealed ? $getMinValue() : null,
|
||||
'minlength' => !$isConcealed ? $getMinLength() : null,
|
||||
'placeholder' => filled($placeholder) ? e($placeholder) : null,
|
||||
'readonly' => $isReadOnly(),
|
||||
'required' => $isRequired() && !$isConcealed,
|
||||
'step' => $getStep(),
|
||||
'type' => $type,
|
||||
$applyStateBindingModifiers('wire:model') => $statePath,
|
||||
'x-bind:type' => $isPasswordRevealable ? 'isPasswordRevealed ? \'text\' : \'password\'' : null,
|
||||
'x-mask' . ($mask instanceof \Filament\Support\RawJs ? ':dynamic' : '') => filled($mask) ? $mask : null,
|
||||
],
|
||||
escape: false,
|
||||
)
|
||||
->class([
|
||||
'fi-revealable' => $isPasswordRevealable,
|
||||
]);
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" :inline-label-vertical-alignment="\Filament\Support\Enums\VerticalAlignment::Center">
|
||||
<x-filament::input.wrapper :disabled="$isDisabled" :inline-prefix="$isPrefixInline" :inline-suffix="$isSuffixInline" :prefix="$prefixLabel" :prefix-actions="$prefixActions"
|
||||
:prefix-icon="$prefixIcon" :prefix-icon-color="$prefixIconColor" :suffix="$suffixLabel" :suffix-actions="$suffixActions" :suffix-icon="$suffixIcon" :suffix-icon-color="$suffixIconColor"
|
||||
:valid="!$errors->has($statePath)" :x-data="$xData" x-on:focus-input.stop="$el.querySelector('input')?.focus()"
|
||||
:attributes="\Filament\Support\prepare_inherited_attributes($extraAttributeBag)->class(['fi-fo-text-input'])">
|
||||
<input
|
||||
{{ $inputAttributes->class([
|
||||
'fi-input',
|
||||
'fi-input-has-inline-prefix' => $isPrefixInline && (count($prefixActions) || $prefixIcon || filled($prefixLabel)),
|
||||
'fi-input-has-inline-suffix' => $isSuffixInline && (count($suffixActions) || $suffixIcon || filled($suffixLabel)),
|
||||
]) }} />
|
||||
</x-filament::input.wrapper>
|
||||
|
||||
@if ($datalistOptions)
|
||||
<datalist id="{{ $id }}-list">
|
||||
@foreach ($datalistOptions as $option)
|
||||
<option value="{{ $option }}"></option>
|
||||
@endforeach
|
||||
</datalist>
|
||||
@endif
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,54 @@
|
||||
@php
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$extraAttributeBag = $getExtraAttributeBag();
|
||||
$isConcealed = $isConcealed();
|
||||
$isDisabled = $isDisabled();
|
||||
$rows = $getRows();
|
||||
$placeholder = $getPlaceholder();
|
||||
$shouldAutosize = $shouldAutosize();
|
||||
$placeholder = $getPlaceholder();
|
||||
$statePath = $getStatePath();
|
||||
|
||||
$initialHeight = ($rows ?? 2) * 1.5 + 0.75;
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" class="fi-fo-textarea-wrp">
|
||||
<x-filament::input.wrapper :disabled="$isDisabled" :valid="!$errors->has($statePath)" :attributes="\Filament\Support\prepare_inherited_attributes($extraAttributeBag)->class([
|
||||
'fi-fo-textarea',
|
||||
'fi-autosizable' => $shouldAutosize,
|
||||
])">
|
||||
<div wire:ignore.self style="height: '{{ $initialHeight . 'rem' }}'">
|
||||
<textarea x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('textarea', 'filament/forms') }}"
|
||||
x-data="textareaFormComponent({
|
||||
initialHeight: @js($initialHeight),
|
||||
shouldAutosize: @js($shouldAutosize),
|
||||
state: $wire.$entangle('{{ $statePath }}'),
|
||||
})"
|
||||
@if ($shouldAutosize) x-intersect.once="resize()"
|
||||
x-on:resize.window="resize()" @endif
|
||||
x-model="state"
|
||||
@if ($isGrammarlyDisabled()) data-gramm="false"
|
||||
data-gramm_editor="false"
|
||||
data-enable-grammarly="false" @endif
|
||||
{{ $getExtraAlpineAttributeBag() }}
|
||||
{{ $getExtraInputAttributeBag()->merge(
|
||||
[
|
||||
'autocomplete' => $getAutocomplete(),
|
||||
'autofocus' => $isAutofocused(),
|
||||
'cols' => $getCols(),
|
||||
'disabled' => $isDisabled,
|
||||
'id' => $getId(),
|
||||
'maxlength' => !$isConcealed ? $getMaxLength() : null,
|
||||
'minlength' => !$isConcealed ? $getMinLength() : null,
|
||||
'placeholder' => filled($placeholder) ? e($placeholder) : null,
|
||||
'readonly' => $isReadOnly(),
|
||||
'required' => $isRequired() && !$isConcealed,
|
||||
'rows' => $rows,
|
||||
$applyStateBindingModifiers('wire:model') => $statePath,
|
||||
],
|
||||
escape: false,
|
||||
) }}></textarea>
|
||||
</div>
|
||||
</x-filament::input.wrapper>
|
||||
</x-dynamic-component>
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
@php
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$id = $getId();
|
||||
$isDisabled = $isDisabled();
|
||||
$isMultiple = $isMultiple();
|
||||
$statePath = $getStatePath();
|
||||
$areButtonLabelsHidden = $areButtonLabelsHidden();
|
||||
$wireModelAttribute = $applyStateBindingModifiers('wire:model');
|
||||
$extraInputAttributeBag = $getExtraInputAttributeBag()->class(['fi-fo-toggle-buttons-input']);
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" tabindex="-1" class="fi-fo-toggle-buttons-wrp">
|
||||
<div {{ $getExtraAttributeBag()->class(['fi-fo-toggle-buttons fi-btn-group']) }}>
|
||||
@foreach ($getOptions() as $value => $label)
|
||||
@php
|
||||
$inputId = "{$id}-{$value}";
|
||||
$shouldOptionBeDisabled = $isDisabled || $isOptionDisabled($value, $label);
|
||||
$color = $getColor($value);
|
||||
$icon = $getIcon($value);
|
||||
$tooltip = $getTooltip($value);
|
||||
@endphp
|
||||
|
||||
<input @disabled($shouldOptionBeDisabled) id="{{ $inputId }}"
|
||||
@if (!$isMultiple) name="{{ $id }}" @endif
|
||||
type="{{ $isMultiple ? 'checkbox' : 'radio' }}" value="{{ $value }}" wire:loading.attr="disabled"
|
||||
{{ $wireModelAttribute }}="{{ $statePath }}" {{ $extraInputAttributeBag }} />
|
||||
|
||||
<x-filament::button :color="$color" :disabled="$shouldOptionBeDisabled" :for="$inputId" grouped :icon="$icon"
|
||||
:label-sr-only="$areButtonLabelsHidden" tag="label" :tooltip="$tooltip">
|
||||
{{ $label }}
|
||||
</x-filament::button>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
@php
|
||||
use Filament\Support\Enums\GridDirection;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$gridDirection = $getGridDirection() ?? GridDirection::Column;
|
||||
$id = $getId();
|
||||
$isDisabled = $isDisabled();
|
||||
$isInline = $isInline();
|
||||
$isMultiple = $isMultiple();
|
||||
$statePath = $getStatePath();
|
||||
$areButtonLabelsHidden = $areButtonLabelsHidden();
|
||||
$wireModelAttribute = $applyStateBindingModifiers('wire:model');
|
||||
$extraInputAttributeBag = $getExtraInputAttributeBag()->class(['fi-fo-toggle-buttons-input']);
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" tabindex="-1" class="fi-fo-toggle-buttons-wrp">
|
||||
<div
|
||||
{{ $getExtraAttributeBag()->when(!$isInline, fn(ComponentAttributeBag $attributes) => $attributes->grid($getColumns(), $gridDirection))->class(['fi-fo-toggle-buttons', 'fi-inline' => $isInline]) }}>
|
||||
@foreach ($getOptions() as $value => $label)
|
||||
@php
|
||||
$inputId = "{$id}-{$value}";
|
||||
$shouldOptionBeDisabled = $isDisabled || $isOptionDisabled($value, $label);
|
||||
$color = $getColor($value);
|
||||
$icon = $getIcon($value);
|
||||
$tooltip = $getTooltip($value);
|
||||
@endphp
|
||||
|
||||
<div class="fi-fo-toggle-buttons-btn-ctn">
|
||||
<input @if ($loop->first && $isAutofocused()) autofocus @endif @disabled($shouldOptionBeDisabled)
|
||||
id="{{ $inputId }}" @if (!$isMultiple) name="{{ $id }}" @endif
|
||||
type="{{ $isMultiple ? 'checkbox' : 'radio' }}" value="{{ $value }}"
|
||||
{{ $wireModelAttribute }}="{{ $statePath }}" {{ $extraInputAttributeBag }} />
|
||||
|
||||
<x-filament::button :color="$color" :disabled="$shouldOptionBeDisabled" :for="$inputId" :icon="$icon"
|
||||
:label-sr-only="$areButtonLabelsHidden" tag="label" :tooltip="$tooltip">
|
||||
{{ $label }}
|
||||
</x-filament::button>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,37 @@
|
||||
@php
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
$statePath = $getStatePath();
|
||||
|
||||
$attributes = new ComponentAttributeBag()
|
||||
->merge(
|
||||
[
|
||||
'aria-checked' => 'false',
|
||||
'autofocus' => $isAutofocused(),
|
||||
'disabled' => $isDisabled(),
|
||||
'id' => $getId(),
|
||||
'offColor' => $getOffColor() ?? 'gray',
|
||||
'offIcon' => $getOffIcon(),
|
||||
'onColor' => $getOnColor() ?? 'primary',
|
||||
'onIcon' => $getOnIcon(),
|
||||
'state' => '$wire.' . $applyStateBindingModifiers('$entangle(\'' . $statePath . '\')'),
|
||||
'wire:loading.attr' => 'disabled',
|
||||
'wire:target' => $statePath,
|
||||
],
|
||||
escape: false,
|
||||
)
|
||||
->merge($getExtraAttributes(), escape: false)
|
||||
->merge($getExtraAlpineAttributes(), escape: false)
|
||||
->class(['fi-fo-toggle']);
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field" :inline-label-vertical-alignment="\Filament\Support\Enums\VerticalAlignment::Center">
|
||||
@if ($isInline())
|
||||
<x-slot name="labelPrefix">
|
||||
<x-filament::toggle :attributes="\Filament\Support\prepare_inherited_attributes($attributes)" />
|
||||
</x-slot>
|
||||
@else
|
||||
<x-filament::toggle :attributes="\Filament\Support\prepare_inherited_attributes($attributes)" />
|
||||
@endif
|
||||
</x-dynamic-component>
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
@props([
|
||||
'alignment' => null,
|
||||
'entry' => null,
|
||||
'hasInlineLabel' => null,
|
||||
'label' => null,
|
||||
'labelSrOnly' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
if ($entry) {
|
||||
$action ??= $entry->getAction();
|
||||
$alignment ??= $entry->getAlignment();
|
||||
$hasInlineLabel ??= $entry->hasInlineLabel();
|
||||
$label ??= $entry->getLabel();
|
||||
$labelSrOnly ??= $entry->isLabelHidden();
|
||||
$url ??= $entry->getUrl();
|
||||
$shouldOpenUrlInNewTab ??= $entry->shouldOpenUrlInNewTab();
|
||||
}
|
||||
|
||||
if (!$alignment instanceof Alignment) {
|
||||
$alignment = filled($alignment) ? Alignment::tryFrom($alignment) ?? $alignment : null;
|
||||
}
|
||||
|
||||
$beforeLabelContainer = $entry?->getChildSchema($entry::BEFORE_LABEL_SCHEMA_KEY)?->toHtmlString();
|
||||
$afterLabelContainer = $entry?->getChildSchema($entry::AFTER_LABEL_SCHEMA_KEY)?->toHtmlString();
|
||||
$beforeContentContainer = $entry?->getChildSchema($entry::BEFORE_CONTENT_SCHEMA_KEY)?->toHtmlString();
|
||||
$afterContentContainer = $entry?->getChildSchema($entry::AFTER_CONTENT_SCHEMA_KEY)?->toHtmlString();
|
||||
@endphp
|
||||
|
||||
<div
|
||||
{{ $attributes->merge($entry?->getExtraEntryWrapperAttributes() ?? [], escape: false)->class(['fi-in-entry', 'fi-in-entry-has-inline-label' => $hasInlineLabel]) }}>
|
||||
@if ($label && $labelSrOnly)
|
||||
<div class="fi-in-entry-label fi-sr-only" role="term">
|
||||
{{ $label }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="fi-in-entry-label-col">
|
||||
{{ $entry?->getChildSchema($entry::ABOVE_LABEL_SCHEMA_KEY) }}
|
||||
|
||||
@if (($label && !$labelSrOnly) || $beforeLabelContainer || $afterLabelContainer)
|
||||
<div @class([
|
||||
'fi-in-entry-label-ctn',
|
||||
$label instanceof \Illuminate\View\ComponentSlot
|
||||
? $label->attributes->get('class')
|
||||
: null,
|
||||
])>
|
||||
{{ $beforeLabelContainer }}
|
||||
|
||||
@if ($label && !$labelSrOnly)
|
||||
<div {{ ($label instanceof \Illuminate\View\ComponentSlot ? $label->attributes : new ComponentAttributeBag())->class(
|
||||
['fi-in-entry-label'],
|
||||
) }}
|
||||
role="term">
|
||||
{{ $label }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{ $afterLabelContainer }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{ $entry?->getChildSchema($entry::BELOW_LABEL_SCHEMA_KEY) }}
|
||||
</div>
|
||||
|
||||
<div class="fi-in-entry-content-col">
|
||||
{{ $entry?->getChildSchema($entry::ABOVE_CONTENT_SCHEMA_KEY) }}
|
||||
|
||||
<div class="fi-in-entry-content-ctn" role="definition">
|
||||
{{ $beforeContentContainer }}
|
||||
|
||||
@if (filled($url))
|
||||
<a {{ \Filament\Support\generate_href_html($url, $shouldOpenUrlInNewTab) }}
|
||||
@class([
|
||||
'fi-in-entry-content',
|
||||
$alignment instanceof Alignment
|
||||
? "fi-align-{$alignment->value}"
|
||||
: (is_string($alignment)
|
||||
? $alignment
|
||||
: ''),
|
||||
])>
|
||||
{{ $slot }}
|
||||
</a>
|
||||
@elseif (filled($action))
|
||||
@php
|
||||
$wireClickAction = $action->getLivewireClickHandler();
|
||||
@endphp
|
||||
|
||||
<button type="button" wire:click="{{ $wireClickAction }}" wire:loading.attr="disabled"
|
||||
wire:target="{{ $wireClickAction }}" @class([
|
||||
'fi-in-entry-content',
|
||||
$alignment instanceof Alignment
|
||||
? "fi-align-{$alignment->value}"
|
||||
: (is_string($alignment)
|
||||
? $alignment
|
||||
: ''),
|
||||
])>
|
||||
{{ $slot }}
|
||||
</button>
|
||||
@else
|
||||
<div @class([
|
||||
'fi-in-entry-content',
|
||||
$alignment instanceof Alignment
|
||||
? "fi-align-{$alignment->value}"
|
||||
: (is_string($alignment)
|
||||
? $alignment
|
||||
: ''),
|
||||
])>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{ $afterContentContainer }}
|
||||
</div>
|
||||
|
||||
{{ $entry?->getChildSchema($entry::BELOW_CONTENT_SCHEMA_KEY) }}
|
||||
</div>
|
||||
</div>
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
@php
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Support\View\Components\BadgeComponent;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
$notifications = $this->getNotifications();
|
||||
$unreadNotificationsCount = $this->getUnreadNotificationsCount();
|
||||
$hasNotifications = $notifications->count();
|
||||
$isPaginated = $notifications instanceof \Illuminate\Contracts\Pagination\Paginator && $notifications->hasPages();
|
||||
$pollingInterval = $this->getPollingInterval();
|
||||
@endphp
|
||||
|
||||
<div class="fi-no-database">
|
||||
<x-filament::modal :alignment="$hasNotifications ? null : Alignment::Center" close-button :description="$hasNotifications ? null : __('filament-notifications::database.modal.empty.description')" :heading="$hasNotifications ? null : __('filament-notifications::database.modal.empty.heading')" :icon="$hasNotifications ? null : \Filament\Support\Icons\Heroicon::OutlinedBellSlash"
|
||||
:icon-alias="$hasNotifications
|
||||
? null
|
||||
: \Filament\Notifications\View\NotificationsIconAlias::DATABASE_MODAL_EMPTY_STATE" :icon-color="$hasNotifications ? null : 'gray'" id="database-notifications" slide-over :sticky-header="$hasNotifications" teleport="body"
|
||||
width="md" class="fi-no-database" :attributes="new \Illuminate\View\ComponentAttributeBag([
|
||||
'wire:poll.' . $pollingInterval => $pollingInterval ? '' : false,
|
||||
])">
|
||||
@if ($trigger = $this->getTrigger())
|
||||
<x-slot name="trigger">
|
||||
{{ $trigger->with(['unreadNotificationsCount' => $unreadNotificationsCount]) }}
|
||||
</x-slot>
|
||||
@endif
|
||||
|
||||
@if ($hasNotifications)
|
||||
<x-slot name="header">
|
||||
<div>
|
||||
<h2 class="fi-modal-heading">
|
||||
{{ __('filament-notifications::database.modal.heading') }}
|
||||
|
||||
@if ($unreadNotificationsCount)
|
||||
<span
|
||||
{{ new ComponentAttributeBag()->color(BadgeComponent::class, 'primary')->class(['fi-badge fi-size-xs']) }}>
|
||||
{{ $unreadNotificationsCount }}
|
||||
</span>
|
||||
@endif
|
||||
</h2>
|
||||
|
||||
<div class="fi-ac">
|
||||
@if ($unreadNotificationsCount && $this->markAllNotificationsAsReadAction?->isVisible())
|
||||
{{ $this->markAllNotificationsAsReadAction }}
|
||||
@endif
|
||||
|
||||
@if ($this->clearNotificationsAction?->isVisible())
|
||||
{{ $this->clearNotificationsAction }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
@foreach ($notifications as $notification)
|
||||
<div @class([
|
||||
'fi-no-notification-read-ctn' => !$notification->unread(),
|
||||
'fi-no-notification-unread-ctn' => $notification->unread(),
|
||||
])>
|
||||
{{ $this->getNotification($notification)->inline() }}
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@if ($broadcastChannel = $this->getBroadcastChannel())
|
||||
@script
|
||||
<script>
|
||||
window.addEventListener('EchoLoaded', () => {
|
||||
window.Echo.private(@js($broadcastChannel)).listen(
|
||||
'.database-notifications.sent',
|
||||
() => {
|
||||
setTimeout(
|
||||
() => $wire.call('$refresh'),
|
||||
500,
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
if (window.Echo) {
|
||||
window.dispatchEvent(new CustomEvent('EchoLoaded'))
|
||||
}
|
||||
</script>
|
||||
@endscript
|
||||
@endif
|
||||
|
||||
@if ($isPaginated)
|
||||
<x-slot name="footer">
|
||||
<x-filament::pagination :paginator="$notifications" />
|
||||
</x-slot>
|
||||
@endif
|
||||
@endif
|
||||
</x-filament::modal>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
@php
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Support\Enums\VerticalAlignment;
|
||||
@endphp
|
||||
|
||||
<div>
|
||||
<div @class([
|
||||
'fi-no',
|
||||
'fi-align-' . static::$alignment->value,
|
||||
'fi-vertical-align-' . static::$verticalAlignment->value,
|
||||
]) role="status">
|
||||
@foreach ($notifications as $notification)
|
||||
{{ $notification }}
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if ($broadcastChannel = $this->getBroadcastChannel())
|
||||
@script
|
||||
<script>
|
||||
window.addEventListener('EchoLoaded', () => {
|
||||
window.Echo.private(@js($broadcastChannel)).notification(
|
||||
(notification) => {
|
||||
setTimeout(
|
||||
() =>
|
||||
$wire.handleBroadcastNotification(
|
||||
notification,
|
||||
),
|
||||
500,
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
if (window.Echo) {
|
||||
window.dispatchEvent(new CustomEvent('EchoLoaded'))
|
||||
}
|
||||
</script>
|
||||
@endscript
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
@php
|
||||
$pageComponent = static::isSimple() ? 'filament-panels::page.simple' : 'filament-panels::page';
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$pageComponent">
|
||||
{{ $this->content }}
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,10 @@
|
||||
@props([
|
||||
'tenant' => filament()->getTenant(),
|
||||
])
|
||||
|
||||
@php
|
||||
$src = filament()->getTenantAvatarUrl($tenant);
|
||||
$alt = __('filament-panels::layout.avatar.alt', ['name' => filament()->getTenantName($tenant)]);
|
||||
@endphp
|
||||
|
||||
<x-filament::avatar :circular="false" :src="$src" :alt="$alt" :attributes="\Filament\Support\prepare_inherited_attributes($attributes)->class(['fi-tenant-avatar'])" />
|
||||
@@ -0,0 +1,10 @@
|
||||
@props([
|
||||
'user' => filament()->auth()->user(),
|
||||
])
|
||||
|
||||
@php
|
||||
$src = filament()->getUserAvatarUrl($user);
|
||||
$alt = __('filament-panels::layout.avatar.alt', ['name' => filament()->getUserName($user)]);
|
||||
@endphp
|
||||
|
||||
<x-filament::avatar :src="$src" :alt="$alt" :attributes="\Filament\Support\prepare_inherited_attributes($attributes)->class(['fi-user-avatar'])" />
|
||||
@@ -0,0 +1,54 @@
|
||||
@props([
|
||||
'actions' => [],
|
||||
'actionsAlignment' => null,
|
||||
'breadcrumbs' => [],
|
||||
'heading' => null,
|
||||
'subheading' => null,
|
||||
])
|
||||
|
||||
<header {{ $attributes->class(['fi-header', 'fi-header-has-breadcrumbs' => $breadcrumbs]) }}>
|
||||
<div>
|
||||
@if ($breadcrumbs)
|
||||
<x-filament::breadcrumbs :breadcrumbs="$breadcrumbs" />
|
||||
@endif
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_HEADER_HEADING_BEFORE, scopes: $this->getRenderHookScopes()) }}
|
||||
|
||||
@if (filled($heading))
|
||||
<h1 class="fi-header-heading">
|
||||
{{ $heading }}
|
||||
</h1>
|
||||
@endif
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_HEADER_HEADING_AFTER, scopes: $this->getRenderHookScopes()) }}
|
||||
|
||||
@if (filled($subheading))
|
||||
<p class="fi-header-subheading">
|
||||
{{ $subheading }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@php
|
||||
$beforeActions = \Filament\Support\Facades\FilamentView::renderHook(
|
||||
\Filament\View\PanelsRenderHook::PAGE_HEADER_ACTIONS_BEFORE,
|
||||
scopes: $this->getRenderHookScopes(),
|
||||
);
|
||||
$afterActions = \Filament\Support\Facades\FilamentView::renderHook(
|
||||
\Filament\View\PanelsRenderHook::PAGE_HEADER_ACTIONS_AFTER,
|
||||
scopes: $this->getRenderHookScopes(),
|
||||
);
|
||||
@endphp
|
||||
|
||||
@if (filled($beforeActions) || $actions || filled($afterActions))
|
||||
<div class="fi-header-actions-ctn">
|
||||
{{ $beforeActions }}
|
||||
|
||||
@if ($actions)
|
||||
<x-filament::actions :actions="$actions" :alignment="$actionsAlignment" />
|
||||
@endif
|
||||
|
||||
{{ $afterActions }}
|
||||
</div>
|
||||
@endif
|
||||
</header>
|
||||
@@ -0,0 +1,23 @@
|
||||
@props([
|
||||
'heading' => null,
|
||||
'logo' => true,
|
||||
'subheading' => null,
|
||||
])
|
||||
|
||||
<header class="fi-simple-header">
|
||||
@if ($logo)
|
||||
<x-filament-panels::logo />
|
||||
@endif
|
||||
|
||||
@if (filled($heading))
|
||||
<h1 class="fi-simple-header-heading">
|
||||
{{ $heading }}
|
||||
</h1>
|
||||
@endif
|
||||
|
||||
@if (filled($subheading))
|
||||
<p class="fi-simple-header-subheading">
|
||||
{{ $subheading }}
|
||||
</p>
|
||||
@endif
|
||||
</header>
|
||||
@@ -0,0 +1,168 @@
|
||||
@props([
|
||||
'livewire' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
$renderHookScopes = $livewire?->getRenderHookScopes();
|
||||
@endphp
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html
|
||||
lang="{{ str_replace('_', '-', app()->getLocale()) }}"
|
||||
dir="{{ __('filament-panels::layout.direction') ?? 'ltr' }}"
|
||||
@class([
|
||||
'fi',
|
||||
'dark' => filament()->hasDarkMode() && filament()->hasDarkModeForced(),
|
||||
])
|
||||
>
|
||||
<head>
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::HEAD_START, scopes: $renderHookScopes) }}
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
@if ($favicon = filament()->getFavicon())
|
||||
<link rel="icon" href="{{ $favicon }}" />
|
||||
@endif
|
||||
|
||||
@php
|
||||
$title = trim(strip_tags($livewire?->getTitle() ?? ''));
|
||||
$brandName = trim(strip_tags(filament()->getBrandName()));
|
||||
@endphp
|
||||
|
||||
<title>
|
||||
{{ filled($title) ? $title : null }}
|
||||
{{ filled($brandName) && filled($title) ? ' - ' : null }}
|
||||
{{ filled($brandName) ? $brandName : null }}
|
||||
</title>
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::STYLES_BEFORE, scopes: $renderHookScopes) }}
|
||||
|
||||
<style>
|
||||
[x-cloak=''],
|
||||
[x-cloak='x-cloak'],
|
||||
[x-cloak='1'] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
[x-cloak='inline-flex'] {
|
||||
display: inline-flex !important;
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
[x-cloak='-lg'] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
[x-cloak='lg'] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@filamentStyles
|
||||
|
||||
{{ filament()->getTheme()->getHtml() }}
|
||||
{{ filament()->getFontPreloadHtml() }}
|
||||
{{ filament()->getMonoFontPreloadHtml() }}
|
||||
{{ filament()->getSerifFontPreloadHtml() }}
|
||||
{{ filament()->getFontHtml() }}
|
||||
{{ filament()->getMonoFontHtml() }}
|
||||
{{ filament()->getSerifFontHtml() }}
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--font-family: '{!! filament()->getFontFamily() !!}';
|
||||
--mono-font-family: '{!! filament()->getMonoFontFamily() !!}';
|
||||
--serif-font-family: '{!! filament()->getSerifFontFamily() !!}';
|
||||
--sidebar-width: {{ filament()->getSidebarWidth() }};
|
||||
--collapsed-sidebar-width: {{ filament()->getCollapsedSidebarWidth() }};
|
||||
--default-theme-mode: {{ filament()->getDefaultThemeMode()->value }};
|
||||
}
|
||||
|
||||
html.fi {
|
||||
--livewire-progress-bar-color: var(--primary-500);
|
||||
}
|
||||
</style>
|
||||
|
||||
@stack('styles')
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::STYLES_AFTER, scopes: $renderHookScopes) }}
|
||||
|
||||
@if (! filament()->hasDarkMode())
|
||||
<script>
|
||||
localStorage.setItem('theme', 'light')
|
||||
</script>
|
||||
@elseif (filament()->hasDarkModeForced())
|
||||
<script>
|
||||
localStorage.setItem('theme', 'dark')
|
||||
</script>
|
||||
@else
|
||||
<script>
|
||||
const loadDarkMode = () => {
|
||||
window.theme = localStorage.getItem('theme') ?? @js(filament()->getDefaultThemeMode()->value)
|
||||
|
||||
if (
|
||||
window.theme === 'dark' ||
|
||||
(window.theme === 'system' &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)')
|
||||
.matches)
|
||||
) {
|
||||
document.documentElement.classList.add('dark')
|
||||
}
|
||||
}
|
||||
|
||||
loadDarkMode()
|
||||
|
||||
document.addEventListener('livewire:navigated', loadDarkMode)
|
||||
</script>
|
||||
@endif
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::HEAD_END, scopes: $renderHookScopes) }}
|
||||
</head>
|
||||
|
||||
<body
|
||||
{{
|
||||
$attributes
|
||||
->
|
||||
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);" aria-hidden="true"></div>merge($livewire?->getExtraBodyAttributes() ?? [], escape: false)
|
||||
->class([
|
||||
'fi-body',
|
||||
'fi-panel-' . filament()->getId(),
|
||||
])
|
||||
}}
|
||||
>
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::BODY_START, scopes: $renderHookScopes) }}
|
||||
|
||||
{{ $slot }}
|
||||
|
||||
@livewire(Filament\Livewire\Notifications::class)
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SCRIPTS_BEFORE, scopes: $renderHookScopes) }}
|
||||
|
||||
@filamentScripts(withCore: true)
|
||||
|
||||
@if (filament()->hasBroadcasting() && config('filament.broadcasting.echo'))
|
||||
<script data-navigate-once>
|
||||
window.Echo = new window.EchoFactory(@js(config('filament.broadcasting.echo')))
|
||||
|
||||
window.dispatchEvent(new CustomEvent('EchoLoaded'))
|
||||
</script>
|
||||
@endif
|
||||
|
||||
@if (filament()->hasDarkMode() && (! filament()->hasDarkModeForced()))
|
||||
<script>
|
||||
loadDarkMode()
|
||||
</script>
|
||||
@endif
|
||||
|
||||
@stack('scripts')
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SCRIPTS_AFTER, scopes: $renderHookScopes) }}
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::BODY_END, scopes: $renderHookScopes) }}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,93 @@
|
||||
@php
|
||||
use Filament\Support\Enums\Width;
|
||||
|
||||
$livewire ??= null;
|
||||
|
||||
$hasTopbar = filament()->hasTopbar();
|
||||
$isSidebarCollapsibleOnDesktop = filament()->isSidebarCollapsibleOnDesktop();
|
||||
$isSidebarFullyCollapsibleOnDesktop = filament()->isSidebarFullyCollapsibleOnDesktop();
|
||||
$hasTopNavigation = filament()->hasTopNavigation();
|
||||
$hasNavigation = filament()->hasNavigation();
|
||||
$renderHookScopes = $livewire?->getRenderHookScopes();
|
||||
$maxContentWidth ??= filament()->getMaxContentWidth() ?? Width::SevenExtraLarge;
|
||||
|
||||
if (is_string($maxContentWidth)) {
|
||||
$maxContentWidth = Width::tryFrom($maxContentWidth) ?? $maxContentWidth;
|
||||
}
|
||||
@endphp
|
||||
|
||||
<x-filament-panels::layout.base :livewire="$livewire" @class([
|
||||
'fi-body-has-navigation' => $hasNavigation,
|
||||
'fi-body-has-sidebar-collapsible-on-desktop' => $isSidebarCollapsibleOnDesktop,
|
||||
'fi-body-has-sidebar-fully-collapsible-on-desktop' => $isSidebarFullyCollapsibleOnDesktop,
|
||||
'fi-body-has-topbar' => $hasTopbar,
|
||||
'fi-body-has-top-navigation' => $hasTopNavigation,
|
||||
])>
|
||||
@if ($hasTopbar)
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::TOPBAR_BEFORE, scopes: $renderHookScopes) }}
|
||||
|
||||
@livewire(filament()->getTopbarLivewireComponent())
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::TOPBAR_AFTER, scopes: $renderHookScopes) }}
|
||||
@elseif ($hasNavigation)
|
||||
<div @if ($isSidebarFullyCollapsibleOnDesktop) x-data="{}"
|
||||
x-bind:class="{ 'lg:fi-hidden': $store.sidebar.isOpen }" @endif
|
||||
@class([
|
||||
'fi-layout-sidebar-toggle-btn-ctn',
|
||||
'lg:fi-hidden' => !$isSidebarFullyCollapsibleOnDesktop,
|
||||
])>
|
||||
<x-filament::icon-button color="gray" :icon="\Filament\Support\Icons\Heroicon::OutlinedBars3" :icon-alias="\Filament\View\PanelsIconAlias::SIDEBAR_EXPAND_BUTTON" icon-size="lg" :label="__('filament-panels::layout.actions.sidebar.expand.label')"
|
||||
x-cloak x-data="{}" x-on:click="$store.sidebar.open()" class="fi-layout-sidebar-toggle-btn" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="fi-layout">
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::LAYOUT_START, scopes: $renderHookScopes) }}
|
||||
|
||||
@if ($hasNavigation)
|
||||
<div x-cloak x-data="{}" x-on:click="$store.sidebar.close()" x-show="$store.sidebar.isOpen"
|
||||
x-transition.opacity.300ms class="fi-sidebar-close-overlay"></div>
|
||||
|
||||
@livewire(filament()->getSidebarLivewireComponent())
|
||||
@endif
|
||||
|
||||
<div @if ($isSidebarCollapsibleOnDesktop) x-data="{}"
|
||||
x-bind:class="{
|
||||
'fi-main-ctn-sidebar-open': $store.sidebar.isOpen,
|
||||
}"
|
||||
x-bind:style="'display: flex; opacity:1;'"
|
||||
{{-- Mimics `x-cloak`, as using `x-cloak` causes visual issues with chart widgets --}}
|
||||
@elseif ($isSidebarFullyCollapsibleOnDesktop)
|
||||
x-data="{}"
|
||||
x-bind:class="{
|
||||
'fi-main-ctn-sidebar-open': $store.sidebar.isOpen,
|
||||
}"
|
||||
x-bind:style="'display: flex; opacity:1;'"
|
||||
{{-- Mimics `x-cloak`, as using `x-cloak` causes visual issues with chart widgets --}}
|
||||
@elseif (!($isSidebarCollapsibleOnDesktop || $isSidebarFullyCollapsibleOnDesktop || $hasTopNavigation || !$hasNavigation))
|
||||
x-data="{}"
|
||||
x-bind:style="'display: flex; opacity:1;'" {{-- Mimics `x-cloak`, as using `x-cloak` causes visual issues with chart widgets --}} @endif
|
||||
class="fi-main-ctn">
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::CONTENT_BEFORE, scopes: $renderHookScopes) }}
|
||||
|
||||
<main @class([
|
||||
'fi-main',
|
||||
$maxContentWidth instanceof Width
|
||||
? "fi-width-{$maxContentWidth->value}"
|
||||
: $maxContentWidth,
|
||||
])>
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::CONTENT_START, scopes: $renderHookScopes) }}
|
||||
|
||||
{{ $slot }}
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::CONTENT_END, scopes: $renderHookScopes) }}
|
||||
</main>
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::CONTENT_AFTER, scopes: $renderHookScopes) }}
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::FOOTER, scopes: $renderHookScopes) }}
|
||||
</div>
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::LAYOUT_END, scopes: $renderHookScopes) }}
|
||||
</div>
|
||||
</x-filament-panels::layout.base>
|
||||
@@ -0,0 +1,54 @@
|
||||
@php
|
||||
use Filament\Support\Enums\Width;
|
||||
|
||||
$livewire ??= null;
|
||||
|
||||
$renderHookScopes = $livewire?->getRenderHookScopes();
|
||||
$maxContentWidth ??= filament()->getSimplePageMaxContentWidth() ?? Width::Large;
|
||||
|
||||
if (is_string($maxContentWidth)) {
|
||||
$maxContentWidth = Width::tryFrom($maxContentWidth) ?? $maxContentWidth;
|
||||
}
|
||||
@endphp
|
||||
|
||||
<x-filament-panels::layout.base :livewire="$livewire">
|
||||
@props([
|
||||
'after' => null,
|
||||
'heading' => null,
|
||||
'subheading' => null,
|
||||
])
|
||||
|
||||
<div class="fi-simple-layout">
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SIMPLE_LAYOUT_START, scopes: $renderHookScopes) }}
|
||||
|
||||
@if (($hasTopbar ?? true) && filament()->auth()->check())
|
||||
<div class="fi-simple-layout-header">
|
||||
@if (filament()->hasDatabaseNotifications())
|
||||
@livewire(filament()->getDatabaseNotificationsLivewireComponent(), [
|
||||
'lazy' => filament()->hasLazyLoadedDatabaseNotifications(),
|
||||
'position' => \Filament\Enums\DatabaseNotificationsPosition::Topbar,
|
||||
])
|
||||
@endif
|
||||
|
||||
@if (filament()->hasUserMenu())
|
||||
@livewire(Filament\Livewire\SimpleUserMenu::class)
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="fi-simple-main-ctn">
|
||||
<main @class([
|
||||
'fi-simple-main',
|
||||
$maxContentWidth instanceof Width
|
||||
? "fi-width-{$maxContentWidth->value}"
|
||||
: $maxContentWidth,
|
||||
])>
|
||||
{{ $slot }}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::FOOTER, scopes: $renderHookScopes) }}
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SIMPLE_LAYOUT_END, scopes: $renderHookScopes) }}
|
||||
</div>
|
||||
</x-filament-panels::layout.base>
|
||||
@@ -0,0 +1,36 @@
|
||||
@php
|
||||
$brandName = filament()->getBrandName();
|
||||
$brandLogo = filament()->getBrandLogo();
|
||||
$brandLogoHeight = filament()->getBrandLogoHeight() ?? '1.5rem';
|
||||
$darkModeBrandLogo = filament()->getDarkModeBrandLogo();
|
||||
$hasDarkModeBrandLogo = filled($darkModeBrandLogo);
|
||||
|
||||
$getLogoClasses = fn(bool $isDarkMode): string => \Illuminate\Support\Arr::toCssClasses([
|
||||
'fi-logo',
|
||||
'fi-logo-light' => $hasDarkModeBrandLogo && !$isDarkMode,
|
||||
'fi-logo-dark' => $isDarkMode,
|
||||
]);
|
||||
|
||||
$logoStyles = 'height: ' . e($brandLogoHeight);
|
||||
@endphp
|
||||
|
||||
@capture($content, $logo, $isDarkMode = false)
|
||||
@if ($logo instanceof \Illuminate\Contracts\Support\Htmlable)
|
||||
<div {{ $attributes->class([$getLogoClasses($isDarkMode)])->style([$logoStyles]) }}>
|
||||
{{ $logo }}
|
||||
</div>
|
||||
@elseif (filled($logo))
|
||||
<img alt="{{ __('filament-panels::layout.logo.alt', ['name' => $brandName]) }}" src="{{ $logo }}"
|
||||
{{ $attributes->class([$getLogoClasses($isDarkMode)])->style([$logoStyles]) }} />
|
||||
@else
|
||||
<div {{ $attributes->class([$getLogoClasses($isDarkMode)]) }}>
|
||||
{{ $brandName }}
|
||||
</div>
|
||||
@endif
|
||||
@endcapture
|
||||
|
||||
{{ $content($brandLogo) }}
|
||||
|
||||
@if ($hasDarkModeBrandLogo)
|
||||
{{ $content($darkModeBrandLogo, isDarkMode: true) }}
|
||||
@endif
|
||||
@@ -0,0 +1,161 @@
|
||||
@props([
|
||||
'fullHeight' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
use Filament\Pages\Enums\SubNavigationPosition;
|
||||
|
||||
$subNavigation = $this->getCachedSubNavigation();
|
||||
$subNavigationPosition = $this->getSubNavigationPosition();
|
||||
$widgetData = $this->getWidgetData();
|
||||
@endphp
|
||||
|
||||
<div
|
||||
{{ $attributes->class([
|
||||
'fi-page',
|
||||
'fi-height-full' => $fullHeight,
|
||||
'fi-page-has-sub-navigation' => $subNavigation,
|
||||
"fi-page-has-sub-navigation-{$subNavigationPosition->value}" => $subNavigation,
|
||||
...$this->getPageClasses(),
|
||||
]) }}>
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_START, scopes: $this->getRenderHookScopes()) }}
|
||||
|
||||
<div class="fi-page-header-main-ctn">
|
||||
@if ($subNavigation)
|
||||
<div class="fi-page-main-sub-navigation-mobile-menu-render-hook-ctn">
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_SUB_NAVIGATION_MOBILE_MENU_BEFORE, scopes: $this->getRenderHookScopes()) }}
|
||||
</div>
|
||||
|
||||
<x-filament-panels::page.sub-navigation.mobile-menu :navigation="$subNavigation" />
|
||||
|
||||
<div class="fi-page-main-sub-navigation-mobile-menu-render-hook-ctn">
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_SUB_NAVIGATION_MOBILE_MENU_AFTER, scopes: $this->getRenderHookScopes()) }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($header = $this->getHeader())
|
||||
{{ $header }}
|
||||
@else
|
||||
@php
|
||||
$heading = $this->getHeading();
|
||||
$headerActions = $this->getCachedHeaderActions();
|
||||
$headerActionsAlignment = $this->getHeaderActionsAlignment();
|
||||
$breadcrumbs = filament()->hasBreadcrumbs() ? $this->getBreadcrumbs() : [];
|
||||
$subheading = $this->getSubheading();
|
||||
@endphp
|
||||
|
||||
@if (filled($headerActions) || $breadcrumbs || filled($heading) || filled($subheading))
|
||||
<x-filament-panels::header :actions="$headerActions" :actions-alignment="$headerActionsAlignment" :breadcrumbs="$breadcrumbs" :heading="$heading"
|
||||
:subheading="$subheading">
|
||||
@if ($heading instanceof \Illuminate\Contracts\Support\Htmlable)
|
||||
<x-slot name="heading">
|
||||
{{ $heading }}
|
||||
</x-slot>
|
||||
@endif
|
||||
|
||||
@if ($subheading instanceof \Illuminate\Contracts\Support\Htmlable)
|
||||
<x-slot name="subheading">
|
||||
{{ $subheading }}
|
||||
</x-slot>
|
||||
@endif
|
||||
</x-filament-panels::header>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<div class="fi-page-main">
|
||||
@if ($subNavigation)
|
||||
@if ($subNavigationPosition === SubNavigationPosition::Start)
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_SUB_NAVIGATION_START_BEFORE, scopes: $this->getRenderHookScopes()) }}
|
||||
|
||||
<x-filament-panels::page.sub-navigation.sidebar :navigation="$subNavigation" />
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_SUB_NAVIGATION_START_AFTER, scopes: $this->getRenderHookScopes()) }}
|
||||
@endif
|
||||
|
||||
@if ($subNavigationPosition === SubNavigationPosition::Top)
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_SUB_NAVIGATION_TOP_BEFORE, scopes: $this->getRenderHookScopes()) }}
|
||||
|
||||
<x-filament-panels::page.sub-navigation.tabs :navigation="$subNavigation" />
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_SUB_NAVIGATION_TOP_AFTER, scopes: $this->getRenderHookScopes()) }}
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<div class="fi-page-content">
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_HEADER_WIDGETS_BEFORE, scopes: $this->getRenderHookScopes()) }}
|
||||
|
||||
{{ $this->headerWidgets }}
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_HEADER_WIDGETS_AFTER, scopes: $this->getRenderHookScopes()) }}
|
||||
|
||||
{{ $slot }}
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_FOOTER_WIDGETS_BEFORE, scopes: $this->getRenderHookScopes()) }}
|
||||
|
||||
{{ $this->footerWidgets }}
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_FOOTER_WIDGETS_AFTER, scopes: $this->getRenderHookScopes()) }}
|
||||
</div>
|
||||
|
||||
@if ($subNavigation && $subNavigationPosition === SubNavigationPosition::End)
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_SUB_NAVIGATION_END_BEFORE, scopes: $this->getRenderHookScopes()) }}
|
||||
|
||||
<x-filament-panels::page.sub-navigation.sidebar :navigation="$subNavigation" />
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_SUB_NAVIGATION_END_AFTER, scopes: $this->getRenderHookScopes()) }}
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if ($footer = $this->getFooter())
|
||||
{{ $footer }}
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if (!($this instanceof \Filament\Tables\Contracts\HasTable))
|
||||
<x-filament-actions::modals />
|
||||
@elseif ($this->isTableLoaded() && filled($this->defaultTableAction))
|
||||
<div
|
||||
wire:init="mountAction(@js($this->defaultTableAction) , @if (filled($this->defaultTableActionArguments)) @js($this->defaultTableActionArguments) @else {} @endif , @js(['table' => true, 'recordKey' => $this->defaultTableActionRecord]))">
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (filled($this->defaultAction))
|
||||
<div
|
||||
wire:init="mountAction(@js($this->defaultAction) @if (filled($this->defaultActionArguments) || filled($this->defaultActionContext)) , @if (filled($this->defaultActionArguments)) @js($this->defaultActionArguments) @else {} @endif @endif @if (filled($this->defaultActionContext)) , @js($this->defaultActionContext) @endif)">
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_END, scopes: $this->getRenderHookScopes()) }}
|
||||
|
||||
@if (method_exists($this, 'hasUnsavedDataChangesAlert') && $this->hasUnsavedDataChangesAlert())
|
||||
@if (\Filament\Support\Facades\FilamentView::hasSpaMode())
|
||||
@script
|
||||
<script>
|
||||
setUpSpaModeUnsavedDataChangesAlert({
|
||||
body: @js(__('filament-panels::unsaved-changes-alert.body')),
|
||||
resolveLivewireComponentUsing: () => @this,
|
||||
$wire,
|
||||
})
|
||||
</script>
|
||||
@endscript
|
||||
@else
|
||||
@script
|
||||
<script>
|
||||
setUpUnsavedDataChangesAlert({
|
||||
$wire
|
||||
})
|
||||
</script>
|
||||
@endscript
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if (!app()->hasDebugModeEnabled())
|
||||
@script
|
||||
<script>
|
||||
window.filamentErrorNotifications = @js($this->hasErrorNotifications() ? $this->getErrorNotifications() : null)
|
||||
</script>
|
||||
@endscript
|
||||
@endif
|
||||
|
||||
<x-filament-panels::unsaved-action-changes-alert />
|
||||
</div>
|
||||
@@ -0,0 +1,28 @@
|
||||
@props([
|
||||
'heading' => null,
|
||||
'subheading' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
$heading ??= $this->getHeading();
|
||||
$subheading ??= $this->getSubHeading();
|
||||
$hasLogo = $this->hasLogo();
|
||||
@endphp
|
||||
|
||||
<div {{ $attributes->class(['fi-simple-page']) }}>
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SIMPLE_PAGE_START, scopes: $this->getRenderHookScopes()) }}
|
||||
|
||||
<div class="fi-simple-page-content">
|
||||
@if (filled($heading) || $hasLogo || filled($subheading))
|
||||
<x-filament-panels::header.simple :heading="$heading" :logo="$hasLogo" :subheading="$subheading" />
|
||||
@endif
|
||||
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
@if (!$this instanceof \Filament\Tables\Contracts\HasTable)
|
||||
<x-filament-actions::modals />
|
||||
@endif
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SIMPLE_PAGE_END, scopes: $this->getRenderHookScopes()) }}
|
||||
</div>
|
||||
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
@props(['navigation'])
|
||||
|
||||
<x-filament::dropdown placement="bottom-start" width="xs" :attributes="\Filament\Support\prepare_inherited_attributes($attributes)->class(['fi-page-sub-navigation-dropdown'])">
|
||||
<x-slot name="trigger">
|
||||
@php
|
||||
$activeItem = null;
|
||||
|
||||
foreach ($navigation as $navigationGroup) {
|
||||
foreach ($navigationGroup->getItems() as $navigationItem) {
|
||||
foreach ([$navigationItem, ...$navigationItem->getChildItems()] as $navigationItemChild) {
|
||||
if ($navigationItemChild->isActive()) {
|
||||
$activeItem = $navigationItemChild;
|
||||
|
||||
break 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
|
||||
<x-filament::button color="gray" :icon="\Filament\Support\Icons\Heroicon::ChevronDown" :icon-alias="\Filament\View\PanelsIconAlias::SUB_NAVIGATION_MOBILE_MENU_BUTTON" icon-position="after">
|
||||
{{ $activeItem?->getLabel() }}
|
||||
</x-filament::button>
|
||||
</x-slot>
|
||||
|
||||
@foreach ($navigation as $navigationGroup)
|
||||
@if (filled($navigationGroupLabel = $navigationGroup->getLabel()))
|
||||
<x-filament::dropdown.header>
|
||||
{{ $navigationGroupLabel }}
|
||||
</x-filament::dropdown.header>
|
||||
@endif
|
||||
|
||||
<x-filament::dropdown.list>
|
||||
@foreach ($navigationGroup->getItems() as $navigationItem)
|
||||
@foreach ([$navigationItem, ...$navigationItem->getChildItems()] as $navigationItemChild)
|
||||
@php
|
||||
$navigationItemBadge = $navigationItem->getBadge();
|
||||
$navigationItemBadgeColor = $navigationItem->getBadgeColor($navigationItemBadge);
|
||||
$navigationItemIcon = $navigationItem->isActive()
|
||||
? $navigationItem->getActiveIcon() ?? $navigationItem->getIcon()
|
||||
: $navigationItem->getIcon();
|
||||
$navigationItemUrl = $navigationItem->getUrl();
|
||||
$shouldNavigationItemOpenUrlInNewTab = $navigationItem->shouldOpenUrlInNewTab();
|
||||
$navigationItemExtraAttributes = $navigationItemChild->getExtraAttributeBag();
|
||||
@endphp
|
||||
|
||||
<x-filament::dropdown.list.item :badge="$navigationItemBadge" :badge-color="$navigationItemBadgeColor" :href="$navigationItemUrl"
|
||||
:icon="$navigationItemIcon" tag="a" :target="$shouldNavigationItemOpenUrlInNewTab ? '_blank' : null" :attributes="\Filament\Support\prepare_inherited_attributes($navigationItemExtraAttributes)">
|
||||
{{ $navigationItemChild->getLabel() }}
|
||||
</x-filament::dropdown.list.item>
|
||||
@endforeach
|
||||
@endforeach
|
||||
</x-filament::dropdown.list>
|
||||
@endforeach
|
||||
</x-filament::dropdown>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
@props(['navigation'])
|
||||
|
||||
<div {{ $attributes->class(['fi-page-sub-navigation-sidebar-ctn']) }}>
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_SUB_NAVIGATION_SIDEBAR_BEFORE, scopes: $this->getRenderHookScopes()) }}
|
||||
|
||||
<ul class="fi-page-sub-navigation-sidebar">
|
||||
@foreach ($navigation as $navigationGroup)
|
||||
@php
|
||||
$isNavigationGroupActive = $navigationGroup->isActive();
|
||||
$isNavigationGroupCollapsible = $navigationGroup->isCollapsible();
|
||||
$navigationGroupIcon = $navigationGroup->getIcon();
|
||||
$navigationGroupItems = $navigationGroup->getItems();
|
||||
$navigationGroupLabel = $navigationGroup->getLabel();
|
||||
$navigationGroupExtraSidebarAttributeBag = $navigationGroup->getExtraSidebarAttributeBag();
|
||||
@endphp
|
||||
|
||||
<x-filament-panels::sidebar.group :active="$isNavigationGroupActive" :collapsible="$isNavigationGroupCollapsible" :icon="$navigationGroupIcon" :items="$navigationGroupItems"
|
||||
:label="$navigationGroupLabel" :sidebar-collapsible="false" sub-navigation :attributes="\Filament\Support\prepare_inherited_attributes($navigationGroupExtraSidebarAttributeBag)" />
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::PAGE_SUB_NAVIGATION_SIDEBAR_AFTER, scopes: $this->getRenderHookScopes()) }}
|
||||
</div>
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
@props(['navigation'])
|
||||
|
||||
<x-filament::tabs :attributes="\Filament\Support\prepare_inherited_attributes($attributes)->class(['fi-page-sub-navigation-tabs'])">
|
||||
@foreach ($navigation as $navigationGroup)
|
||||
@php
|
||||
$navigationGroupLabel = $navigationGroup->getLabel();
|
||||
$isNavigationGroupActive = $navigationGroup->isActive();
|
||||
$navigationGroupIcon = $navigationGroup->getIcon();
|
||||
@endphp
|
||||
|
||||
@if ($navigationGroupLabel)
|
||||
<x-filament::dropdown placement="bottom-start">
|
||||
<x-slot name="trigger">
|
||||
<x-filament::tabs.item :active="$isNavigationGroupActive" :icon="$navigationGroupIcon">
|
||||
{{ $navigationGroupLabel }}
|
||||
</x-filament::tabs.item>
|
||||
</x-slot>
|
||||
|
||||
<x-filament::dropdown.list>
|
||||
@foreach ($navigationGroup->getItems() as $navigationItem)
|
||||
@php
|
||||
$navigationItemBadge = $navigationItem->getBadge();
|
||||
$navigationItemBadgeColor = $navigationItem->getBadgeColor($navigationItemBadge);
|
||||
$navigationItemBadgeTooltip = $navigationItem->getBadgeTooltip($navigationItemBadge);
|
||||
$navigationItemIcon = $navigationItem->isActive()
|
||||
? $navigationItem->getActiveIcon() ?? $navigationItem->getIcon()
|
||||
: $navigationItem->getIcon();
|
||||
$navigationItemUrl = $navigationItem->getUrl();
|
||||
$shouldNavigationItemOpenUrlInNewTab = $navigationItem->shouldOpenUrlInNewTab();
|
||||
$navigationItemExtraAttributes = $navigationItem->getExtraAttributeBag();
|
||||
@endphp
|
||||
|
||||
<x-filament::dropdown.list.item :badge="$navigationItemBadge" :badge-color="$navigationItemBadgeColor" :badge-tooltip="$navigationItemBadgeTooltip"
|
||||
:href="$navigationItemUrl" :icon="$navigationItemIcon" tag="a" :target="$shouldNavigationItemOpenUrlInNewTab ? '_blank' : null" :attributes="\Filament\Support\prepare_inherited_attributes($navigationItemExtraAttributes)">
|
||||
{{ $navigationItem->getLabel() }}
|
||||
|
||||
@if ($navigationItemIcon instanceof \Illuminate\Contracts\Support\Htmlable)
|
||||
<x-slot name="icon">
|
||||
{{ $navigationItemIcon }}
|
||||
</x-slot>
|
||||
@endif
|
||||
</x-filament::dropdown.list.item>
|
||||
@endforeach
|
||||
</x-filament::dropdown.list>
|
||||
</x-filament::dropdown>
|
||||
@else
|
||||
@foreach ($navigationGroup->getItems() as $navigationItem)
|
||||
@php
|
||||
$isNavigationItemActive = $navigationItem->isActive();
|
||||
$navigationItemBadge = $navigationItem->getBadge();
|
||||
$navigationItemBadgeColor = $navigationItem->getBadgeColor($navigationItemBadge);
|
||||
$navigationItemBadgeTooltip = $navigationItem->getBadgeTooltip($navigationItemBadge);
|
||||
$navigationItemIcon = $navigationItem->isActive()
|
||||
? $navigationItem->getActiveIcon() ?? $navigationItem->getIcon()
|
||||
: $navigationItem->getIcon();
|
||||
$navigationItemUrl = $navigationItem->getUrl();
|
||||
$shouldNavigationItemOpenUrlInNewTab = $navigationItem->shouldOpenUrlInNewTab();
|
||||
$navigationItemExtraAttributes = $navigationItem->getExtraAttributeBag();
|
||||
@endphp
|
||||
|
||||
<x-filament::tabs.item :active="$isNavigationItemActive" :badge="$navigationItemBadge" :badge-color="$navigationItemBadgeColor" :badge-tooltip="$navigationItemBadgeTooltip"
|
||||
:href="$navigationItemUrl" :icon="$navigationItemIcon" tag="a" :target="$shouldNavigationItemOpenUrlInNewTab ? '_blank' : null" :attributes="\Filament\Support\prepare_inherited_attributes($navigationItemExtraAttributes)">
|
||||
{{ $navigationItem->getLabel() }}
|
||||
|
||||
@if ($navigationItemIcon instanceof \Illuminate\Contracts\Support\Htmlable)
|
||||
<x-slot name="icon">
|
||||
{{ $navigationItemIcon }}
|
||||
</x-slot>
|
||||
@endif
|
||||
</x-filament::tabs.item>
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
</x-filament::tabs>
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
@php
|
||||
$isSidebarCollapsibleOnDesktop = filament()->isSidebarCollapsibleOnDesktop();
|
||||
@endphp
|
||||
|
||||
<button class="fi-sidebar-database-notifications-btn">
|
||||
{{ \Filament\Support\generate_icon_html(\Filament\Support\Icons\Heroicon::OutlinedBell, alias: \Filament\View\PanelsIconAlias::SIDEBAR_OPEN_DATABASE_NOTIFICATIONS_BUTTON, size: \Filament\Support\Enums\IconSize::Large) }}
|
||||
|
||||
<span
|
||||
@if ($isSidebarCollapsibleOnDesktop) x-show="$store.sidebar.isOpen"
|
||||
x-transition:enter="fi-transition-enter"
|
||||
x-transition:enter-start="fi-transition-enter-start"
|
||||
x-transition:enter-end="fi-transition-enter-end" @endif
|
||||
class="fi-sidebar-database-notifications-btn-label">
|
||||
{{ __('filament-panels::layout.actions.open_database_notifications.label') }}
|
||||
</span>
|
||||
|
||||
@if ($unreadNotificationsCount)
|
||||
<span
|
||||
@if ($isSidebarCollapsibleOnDesktop) x-show="$store.sidebar.isOpen"
|
||||
x-transition:enter="fi-transition-enter"
|
||||
x-transition:enter-start="fi-transition-enter-start"
|
||||
x-transition:enter-end="fi-transition-enter-end" @endif
|
||||
class="fi-sidebar-database-notifications-btn-badge-ctn">
|
||||
<x-filament::badge>
|
||||
{{ $unreadNotificationsCount }}
|
||||
</x-filament::badge>
|
||||
</span>
|
||||
@endif
|
||||
</button>
|
||||
@@ -0,0 +1,174 @@
|
||||
@props([
|
||||
'active' => false,
|
||||
'collapsible' => true,
|
||||
'icon' => null,
|
||||
'items' => [],
|
||||
'label' => null,
|
||||
'sidebarCollapsible' => true,
|
||||
'subNavigation' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
$sidebarCollapsible = $sidebarCollapsible && filament()->isSidebarCollapsibleOnDesktop();
|
||||
$hasDropdown = filled($label) && filled($icon) && $sidebarCollapsible;
|
||||
@endphp
|
||||
|
||||
<li x-data="{ label: @js($subNavigation ? "sub_navigation_{$label}" : $label) }" data-group-label="{{ $subNavigation ? "sub_navigation_{$label}" : $label }}"
|
||||
x-bind:class="{ 'fi-collapsed': $store.sidebar.groupIsCollapsed(label) }"
|
||||
{{ $attributes->class(['fi-sidebar-group', 'fi-active' => $active, 'fi-collapsible' => $collapsible]) }}>
|
||||
@if ($label)
|
||||
<div @if ($collapsible) x-on:click="$store.sidebar.toggleCollapsedGroup(label)" @endif
|
||||
@if ($sidebarCollapsible) x-show="$store.sidebar.isOpen"
|
||||
x-transition:enter="fi-transition-enter"
|
||||
x-transition:enter-start="fi-transition-enter-start"
|
||||
x-transition:enter-end="fi-transition-enter-end" @endif
|
||||
class="fi-sidebar-group-btn">
|
||||
@if ($icon)
|
||||
{{ \Filament\Support\generate_icon_html($icon, size: \Filament\Support\Enums\IconSize::Large) }}
|
||||
@endif
|
||||
|
||||
<span class="fi-sidebar-group-label">
|
||||
{{ $label }}
|
||||
</span>
|
||||
|
||||
@if ($collapsible)
|
||||
<x-filament::icon-button color="gray" :icon="\Filament\Support\Icons\Heroicon::ChevronUp" :icon-alias="\Filament\View\PanelsIconAlias::SIDEBAR_GROUP_COLLAPSE_BUTTON" :label="$label"
|
||||
x-bind:aria-expanded="!$store.sidebar.groupIsCollapsed(label)"
|
||||
x-on:click.stop="$store.sidebar.toggleCollapsedGroup(label)" class="fi-sidebar-group-collapse-btn" />
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($hasDropdown)
|
||||
<x-filament::dropdown :placement="(__('filament-panels::layout.direction') === 'rtl') ? 'left-start' : 'right-start'" x-show="! $store.sidebar.isOpen">
|
||||
<x-slot name="trigger">
|
||||
<button x-data="{ tooltip: false }"
|
||||
x-effect="
|
||||
tooltip = $store.sidebar.isOpen
|
||||
? false
|
||||
: {
|
||||
content: @js($label),
|
||||
placement: document.dir === 'rtl' ? 'left' : 'right',
|
||||
theme: $store.theme,
|
||||
}
|
||||
"
|
||||
x-tooltip.html="tooltip" class="fi-sidebar-group-dropdown-trigger-btn">
|
||||
{{ \Filament\Support\generate_icon_html($icon, size: \Filament\Support\Enums\IconSize::Large) }}
|
||||
</button>
|
||||
</x-slot>
|
||||
|
||||
@php
|
||||
$lists = [];
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($childItems = $item->getChildItems()) {
|
||||
$lists[] = [$item, ...$childItems];
|
||||
$lists[] = [];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($lists)) {
|
||||
$lists[] = [$item];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$lists[count($lists) - 1][] = $item;
|
||||
}
|
||||
|
||||
if (empty($lists[count($lists) - 1])) {
|
||||
array_pop($lists);
|
||||
}
|
||||
@endphp
|
||||
|
||||
@if (filled($label))
|
||||
<x-filament::dropdown.header>
|
||||
{{ $label }}
|
||||
</x-filament::dropdown.header>
|
||||
@endif
|
||||
|
||||
@foreach ($lists as $list)
|
||||
<x-filament::dropdown.list>
|
||||
@foreach ($list as $item)
|
||||
@php
|
||||
$itemIsActive = $item->isActive();
|
||||
$itemBadge = $item->getBadge();
|
||||
$itemBadgeColor = $item->getBadgeColor($itemBadge);
|
||||
$itemBadgeTooltip = $item->getBadgeTooltip($itemBadge);
|
||||
$itemUrl = $item->getUrl();
|
||||
$itemIcon = $itemIsActive ? $item->getActiveIcon() ?? $item->getIcon() : $item->getIcon();
|
||||
$shouldItemOpenUrlInNewTab = $item->shouldOpenUrlInNewTab();
|
||||
$itemExtraAttributes = $item->getExtraAttributeBag();
|
||||
@endphp
|
||||
|
||||
<x-filament::dropdown.list.item :badge="$itemBadge" :badge-color="$itemBadgeColor" :badge-tooltip="$itemBadgeTooltip"
|
||||
:color="$itemIsActive ? 'primary' : 'gray'" :href="$itemUrl" :icon="$itemIcon" tag="a" :target="$shouldItemOpenUrlInNewTab ? '_blank' : null"
|
||||
:attributes="\Filament\Support\prepare_inherited_attributes($itemExtraAttributes)">
|
||||
{{ $item->getLabel() }}
|
||||
</x-filament::dropdown.list.item>
|
||||
@endforeach
|
||||
</x-filament::dropdown.list>
|
||||
@endforeach
|
||||
</x-filament::dropdown>
|
||||
@endif
|
||||
|
||||
<ul @if (filled($label)) @if ($sidebarCollapsible)
|
||||
x-show="$store.sidebar.isOpen ? ! $store.sidebar.groupIsCollapsed(label) : ! @js($hasDropdown)"
|
||||
@else
|
||||
x-show="! $store.sidebar.groupIsCollapsed(label)" @endif
|
||||
x-collapse.duration.200ms @endif
|
||||
@if ($sidebarCollapsible) x-transition:enter="fi-transition-enter"
|
||||
x-transition:enter-start="fi-transition-enter-start"
|
||||
x-transition:enter-end="fi-transition-enter-end" @endif
|
||||
class="fi-sidebar-group-items"
|
||||
>
|
||||
@foreach ($items as $item)
|
||||
@php
|
||||
$isItemChildItemsActive = $item->isChildItemsActive();
|
||||
$isItemActive = !$isItemChildItemsActive && $item->isActive();
|
||||
$itemActiveIcon = $item->getActiveIcon();
|
||||
$itemBadge = $item->getBadge();
|
||||
$itemBadgeColor = $item->getBadgeColor($itemBadge);
|
||||
$itemBadgeTooltip = $item->getBadgeTooltip($itemBadge);
|
||||
$itemChildItems = $item->getChildItems();
|
||||
$itemIcon = $item->getIcon();
|
||||
$shouldItemOpenUrlInNewTab = $item->shouldOpenUrlInNewTab();
|
||||
$itemUrl = $item->getUrl();
|
||||
$itemExtraAttributes = $item->getExtraAttributeBag();
|
||||
|
||||
if ($icon) {
|
||||
if ($hasDropdown || (blank($itemIcon) && blank($itemActiveIcon))) {
|
||||
$itemIcon = null;
|
||||
$itemActiveIcon = null;
|
||||
} else {
|
||||
throw new \Exception(
|
||||
'Navigation group [' .
|
||||
$label .
|
||||
'] has an icon but one or more of its items also have icons. Either the group or its items can have icons, but not both. This is to ensure a proper user experience.',
|
||||
);
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
|
||||
<x-filament-panels::sidebar.item :active="$isItemActive" :active-child-items="$isItemChildItemsActive" :active-icon="$itemActiveIcon" :badge="$itemBadge"
|
||||
:badge-color="$itemBadgeColor" :badge-tooltip="$itemBadgeTooltip" :child-items="$itemChildItems" :first="$loop->first" :grouped="filled($label)"
|
||||
:icon="$itemIcon" :last="$loop->last" :should-open-url-in-new-tab="$shouldItemOpenUrlInNewTab" :sidebar-collapsible="$sidebarCollapsible" :sub-navigation="$subNavigation"
|
||||
:url="$itemUrl" :attributes="\Filament\Support\prepare_inherited_attributes($itemExtraAttributes)">
|
||||
{{ $item->getLabel() }}
|
||||
|
||||
@if ($itemIcon instanceof \Illuminate\Contracts\Support\Htmlable)
|
||||
<x-slot name="icon">
|
||||
{{ $itemIcon }}
|
||||
</x-slot>
|
||||
@endif
|
||||
|
||||
@if ($itemActiveIcon instanceof \Illuminate\Contracts\Support\Htmlable)
|
||||
<x-slot name="activeIcon">
|
||||
{{ $itemActiveIcon }}
|
||||
</x-slot>
|
||||
@endif
|
||||
</x-filament-panels::sidebar.item>
|
||||
@endforeach
|
||||
</ul>
|
||||
</li>
|
||||
@@ -0,0 +1,117 @@
|
||||
@props([
|
||||
'active' => false,
|
||||
'activeChildItems' => false,
|
||||
'activeIcon' => null,
|
||||
'badge' => null,
|
||||
'badgeColor' => null,
|
||||
'badgeTooltip' => null,
|
||||
'childItems' => [],
|
||||
'first' => false,
|
||||
'grouped' => false,
|
||||
'icon' => null,
|
||||
'last' => false,
|
||||
'shouldOpenUrlInNewTab' => false,
|
||||
'sidebarCollapsible' => true,
|
||||
'subGrouped' => false,
|
||||
'subNavigation' => false,
|
||||
'url',
|
||||
])
|
||||
|
||||
@php
|
||||
$sidebarCollapsible = $sidebarCollapsible && filament()->isSidebarCollapsibleOnDesktop();
|
||||
@endphp
|
||||
|
||||
<li
|
||||
{{ $attributes->class([
|
||||
'fi-sidebar-item',
|
||||
'fi-active' => $active,
|
||||
'fi-sidebar-item-has-active-child-items' => $activeChildItems,
|
||||
'fi-sidebar-item-has-url' => filled($url),
|
||||
]) }}>
|
||||
<a {{ \Filament\Support\generate_href_html($url, $shouldOpenUrlInNewTab) }}
|
||||
x-on:click="window.matchMedia(`(max-width: 1024px)`).matches && $store.sidebar.close()"
|
||||
@if ($sidebarCollapsible && !$subNavigation) x-data="{ tooltip: false }"
|
||||
x-effect="
|
||||
tooltip = $store.sidebar.isOpen
|
||||
? false
|
||||
: {
|
||||
content: @js($slot->toHtml()),
|
||||
placement: document.dir === 'rtl' ? 'left' : 'right',
|
||||
theme: $store.theme,
|
||||
}
|
||||
"
|
||||
x-tooltip.html="tooltip" @endif
|
||||
class="fi-sidebar-item-btn">
|
||||
@if (filled($icon) && (!$subGrouped || ($sidebarCollapsible && !$subNavigation)))
|
||||
{{ \Filament\Support\generate_icon_html(
|
||||
$active && $activeIcon ? $activeIcon : $icon,
|
||||
attributes: new \Illuminate\View\ComponentAttributeBag([
|
||||
'x-show' => $subGrouped && $sidebarCollapsible ? '! $store.sidebar.isOpen' : false,
|
||||
])->class(['fi-sidebar-item-icon']),
|
||||
size: \Filament\Support\Enums\IconSize::Large,
|
||||
) }}
|
||||
@endif
|
||||
|
||||
@if ((blank($icon) && $grouped) || $subGrouped)
|
||||
<div @if (filled($icon) && $subGrouped && $sidebarCollapsible && !$subNavigation) x-show="$store.sidebar.isOpen" @endif
|
||||
class="fi-sidebar-item-grouped-border">
|
||||
@if (!$first)
|
||||
<div class="fi-sidebar-item-grouped-border-part-not-first"></div>
|
||||
@endif
|
||||
|
||||
@if (!$last)
|
||||
<div class="fi-sidebar-item-grouped-border-part-not-last"></div>
|
||||
@endif
|
||||
|
||||
<div class="fi-sidebar-item-grouped-border-part"></div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<span
|
||||
@if ($sidebarCollapsible && !$subNavigation) x-show="$store.sidebar.isOpen"
|
||||
x-transition:enter="fi-transition-enter"
|
||||
x-transition:enter-start="fi-transition-enter-start"
|
||||
x-transition:enter-end="fi-transition-enter-end" @endif
|
||||
class="fi-sidebar-item-label">
|
||||
{{ $slot }}
|
||||
</span>
|
||||
|
||||
@if (filled($badge))
|
||||
<span
|
||||
@if ($sidebarCollapsible && !$subNavigation) x-show="$store.sidebar.isOpen"
|
||||
x-transition:enter="fi-transition-enter"
|
||||
x-transition:enter-start="fi-transition-enter-start"
|
||||
x-transition:enter-end="fi-transition-enter-end" @endif
|
||||
class="fi-sidebar-item-badge-ctn">
|
||||
<x-filament::badge :color="$badgeColor" :tooltip="$badgeTooltip">
|
||||
{{ $badge }}
|
||||
</x-filament::badge>
|
||||
</span>
|
||||
@endif
|
||||
</a>
|
||||
|
||||
@if ($childItems && (blank($url) || $active || $activeChildItems))
|
||||
<ul class="fi-sidebar-sub-group-items">
|
||||
@foreach ($childItems as $childItem)
|
||||
@php
|
||||
$isChildItemChildItemsActive = $childItem->isChildItemsActive();
|
||||
$isChildActive = !$isChildItemChildItemsActive && $childItem->isActive();
|
||||
$childItemActiveIcon = $childItem->getActiveIcon();
|
||||
$childItemBadge = $childItem->getBadge();
|
||||
$childItemBadgeColor = $childItem->getBadgeColor($childItemBadge);
|
||||
$childItemBadgeTooltip = $childItem->getBadgeTooltip($childItemBadge);
|
||||
$childItemIcon = $childItem->getIcon();
|
||||
$shouldChildItemOpenUrlInNewTab = $childItem->shouldOpenUrlInNewTab();
|
||||
$childItemUrl = $childItem->getUrl();
|
||||
$childItemExtraAttributes = $childItem->getExtraAttributeBag();
|
||||
@endphp
|
||||
|
||||
<x-filament-panels::sidebar.item :active="$isChildActive" :active-child-items="$isChildItemChildItemsActive" :active-icon="$childItemActiveIcon" :badge="$childItemBadge"
|
||||
:badge-color="$childItemBadgeColor" :badge-tooltip="$childItemBadgeTooltip" :first="$loop->first" grouped :icon="$childItemIcon" :last="$loop->last"
|
||||
:should-open-url-in-new-tab="$shouldChildItemOpenUrlInNewTab" sub-grouped :sub-navigation="$subNavigation" :url="$childItemUrl" :attributes="\Filament\Support\prepare_inherited_attributes($childItemExtraAttributes)">
|
||||
{{ $childItem->getLabel() }}
|
||||
</x-filament-panels::sidebar.item>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
</li>
|
||||
@@ -0,0 +1,131 @@
|
||||
@props([
|
||||
'teleport' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
$currentTenant = filament()->getTenant();
|
||||
$currentTenantName = filament()->getTenantName($currentTenant);
|
||||
|
||||
$items = $this->getTenantMenuItems();
|
||||
|
||||
$canSwitchTenants =
|
||||
filament()->hasTenantSwitcher() &&
|
||||
filled(
|
||||
$tenants = array_filter(
|
||||
filament()->getUserTenants(filament()->auth()->user()),
|
||||
fn(\Illuminate\Database\Eloquent\Model $tenant): bool => !$tenant->is($currentTenant),
|
||||
),
|
||||
);
|
||||
|
||||
$isSearchable = $canSwitchTenants && (filament()->isTenantMenuSearchable() ?? count($tenants) >= 10);
|
||||
|
||||
$itemsBeforeAndAfterTenantSwitcher = collect($items)
|
||||
->groupBy(fn(Action $item): bool => $canSwitchTenants && $item->getSort() < 0, preserveKeys: true)
|
||||
->all();
|
||||
$itemsBeforeTenantSwitcher = $itemsBeforeAndAfterTenantSwitcher[true] ?? collect();
|
||||
$itemsAfterTenantSwitcher = $itemsBeforeAndAfterTenantSwitcher[false] ?? collect();
|
||||
|
||||
$isSidebarCollapsibleOnDesktop = filament()->isSidebarCollapsibleOnDesktop();
|
||||
@endphp
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::TENANT_MENU_BEFORE) }}
|
||||
|
||||
<x-filament::dropdown placement="bottom-start" size :teleport="$teleport" :attributes="\Filament\Support\prepare_inherited_attributes($attributes)->class(['fi-tenant-menu'])">
|
||||
<x-slot name="trigger">
|
||||
<button
|
||||
@if ($isSidebarCollapsibleOnDesktop) x-data="{ tooltip: false }"
|
||||
x-effect="
|
||||
tooltip = $store.sidebar.isOpen
|
||||
? false
|
||||
: {
|
||||
content: @js($currentTenantName),
|
||||
placement: document.dir === 'rtl' ? 'left' : 'right',
|
||||
theme: $store.theme,
|
||||
}
|
||||
"
|
||||
x-tooltip.html="tooltip" @endif
|
||||
type="button" class="fi-tenant-menu-trigger">
|
||||
<x-filament-panels::avatar.tenant :tenant="$currentTenant" loading="lazy" />
|
||||
|
||||
<span @if ($isSidebarCollapsibleOnDesktop) x-show="$store.sidebar.isOpen" @endif
|
||||
class="fi-tenant-menu-trigger-text">
|
||||
@if ($currentTenant instanceof \Filament\Models\Contracts\HasCurrentTenantLabel)
|
||||
<span class="fi-tenant-menu-trigger-current-tenant-label">
|
||||
{{ $currentTenant->getCurrentTenantLabel() }}
|
||||
</span>
|
||||
@endif
|
||||
|
||||
<span class="fi-tenant-menu-trigger-tenant-name">
|
||||
{{ $currentTenantName }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
{{ \Filament\Support\generate_icon_html(
|
||||
\Filament\Support\Icons\Heroicon::ChevronDown,
|
||||
alias: \Filament\View\PanelsIconAlias::TENANT_MENU_TOGGLE_BUTTON,
|
||||
attributes: new \Illuminate\View\ComponentAttributeBag([
|
||||
'x-show' => $isSidebarCollapsibleOnDesktop ? '$store.sidebar.isOpen' : null,
|
||||
]),
|
||||
) }}
|
||||
</button>
|
||||
</x-slot>
|
||||
|
||||
@if ($itemsBeforeTenantSwitcher->isNotEmpty())
|
||||
<x-filament::dropdown.list>
|
||||
@foreach ($itemsBeforeTenantSwitcher as $item)
|
||||
{{ $item }}
|
||||
@endforeach
|
||||
</x-filament::dropdown.list>
|
||||
@endif
|
||||
|
||||
@if ($canSwitchTenants)
|
||||
<div x-data="{ search: '' }">
|
||||
<x-filament::dropdown.list>
|
||||
@if ($isSearchable)
|
||||
<div x-id="['input']">
|
||||
<label x-bind:for="$id('input')" class="fi-sr-only">
|
||||
{{ __('filament-panels::layout.tenant_menu.search_field.label') }}
|
||||
</label>
|
||||
|
||||
<x-filament::input x-bind:id="$id('input')" x-model="search"
|
||||
placeholder="{{ __('filament-panels::layout.tenant_menu.search_field.placeholder') }}"
|
||||
type="search" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@foreach ($tenants as $tenant)
|
||||
@php
|
||||
$tenantImage = filament()->getTenantAvatarUrl($tenant);
|
||||
$tenantName = filament()->getTenantName($tenant);
|
||||
$tenantUrl = filament()->getUrl($tenant);
|
||||
@endphp
|
||||
|
||||
<div
|
||||
x-show="
|
||||
search === '' ||
|
||||
@js($tenantName).replace(/ /g, '')
|
||||
.toLowerCase()
|
||||
.includes(search.replace(/ /g, '').toLowerCase())
|
||||
">
|
||||
<x-filament::dropdown.list.item :href="$tenantUrl" :image="$tenantImage" tag="a">
|
||||
{{ $tenantName }}
|
||||
</x-filament::dropdown.list.item>
|
||||
</div>
|
||||
@endforeach
|
||||
</x-filament::dropdown.list>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($itemsAfterTenantSwitcher->isNotEmpty())
|
||||
<x-filament::dropdown.list>
|
||||
@foreach ($itemsAfterTenantSwitcher as $item)
|
||||
{{ $item }}
|
||||
@endforeach
|
||||
</x-filament::dropdown.list>
|
||||
@endif
|
||||
</x-filament::dropdown>
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::TENANT_MENU_AFTER) }}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
@props(['icon', 'theme'])
|
||||
|
||||
@php
|
||||
$label = __("filament-panels::layout.actions.theme_switcher.{$theme}.label");
|
||||
@endphp
|
||||
|
||||
<button aria-label="{{ $label }}" type="button" x-on:click="(theme = @js($theme)) && close()"
|
||||
x-tooltip="{
|
||||
content: @js($label),
|
||||
theme: $store.theme,
|
||||
}"
|
||||
x-bind:class="{ 'fi-active': theme === @js($theme) }" class="fi-theme-switcher-btn">
|
||||
{{ \Filament\Support\generate_icon_html(
|
||||
$icon,
|
||||
alias: match ($theme) {
|
||||
'light' => \Filament\View\PanelsIconAlias::THEME_SWITCHER_LIGHT_BUTTON,
|
||||
'dark' => \Filament\View\PanelsIconAlias::THEME_SWITCHER_DARK_BUTTON,
|
||||
'system' => \Filament\View\PanelsIconAlias::THEME_SWITCHER_SYSTEM_BUTTON,
|
||||
},
|
||||
) }}
|
||||
</button>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<div x-data="{ theme: null }" x-init="$watch('theme', () => {
|
||||
$dispatch('theme-changed', theme)
|
||||
})
|
||||
|
||||
theme = localStorage.getItem('theme') || @js(filament()->getDefaultThemeMode()->value)" class="fi-theme-switcher">
|
||||
<x-filament-panels::theme-switcher.button :icon="\Filament\Support\Icons\Heroicon::Sun" theme="light" />
|
||||
|
||||
<x-filament-panels::theme-switcher.button :icon="\Filament\Support\Icons\Heroicon::Moon" theme="dark" />
|
||||
|
||||
<x-filament-panels::theme-switcher.button :icon="\Filament\Support\Icons\Heroicon::ComputerDesktop" theme="system" />
|
||||
</div>
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
<x-filament::icon-button :badge="$unreadNotificationsCount ?: null" color="gray" :icon="\Filament\Support\Icons\Heroicon::OutlinedBell" :icon-alias="\Filament\View\PanelsIconAlias::TOPBAR_OPEN_DATABASE_NOTIFICATIONS_BUTTON" icon-size="lg"
|
||||
:label="__('filament-panels::layout.actions.open_database_notifications.label')" class="fi-topbar-database-notifications-btn" />
|
||||
@@ -0,0 +1,40 @@
|
||||
@props([
|
||||
'active' => false,
|
||||
'activeIcon' => null,
|
||||
'badge' => null,
|
||||
'badgeColor' => null,
|
||||
'badgeTooltip' => null,
|
||||
'icon' => null,
|
||||
'shouldOpenUrlInNewTab' => false,
|
||||
'url' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
$tag = $url ? 'a' : 'button';
|
||||
@endphp
|
||||
|
||||
<li {{ $attributes->class(['fi-topbar-item', 'fi-active' => $active]) }}>
|
||||
<{{ $tag }}
|
||||
@if ($url) {{ \Filament\Support\generate_href_html($url, $shouldOpenUrlInNewTab) }}
|
||||
@else
|
||||
type="button" @endif
|
||||
class="fi-topbar-item-btn">
|
||||
@if ($icon || $activeIcon)
|
||||
{{ \Filament\Support\generate_icon_html($active && $activeIcon ? $activeIcon : $icon, attributes: new \Illuminate\View\ComponentAttributeBag()->class(['fi-topbar-item-icon'])) }}
|
||||
@endif
|
||||
|
||||
<span class="fi-topbar-item-label">
|
||||
{{ $slot }}
|
||||
</span>
|
||||
|
||||
@if (filled($badge))
|
||||
<x-filament::badge :color="$badgeColor" size="sm" :tooltip="$badgeTooltip">
|
||||
{{ $badge }}
|
||||
</x-filament::badge>
|
||||
@endif
|
||||
|
||||
@if (!$url)
|
||||
{{ \Filament\Support\generate_icon_html(\Filament\Support\Icons\Heroicon::ChevronDown, alias: \Filament\View\PanelsIconAlias::TOPBAR_GROUP_TOGGLE_BUTTON, attributes: new \Illuminate\View\ComponentAttributeBag()->class(['fi-topbar-group-toggle-icon'])) }}
|
||||
@endif
|
||||
</{{ $tag }}>
|
||||
</li>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
@if (filament()->hasUnsavedChangesAlerts())
|
||||
@script
|
||||
<script>
|
||||
setUpUnsavedActionChangesAlert({
|
||||
resolveLivewireComponentUsing: () => @this,
|
||||
$wire,
|
||||
})
|
||||
</script>
|
||||
@endscript
|
||||
@endif
|
||||
@@ -0,0 +1,124 @@
|
||||
@props([
|
||||
'position' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Enums\UserMenuPosition;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
$user = filament()->auth()->user();
|
||||
|
||||
$items = $this->getUserMenuItems();
|
||||
|
||||
$itemsBeforeAndAfterThemeSwitcher = collect($items)
|
||||
->groupBy(fn(Action $item): bool => $item->getSort() < 0, preserveKeys: true)
|
||||
->all();
|
||||
$itemsBeforeThemeSwitcher = $itemsBeforeAndAfterThemeSwitcher[true] ?? collect();
|
||||
$itemsAfterThemeSwitcher = $itemsBeforeAndAfterThemeSwitcher[false] ?? collect();
|
||||
|
||||
$hasProfileHeader =
|
||||
$itemsBeforeThemeSwitcher->has('profile') &&
|
||||
blank(($item = Arr::first($itemsBeforeThemeSwitcher))->getUrl()) &&
|
||||
!$item->hasAction();
|
||||
|
||||
if ($itemsBeforeThemeSwitcher->has('profile')) {
|
||||
$itemsBeforeThemeSwitcher = $itemsBeforeThemeSwitcher->prepend(
|
||||
$itemsBeforeThemeSwitcher->pull('profile'),
|
||||
'profile',
|
||||
);
|
||||
}
|
||||
|
||||
$position ??= filament()->getUserMenuPosition();
|
||||
|
||||
$isSidebarCollapsibleOnDesktop = filament()->isSidebarCollapsibleOnDesktop();
|
||||
@endphp
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::USER_MENU_BEFORE) }}
|
||||
|
||||
<x-filament::dropdown :placement="$position === UserMenuPosition::Topbar ? 'bottom-end' : 'top-end'" :teleport="$position === UserMenuPosition::Topbar" :attributes="\Filament\Support\prepare_inherited_attributes($attributes)->class(['fi-user-menu'])">
|
||||
<x-slot name="trigger">
|
||||
@if ($position === UserMenuPosition::Topbar)
|
||||
<button aria-label="{{ __('filament-panels::layout.actions.open_user_menu.label') }}" type="button"
|
||||
class="fi-user-menu-trigger">
|
||||
<x-filament-panels::avatar.user :user="$user" loading="lazy" />
|
||||
</button>
|
||||
@else
|
||||
<button aria-label="{{ __('filament-panels::layout.actions.open_user_menu.label') }}" type="button"
|
||||
class="fi-user-menu-trigger">
|
||||
<x-filament-panels::avatar.user :user="$user" loading="lazy" />
|
||||
|
||||
<span @if ($isSidebarCollapsibleOnDesktop) x-show="$store.sidebar.isOpen" @endif
|
||||
class="fi-user-menu-trigger-text">
|
||||
{{ filament()->getUserName($user) }}
|
||||
</span>
|
||||
|
||||
{{ \Filament\Support\generate_icon_html(
|
||||
\Filament\Support\Icons\Heroicon::ChevronUp,
|
||||
alias: \Filament\View\PanelsIconAlias::USER_MENU_TOGGLE_BUTTON,
|
||||
attributes: new \Illuminate\View\ComponentAttributeBag([
|
||||
'x-show' => $isSidebarCollapsibleOnDesktop ? '$store.sidebar.isOpen' : null,
|
||||
]),
|
||||
) }}
|
||||
</button>
|
||||
@endif
|
||||
</x-slot>
|
||||
|
||||
@if ($hasProfileHeader)
|
||||
@php
|
||||
$item = $itemsBeforeThemeSwitcher['profile'];
|
||||
$itemColor = $item->getColor();
|
||||
$itemIcon = $item->getIcon();
|
||||
|
||||
unset($itemsBeforeThemeSwitcher['profile']);
|
||||
@endphp
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::USER_MENU_PROFILE_BEFORE) }}
|
||||
|
||||
<x-filament::dropdown.header :color="$itemColor" :icon="$itemIcon">
|
||||
{{ $item->getLabel() }}
|
||||
</x-filament::dropdown.header>
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::USER_MENU_PROFILE_AFTER) }}
|
||||
@endif
|
||||
|
||||
@if ($itemsBeforeThemeSwitcher->isNotEmpty())
|
||||
<x-filament::dropdown.list>
|
||||
@foreach ($itemsBeforeThemeSwitcher as $key => $item)
|
||||
@if ($key === 'profile')
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::USER_MENU_PROFILE_BEFORE) }}
|
||||
|
||||
{{ $item }}
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::USER_MENU_PROFILE_AFTER) }}
|
||||
@else
|
||||
{{ $item }}
|
||||
@endif
|
||||
@endforeach
|
||||
</x-filament::dropdown.list>
|
||||
@endif
|
||||
|
||||
@if (filament()->hasDarkMode() && !filament()->hasDarkModeForced())
|
||||
<x-filament::dropdown.list>
|
||||
<x-filament-panels::theme-switcher />
|
||||
</x-filament::dropdown.list>
|
||||
@endif
|
||||
|
||||
@if ($itemsAfterThemeSwitcher->isNotEmpty())
|
||||
<x-filament::dropdown.list>
|
||||
@foreach ($itemsAfterThemeSwitcher as $key => $item)
|
||||
@if ($key === 'profile')
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::USER_MENU_PROFILE_BEFORE) }}
|
||||
|
||||
{{ $item }}
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::USER_MENU_PROFILE_AFTER) }}
|
||||
@else
|
||||
{{ $item }}
|
||||
@endif
|
||||
@endforeach
|
||||
</x-filament::dropdown.list>
|
||||
@endif
|
||||
</x-filament::dropdown>
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::USER_MENU_AFTER) }}
|
||||
@@ -0,0 +1,112 @@
|
||||
@php
|
||||
$debounce = filament()->getGlobalSearchDebounce();
|
||||
$keyBindings = filament()->getGlobalSearchKeyBindings();
|
||||
$suffix = filament()->getGlobalSearchFieldSuffix();
|
||||
@endphp
|
||||
|
||||
<div class="fi-global-search-ctn">
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::GLOBAL_SEARCH_START) }}
|
||||
|
||||
<div x-on:focus-first-global-search-result.stop="$el.querySelector('.fi-global-search-result-link')?.focus()"
|
||||
class="fi-global-search">
|
||||
<div x-id="['input']" class="fi-global-search-field">
|
||||
<label x-bind:for="$id('input')" class="fi-sr-only">
|
||||
{{ __('filament-panels::global-search.field.label') }}
|
||||
</label>
|
||||
|
||||
<x-filament::input.wrapper :prefix-icon="\Filament\Support\Icons\Heroicon::MagnifyingGlass" :prefix-icon-alias="\Filament\View\PanelsIconAlias::GLOBAL_SEARCH_FIELD" inline-prefix :suffix="$suffix" inline-suffix
|
||||
wire:target="search">
|
||||
<input autocomplete="off" maxlength="1000"
|
||||
placeholder="{{ __('filament-panels::global-search.field.placeholder') }}" type="search"
|
||||
wire:key="global-search.field.input" x-bind:id="$id('input')"
|
||||
x-on:keydown.down.prevent.stop="$dispatch('focus-first-global-search-result')"
|
||||
wire:model.live.debounce.{{ $debounce }}="search"
|
||||
x-mousetrap.global.{{ collect($keyBindings)->map(fn(string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($id('input'))?.focus()"
|
||||
class="fi-input fi-input-has-inline-prefix" />
|
||||
</x-filament::input.wrapper>
|
||||
</div>
|
||||
|
||||
@if ($results !== null)
|
||||
<div x-data="{
|
||||
isOpen: false,
|
||||
|
||||
open(event) {
|
||||
this.isOpen = true
|
||||
},
|
||||
|
||||
close(event) {
|
||||
this.isOpen = false
|
||||
},
|
||||
}" x-init="$nextTick(() => open())" x-on:click.away="close()"
|
||||
x-on:keydown.escape.window="close()" x-on:keydown.up.prevent="$focus.wrap().previous()"
|
||||
x-on:keydown.down.prevent="$focus.wrap().next()"
|
||||
x-on:open-global-search-results.window="$nextTick(() => open())" x-show="isOpen"
|
||||
x-transition:enter-start="fi-transition-enter-start" x-transition:leave-end="fi-transition-leave-end"
|
||||
class="fi-global-search-results-ctn">
|
||||
@if ($results->getCategories()->isEmpty())
|
||||
<p class="fi-global-search-no-results-message">
|
||||
{{ __('filament-panels::global-search.no_results_message') }}
|
||||
</p>
|
||||
@else
|
||||
<ul class="fi-global-search-results">
|
||||
@foreach ($results->getCategories() as $group => $groupedResults)
|
||||
<li class="fi-global-search-result-group">
|
||||
<h3 class="fi-global-search-result-group-header">
|
||||
{{ $group }}
|
||||
</h3>
|
||||
|
||||
<ul class="fi-global-search-result-group-results">
|
||||
@foreach ($groupedResults as $result)
|
||||
@php
|
||||
$resultVisibleActions = $result->getVisibleActions();
|
||||
@endphp
|
||||
|
||||
<li @class([
|
||||
'fi-global-search-result',
|
||||
'fi-global-search-result-has-actions' => $resultVisibleActions,
|
||||
])>
|
||||
<a {{ \Filament\Support\generate_href_html($result->url) }}
|
||||
x-on:click="close()" class="fi-global-search-result-link">
|
||||
<h4 class="fi-global-search-result-heading">
|
||||
{{ $result->title }}
|
||||
</h4>
|
||||
|
||||
@if ($result->details)
|
||||
<dl class="fi-global-search-result-details">
|
||||
@foreach ($result->details as $label => $value)
|
||||
<div class="fi-global-search-result-detail">
|
||||
@if ($isAssoc ??= \Illuminate\Support\Arr::isAssoc($result->details))
|
||||
<dt class="fi-global-search-result-detail-label">
|
||||
{{ $label }}:
|
||||
</dt>
|
||||
@endif
|
||||
|
||||
<dd class="fi-global-search-result-detail-value">
|
||||
{{ $value }}
|
||||
</dd>
|
||||
</div>
|
||||
@endforeach
|
||||
</dl>
|
||||
@endif
|
||||
</a>
|
||||
|
||||
@if ($resultVisibleActions)
|
||||
<div class="fi-global-search-result-actions">
|
||||
@foreach ($resultVisibleActions as $action)
|
||||
{{ $action }}
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::GLOBAL_SEARCH_END) }}
|
||||
</div>
|
||||
@@ -0,0 +1,163 @@
|
||||
<div>
|
||||
@php
|
||||
$navigation = filament()->getNavigation();
|
||||
$isRtl = __('filament-panels::layout.direction') === 'rtl';
|
||||
$isSidebarCollapsibleOnDesktop = filament()->isSidebarCollapsibleOnDesktop();
|
||||
$isSidebarFullyCollapsibleOnDesktop = filament()->isSidebarFullyCollapsibleOnDesktop();
|
||||
$hasNavigation = filament()->hasNavigation();
|
||||
$hasTopbar = filament()->hasTopbar();
|
||||
@endphp
|
||||
|
||||
{{-- format-ignore-start --}}
|
||||
<aside x-data="{}"
|
||||
@if ($isSidebarCollapsibleOnDesktop || $isSidebarFullyCollapsibleOnDesktop) x-cloak
|
||||
@else
|
||||
x-cloak="-lg" @endif
|
||||
x-bind:class="{ 'fi-sidebar-open': $store.sidebar.isOpen }" class="fi-sidebar fi-main-sidebar">
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SIDEBAR_START) }}
|
||||
|
||||
<div class="fi-sidebar-header-ctn">
|
||||
<header class="fi-sidebar-header">
|
||||
@if (!$hasTopbar && $isSidebarCollapsibleOnDesktop)
|
||||
<x-filament::icon-button color="gray" :icon="$isRtl
|
||||
? \Filament\Support\Icons\Heroicon::OutlinedChevronLeft
|
||||
: \Filament\Support\Icons\Heroicon::OutlinedChevronRight" {{-- @deprecated Use `PanelsIconAlias::SIDEBAR_EXPAND_BUTTON_RTL` instead of `PanelsIconAlias::SIDEBAR_EXPAND_BUTTON` for RTL. --}} :icon-alias="$isRtl
|
||||
? [
|
||||
\Filament\View\PanelsIconAlias::SIDEBAR_EXPAND_BUTTON_RTL,
|
||||
\Filament\View\PanelsIconAlias::SIDEBAR_EXPAND_BUTTON,
|
||||
]
|
||||
: \Filament\View\PanelsIconAlias::SIDEBAR_EXPAND_BUTTON"
|
||||
icon-size="lg" :label="__('filament-panels::layout.actions.sidebar.expand.label')" x-cloak x-data="{}" x-on:click="$store.sidebar.open()"
|
||||
x-show="! $store.sidebar.isOpen" class="fi-sidebar-open-collapse-sidebar-btn" />
|
||||
@endif
|
||||
|
||||
@if (!$hasTopbar && ($isSidebarCollapsibleOnDesktop || $isSidebarFullyCollapsibleOnDesktop))
|
||||
<x-filament::icon-button color="gray" :icon="$isRtl
|
||||
? \Filament\Support\Icons\Heroicon::OutlinedChevronRight
|
||||
: \Filament\Support\Icons\Heroicon::OutlinedChevronLeft" {{-- @deprecated Use `PanelsIconAlias::SIDEBAR_COLLAPSE_BUTTON_RTL` instead of `PanelsIconAlias::SIDEBAR_COLLAPSE_BUTTON` for RTL. --}} :icon-alias="$isRtl
|
||||
? [
|
||||
\Filament\View\PanelsIconAlias::SIDEBAR_COLLAPSE_BUTTON_RTL,
|
||||
\Filament\View\PanelsIconAlias::SIDEBAR_COLLAPSE_BUTTON,
|
||||
]
|
||||
: \Filament\View\PanelsIconAlias::SIDEBAR_COLLAPSE_BUTTON"
|
||||
icon-size="lg" :label="__('filament-panels::layout.actions.sidebar.collapse.label')" x-cloak x-data="{}"
|
||||
x-on:click="$store.sidebar.close()" x-show="$store.sidebar.isOpen"
|
||||
class="fi-sidebar-close-collapse-sidebar-btn" />
|
||||
@endif
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SIDEBAR_LOGO_BEFORE) }}
|
||||
|
||||
<div @if ($isSidebarCollapsibleOnDesktop || $isSidebarFullyCollapsibleOnDesktop) x-show="$store.sidebar.isOpen" @endif
|
||||
class="fi-sidebar-header-logo-ctn">
|
||||
@if ($homeUrl = filament()->getHomeUrl())
|
||||
<a {{ \Filament\Support\generate_href_html($homeUrl) }}>
|
||||
<x-filament-panels::logo />
|
||||
</a>
|
||||
@else
|
||||
<x-filament-panels::logo />
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SIDEBAR_LOGO_AFTER) }}
|
||||
</header>
|
||||
</div>
|
||||
|
||||
@if (filament()->hasTenancy() && filament()->hasTenantMenu())
|
||||
<x-filament-panels::tenant-menu />
|
||||
@endif
|
||||
|
||||
@if (filament()->isGlobalSearchEnabled() &&
|
||||
filament()->getGlobalSearchPosition() === \Filament\Enums\GlobalSearchPosition::Sidebar)
|
||||
<div @if ($isSidebarCollapsibleOnDesktop || $isSidebarFullyCollapsibleOnDesktop) x-show="$store.sidebar.isOpen" @endif>
|
||||
@livewire(Filament\Livewire\GlobalSearch::class)
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<nav class="fi-sidebar-nav">
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SIDEBAR_NAV_START) }}
|
||||
|
||||
<ul class="fi-sidebar-nav-groups">
|
||||
@foreach ($navigation as $group)
|
||||
@php
|
||||
$isGroupActive = $group->isActive();
|
||||
$isGroupCollapsible = $group->isCollapsible();
|
||||
$groupIcon = $group->getIcon();
|
||||
$groupItems = $group->getItems();
|
||||
$groupLabel = $group->getLabel();
|
||||
$groupExtraSidebarAttributeBag = $group->getExtraSidebarAttributeBag();
|
||||
@endphp
|
||||
|
||||
<x-filament-panels::sidebar.group :active="$isGroupActive" :collapsible="$isGroupCollapsible" :icon="$groupIcon"
|
||||
:items="$groupItems" :label="$groupLabel" :attributes="\Filament\Support\prepare_inherited_attributes($groupExtraSidebarAttributeBag)" />
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
var collapsedGroups = JSON.parse(
|
||||
localStorage.getItem('collapsedGroups'),
|
||||
)
|
||||
|
||||
if (collapsedGroups === null || collapsedGroups === 'null') {
|
||||
localStorage.setItem(
|
||||
'collapsedGroups',
|
||||
JSON.stringify(@js(collect($navigation)->filter(fn(\Filament\Navigation\NavigationGroup $group): bool => $group->isCollapsed())->map(fn(\Filament\Navigation\NavigationGroup $group): string => $group->getLabel())->values()->all())),
|
||||
)
|
||||
}
|
||||
|
||||
collapsedGroups = JSON.parse(
|
||||
localStorage.getItem('collapsedGroups'),
|
||||
)
|
||||
|
||||
document
|
||||
.querySelectorAll('.fi-sidebar-group')
|
||||
.forEach((group) => {
|
||||
if (
|
||||
!collapsedGroups.includes(group.dataset.groupLabel)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
// Alpine.js loads too slow, so attempt to hide a
|
||||
// collapsed sidebar group earlier.
|
||||
group.querySelector(
|
||||
'.fi-sidebar-group-items',
|
||||
).style.display = 'none'
|
||||
group.classList.add('fi-collapsed')
|
||||
})
|
||||
</script>
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SIDEBAR_NAV_END) }}
|
||||
</nav>
|
||||
|
||||
@php
|
||||
$isAuthenticated = filament()->auth()->check();
|
||||
$hasDatabaseNotificationsInSidebar =
|
||||
filament()->hasDatabaseNotifications() &&
|
||||
filament()->getDatabaseNotificationsPosition() ===
|
||||
\Filament\Enums\DatabaseNotificationsPosition::Sidebar;
|
||||
$hasUserMenuInSidebar =
|
||||
filament()->hasUserMenu() &&
|
||||
filament()->getUserMenuPosition() === \Filament\Enums\UserMenuPosition::Sidebar;
|
||||
$shouldRenderFooter = $isAuthenticated && ($hasDatabaseNotificationsInSidebar || $hasUserMenuInSidebar);
|
||||
@endphp
|
||||
|
||||
@if ($shouldRenderFooter)
|
||||
<div class="fi-sidebar-footer">
|
||||
@if ($hasDatabaseNotificationsInSidebar)
|
||||
@livewire(filament()->getDatabaseNotificationsLivewireComponent(), [
|
||||
'lazy' => filament()->hasLazyLoadedDatabaseNotifications(),
|
||||
])
|
||||
@endif
|
||||
|
||||
@if ($hasUserMenuInSidebar)
|
||||
<x-filament-panels::user-menu />
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::SIDEBAR_FOOTER) }}
|
||||
</aside>
|
||||
{{-- format-ignore-end --}}
|
||||
|
||||
<x-filament-actions::modals />
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
<div>
|
||||
<div class="fi-simple-user-menu-ctn">
|
||||
<x-filament-panels::user-menu :position="\Filament\Enums\UserMenuPosition::Topbar" />
|
||||
</div>
|
||||
|
||||
<x-filament-actions::modals />
|
||||
</div>
|
||||
@@ -0,0 +1,207 @@
|
||||
<div class="fi-topbar-ctn">
|
||||
@php
|
||||
$isRtl = __('filament-panels::layout.direction') === 'rtl';
|
||||
$isSidebarCollapsibleOnDesktop = filament()->isSidebarCollapsibleOnDesktop();
|
||||
$isSidebarFullyCollapsibleOnDesktop = filament()->isSidebarFullyCollapsibleOnDesktop();
|
||||
$hasTopNavigation = filament()->hasTopNavigation();
|
||||
$hasNavigation = filament()->hasNavigation();
|
||||
$hasTenancy = filament()->hasTenancy();
|
||||
@endphp
|
||||
|
||||
<nav class="fi-topbar">
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::TOPBAR_START) }}
|
||||
|
||||
@if ($hasNavigation)
|
||||
<x-filament::icon-button color="gray" :icon="\Filament\Support\Icons\Heroicon::OutlinedBars3" :icon-alias="\Filament\View\PanelsIconAlias::TOPBAR_OPEN_SIDEBAR_BUTTON" icon-size="lg" :label="__('filament-panels::layout.actions.sidebar.expand.label')"
|
||||
x-cloak x-data="{}" x-on:click="$store.sidebar.open()" x-show="! $store.sidebar.isOpen"
|
||||
class="fi-topbar-open-sidebar-btn" />
|
||||
|
||||
<x-filament::icon-button color="gray" :icon="\Filament\Support\Icons\Heroicon::OutlinedXMark" :icon-alias="\Filament\View\PanelsIconAlias::TOPBAR_CLOSE_SIDEBAR_BUTTON" icon-size="lg" :label="__('filament-panels::layout.actions.sidebar.collapse.label')"
|
||||
x-cloak x-data="{}" x-on:click="$store.sidebar.close()" x-show="$store.sidebar.isOpen"
|
||||
class="fi-topbar-close-sidebar-btn" />
|
||||
@endif
|
||||
|
||||
<div class="fi-topbar-start">
|
||||
@if ($isSidebarCollapsibleOnDesktop || $isSidebarFullyCollapsibleOnDesktop)
|
||||
<div x-show="$store.sidebar.isOpen || @js($isSidebarCollapsibleOnDesktop)"
|
||||
class="fi-topbar-collapse-sidebar-btn-ctn">
|
||||
@if ($isSidebarCollapsibleOnDesktop)
|
||||
<x-filament::icon-button color="gray" :icon="$isRtl
|
||||
? \Filament\Support\Icons\Heroicon::OutlinedChevronLeft
|
||||
: \Filament\Support\Icons\Heroicon::OutlinedChevronRight" {{-- @deprecated Use `PanelsIconAlias::SIDEBAR_EXPAND_BUTTON_RTL` instead of `PanelsIconAlias::SIDEBAR_EXPAND_BUTTON` for RTL. --}}
|
||||
:icon-alias="$isRtl
|
||||
? [
|
||||
\Filament\View\PanelsIconAlias::SIDEBAR_EXPAND_BUTTON_RTL,
|
||||
\Filament\View\PanelsIconAlias::SIDEBAR_EXPAND_BUTTON,
|
||||
]
|
||||
: \Filament\View\PanelsIconAlias::SIDEBAR_EXPAND_BUTTON" icon-size="lg" :label="__('filament-panels::layout.actions.sidebar.expand.label')" x-cloak x-data="{}"
|
||||
x-on:click="$store.sidebar.open()" x-show="! $store.sidebar.isOpen"
|
||||
class="fi-topbar-open-collapse-sidebar-btn" />
|
||||
@endif
|
||||
|
||||
@if ($isSidebarCollapsibleOnDesktop || $isSidebarFullyCollapsibleOnDesktop)
|
||||
<x-filament::icon-button color="gray" :icon="$isRtl
|
||||
? \Filament\Support\Icons\Heroicon::OutlinedChevronRight
|
||||
: \Filament\Support\Icons\Heroicon::OutlinedChevronLeft" {{-- @deprecated Use `PanelsIconAlias::SIDEBAR_COLLAPSE_BUTTON_RTL` instead of `PanelsIconAlias::SIDEBAR_COLLAPSE_BUTTON` for RTL. --}}
|
||||
:icon-alias="$isRtl
|
||||
? [
|
||||
\Filament\View\PanelsIconAlias::SIDEBAR_COLLAPSE_BUTTON_RTL,
|
||||
\Filament\View\PanelsIconAlias::SIDEBAR_COLLAPSE_BUTTON,
|
||||
]
|
||||
: \Filament\View\PanelsIconAlias::SIDEBAR_COLLAPSE_BUTTON" icon-size="lg" :label="__('filament-panels::layout.actions.sidebar.collapse.label')" x-cloak x-data="{}"
|
||||
x-on:click="$store.sidebar.close()" x-show="$store.sidebar.isOpen"
|
||||
class="fi-topbar-close-collapse-sidebar-btn" />
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::TOPBAR_LOGO_BEFORE) }}
|
||||
|
||||
@if ($homeUrl = filament()->getHomeUrl())
|
||||
<a {{ \Filament\Support\generate_href_html($homeUrl) }}>
|
||||
<x-filament-panels::logo />
|
||||
</a>
|
||||
@else
|
||||
<x-filament-panels::logo />
|
||||
@endif
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::TOPBAR_LOGO_AFTER) }}
|
||||
</div>
|
||||
|
||||
@if ($hasTopNavigation || !$hasNavigation)
|
||||
@if ($hasTenancy && filament()->hasTenantMenu())
|
||||
<x-filament-panels::tenant-menu teleport />
|
||||
@endif
|
||||
|
||||
@if ($hasNavigation)
|
||||
@php
|
||||
$navigation = filament()->getNavigation();
|
||||
@endphp
|
||||
|
||||
<ul class="fi-topbar-nav-groups">
|
||||
@foreach ($navigation as $group)
|
||||
@php
|
||||
$groupLabel = $group->getLabel();
|
||||
$groupExtraTopbarAttributeBag = $group->getExtraTopbarAttributeBag();
|
||||
$isGroupActive = $group->isActive();
|
||||
$groupIcon = $group->getIcon();
|
||||
@endphp
|
||||
|
||||
@if ($groupLabel)
|
||||
<x-filament::dropdown placement="bottom-start" teleport :attributes="\Filament\Support\prepare_inherited_attributes($groupExtraTopbarAttributeBag)">
|
||||
<x-slot name="trigger">
|
||||
<x-filament-panels::topbar.item :active="$isGroupActive" :icon="$groupIcon">
|
||||
{{ $groupLabel }}
|
||||
</x-filament-panels::topbar.item>
|
||||
</x-slot>
|
||||
|
||||
@php
|
||||
$lists = [];
|
||||
|
||||
foreach ($group->getItems() as $item) {
|
||||
if ($childItems = $item->getChildItems()) {
|
||||
$lists[] = [$item, ...$childItems];
|
||||
$lists[] = [];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($lists)) {
|
||||
$lists[] = [$item];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$lists[count($lists) - 1][] = $item;
|
||||
}
|
||||
|
||||
if (empty($lists[count($lists) - 1])) {
|
||||
array_pop($lists);
|
||||
}
|
||||
@endphp
|
||||
|
||||
@foreach ($lists as $list)
|
||||
<x-filament::dropdown.list>
|
||||
@foreach ($list as $item)
|
||||
@php
|
||||
$isItemActive = $item->isActive();
|
||||
$itemBadge = $item->getBadge();
|
||||
$itemBadgeColor = $item->getBadgeColor($itemBadge);
|
||||
$itemBadgeTooltip = $item->getBadgeTooltip($itemBadge);
|
||||
$itemUrl = $item->getUrl();
|
||||
$itemIcon = $isItemActive
|
||||
? $item->getActiveIcon() ?? $item->getIcon()
|
||||
: $item->getIcon();
|
||||
$shouldItemOpenUrlInNewTab = $item->shouldOpenUrlInNewTab();
|
||||
$itemExtraAttributes = $item->getExtraAttributeBag();
|
||||
@endphp
|
||||
|
||||
<x-filament::dropdown.list.item :badge="$itemBadge" :badge-color="$itemBadgeColor"
|
||||
:badge-tooltip="$itemBadgeTooltip" :color="$isItemActive ? 'primary' : 'gray'" :href="$itemUrl"
|
||||
:icon="$itemIcon" tag="a" :target="$shouldItemOpenUrlInNewTab ? '_blank' : null" :attributes="\Filament\Support\prepare_inherited_attributes(
|
||||
$itemExtraAttributes,
|
||||
)">
|
||||
{{ $item->getLabel() }}
|
||||
</x-filament::dropdown.list.item>
|
||||
@endforeach
|
||||
</x-filament::dropdown.list>
|
||||
@endforeach
|
||||
</x-filament::dropdown>
|
||||
@else
|
||||
@foreach ($group->getItems() as $item)
|
||||
@php
|
||||
$isItemActive = $item->isActive();
|
||||
$itemActiveIcon = $item->getActiveIcon();
|
||||
$itemBadge = $item->getBadge();
|
||||
$itemBadgeColor = $item->getBadgeColor($itemBadge);
|
||||
$itemBadgeTooltip = $item->getBadgeTooltip($itemBadge);
|
||||
$itemIcon = $item->getIcon();
|
||||
$shouldItemOpenUrlInNewTab = $item->shouldOpenUrlInNewTab();
|
||||
$itemUrl = $item->getUrl();
|
||||
$itemExtraAttributes = $item->getExtraAttributeBag();
|
||||
@endphp
|
||||
|
||||
<x-filament-panels::topbar.item :active="$isItemActive" :active-icon="$itemActiveIcon" :badge="$itemBadge"
|
||||
:badge-color="$itemBadgeColor" :badge-tooltip="$itemBadgeTooltip" :icon="$itemIcon" :should-open-url-in-new-tab="$shouldItemOpenUrlInNewTab"
|
||||
:url="$itemUrl" :attributes="\Filament\Support\prepare_inherited_attributes($itemExtraAttributes)">
|
||||
{{ $item->getLabel() }}
|
||||
</x-filament-panels::topbar.item>
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<div @if ($hasTenancy) x-persist="topbar.end.panel-{{ filament()->getId() }}.tenant-{{ filament()->getTenant()?->getKey() }}"
|
||||
@else
|
||||
x-persist="topbar.end.panel-{{ filament()->getId() }}" @endif
|
||||
class="fi-topbar-end">
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::GLOBAL_SEARCH_BEFORE) }}
|
||||
|
||||
@if (filament()->isGlobalSearchEnabled() &&
|
||||
filament()->getGlobalSearchPosition() === \Filament\Enums\GlobalSearchPosition::Topbar)
|
||||
@livewire(Filament\Livewire\GlobalSearch::class)
|
||||
@endif
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::GLOBAL_SEARCH_AFTER) }}
|
||||
|
||||
@if (filament()->auth()->check())
|
||||
@if (filament()->hasDatabaseNotifications() &&
|
||||
filament()->getDatabaseNotificationsPosition() === \Filament\Enums\DatabaseNotificationsPosition::Topbar)
|
||||
@livewire(filament()->getDatabaseNotificationsLivewireComponent(), [
|
||||
'lazy' => filament()->hasLazyLoadedDatabaseNotifications(),
|
||||
])
|
||||
@endif
|
||||
|
||||
@if (filament()->hasUserMenu() && filament()->getUserMenuPosition() === \Filament\Enums\UserMenuPosition::Topbar)
|
||||
<x-filament-panels::user-menu />
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::TOPBAR_END) }}
|
||||
</nav>
|
||||
|
||||
<x-filament-actions::modals />
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<x-filament-panels::page>
|
||||
{{ $this->content }}
|
||||
</x-filament-panels::page>
|
||||
@@ -0,0 +1,3 @@
|
||||
<x-filament-panels::page.simple>
|
||||
{{ $this->content }}
|
||||
</x-filament-panels::page.simple>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="fi-resource-relation-manager">
|
||||
{{ $this->content }}
|
||||
|
||||
<x-filament-panels::unsaved-action-changes-alert />
|
||||
</div>
|
||||
@@ -0,0 +1,28 @@
|
||||
@php
|
||||
$user = filament()->auth()->user();
|
||||
@endphp
|
||||
|
||||
<x-filament-widgets::widget class="fi-account-widget">
|
||||
<x-filament::section>
|
||||
<x-filament-panels::avatar.user size="lg" :user="$user" loading="lazy" />
|
||||
|
||||
<div class="fi-account-widget-main">
|
||||
<h2 class="fi-account-widget-heading">
|
||||
{{ __('filament-panels::widgets/account-widget.welcome', ['app' => config('app.name')]) }}
|
||||
</h2>
|
||||
|
||||
<p class="fi-account-widget-user-name">
|
||||
{{ filament()->getUserName($user) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form action="{{ filament()->getLogoutUrl() }}" method="post" class="fi-account-widget-logout-form">
|
||||
@csrf
|
||||
|
||||
<x-filament::button color="gray" :icon="\Filament\Support\Icons\Heroicon::ArrowLeftEndOnRectangle" :icon-alias="\Filament\View\PanelsIconAlias::WIDGETS_ACCOUNT_LOGOUT_BUTTON" labeled-from="sm" tag="button"
|
||||
type="submit">
|
||||
{{ __('filament-panels::widgets/account-widget.actions.logout.label') }}
|
||||
</x-filament::button>
|
||||
</form>
|
||||
</x-filament::section>
|
||||
</x-filament-widgets::widget>
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<x-filament-widgets::widget class="fi-filament-info-widget">
|
||||
<x-filament::section>
|
||||
<div class="fi-filament-info-widget-main">
|
||||
<a href="https://filamentphp.com" rel="noopener noreferrer" target="_blank">
|
||||
<svg aria-label="Filament" fill="currentColor" role="img" viewBox="0 0 1988.74 399.55"
|
||||
xmlns="http://www.w3.org/2000/svg" class="fi-filament-info-widget-logo">
|
||||
<path
|
||||
d="M115.85,138.9v-20.22c0-31.15,10.38-54.64,41.53-54.64h27.87V.1h-27.87C85.79.1,48.64,44.36,48.64,118.68v20.22H0v53.55h48.64v201.63h67.21v-201.63h82.19v-53.55h-82.19Z" />
|
||||
<rect x="382.68" y=".11" width="66.67" height="393.98" />
|
||||
<path
|
||||
d="M628.22,335.07c-36.62,0-70.49-27.87-70.49-68.85s33.88-68.86,70.49-68.86,69.94,24.59,69.94,68.86-33.88,68.85-69.94,68.85M698.16,170.59c-18.57-28.42-56.28-37.16-79.23-37.16-67.21,0-130.06,50.82-130.06,132.79s62.84,132.79,130.06,132.79c25.14,0,62.84-11.48,79.23-37.71v32.79h66.67v-255.2h-66.67v31.7Z" />
|
||||
<path
|
||||
d="M1116.15,133.44c-20.22,0-56.28,6.01-80.33,48.09-14.21-31.16-40.98-45.36-81.42-47.55-24.04,0-59.01,11.48-73.22,42.63v-37.7h-66.67v255.19h66.67v-136.07c0-43.71,26.23-59.56,53.55-59.56s46.45,19.68,47,55.19v140.44h66.66v-136.07c0-38.79,21.86-59.56,51.92-59.56,27.32,0,48.09,20.22,48.09,56.83v138.8h66.12v-145.9c0-74.87-36.61-114.75-98.36-114.75" />
|
||||
<path
|
||||
d="M1314.33,243.27c4.92-34.43,32.23-54.1,67.21-54.1,32.78,0,59.01,19.12,63.93,54.1h-131.14ZM1380.99,133.44c-72.68,0-132.24,51.91-132.24,132.24s59.56,133.87,132.24,133.87c46.99,0,96.17-18.03,119.12-61.74-16.39-8.75-34.97-18.58-50.82-27.32-12.02,21.86-38.25,32.78-63.93,32.78-37.71,0-66.12-20.76-70.49-54.64h194.53c.55-6.01,1.09-16.4,1.09-22.95,0-80.33-56.83-132.24-129.51-132.24" />
|
||||
<path
|
||||
d="M1698.38,133.44c-29.51,0-61.75,15.84-75.96,43.17v-37.7h-66.66v255.19h66.66v-136.07c0-44.26,25.69-59.56,55.74-59.56s48.64,19.68,48.64,56.83v138.8h66.66v-150.82c0-72.13-35.51-109.84-95.08-109.84" />
|
||||
<polygon
|
||||
points="1868.52 41.64 1868.52 138.9 1822.07 138.9 1822.07 192.45 1868.52 192.45 1868.52 394.09 1934.64 394.09 1934.64 192.45 1988.74 192.45 1988.74 138.9 1934.64 138.9 1934.64 41.64 1868.52 41.64" />
|
||||
<path
|
||||
d="M277.05,80.43c22.95,0,42.07-17.49,42.07-40.44S300,.11,277.05.11s-43.17,17.48-43.17,39.89,19.67,40.44,43.17,40.44" />
|
||||
<path
|
||||
d="M323.62,164.94l-40.54-24.73,2.36-20.71-42.93,1.25.26,1.6c.38,2.33.06,5.14-.29,8.12-.71,6.12-1.52,13.07,4.08,17.83,1.93,1.64,8.35,5.31,15.14,9.19,4.29,2.45,9.81,5.6,12.53,7.37-1.73.58-4.17.44-6.56.31-1.56-.09-3.15-.19-4.59-.07l-2.06.14c-6.77.44-13.01.91-14.89,1.8-3.26,1.55-4.26,8.19-3.8,13.71.23,2.77,1.22,9.52,5.54,12.25l26.35,15.94-27.62,2.43-.53.15c-2.69,1.35-3.09,6.74-3.08,9.83,0,5.09,1.15,12.03,4.67,15.03l27.31,16.82c-1.02.02-2.05.06-3.07.08-8.85.23-18.02.46-26.14,2.81l-.44.22c-2.03,1.52-2.19,11.99-.76,17.93.85,3.58,2.3,5.87,4.09,6.72l26.38,15.93-26.59,2.44-.49.13c-5.61,2.66-3.51,17.42-2.07,21.75l.23.7,30.04,19.34-26.51,1.8c-1.81.09-2.69,1.08-3.38,1.99-2.17,2.88-2.03,13.55-1.59,29.49.23,7.83.43,15.22-.06,18.24l-.36,2.25h38.49l.21-23.88,45.27-4.62.38-.04.3-.21c1.94-1.39,2.28-6.6,2.3-9.58.02-2.34-.14-10.21-3-13.37l-28.81-18.02,28.71-2.66.31-.23c2.37-1.76,2.67-12.39,1.09-18.54-.98-3.82-2.63-6.25-4.65-7.1l-26.3-15.92c2.59-.14,5.31-.21,8.12-.28,6.36-.16,12.94-.33,18.45-1.47,1.94-.41,3.34-2.03,4.12-4.84,1.6-5.69.46-15.62-2.22-19.32-1.28-1.76-7.11-5.22-20.58-12.82-3.97-2.24-7.74-4.37-9.73-5.6,2.12-.09,4.84-.12,7.66-.14,11.65-.11,20.1-.36,22.78-2.58,2.23-1.86,2.89-12.01,1.22-18.4-.91-3.44-2.42-5.68-4.31-6.56l-26.32-15.92,26.83-2.23.53-.15c3.22-1.62,3.79-9.61,3.39-14.27-.27-3.21-1.26-8.94-4.89-11.31" />
|
||||
<path
|
||||
d="M279.71,0c-23.51,0-43.2,17.5-43.2,39.92s19.69,40.46,43.2,40.46,42.11-17.5,42.11-40.46S302.68,0,279.71,0" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<p class="fi-filament-info-widget-version">
|
||||
{{ \Composer\InstalledVersions::getPrettyVersion('filament/filament') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="fi-filament-info-widget-links">
|
||||
<x-filament::link color="gray" href="https://filamentphp.com/docs" :icon="\Filament\Support\Icons\Heroicon::BookOpen" :icon-alias="\Filament\View\PanelsIconAlias::WIDGETS_FILAMENT_INFO_OPEN_DOCUMENTATION_BUTTON"
|
||||
rel="noopener noreferrer" target="_blank">
|
||||
{{ __('filament-panels::widgets/filament-info-widget.actions.open_documentation.label') }}
|
||||
</x-filament::link>
|
||||
|
||||
<x-filament::link color="gray" href="https://github.com/filamentphp/filament" :icon-alias="\Filament\View\PanelsIconAlias::WIDGETS_FILAMENT_INFO_OPEN_GITHUB_BUTTON"
|
||||
rel="noopener noreferrer" target="_blank">
|
||||
<x-slot name="icon">
|
||||
<svg viewBox="0 0 98 96" xmlns="http://www.w3.org/2000/svg">
|
||||
<path clip-rule="evenodd" fill="currentColor" fill-rule="evenodd"
|
||||
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" />
|
||||
</svg>
|
||||
</x-slot>
|
||||
|
||||
{{ __('filament-panels::widgets/filament-info-widget.actions.open_github.label') }}
|
||||
</x-filament::link>
|
||||
</div>
|
||||
</x-filament::section>
|
||||
</x-filament-widgets::widget>
|
||||
@@ -0,0 +1,54 @@
|
||||
@php
|
||||
use Filament\Support\Enums\VerticalAlignment;
|
||||
|
||||
$actions = $getChildSchema()->getComponents();
|
||||
$alignment = $getAlignment();
|
||||
$isFullWidth = $isFullWidth();
|
||||
$isSticky = $isSticky();
|
||||
$verticalAlignment = $getVerticalAlignment();
|
||||
|
||||
if (!$verticalAlignment instanceof VerticalAlignment) {
|
||||
$verticalAlignment = filled($verticalAlignment)
|
||||
? VerticalAlignment::tryFrom($verticalAlignment) ?? $verticalAlignment
|
||||
: null;
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div @if ($isSticky) x-data="filamentActionsSchemaComponent()"
|
||||
x-intersect:enter.half="disableSticky"
|
||||
x-intersect:leave="enableSticky"
|
||||
x-bind:class="{ 'fi-sticky': isSticky }" @endif
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $getId(),
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->class([
|
||||
'fi-sc-actions',
|
||||
$verticalAlignment instanceof VerticalAlignment
|
||||
? "fi-vertical-align-{$verticalAlignment->value}"
|
||||
: $verticalAlignment,
|
||||
]) }}>
|
||||
@if (filled($label = $getLabel()))
|
||||
<div class="fi-sc-actions-label-ctn">
|
||||
{{ $getChildSchema($schemaComponent::BEFORE_LABEL_SCHEMA_KEY) }}
|
||||
|
||||
<div class="fi-sc-actions-label">
|
||||
{{ $label }}
|
||||
</div>
|
||||
|
||||
{{ $getChildSchema($schemaComponent::AFTER_LABEL_SCHEMA_KEY) }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($aboveContentContainer = $getChildSchema($schemaComponent::ABOVE_CONTENT_SCHEMA_KEY)?->toHtmlString())
|
||||
{{ $aboveContentContainer }}
|
||||
@endif
|
||||
|
||||
<x-filament::actions :actions="$actions" :alignment="$alignment" :full-width="$isFullWidth"
|
||||
:x-bind:style="$isSticky ? 'isSticky ? `width: ${width}px;` : \'\'' : null" />
|
||||
|
||||
@if ($belowContentContainer = $getChildSchema($schemaComponent::BELOW_CONTENT_SCHEMA_KEY)?->toHtmlString())
|
||||
{{ $belowContentContainer }}
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
@php
|
||||
use Filament\Support\Enums\IconSize;
|
||||
|
||||
$controls = $getChildSchema($schemaComponent::CONTROLS_SCHEMA_KEY)?->toHtmlString();
|
||||
$extraAttributeBag = $getExtraAttributeBag();
|
||||
$footer = $getChildSchema($schemaComponent::FOOTER_SCHEMA_KEY)?->toHtmlString();
|
||||
$color = $getColor();
|
||||
$description = $getDescription();
|
||||
$heading = $getHeading();
|
||||
$icon = $getIcon();
|
||||
$iconColor = $getIconColor();
|
||||
$iconSize = $getIconSize() ?? IconSize::Large;
|
||||
@endphp
|
||||
|
||||
<x-filament::callout :attributes="\Filament\Support\prepare_inherited_attributes($extraAttributeBag)->class(['fi-sc-callout'])" :color="$color ?? 'gray'" :description="$description" :heading="$heading" :icon="$icon"
|
||||
:icon-color="$iconColor" :icon-size="$iconSize">
|
||||
<x-slot name="footer">
|
||||
{{ $footer }}
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="controls">
|
||||
{{ $controls }}
|
||||
</x-slot>
|
||||
</x-filament::callout>
|
||||
@@ -0,0 +1,16 @@
|
||||
@php
|
||||
$description = $getDescription();
|
||||
$footer = $getChildSchema($schemaComponent::FOOTER_SCHEMA_KEY)?->toHtmlString();
|
||||
$heading = $getHeading();
|
||||
$headingTag = $getHeadingTag();
|
||||
$icon = $getIcon();
|
||||
$iconColor = $getIconColor();
|
||||
$iconSize = $getIconSize();
|
||||
$isCompact = $isCompact();
|
||||
$isContained = $isContained();
|
||||
@endphp
|
||||
|
||||
<div {{ $attributes->merge($getExtraAttributes(), escape: false)->class(['fi-sc-empty-state']) }}>
|
||||
<x-filament::empty-state :compact="$isCompact" :contained="$isContained" :description="$description" :footer="$footer" :heading="$heading"
|
||||
:heading-tag="$headingTag" :icon="$icon" :icon-color="$iconColor" :icon-size="$iconSize" />
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
@php
|
||||
$extraAttributes = $getExtraAttributes();
|
||||
$id = $getId();
|
||||
$isLabelHidden = $isLabelHidden();
|
||||
$label = $getLabel();
|
||||
$isContained = $isContained();
|
||||
@endphp
|
||||
|
||||
<x-filament::fieldset :contained="$isContained" :label="$label" :label-hidden="$isLabelHidden" :required="isset($isMarkedAsRequired) ? $isMarkedAsRequired() : false" :attributes="\Filament\Support\prepare_inherited_attributes($attributes)
|
||||
->merge(
|
||||
[
|
||||
'id' => $id,
|
||||
],
|
||||
escape: false,
|
||||
)
|
||||
->merge($extraAttributes, escape: false)
|
||||
->class(['fi-sc-fieldset'])">
|
||||
{{ $getChildSchema() }}
|
||||
</x-filament::fieldset>
|
||||
@@ -0,0 +1,79 @@
|
||||
@php
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Schemas\Components\Component;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Support\Enums\VerticalAlignment;
|
||||
|
||||
$statePath = $getStatePath();
|
||||
|
||||
$fromBreakpoint = $getFromBreakpoint();
|
||||
$verticalAlignment = $getVerticalAlignment();
|
||||
$alignment = $getAlignment();
|
||||
|
||||
if (!$verticalAlignment instanceof VerticalAlignment) {
|
||||
$verticalAlignment = filled($verticalAlignment)
|
||||
? VerticalAlignment::tryFrom($verticalAlignment) ?? $verticalAlignment
|
||||
: null;
|
||||
}
|
||||
|
||||
if (!$alignment instanceof Alignment) {
|
||||
$alignment = filled($alignment) ? Alignment::tryFrom($alignment) ?? $alignment : null;
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div
|
||||
{{ $attributes->merge($getExtraAttributes(), escape: false)->class([
|
||||
'fi-sc-flex',
|
||||
'fi-dense' => $isDense(),
|
||||
'fi-from-' . ($fromBreakpoint ?? 'default'),
|
||||
$verticalAlignment instanceof VerticalAlignment
|
||||
? "fi-vertical-align-{$verticalAlignment->value}"
|
||||
: $verticalAlignment,
|
||||
$alignment instanceof Alignment ? "fi-align-{$alignment->value}" : $alignment,
|
||||
]) }}>
|
||||
@foreach ($getChildSchema()->getComponents() as $schemaComponent)
|
||||
@if ($schemaComponent instanceof Action || $schemaComponent instanceof ActionGroup)
|
||||
<div>
|
||||
{{ $schemaComponent }}
|
||||
</div>
|
||||
@else
|
||||
@php
|
||||
$hiddenJs = $schemaComponent->getHiddenJs();
|
||||
$visibleJs = $schemaComponent->getVisibleJs();
|
||||
|
||||
$schemaComponentStatePath = $schemaComponent->getStatePath();
|
||||
@endphp
|
||||
|
||||
<div x-data="filamentSchemaComponent({
|
||||
path: @js($schemaComponentStatePath),
|
||||
containerPath: @js($statePath),
|
||||
$wire,
|
||||
})" @if ($afterStateUpdatedJs = $schemaComponent->getAfterStateUpdatedJs()) x-init="{!! implode(
|
||||
';',
|
||||
array_map(
|
||||
fn(string $js): string => '$wire; $wire.watch(' .
|
||||
Js::from($schemaComponentStatePath) .
|
||||
', ($state, $old) => isStateChanged($state, $old) && eval(' .
|
||||
Js::from($js) .
|
||||
'))',
|
||||
$afterStateUpdatedJs,
|
||||
),
|
||||
) !!}" @endif
|
||||
@if (filled(
|
||||
$visibilityJs = match ([filled($hiddenJs), filled($visibleJs)]) {
|
||||
[true, true] => "(! ({$hiddenJs})) && ({$visibleJs})",
|
||||
[true, false] => "! ({$hiddenJs})",
|
||||
[false, true] => $visibleJs,
|
||||
default => null,
|
||||
})) x-bind:class="{ 'fi-hidden': ! ({!! $visibilityJs !!}) }"
|
||||
x-cloak @endif
|
||||
@class([
|
||||
'fi-growable' =>
|
||||
$schemaComponent instanceof Component && $schemaComponent->canGrow(),
|
||||
])>
|
||||
{{ $schemaComponent }}
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
<form
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $getId(),
|
||||
'wire:submit' => $getLivewireSubmitHandler(),
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->class(['fi-sc-form', 'fi-dense' => $isDense()]) }}>
|
||||
{{ $getChildSchema($schemaComponent::HEADER_SCHEMA_KEY) }}
|
||||
|
||||
{{ $getChildSchema() }}
|
||||
|
||||
{{ $getChildSchema($schemaComponent::FOOTER_SCHEMA_KEY) }}
|
||||
</form>
|
||||
@@ -0,0 +1,64 @@
|
||||
@php
|
||||
use Filament\Forms\Components\Contracts\HasNestedRecursiveValidationRules;
|
||||
use Filament\Forms\Components\Field;
|
||||
|
||||
$fieldWrapperView = $getFieldWrapperView();
|
||||
|
||||
$errorMessages = null;
|
||||
$errorMessage = null;
|
||||
|
||||
foreach ($getChildComponentContainer()->getComponents() as $childComponent) {
|
||||
if (!($childComponent instanceof Field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$statePath = $childComponent->getStatePath();
|
||||
|
||||
if (blank($statePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($errors->has($statePath)) {
|
||||
if ($childComponent->shouldShowAllValidationMessages()) {
|
||||
$errorMessages = $errors->get($statePath);
|
||||
$shouldShowAllValidationMessages = true;
|
||||
} else {
|
||||
$errorMessage = $errors->first($statePath);
|
||||
}
|
||||
|
||||
$areHtmlValidationMessagesAllowed = $childComponent->areHtmlValidationMessagesAllowed();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!($childComponent instanceof HasNestedRecursiveValidationRules)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($errors->has("{$statePath}.*")) {
|
||||
if ($childComponent->shouldShowAllValidationMessages()) {
|
||||
$errorMessages = $errors->get("{$statePath}.*");
|
||||
$shouldShowAllValidationMessages = true;
|
||||
} else {
|
||||
$errorMessage = $errors->first("{$statePath}.*");
|
||||
}
|
||||
|
||||
$areHtmlValidationMessagesAllowed = $childComponent->areHtmlValidationMessagesAllowed();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :error-message="$errorMessage" :error-messages="$errorMessages" :are-html-error-messages-allowed="$areHtmlValidationMessagesAllowed ?? false" :should-show-all-validation-messages="$shouldShowAllValidationMessages ?? false"
|
||||
:field="$schemaComponent">
|
||||
<div
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $getId(),
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->class(['fi-sc-fused-group']) }}>
|
||||
{{ $getChildSchema() }}
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
@@ -0,0 +1,9 @@
|
||||
<div
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $getId(),
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false) }}>
|
||||
{{ $getChildSchema() }}
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
@php
|
||||
use Filament\Support\Enums\Alignment;
|
||||
|
||||
$alignment = $getAlignment();
|
||||
$height = $getImageHeight() ?? '8rem';
|
||||
$width = $getImageWidth();
|
||||
$tooltip = $getTooltip();
|
||||
|
||||
if (!$alignment instanceof Alignment) {
|
||||
$alignment = filled($alignment) ? Alignment::tryFrom($alignment) ?? $alignment : null;
|
||||
}
|
||||
@endphp
|
||||
|
||||
<img alt="{{ $getAlt() }}" src="{{ $getUrl() }}"
|
||||
@if (filled($tooltip)) x-tooltip="{ content: @js($tooltip), theme: $store.theme, allowHTML: @js($tooltip instanceof \Illuminate\Contracts\Support\Htmlable) }" @endif
|
||||
{{ $getExtraAttributeBag()->class(['fi-sc-image', $alignment instanceof Alignment ? "fi-align-{$alignment->value}" : $alignment])->style([
|
||||
'height: ' . e($height) => $height,
|
||||
'width: ' . e($width) => $width,
|
||||
]) }} />
|
||||
@@ -0,0 +1,26 @@
|
||||
@php
|
||||
$extraAttributes = $getExtraAttributes();
|
||||
$id = $getId();
|
||||
@endphp
|
||||
|
||||
@if (filled($id) || filled($extraAttributes))
|
||||
{!! '<div' !!}
|
||||
{{-- Avoid formatting issues with unclosed elements --}}
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $id,
|
||||
],
|
||||
escape: false,
|
||||
)->merge($extraAttributes, escape: false) }}
|
||||
>
|
||||
@endif
|
||||
|
||||
@if (filled($key = $getLivewireKey()))
|
||||
@livewire($getComponent(), $getComponentProperties(), key($key))
|
||||
@else
|
||||
@livewire($getComponent(), $getComponentProperties())
|
||||
@endif
|
||||
@if (filled($id) || filled($extraAttributes))
|
||||
{!! '</div>' !!}
|
||||
{{-- Avoid formatting issues with unclosed elements --}}
|
||||
@endif
|
||||
@@ -0,0 +1,69 @@
|
||||
@php
|
||||
use Illuminate\Support\Js;
|
||||
|
||||
$afterHeader = $getChildSchema($schemaComponent::AFTER_HEADER_SCHEMA_KEY)?->toHtmlString();
|
||||
$isAside = $isAside();
|
||||
$isCollapsed = $isCollapsed();
|
||||
$isCollapsible = $isCollapsible();
|
||||
$isCompact = $isCompact();
|
||||
$isContained = $isContained();
|
||||
$isDivided = $isDivided();
|
||||
$isFormBefore = $isFormBefore();
|
||||
$description = $getDescription();
|
||||
$footer = $getChildSchema($schemaComponent::FOOTER_SCHEMA_KEY)?->toHtmlString();
|
||||
$heading = $getHeading();
|
||||
$headingTag = $getHeadingTag();
|
||||
$icon = $getIcon();
|
||||
$iconColor = $getIconColor();
|
||||
$iconSize = $getIconSize();
|
||||
$shouldPersistCollapsed = $shouldPersistCollapsed();
|
||||
$isSecondary = $isSecondary();
|
||||
$id = $getId();
|
||||
|
||||
$stateResetHandler =
|
||||
$isCollapsible && !$isAside && !$shouldPersistCollapsed
|
||||
? 'if (($event.detail.livewireId === ' .
|
||||
Js::from($this->getId()) .
|
||||
') && ($event.detail.schemaKey === ' .
|
||||
Js::from($getRootContainer()->getKey()) .
|
||||
')) $nextTick(() => isCollapsed = ' .
|
||||
Js::from($isCollapsed) .
|
||||
')'
|
||||
: null;
|
||||
@endphp
|
||||
|
||||
<div
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $id,
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->merge($getExtraAlpineAttributes(), escape: false)->class(['fi-sc-section']) }}>
|
||||
@if (filled($label = $getLabel()))
|
||||
<div class="fi-sc-section-label-ctn">
|
||||
{{ $getChildSchema($schemaComponent::BEFORE_LABEL_SCHEMA_KEY) }}
|
||||
|
||||
<div class="fi-sc-section-label">
|
||||
{{ $label }}
|
||||
</div>
|
||||
|
||||
{{ $getChildSchema($schemaComponent::AFTER_LABEL_SCHEMA_KEY) }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($aboveContentContainer = $getChildSchema($schemaComponent::ABOVE_CONTENT_SCHEMA_KEY)?->toHtmlString())
|
||||
{{ $aboveContentContainer }}
|
||||
@endif
|
||||
|
||||
<x-filament::section :x-on:reset-schema-component-state.window="$stateResetHandler" :after-header="$afterHeader"
|
||||
:aside="$isAside" :collapsed="$isCollapsed" :collapse-id="$id" :collapsible="$isCollapsible && !$isAside" :compact="$isCompact" :contained="$isContained"
|
||||
:content-before="$isFormBefore" :description="$description" :divided="$isDivided" :footer="$footer" :has-content-el="false" :heading="$heading"
|
||||
:heading-tag="$headingTag" :icon="$icon" :icon-color="$iconColor" :icon-size="$iconSize" :persist-collapsed="$shouldPersistCollapsed"
|
||||
:secondary="$isSecondary">
|
||||
{{ $getChildSchema()->extraAttributes(['class' => 'fi-section-content']) }}
|
||||
</x-filament::section>
|
||||
|
||||
@if ($belowContentContainer = $getChildSchema($schemaComponent::BELOW_CONTENT_SCHEMA_KEY)?->toHtmlString())
|
||||
{{ $belowContentContainer }}
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,256 @@
|
||||
@php
|
||||
use Filament\Schemas\Components\Tabs\Tab;
|
||||
use Filament\Schemas\View\SchemaIconAlias;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
|
||||
$activeTab = $getActiveTab();
|
||||
$hasDeferredBadges = $hasDeferredBadges();
|
||||
$id = $getId();
|
||||
$isContained = $isContained();
|
||||
$isScrollable = $isScrollable();
|
||||
$isVertical = $isVertical();
|
||||
$label = $getLabel();
|
||||
$livewireProperty = $getLivewireProperty();
|
||||
$renderHookScopes = $getRenderHookScopes();
|
||||
$tabs = $getChildSchema()->getComponents();
|
||||
$tabsKey = $getKey();
|
||||
|
||||
$getTabVisibilityJs = function (Tab $tab, ?int $index = null, ?string $mode = null) use ($isScrollable): ?string {
|
||||
$hiddenJs = $tab->getHiddenJs();
|
||||
$visibleJs = $tab->getVisibleJs();
|
||||
|
||||
$baseJs = match ([filled($hiddenJs), filled($visibleJs)]) {
|
||||
[true, true] => "(! ({$hiddenJs})) && ({$visibleJs})",
|
||||
[true, false] => "! ({$hiddenJs})",
|
||||
[false, true] => $visibleJs,
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($isScrollable || $index === null || $mode === null) {
|
||||
return $baseJs;
|
||||
}
|
||||
|
||||
$tabKey = $tab->getKey(isAbsolute: false);
|
||||
|
||||
$dropdownJs = match ($mode) {
|
||||
'inline' => "(!withinDropdownMounted || withinDropdownIndex === null || {$index} < withinDropdownIndex)",
|
||||
'trigger'
|
||||
=> "(withinDropdownMounted && withinDropdownIndex !== null && {$index} >= withinDropdownIndex && '{$tabKey}' === tab)",
|
||||
default => null,
|
||||
};
|
||||
|
||||
return $baseJs ? "{$baseJs} && {$dropdownJs}" : $dropdownJs;
|
||||
};
|
||||
@endphp
|
||||
|
||||
@if (blank($livewireProperty))
|
||||
<div x-data="tabsSchemaComponent({
|
||||
activeTab: @js($activeTab),
|
||||
isScrollable: @js($isScrollable),
|
||||
isTabPersisted: @js($isTabPersisted()),
|
||||
isTabPersistedInQueryString: @js($isTabPersistedInQueryString()),
|
||||
livewireId: @js($this->getId()),
|
||||
schemaKey: @js($getRootContainer()->getKey()),
|
||||
tab: @if ($isTabPersisted() && filled($id)) $persist(null).as(@js($id)) @else @js(null) @endif,
|
||||
tabQueryStringKey: @js($getTabQueryStringKey()),
|
||||
})" x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('tabs', 'filament/schemas') }}"
|
||||
wire:ignore.self
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $id,
|
||||
'wire:key' => $getLivewireKey() . '.container',
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->merge($getExtraAlpineAttributes(), escape: false)->class(['fi-sc-tabs', 'fi-contained' => $isContained, 'fi-vertical' => $isVertical]) }}>
|
||||
<input type="hidden"
|
||||
value="{{ collect($tabs)->filter(static fn(Tab $tab): bool => $tab->isVisible())->map(static fn(Tab $tab) => $tab->getKey(isAbsolute: false))->values()->toJson() }}"
|
||||
x-ref="tabsData" />
|
||||
|
||||
<x-filament::tabs :contained="$isContained" :label="$label" :vertical="$isVertical" x-cloak
|
||||
:x-bind:class="!$isScrollable ? '{ \'fi-invisible\': ! withinDropdownMounted }' : null" :x-data="$hasDeferredBadges ? '{
|
||||
deferredBadges: {},
|
||||
isLoadingDeferredBadges: true,
|
||||
|
||||
async init() {
|
||||
try {
|
||||
const badges = await $wire.callSchemaComponentMethod(' . \Illuminate\Support\Js::from($tabsKey) . ', \'getDeferredTabBadges\')
|
||||
this.deferredBadges = badges ?? {}
|
||||
}
|
||||
finally {
|
||||
this.isLoadingDeferredBadges = false
|
||||
}
|
||||
},
|
||||
}
|
||||
' : null">
|
||||
@foreach ($getStartRenderHooks() as $startRenderHook)
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook($startRenderHook, scopes: $renderHookScopes) }}
|
||||
@endforeach
|
||||
|
||||
@foreach ($tabs as $index => $tab)
|
||||
@php
|
||||
$isTabBadgeDeferred = $tab->isBadgeDeferred();
|
||||
$tabBadge = $isTabBadgeDeferred ? null : $tab->getBadge();
|
||||
$tabBadgeColor = $isTabBadgeDeferred ? null : $tab->getBadgeColor($tabBadge);
|
||||
$tabBadgeIcon = $isTabBadgeDeferred ? null : $tab->getBadgeIcon($tabBadge);
|
||||
$tabBadgeIconPosition = $isTabBadgeDeferred ? null : $tab->getBadgeIconPosition($tabBadge);
|
||||
$tabBadgeTooltip = $isTabBadgeDeferred ? null : $tab->getBadgeTooltip($tabBadge);
|
||||
$tabExtraAttributeBag = $tab->getExtraAttributeBag();
|
||||
$tabIcon = $tab->getIcon();
|
||||
$tabIconPosition = $tab->getIconPosition();
|
||||
$tabKey = $tab->getKey(isAbsolute: false);
|
||||
$tabLabel = $tab->getLabel();
|
||||
$tabVisibilityJs = $getTabVisibilityJs($tab, $index, 'inline');
|
||||
@endphp
|
||||
|
||||
<x-filament::tabs.item :alpine-active="'tab === \'' . $tabKey . '\''" :alpine-deferred-badge-data="$isTabBadgeDeferred ? 'deferredBadges[' . \Illuminate\Support\Js::from($index) . ']' : null" :alpine-deferred-badge-loading="$isTabBadgeDeferred ? 'isLoadingDeferredBadges' : null" :attributes="$tabExtraAttributeBag"
|
||||
:badge="$tabBadge" :badge-color="$tabBadgeColor" :badge-icon="$tabBadgeIcon" :badge-icon-position="$tabBadgeIconPosition" :badge-tooltip="$tabBadgeTooltip"
|
||||
:data-tab-key="$tabKey" :icon="$tabIcon" :icon-position="$tabIconPosition" :x-cloak="$tabVisibilityJs !== null"
|
||||
:x-on:click="'tab = \'' . $tabKey . '\''" :x-show="$tabVisibilityJs">
|
||||
{{ $tabLabel }}
|
||||
</x-filament::tabs.item>
|
||||
@endforeach
|
||||
|
||||
@if (!$isScrollable)
|
||||
<x-filament::dropdown :placement="__('filament-panels::layout.direction') === 'ltr' ? 'bottom-start' : 'bottom-end'">
|
||||
<x-slot name="trigger">
|
||||
@foreach ($tabs as $index => $tab)
|
||||
@php
|
||||
$isTabBadgeDeferred = $tab->isBadgeDeferred();
|
||||
$tabBadge = $isTabBadgeDeferred ? null : $tab->getBadge();
|
||||
$tabBadgeColor = $isTabBadgeDeferred ? null : $tab->getBadgeColor($tabBadge);
|
||||
$tabBadgeTooltip = $isTabBadgeDeferred ? null : $tab->getBadgeTooltip($tabBadge);
|
||||
$tabExtraAttributeBag = $tab->getExtraAttributeBag();
|
||||
$tabKey = $tab->getKey(isAbsolute: false);
|
||||
$tabLabel = $tab->getLabel();
|
||||
$tabVisibilityJs = $getTabVisibilityJs($tab, $index, 'trigger');
|
||||
@endphp
|
||||
|
||||
<x-filament::tabs.item :alpine-active="'tab === \'' . $tabKey . '\''" :alpine-deferred-badge-data="$isTabBadgeDeferred
|
||||
? 'deferredBadges[' . \Illuminate\Support\Js::from($index) . ']'
|
||||
: null" :alpine-deferred-badge-loading="$isTabBadgeDeferred ? 'isLoadingDeferredBadges' : null"
|
||||
:attributes="$tabExtraAttributeBag" :badge="$tabBadge" :badge-color="$tabBadgeColor" :badge-tooltip="$tabBadgeTooltip"
|
||||
:icon="Heroicon::ChevronDown" :icon-alias="SchemaIconAlias::COMPONENTS_TABS_DROPDOWN_TRIGGER_BUTTON" :x-cloak="$tabVisibilityJs !== null" :x-show="$tabVisibilityJs">
|
||||
{{ $tabLabel }}
|
||||
</x-filament::tabs.item>
|
||||
@endforeach
|
||||
|
||||
<x-filament::tabs.item x-show="isDropdownButtonVisible">
|
||||
{{ \Filament\Support\generate_icon_html(
|
||||
Heroicon::EllipsisHorizontal,
|
||||
alias: SchemaIconAlias::COMPONENTS_TABS_MORE_TABS_BUTTON,
|
||||
) }}
|
||||
</x-filament::tabs.item>
|
||||
</x-slot>
|
||||
|
||||
<x-filament::dropdown.list>
|
||||
@foreach ($tabs as $index => $tab)
|
||||
@php
|
||||
$isTabBadgeDeferred = $tab->isBadgeDeferred();
|
||||
$tabBadge = $isTabBadgeDeferred ? null : $tab->getBadge();
|
||||
$tabBadgeColor = $isTabBadgeDeferred ? null : $tab->getBadgeColor($tabBadge);
|
||||
$tabBadgeTooltip = $isTabBadgeDeferred ? null : $tab->getBadgeTooltip($tabBadge);
|
||||
$tabIcon = $tab->getIcon();
|
||||
$tabKey = $tab->getKey(isAbsolute: false);
|
||||
$tabLabel = $tab->getLabel();
|
||||
@endphp
|
||||
|
||||
<x-filament::dropdown.list.item :alpine-deferred-badge-data="$isTabBadgeDeferred
|
||||
? 'deferredBadges[' . \Illuminate\Support\Js::from($index) . ']'
|
||||
: null" :alpine-deferred-badge-loading="$isTabBadgeDeferred ? 'isLoadingDeferredBadges' : null" :badge="$tabBadge"
|
||||
:badge-color="$tabBadgeColor" :badge-tooltip="$tabBadgeTooltip" :icon="$tabIcon"
|
||||
x-bind:class="{ 'fi-selected': tab === '{{ $tabKey }}' }"
|
||||
:x-on:click="'tab = \'' . $tabKey . '\'; close($event);'" :x-show="$index . ' >= withinDropdownIndex'">
|
||||
{{ $tabLabel }}
|
||||
</x-filament::dropdown.list.item>
|
||||
@endforeach
|
||||
</x-filament::dropdown.list>
|
||||
</x-filament::dropdown>
|
||||
@endif
|
||||
|
||||
@foreach ($getEndRenderHooks() as $endRenderHook)
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook($endRenderHook, scopes: $renderHookScopes) }}
|
||||
@endforeach
|
||||
</x-filament::tabs>
|
||||
|
||||
@foreach ($tabs as $tab)
|
||||
@php
|
||||
$tabVisibilityJs = $getTabVisibilityJs($tab);
|
||||
@endphp
|
||||
|
||||
@if ($tabVisibilityJs)
|
||||
<div x-cloak x-show="{!! $tabVisibilityJs !!}">
|
||||
{{ $tab }}
|
||||
</div>
|
||||
@else
|
||||
{{ $tab }}
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
@php
|
||||
$activeTab = strval($this->{$livewireProperty});
|
||||
@endphp
|
||||
|
||||
<div @if ($hasDeferredBadges) x-data="{
|
||||
deferredBadges: {},
|
||||
isLoadingDeferredBadges: true,
|
||||
|
||||
async init() {
|
||||
try {
|
||||
const badges = await $wire.callSchemaComponentMethod(
|
||||
@js($tabsKey),
|
||||
'getDeferredTabBadges',
|
||||
)
|
||||
|
||||
this.deferredBadges = badges ?? {}
|
||||
} finally {
|
||||
this.isLoadingDeferredBadges = false
|
||||
}
|
||||
},
|
||||
}" @endif
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $id,
|
||||
'wire:key' => $getLivewireKey() . '.container',
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->class(['fi-sc-tabs', 'fi-contained' => $isContained, 'fi-vertical' => $isVertical]) }}>
|
||||
<x-filament::tabs :contained="$isContained" :label="$label" :vertical="$isVertical">
|
||||
@foreach ($getStartRenderHooks() as $startRenderHook)
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook($startRenderHook, scopes: $renderHookScopes) }}
|
||||
@endforeach
|
||||
|
||||
@foreach ($getChildSchema()->getComponents(withOriginalKeys: true) as $tabKey => $tab)
|
||||
@php
|
||||
$isTabBadgeDeferred = $tab->isBadgeDeferred();
|
||||
$tabBadge = $isTabBadgeDeferred ? null : $tab->getBadge();
|
||||
$tabBadgeColor = $isTabBadgeDeferred ? null : $tab->getBadgeColor($tabBadge);
|
||||
$tabBadgeIcon = $isTabBadgeDeferred ? null : $tab->getBadgeIcon($tabBadge);
|
||||
$tabBadgeIconPosition = $isTabBadgeDeferred ? null : $tab->getBadgeIconPosition($tabBadge);
|
||||
$tabBadgeTooltip = $isTabBadgeDeferred ? null : $tab->getBadgeTooltip($tabBadge);
|
||||
$tabExtraAttributeBag = $tab->getExtraAttributeBag();
|
||||
$tabIcon = $tab->getIcon();
|
||||
$tabIconPosition = $tab->getIconPosition();
|
||||
$tabKey = strval($tabKey);
|
||||
$tabLabel = $tab->getLabel() ?? $this->generateTabLabel($tabKey);
|
||||
@endphp
|
||||
|
||||
<x-filament::tabs.item :active="$activeTab === $tabKey" :alpine-deferred-badge-data="$isTabBadgeDeferred ? 'deferredBadges[' . \Illuminate\Support\Js::from($tabKey) . ']' : null" :alpine-deferred-badge-loading="$isTabBadgeDeferred ? 'isLoadingDeferredBadges' : null" :attributes="$tabExtraAttributeBag"
|
||||
:badge="$tabBadge" :badge-color="$tabBadgeColor" :badge-icon="$tabBadgeIcon" :badge-icon-position="$tabBadgeIconPosition" :badge-tooltip="$tabBadgeTooltip"
|
||||
:icon="$tabIcon" :icon-position="$tabIconPosition"
|
||||
:wire:click="'$set(\'' . $livewireProperty . '\', ' . (filled($tabKey) ? ('\'' . $tabKey . '\'') : 'null') . ')'">
|
||||
{{ $tabLabel }}
|
||||
</x-filament::tabs.item>
|
||||
@endforeach
|
||||
|
||||
@foreach ($getEndRenderHooks() as $endRenderHook)
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook($endRenderHook, scopes: $renderHookScopes) }}
|
||||
@endforeach
|
||||
</x-filament::tabs>
|
||||
|
||||
@foreach ($getChildSchema()->getComponents(withOriginalKeys: true) as $tabKey => $tab)
|
||||
{{ $tab->key($tabKey) }}
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,42 @@
|
||||
@php
|
||||
$id = $getId();
|
||||
$key = $getKey(isAbsolute: false);
|
||||
$tabs = $getContainer()->getParentComponent();
|
||||
$isContained = $tabs->isContained();
|
||||
$livewireProperty = $tabs->getLivewireProperty();
|
||||
|
||||
$childSchema = $getChildSchema();
|
||||
@endphp
|
||||
|
||||
@if (!empty($childSchema->getComponents()))
|
||||
@if (blank($livewireProperty))
|
||||
<div x-bind:class="{
|
||||
'fi-active': tab === @js($key),
|
||||
}"
|
||||
x-on:expand="tab = @js($key)"
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'aria-labelledby' => $id,
|
||||
'id' => $id,
|
||||
'role' => 'tabpanel',
|
||||
'wire:key' => $getLivewireKey() . '.container',
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->class(['fi-sc-tabs-tab']) }}>
|
||||
{{ $childSchema }}
|
||||
</div>
|
||||
@elseif (strval($this->{$livewireProperty}) === strval($key))
|
||||
<div
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'aria-labelledby' => $id,
|
||||
'id' => $id,
|
||||
'role' => 'tabpanel',
|
||||
'wire:key' => $getLivewireKey() . '.container',
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->class(['fi-sc-tabs-tab fi-active']) }}>
|
||||
{{ $childSchema }}
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
@@ -0,0 +1,59 @@
|
||||
@php
|
||||
use Filament\Schemas\View\Components\TextComponent;
|
||||
use Filament\Support\Enums\FontFamily;
|
||||
use Filament\Support\Enums\FontWeight;
|
||||
use Filament\Support\RawJs;
|
||||
|
||||
$color = $getColor();
|
||||
$content = $getContent();
|
||||
$icon = $getIcon();
|
||||
$iconPosition = $getIconPosition();
|
||||
$iconSize = $getIconSize();
|
||||
$size = $getSize();
|
||||
$tooltip = $getTooltip();
|
||||
$weight = $getWeight();
|
||||
$fontFamily = $getFontFamily();
|
||||
|
||||
$copyableState = $getCopyableState($content) ?? $content;
|
||||
$copyMessage = $getCopyMessage($copyableState);
|
||||
$copyMessageDuration = $getCopyMessageDuration($copyableState);
|
||||
$isCopyable = $isCopyable($copyableState);
|
||||
@endphp
|
||||
|
||||
@if ($isBadge())
|
||||
<x-filament::badge :color="$color" :icon="$icon" :icon-position="$iconPosition" :icon-size="$iconSize" :size="$size instanceof \Filament\Support\Enums\TextSize ? $size->value : $size"
|
||||
:x-on:click="
|
||||
$isCopyable ? '
|
||||
window.navigator.clipboard.writeText(' . \Illuminate\Support\Js::from($copyableState) . ')
|
||||
$tooltip(' . \Illuminate\Support\Js::from($copyMessage) . ', {
|
||||
theme: $store.theme,
|
||||
timeout: ' . \Illuminate\Support\Js::from($copyMessageDuration) . ',
|
||||
})
|
||||
' : null
|
||||
" :tag="$isCopyable ? 'button' : 'span'" :tooltip="$tooltip" :attributes="\Filament\Support\prepare_inherited_attributes($getExtraAttributeBag()->class(['fi-sc-text']))">
|
||||
{{ $content }}
|
||||
</x-filament::badge>
|
||||
@else
|
||||
<span
|
||||
@if ($isCopyable) x-on:click="
|
||||
window.navigator.clipboard.writeText(@js($copyableState))
|
||||
$tooltip(@js($copyMessage), {
|
||||
theme: $store.theme,
|
||||
timeout: @js($copyMessageDuration),
|
||||
})
|
||||
" @endif
|
||||
@if (filled($tooltip)) x-tooltip="{
|
||||
content: @js($tooltip),
|
||||
theme: $store.theme,
|
||||
allowHTML: @js($tooltip instanceof \Illuminate\Contracts\Support\Htmlable),
|
||||
}" @endif
|
||||
{{ new \Illuminate\View\ComponentAttributeBag()->color(TextComponent::class, $color)->class([
|
||||
'fi-sc-text',
|
||||
'fi-copyable' => $isCopyable,
|
||||
$size instanceof \BackedEnum ? "fi-size-{$size->value}" : $size,
|
||||
$weight instanceof FontWeight ? "fi-font-{$weight->value}" : $weight,
|
||||
$fontFamily instanceof FontFamily ? "fi-font-{$fontFamily->value}" : $fontFamily,
|
||||
])->merge($getExtraAttributes(), escape: false) }}>
|
||||
{{ $content }}
|
||||
</span>
|
||||
@endif
|
||||
@@ -0,0 +1,11 @@
|
||||
<ul
|
||||
{{ $getExtraAttributeBag()->grid($getColumns(), \Filament\Support\Enums\GridDirection::Column)->class([
|
||||
'fi-sc-unordered-list',
|
||||
($size = $getSize()) instanceof \Filament\Support\Enums\TextSize ? "fi-size-{$size->value}" : $size,
|
||||
]) }}>
|
||||
@foreach ($getChildSchema()->getComponents() as $schemaComponent)
|
||||
<li>
|
||||
{{ $schemaComponent }}
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@@ -0,0 +1,129 @@
|
||||
@php
|
||||
$isContained = $isContained();
|
||||
$key = $getKey();
|
||||
$previousAction = $getAction('previous');
|
||||
$nextAction = $getAction('next');
|
||||
$steps = $getChildSchema()->getComponents();
|
||||
$isHeaderHidden = $isHeaderHidden();
|
||||
@endphp
|
||||
|
||||
<div x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('wizard', 'filament/schemas') }}"
|
||||
x-data="wizardSchemaComponent({
|
||||
isSkippable: @js($isSkippable()),
|
||||
isStepPersistedInQueryString: @js($isStepPersistedInQueryString()),
|
||||
key: @js($key),
|
||||
livewireId: @js($this->getId()),
|
||||
schemaKey: @js($getRootContainer()->getKey()),
|
||||
startStep: @js($getStartStep()),
|
||||
stepQueryStringKey: @js($getStepQueryStringKey()),
|
||||
})"
|
||||
x-on:next-wizard-step.window="if ($event.detail.key === @js($key)) goToNextStep()"
|
||||
x-on:go-to-wizard-step.window="$event.detail.key === @js($key) && goToStep($event.detail.step)"
|
||||
wire:ignore.self
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'id' => $getId(),
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->merge($getExtraAlpineAttributes(), escape: false)->class(['fi-sc-wizard', 'fi-contained' => $isContained, 'fi-sc-wizard-header-hidden' => $isHeaderHidden]) }}>
|
||||
<input type="hidden"
|
||||
value="{{ collect($steps)->filter(static fn(\Filament\Schemas\Components\Wizard\Step $step): bool => $step->isVisible())->map(static fn(\Filament\Schemas\Components\Wizard\Step $step): ?string => $step->getKey())->values()->toJson() }}"
|
||||
x-ref="stepsData" />
|
||||
|
||||
@if (!$isHeaderHidden)
|
||||
<ol @if (filled($label = $getLabel())) aria-label="{{ $label }}" @endif role="list" x-cloak
|
||||
x-ref="header" class="fi-sc-wizard-header">
|
||||
@foreach ($steps as $step)
|
||||
<li class="fi-sc-wizard-header-step"
|
||||
x-bind:class="{
|
||||
'fi-active': getStepIndex(step) === {{ $loop->index }},
|
||||
'fi-completed': getStepIndex(step) > {{ $loop->index }},
|
||||
}">
|
||||
<button type="button"
|
||||
x-bind:aria-current="getStepIndex(step) === {{ $loop->index }} ? 'step' : null"
|
||||
x-on:click="step = @js($step->getKey())"
|
||||
x-bind:disabled="!isStepAccessible(@js($step->getKey())) || @js($previousAction->isDisabled())"
|
||||
class="fi-sc-wizard-header-step-btn">
|
||||
<div class="fi-sc-wizard-header-step-icon-ctn">
|
||||
@php
|
||||
$completedIcon = $step->getCompletedIcon();
|
||||
@endphp
|
||||
|
||||
{{ \Filament\Support\generate_icon_html(
|
||||
$completedIcon ?? \Filament\Support\Icons\Heroicon::OutlinedCheck,
|
||||
alias: filled($completedIcon) ? null : \Filament\Schemas\View\SchemaIconAlias::COMPONENTS_WIZARD_COMPLETED_STEP,
|
||||
attributes: new \Illuminate\View\ComponentAttributeBag([
|
||||
'x-cloak' => 'x-cloak',
|
||||
'x-show' => "getStepIndex(step) > {$loop->index}",
|
||||
]),
|
||||
size: \Filament\Support\Enums\IconSize::Large,
|
||||
) }}
|
||||
|
||||
@if (filled($icon = $step->getIcon()))
|
||||
{{ \Filament\Support\generate_icon_html(
|
||||
$icon,
|
||||
attributes: new \Illuminate\View\ComponentAttributeBag([
|
||||
'x-cloak' => 'x-cloak',
|
||||
'x-show' => "getStepIndex(step) <= {$loop->index}",
|
||||
]),
|
||||
size: \Filament\Support\Enums\IconSize::Large,
|
||||
) }}
|
||||
@else
|
||||
<span x-show="getStepIndex(step) <= {{ $loop->index }}"
|
||||
class="fi-sc-wizard-header-step-number">
|
||||
{{ str_pad($loop->index + 1, 2, '0', STR_PAD_LEFT) }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="fi-sc-wizard-header-step-text">
|
||||
@if (!$step->isLabelHidden())
|
||||
<span class="fi-sc-wizard-header-step-label">
|
||||
{{ $step->getLabel() }}
|
||||
</span>
|
||||
@endif
|
||||
|
||||
@if (filled($description = $step->getDescription()))
|
||||
<span class="fi-sc-wizard-header-step-description">
|
||||
{{ $description }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@if (!$loop->last)
|
||||
<svg fill="none" preserveAspectRatio="none" viewBox="0 0 22 80" aria-hidden="true"
|
||||
class="fi-sc-wizard-header-step-separator">
|
||||
<path d="M0 -2L20 40L0 82" stroke-linejoin="round" stroke="currentcolor"
|
||||
vector-effect="non-scaling-stroke"></path>
|
||||
</svg>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ol>
|
||||
@endif
|
||||
|
||||
@foreach ($steps as $step)
|
||||
{{ $step }}
|
||||
@endforeach
|
||||
|
||||
<div x-cloak class="fi-sc-wizard-footer">
|
||||
<div x-cloak @if (!$previousAction->isDisabled()) x-on:click="goToPreviousStep" @endif x-show="! isFirstStep()">
|
||||
{{ $previousAction }}
|
||||
</div>
|
||||
|
||||
<div x-show="isFirstStep()">
|
||||
{{ $getCancelAction() }}
|
||||
</div>
|
||||
|
||||
<div x-cloak @if (!$nextAction->isDisabled()) x-on:click="requestNextStep()" @endif
|
||||
x-bind:class="{ 'fi-hidden': isLastStep() }" wire:loading.class="fi-disabled">
|
||||
{{ $nextAction }}
|
||||
</div>
|
||||
|
||||
<div x-bind:class="{ 'fi-hidden': !isLastStep() }">
|
||||
{{ $getSubmitAction() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,36 @@
|
||||
@php
|
||||
$id = $getId();
|
||||
$key = $getKey();
|
||||
$wizard = $getContainer()->getParentComponent();
|
||||
$isContained = $wizard->isContained();
|
||||
$alpineSubmitHandler = $hasFormWrapper() ? $wizard->getAlpineSubmitHandler() : null;
|
||||
@endphp
|
||||
|
||||
<{{ filled($alpineSubmitHandler) ? 'form' : 'div' }} x-bind:tabindex="$el.querySelector('[autofocus]') ? '-1' : '0'"
|
||||
x-bind:class="{
|
||||
'fi-active': step === @js($key),
|
||||
}"
|
||||
x-on:expand="
|
||||
if (! isStepAccessible(@js($key))) {
|
||||
return
|
||||
}
|
||||
|
||||
step = @js($key)
|
||||
"
|
||||
@if (filled($alpineSubmitHandler)) x-on:submit.prevent="isLastStep() ? {!! $alpineSubmitHandler !!} : requestNextStep()" @endif
|
||||
x-cloak x-ref="step-{{ $key }}"
|
||||
{{ $attributes->merge(
|
||||
[
|
||||
'aria-labelledby' => $id,
|
||||
'id' => $id,
|
||||
'role' => 'tabpanel',
|
||||
],
|
||||
escape: false,
|
||||
)->merge($getExtraAttributes(), escape: false)->class(['fi-sc-wizard-step']) }}>
|
||||
{{ $getChildSchema() }}
|
||||
|
||||
@if (filled($alpineSubmitHandler))
|
||||
{{-- This is a hack to allow the form to submit when the user presses the enter key, even if there is no other submit button in the form. --}}
|
||||
<input type="submit" hidden />
|
||||
@endif
|
||||
</{{ filled($alpineSubmitHandler) ? 'form' : 'div' }}>
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
@php
|
||||
use Filament\Support\Enums\GridDirection;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'columns' => null,
|
||||
'hasReorderableColumns',
|
||||
'hasToggleableColumns',
|
||||
'reorderAnimationDuration' => 300,
|
||||
])
|
||||
|
||||
<div @if ($hasReorderableColumns) x-sortable
|
||||
x-on:end.stop="reorderColumns($event.target.sortable.toArray())"
|
||||
data-sortable-animation-duration="{{ $reorderAnimationDuration }}" @endif
|
||||
{{ new ComponentAttributeBag()->grid($columns, GridDirection::Column)->class(['fi-ta-col-manager-items']) }}>
|
||||
<template x-for="(column, index) in columns.filter((column) => ! column.isHidden && column.label)"
|
||||
x-bind:key="(column.type === 'group' ? 'group::' : 'column::') + column.name + '_' + index">
|
||||
<div
|
||||
@if ($hasReorderableColumns) x-bind:x-sortable-item="column.type === 'group' ? 'group::' + column.name : 'column::' + column.name" @endif>
|
||||
<template x-if="column.type === 'group'">
|
||||
<div class="fi-ta-col-manager-group">
|
||||
<div class="fi-ta-col-manager-item">
|
||||
<label class="fi-ta-col-manager-label">
|
||||
@if ($hasToggleableColumns)
|
||||
<input type="checkbox" class="fi-checkbox-input fi-valid"
|
||||
x-bind:id="'group-' + column.name"
|
||||
x-bind:checked="(groupedColumns[column.name] || {}).checked || false"
|
||||
x-bind:disabled="(groupedColumns[column.name] || {}).disabled || false"
|
||||
x-effect="$el.indeterminate = (groupedColumns[column.name] || {}).indeterminate || false"
|
||||
x-on:change="toggleGroup(column.name)" />
|
||||
@endif
|
||||
|
||||
<span x-html="column.label"></span>
|
||||
</label>
|
||||
|
||||
@if ($hasReorderableColumns)
|
||||
<button x-sortable-handle x-on:click.stop
|
||||
class="fi-ta-col-manager-reorder-handle fi-icon-btn" type="button">
|
||||
{{ \Filament\Support\generate_icon_html(\Filament\Support\Icons\Heroicon::Bars2, alias: \Filament\Tables\View\TablesIconAlias::REORDER_HANDLE) }}
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
<div @if ($hasReorderableColumns) x-sortable
|
||||
x-on:end.stop="reorderGroupColumns($event.target.sortable.toArray(), column.name)"
|
||||
data-sortable-animation-duration="{{ $reorderAnimationDuration }}" @endif
|
||||
class="fi-ta-col-manager-group-items">
|
||||
<template
|
||||
x-for="
|
||||
(groupColumn, index) in
|
||||
column.columns.filter((column) => ! column.isHidden && column.label)
|
||||
"
|
||||
x-bind:key="'column::' + groupColumn.name + '_' + index">
|
||||
<div
|
||||
@if ($hasReorderableColumns) x-bind:x-sortable-item="'column::' + groupColumn.name" @endif>
|
||||
<div class="fi-ta-col-manager-item">
|
||||
<label class="fi-ta-col-manager-label">
|
||||
@if ($hasToggleableColumns)
|
||||
<input type="checkbox" class="fi-checkbox-input fi-valid"
|
||||
x-bind:id="'column-' + groupColumn.name.replace('.', '-')"
|
||||
x-bind:checked="(getColumn(groupColumn.name, column.name) || {}).isToggled || false"
|
||||
x-bind:disabled="(getColumn(groupColumn.name, column.name) || {}).isToggleable === false"
|
||||
x-on:change="toggleColumn(groupColumn.name, column.name)" />
|
||||
@endif
|
||||
|
||||
<span x-html="groupColumn.label"></span>
|
||||
</label>
|
||||
|
||||
@if ($hasReorderableColumns)
|
||||
<button x-sortable-handle x-on:click.stop
|
||||
class="fi-ta-col-manager-reorder-handle fi-icon-btn" type="button">
|
||||
{{ \Filament\Support\generate_icon_html(\Filament\Support\Icons\Heroicon::Bars2, alias: \Filament\Tables\View\TablesIconAlias::REORDER_HANDLE) }}
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="column.type !== 'group'">
|
||||
<div class="fi-ta-col-manager-item">
|
||||
<label class="fi-ta-col-manager-label">
|
||||
@if ($hasToggleableColumns)
|
||||
<input type="checkbox" class="fi-checkbox-input fi-valid"
|
||||
x-bind:id="'column-' + column.name.replace('.', '-')"
|
||||
x-bind:checked="(getColumn(column.name, null) || {}).isToggled || false"
|
||||
x-bind:disabled="(getColumn(column.name, null) || {}).isToggleable === false"
|
||||
x-on:change="toggleColumn(column.name)" />
|
||||
@endif
|
||||
|
||||
<span x-html="column.label"></span>
|
||||
</label>
|
||||
|
||||
@if ($hasReorderableColumns)
|
||||
<button x-sortable-handle x-on:click.stop class="fi-ta-col-manager-reorder-handle fi-icon-btn"
|
||||
type="button">
|
||||
{{ \Filament\Support\generate_icon_html(\Filament\Support\Icons\Heroicon::Bars2, alias: \Filament\Tables\View\TablesIconAlias::REORDER_HANDLE) }}
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
@php
|
||||
use Filament\Tables\Enums\ColumnManagerResetActionPosition;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'applyAction',
|
||||
'columns' => null,
|
||||
'hasReorderableColumns',
|
||||
'hasToggleableColumns',
|
||||
'headingTag' => 'h3',
|
||||
'reorderAnimationDuration' => 300,
|
||||
'resetActionPosition' => ColumnManagerResetActionPosition::Header,
|
||||
])
|
||||
|
||||
<div x-data="filamentTableColumnManager({
|
||||
columns: $wire.entangle('tableColumns'),
|
||||
isLive: {{ $applyAction->isVisible() ? 'false' : 'true' }},
|
||||
})" class="fi-ta-col-manager">
|
||||
<div class="fi-ta-col-manager-header">
|
||||
<{{ $headingTag }} class="fi-ta-col-manager-heading">
|
||||
{{ __('filament-tables::table.column_manager.heading') }}
|
||||
</{{ $headingTag }}>
|
||||
|
||||
@if ($resetActionPosition === ColumnManagerResetActionPosition::Header)
|
||||
<div>
|
||||
<x-filament::link :attributes="\Filament\Support\prepare_inherited_attributes(
|
||||
new ComponentAttributeBag([
|
||||
'color' => 'danger',
|
||||
'tag' => 'button',
|
||||
'wire:click' => 'resetTableColumnManager',
|
||||
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => '',
|
||||
'wire:target' => 'resetTableColumnManager',
|
||||
'x-on:click' => 'resetDeferredColumns',
|
||||
]),
|
||||
)">
|
||||
{{ __('filament-tables::table.column_manager.actions.reset.label') }}
|
||||
</x-filament::link>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<x-filament-tables::column-manager.content :columns="$columns" :has-reorderable-columns="$hasReorderableColumns" :has-toggleable-columns="$hasToggleableColumns"
|
||||
:reorder-animation-duration="$reorderAnimationDuration" />
|
||||
|
||||
@if ($applyAction->isVisible() || $resetActionPosition === ColumnManagerResetActionPosition::Footer)
|
||||
<div class="fi-ta-col-manager-actions-ctn">
|
||||
@if ($applyAction->isVisible())
|
||||
{{ $applyAction }}
|
||||
@endif
|
||||
|
||||
@if ($resetActionPosition === ColumnManagerResetActionPosition::Footer)
|
||||
<x-filament::button color="danger" wire:click="resetTableColumnManager" x-on:click="resetDeferredColumns">
|
||||
{{ __('filament-tables::table.column_manager.actions.reset.label') }}
|
||||
</x-filament::button>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
{{-- @deprecated Copy the code from this view directly into your own view to improve performance. --}}
|
||||
|
||||
@props(['components', 'record', 'recordKey' => null, 'rowLoop' => null])
|
||||
|
||||
@foreach ($components as $layoutComponent)
|
||||
{{ $layoutComponent->record($record)->recordKey($recordKey)->rowLoop($rowLoop)->renderInLayout() }}
|
||||
@endforeach
|
||||
@@ -0,0 +1,45 @@
|
||||
@php
|
||||
use Filament\Tables\Enums\FiltersResetActionPosition;
|
||||
@endphp
|
||||
|
||||
@props(['applyAction', 'form', 'headingTag' => 'h3', 'resetActionPosition' => FiltersResetActionPosition::Header])
|
||||
|
||||
<div {{ $attributes->class(['fi-ta-filters']) }}>
|
||||
<div class="fi-ta-filters-header">
|
||||
<{{ $headingTag }} class="fi-ta-filters-heading">
|
||||
{{ __('filament-tables::table.filters.heading') }}
|
||||
</{{ $headingTag }}>
|
||||
|
||||
@if ($resetActionPosition === FiltersResetActionPosition::Header)
|
||||
<div>
|
||||
<x-filament::link :attributes="\Filament\Support\prepare_inherited_attributes(
|
||||
new \Illuminate\View\ComponentAttributeBag([
|
||||
'color' => 'danger',
|
||||
'tag' => 'button',
|
||||
'wire:click' => 'resetTableFiltersForm',
|
||||
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => '',
|
||||
'wire:target' => 'resetTableFiltersForm',
|
||||
]),
|
||||
)">
|
||||
{{ __('filament-tables::table.filters.actions.reset.label') }}
|
||||
</x-filament::link>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{ $form }}
|
||||
|
||||
@if ($applyAction->isVisible() || $resetActionPosition === FiltersResetActionPosition::Footer)
|
||||
<div class="fi-ta-filters-actions-ctn">
|
||||
@if ($applyAction->isVisible())
|
||||
{{ $applyAction }}
|
||||
@endif
|
||||
|
||||
@if ($resetActionPosition === FiltersResetActionPosition::Footer)
|
||||
<x-filament::button color="danger" wire:click="resetTableFiltersForm">
|
||||
{{ __('filament-tables::table.filters.actions.reset.label') }}
|
||||
</x-filament::button>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,37 @@
|
||||
@php
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
@endphp
|
||||
|
||||
@props([
|
||||
'debounce' => '500ms',
|
||||
'onBlur' => false,
|
||||
'placeholder' => __('filament-tables::table.fields.search.placeholder'),
|
||||
'wireModel' => 'tableSearch',
|
||||
])
|
||||
|
||||
@php
|
||||
$wireModelAttribute = $onBlur ? 'wire:model.live.blur' : "wire:model.live.debounce.{$debounce}";
|
||||
@endphp
|
||||
|
||||
<div x-id="['input']" {{ $attributes->class(['fi-ta-search-field']) }}>
|
||||
<label x-bind:for="$id('input')" class="fi-sr-only">
|
||||
{{ __('filament-tables::table.fields.search.label') }}
|
||||
</label>
|
||||
|
||||
<x-filament::input.wrapper inline-prefix :prefix-icon="\Filament\Support\Icons\Heroicon::MagnifyingGlass" :prefix-icon-alias="\Filament\Tables\View\TablesIconAlias::SEARCH_FIELD" :wire:target="$wireModel">
|
||||
<x-filament::input :attributes="new ComponentAttributeBag()->merge(
|
||||
[
|
||||
'autocomplete' => 'off',
|
||||
'inlinePrefix' => true,
|
||||
'maxlength' => 1000,
|
||||
'placeholder' => $placeholder,
|
||||
'type' => 'search',
|
||||
'wire:key' => $this->getId() . '.table.' . $wireModel . '.field.input',
|
||||
$wireModelAttribute => $wireModel,
|
||||
'x-bind:id' => '$id(\'input\')',
|
||||
'x-on:keyup' => 'if ($event.key === \'Enter\') { $wire.$refresh() }',
|
||||
],
|
||||
escape: false,
|
||||
)" />
|
||||
</x-filament::input.wrapper>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user