This commit is contained in:
2025-10-22 20:08:22 +05:00
commit 736e3bef18
2573 changed files with 120385 additions and 0 deletions

View File

@@ -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>

View File

@@ -0,0 +1,18 @@
@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'])
"
/>

View File

@@ -0,0 +1,17 @@
@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'])
"
/>

View File

@@ -0,0 +1,48 @@
@props([
'actions' => [],
'breadcrumbs' => [],
'heading',
'subheading' => null,
])
<header
{{
$attributes->class([
'fi-header',
'fi-header-has-breadcrumbs' => $breadcrumbs,
])
}}
>
<div>
@if ($breadcrumbs)
<x-filament::breadcrumbs :breadcrumbs="$breadcrumbs" />
@endif
<h1 class="fi-header-heading">
{{ $heading }}
</h1>
@if ($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" />
@endif
{{ $afterActions }}
</div>
@endif
</header>

View File

@@ -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>

View File

@@ -0,0 +1,158 @@
@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()->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 }} {{ $brandName }}
</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()->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 }};
}
</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
->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>

View File

@@ -0,0 +1,119 @@
@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>

View File

@@ -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\Livewire\DatabaseNotifications::class, [
'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>

View File

@@ -0,0 +1,55 @@
@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: {$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

View File

@@ -0,0 +1,201 @@
@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 }}
@elseif ($heading = $this->getHeading())
@php
$headerActions = $this->getCachedHeaderActions();
$breadcrumbs = filament()->hasBreadcrumbs() ? $this->getBreadcrumbs() : [];
$subheading = $this->getSubheading();
@endphp
<x-filament-panels::header
:actions="$headerActions"
: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
<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()) && $this->hasErrorNotifications())
@script
<script>
const errorNotifications = @js($this->getErrorNotifications())
Livewire.hook('request', ({ payload, fail }) => {
fail(({ status, preventDefault }) => {
if (JSON.parse(payload).components.length === 1) {
for (const component of JSON.parse(payload)
.components) {
if (
JSON.parse(component.snapshot).data
.isFilamentNotificationsComponent
) {
return
}
}
}
preventDefault()
const errorNotification =
errorNotifications[status] ?? errorNotifications['']
new FilamentNotification()
.title(errorNotification.title)
.body(errorNotification.body)
.danger()
.send()
})
})
</script>
@endscript
@endif
<x-filament-panels::unsaved-action-changes-alert />
</div>

View File

@@ -0,0 +1,30 @@
@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">
<x-filament-panels::header.simple
:heading="$heading"
:logo="$hasLogo"
:subheading="$subheading"
/>
{{ $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>

View File

@@ -0,0 +1,72 @@
@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();
$navigationItemIcon = $navigationItem->isActive() ? ($navigationItem->getActiveIcon() ?? $navigationItem->getIcon()) : $navigationItem->getIcon();
$navigationItemUrl = $navigationItem->getUrl();
$shouldNavigationItemOpenUrlInNewTab = $navigationItem->shouldOpenUrlInNewTab();
@endphp
<x-filament::dropdown.list.item
:badge="$navigationItemBadge"
:badge-color="$navigationItemBadgeColor"
:href="$navigationItemUrl"
:icon="$navigationItemIcon"
tag="a"
:target="$shouldNavigationItemOpenUrlInNewTab ? '_blank' : null"
>
{{ $navigationItemChild->getLabel() }}
</x-filament::dropdown.list.item>
@endforeach
@endforeach
</x-filament::dropdown.list>
@endforeach
</x-filament::dropdown>

View File

@@ -0,0 +1,35 @@
@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 wire:ignore 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>

View File

@@ -0,0 +1,90 @@
@props([
'navigation',
])
<x-filament::tabs
wire:ignore
: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();
$navigationItemIcon = $navigationItem->isActive() ? ($navigationItem->getActiveIcon() ?? $navigationItem->getIcon()) : $navigationItem->getIcon();
$navigationItemUrl = $navigationItem->getUrl();
$shouldNavigationItemOpenUrlInNewTab = $navigationItem->shouldOpenUrlInNewTab();
@endphp
<x-filament::dropdown.list.item
:badge="$navigationItemBadge"
:badge-color="$navigationItemBadgeColor"
:href="$navigationItemUrl"
:icon="$navigationItemIcon"
tag="a"
:target="$shouldNavigationItemOpenUrlInNewTab ? '_blank' : null"
>
{{ $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();
$navigationItemIcon = $navigationItem->isActive() ? ($navigationItem->getActiveIcon() ?? $navigationItem->getIcon()) : $navigationItem->getIcon();
$navigationItemUrl = $navigationItem->getUrl();
$shouldNavigationItemOpenUrlInNewTab = $navigationItem->shouldOpenUrlInNewTab();
@endphp
<x-filament::tabs.item
:active="$isNavigationItemActive"
:badge="$navigationItemBadge"
:badge-color="$navigationItemBadgeColor"
:href="$navigationItemUrl"
:icon="$navigationItemIcon"
tag="a"
:target="$shouldNavigationItemOpenUrlInNewTab ? '_blank' : null"
>
{{ $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>

View File

@@ -0,0 +1,35 @@
@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>

View File

@@ -0,0 +1,225 @@
@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)"
role="button"
@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'"
teleport
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();
$itemBadgeTooltip = $item->getBadgeTooltip();
$itemUrl = $item->getUrl();
$itemIcon = $itemIsActive ? ($item->getActiveIcon() ?? $item->getIcon()) : $item->getIcon();
$shouldItemOpenUrlInNewTab = $item->shouldOpenUrlInNewTab();
@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"
>
{{ $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();
$itemBadgeTooltip = $item->getBadgeTooltip();
$itemChildItems = $item->getChildItems();
$itemIcon = $item->getIcon();
$shouldItemOpenUrlInNewTab = $item->shouldOpenUrlInNewTab();
$itemUrl = $item->getUrl();
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"
:url="$itemUrl"
>
{{ $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>

View File

@@ -0,0 +1,149 @@
@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,
'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)
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))
{{
\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)
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)
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)
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 (($active || $activeChildItems) && $childItems)
<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();
$childItemBadgeTooltip = $childItem->getBadgeTooltip();
$childItemIcon = $childItem->getIcon();
$shouldChildItemOpenUrlInNewTab = $childItem->shouldOpenUrlInNewTab();
$childItemUrl = $childItem->getUrl();
@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
:url="$childItemUrl"
>
{{ $childItem->getLabel() }}
</x-filament-panels::sidebar.item>
@endforeach
</ul>
@endif
</li>

View File

@@ -0,0 +1,152 @@
@props([
'teleport' => false,
])
@php
use Filament\Actions\Action;
use Illuminate\Support\Arr;
$currentTenant = filament()->getTenant();
$currentTenantName = filament()->getTenantName($currentTenant);
$items = $this->getTenantMenuItems();
$canSwitchTenants = count($tenants = array_filter(
filament()->getUserTenants(filament()->auth()->user()),
fn (\Illuminate\Database\Eloquent\Model $tenant): bool => ! $tenant->is($currentTenant),
));
$isSearchable = filled($canSwitchTenants) ? (filament()->isTenantMenuSearchable() ?? (count($tenants) >= 10)) : false;
$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) }}

View File

@@ -0,0 +1,28 @@
@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>

View File

@@ -0,0 +1,26 @@
<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>

View File

@@ -0,0 +1,9 @@
<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"
/>

View File

@@ -0,0 +1,50 @@
@props([
'active' => false,
'activeIcon' => null,
'badge' => null,
'badgeColor' => null,
'badgeTooltip' => null,
'icon' => null,
'shouldOpenUrlInNewTab' => false,
'url' => null,
])
@php
$tag = $url ? 'a' : 'button';
@endphp
<li @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>

View File

@@ -0,0 +1,10 @@
@if (filament()->hasUnsavedChangesAlerts())
@script
<script>
setUpUnsavedActionChangesAlert({
resolveLivewireComponentUsing: () => @this,
$wire,
})
</script>
@endscript
@endif

View File

@@ -0,0 +1,135 @@
@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) }}

View File

@@ -0,0 +1,153 @@
@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>

View File

@@ -0,0 +1,198 @@
<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 x-show="$store.sidebar.isOpen" 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\Livewire\DatabaseNotifications::class, [
'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>

View File

@@ -0,0 +1,9 @@
<div>
<div class="fi-simple-user-menu-ctn">
<x-filament-panels::user-menu
:position="\Filament\Enums\UserMenuPosition::Topbar"
/>
</div>
<x-filament-actions::modals />
</div>

View File

@@ -0,0 +1,258 @@
<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)
<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
{{ \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();
$itemBadgeTooltip = $item->getBadgeTooltip();
$itemUrl = $item->getUrl();
$itemIcon = $isItemActive ? ($item->getActiveIcon() ?? $item->getIcon()) : $item->getIcon();
$shouldItemOpenUrlInNewTab = $item->shouldOpenUrlInNewTab();
@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"
>
{{ $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();
$itemBadgeTooltip = $item->getBadgeTooltip();
$itemIcon = $item->getIcon();
$shouldItemOpenUrlInNewTab = $item->shouldOpenUrlInNewTab();
$itemUrl = $item->getUrl();
@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"
>
{{ $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\Livewire\DatabaseNotifications::class, [
'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>

View File

@@ -0,0 +1,3 @@
<x-filament-panels::page>
{{ $this->content }}
</x-filament-panels::page>

View File

@@ -0,0 +1,3 @@
<x-filament-panels::page.simple>
{{ $this->content }}
</x-filament-panels::page.simple>

View File

@@ -0,0 +1,5 @@
<div class="fi-resource-relation-manager">
{{ $this->content }}
<x-filament-panels::unsaved-action-changes-alert />
</div>

View File

@@ -0,0 +1,42 @@
@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>

File diff suppressed because one or more lines are too long