- Основы
- Управление компонентами
- Создание шаблона
- Изменение шаблона страницы
- Assets
- Favicons
- Меню
- Темы оформления
- Цвета
- Blade
Основы
Layout в MoonShine представляет собой набор компонентов, формирующих структуру страницы административной панели.
Каждый элемент страницы, включая HTML теги, является компонентом MoonShine.
Это обеспечивает высокую степень гибкости и возможность кастомизации.
При установке MoonShine, публикуется шаблон по умолчанию app/MoonShine/Layouts/AppLayout.php и регистрируется в конфигурационном файле.
Вы можете:
- Модифицировать существующий шаблон,
- Создать новый шаблон,
- Применять разные шаблоны для различных страниц.
Полный список компонентов ищите в разделе Компоненты.
Как можно заметить, компонентов огромное количество, и для удобства мы объединили их в группы, чтобы вы могли удобно переопределять только те группы, которые требуются.
use MoonShine\Laravel\Layouts\AppLayout;final class MoonShineLayout extends AppLayout{// ...protected function getFooterMenu(): array{return ['https://example.com' => 'Custom link',];}protected function getFooterCopyright(): string{return 'MoonShine';}public function build(): Layout{return parent::build();}}
use MoonShine\Laravel\Layouts\AppLayout;final class MoonShineLayout extends AppLayout{// ...protected function getFooterMenu(): array{return ['https://example.com' => 'Custom link',];}protected function getFooterCopyright(): string{return 'MoonShine';}public function build(): Layout{return parent::build();}}
В примере выше, с помощью методов getFooterMenu() и getFooterCopyright(), мы переопределили вывод меню в футере и copyright.
Доступные быстрые методы:
Переопределить компонент Head
protected function getHeadComponent(bool $withAssetsFragment = true): Head{return Head::make([// ...]);}protected function getHeadComponent(bool $withAssetsFragment = true): Head{return Head::make([// ...]);}
Переопределить компонент Logo
protected function getLogoComponent(): Logo{return Logo::make($this->getHomeUrl(),$this->getLogo(),$this->getLogo(small: true),);}protected function getLogoComponent(): Logo{return Logo::make($this->getHomeUrl(),$this->getLogo(),$this->getLogo(small: true),);}
Переопределить компонент Sidebar
protected function getSidebarComponent(): Sidebar{return Sidebar::make([// ...]);}protected function getSidebarComponent(): Sidebar{return Sidebar::make([// ...]);}
Переопределить компонент Header
protected function getHeaderComponent(): Header{Header::make([// ...]);}protected function getHeaderComponent(): Header{Header::make([// ...]);}
Переопределить или интегрировать компонент TopBar
protected function getTopBarComponent(): Topbar{Topbar::make([// ...]);}protected function getTopBarComponent(): Topbar{Topbar::make([// ...]);}
Переопределить компонент Footer
protected function getFooterComponent(): Footer{Footer::make([// ...]);}protected function getFooterComponent(): Footer{Footer::make([// ...]);}
Переопределить компонент Profile
protected function getProfileComponent(): Profile{return Profile::make();}protected function getProfileComponent(): Profile{return Profile::make();}
Переопределить содержимое компонента Content
protected function getContentComponents(): array{// ...}protected function getContentComponents(): array{// ...}
Content::make($this->getContentComponents())Content::make($this->getContentComponents())
Путь до логотипа
protected function getLogo(bool $small = false): string{// ...}protected function getLogo(bool $small = false): string{// ...}
URL главной страницы
protected function getHomeUrl(): string{// ...}protected function getHomeUrl(): string{// ...}
Упрощенное отображение контента
Вы можете убрать обводку и фон у контентной части страницы, установив свойство $contentSimpled = true в вашем Layout:
protected bool $contentSimpled = true; // default falseprotected bool $contentSimpled = true; // default false
Отображение контента по центру
По умолчанию контент страницы занимает всю ширину экрана.
Чтобы разместить его в центрированном контейнере фиксированной ширины, установите свойство $contentCentered = true в вашем Layout.
protected bool $contentCentered = true; // default falseprotected bool $contentCentered = true; // default false
Slots
С помощью "слотов" вы можете быстро добавить компоненты в Sidebar или Topbar.
protected function sidebarSlot(): array{return [Search::make()->enabled(),// ...];}protected function sidebarTopSlot(): array{return [Notifications::make(),// ...];}protected function topBarSlot(): array{return [// ...];}protected function sidebarSlot(): array{return [Search::make()->enabled(),// ...];}protected function sidebarTopSlot(): array{return [Notifications::make(),// ...];}protected function topBarSlot(): array{return [// ...];}
Вы также можете создать собственный шаблон со своим набором удобных методов для дальнейшего удобного взаимодействия.
В стандартном Layout компоненты Sidebar, Topbar и Mobilebar оформлены в темных цветах.
Но если добавить в них другие компоненты, они будут меняться в зависимости от выбранной темы, что приведёт к некорректному их отображению.
Чтобы избежать такого поведения, можно принудительно перевести их в тёмный режим добавив класс 'dark'.
$this->getSidebarComponent()->class('dark'),$this->getTopBarComponent()->class('dark'),MobileBar::make([// ...])->class('dark'),$this->getSidebarComponent()->class('dark'),$this->getTopBarComponent()->class('dark'),MobileBar::make([// ...])->class('dark'),
Управление компонентами
MoonShine предоставляет удобный способ управления отображением основных компонентов шаблона через защищённые свойства класса.
Это позволяет быстро включать или отключать элементы интерфейса без переопределения метода build().
Sidebar
По умолчанию боковое меню включено. Чтобы отключить его, установите свойство $sidebar = false в вашем шаблоне:
use MoonShine\Laravel\Layouts\AppLayout;final class MoonShineLayout extends AppLayout{protected bool $sidebar = false;// ...}
use MoonShine\Laravel\Layouts\AppLayout;final class MoonShineLayout extends AppLayout{protected bool $sidebar = false;// ...}
TopBar
Верхняя панель по умолчанию отключена. Чтобы включить её, установите свойство $topBar = true:
use MoonShine\Laravel\Layouts\AppLayout;final class MoonShineLayout extends AppLayout{protected bool $topBar = true;// ...}
use MoonShine\Laravel\Layouts\AppLayout;final class MoonShineLayout extends AppLayout{protected bool $topBar = true;// ...}
Если вы используете и Sidebar и TopBar одновременно, обязательно соблюдайте очередность в методе build() — первым должен идти TopBar.
BottomBar
Нижняя панель по умолчанию отключена. Чтобы включить её, установите свойство $bottomBar = true:
use MoonShine\Laravel\Layouts\AppLayout;final class MoonShineLayout extends AppLayout{protected bool $bottomBar = true;// ...}
use MoonShine\Laravel\Layouts\AppLayout;final class MoonShineLayout extends AppLayout{protected bool $bottomBar = true;// ...}
Компонент BottomBar автоматически отображает верхнее меню внутри себя.
Мобильный режим
Вы можете включить специальный мобильный режим, который оптимизирует интерфейс для мобильных устройств.
При включении этого режима автоматически применяются компактные стили и активируется компонент BottomBar:
use MoonShine\Laravel\Layouts\AppLayout;final class MoonShineLayout extends AppLayout{protected bool $mobileMode = true;// ...}
use MoonShine\Laravel\Layouts\AppLayout;final class MoonShineLayout extends AppLayout{protected bool $mobileMode = true;// ...}
При включении мобильного режима:
- применяются компактные отступы и размеры элементов,
- скрывается компонент
Burger, - автоматически включается
BottomBarс верхним меню.
Вы можете комбинировать эти свойства для создания различных вариантов шаблонов — например,
отключить Sidebar и включить только TopBar для более горизонтального интерфейса.
Создание шаблона
Чтобы создать еще один шаблон, воспользуйтесь командой:
php artisan moonshine:layoutphp artisan moonshine:layout
О всех поддерживаемых опциях можно узнать в разделе Команды.
Изменение шаблона страницы
По умолчанию страницы используют шаблон отображения AppLayout.
Но вы можете изменить его на собственный шаблон, просто заменив значение свойства $layout.
Подробнее про страницы читайте в разделе Страница.
use App\MoonShine\Layouts\MyLayout;use MoonShine\Laravel\Pages\Page;class CustomPage extends Page{protected ?string $layout = MyLayout::class;// ...}
use App\MoonShine\Layouts\MyLayout;use MoonShine\Laravel\Pages\Page;class CustomPage extends Page{protected ?string $layout = MyLayout::class;// ...}
Assets
Каждый шаблон может иметь свой набор стилей и скриптов, определяемых через метод assets().
use MoonShine\Laravel\Layouts\AppLayout;use MoonShine\AssetManager\Css;final class MyLayout extends AppLayout{// ...protected function assets(): array{return [...parent::assets(),Css::make('/vendor/moonshine/assets/minimalistic.css')->defer(),];}// ...}
use MoonShine\Laravel\Layouts\AppLayout;use MoonShine\AssetManager\Css;final class MyLayout extends AppLayout{// ...protected function assets(): array{return [...parent::assets(),Css::make('/vendor/moonshine/assets/minimalistic.css')->defer(),];}// ...}
За более подробной информацией обратитесь в раздел Assets.
Добавление инлайн-стилей
protected function assets(): array{return [...parent::assets(),InlineCss::make(<<<'Style':root {--spacing: 0.15rem;}Style),];}protected function assets(): array{return [...parent::assets(),InlineCss::make(<<<'Style':root {--spacing: 0.15rem;}Style),];}
Favicons
Вы можете заменить набор favicons в шаблоне через переопределение метода getFaviconComponent().
use MoonShine\Laravel\Layouts\AppLayout;final class MyLayout extends AppLayout{// ...protected function getFaviconComponent(): Favicon{return parent::getFaviconComponent()->customAssets(['apple-touch' => 'favicon_path','32' => 'favicon_path','16' => 'favicon_path','safari-pinned-tab' => 'favicon_path',]);}}
use MoonShine\Laravel\Layouts\AppLayout;final class MyLayout extends AppLayout{// ...protected function getFaviconComponent(): Favicon{return parent::getFaviconComponent()->customAssets(['apple-touch' => 'favicon_path','32' => 'favicon_path','16' => 'favicon_path','safari-pinned-tab' => 'favicon_path',]);}}
Меню
Для каждого шаблона можно объявить список пунктов меню через метод menu(), которые автоматически будут переданы в компонент Menu.
use MoonShine\Laravel\Layouts\AppLayout;use MoonShine\MenuManager\MenuItem;final class MyLayout extends AppLayout{// ...protected function menu(): array{return [...parent::menu(),MenuItem::make(ArticleResource::class),];}}
use MoonShine\Laravel\Layouts\AppLayout;use MoonShine\MenuManager\MenuItem;final class MyLayout extends AppLayout{// ...protected function menu(): array{return [...parent::menu(),MenuItem::make(ArticleResource::class),];}}
За более подробной информацией обратитесь в раздел Меню.
Вы также можете не пользоваться методом menu(), а передать список вручную в компонент Menu.
Верхнее меню
По умолчанию MoonShine имеет компонент верхнего меню, который можно использовать вместо Sidebar или совместно с ним.
Чтобы заменить Sidebar на TopBar, переопределите метод build() и заменит в нём вызов метода getSidebarComponent() на getTopBarComponent().
Если вы хотите оставить и Sidebar и TopBar одновременно, то обязательно соблюдайте очередность, первым должен идти TopBar.
Темы оформления
В Moonshine "из коробки" доступна поддержка двух тем оформления — светлой и тёмной. По умолчанию используется тема, заданная в системе, либо светлая, если определить не удалось.
Тёмная тема
Если вы хотите, чтобы тёмная тема всегда была включена, переопределите метод isAlwaysDark() и верните true. Переключатель тем при этом отображаться не будет.
protected function isAlwaysDark(): bool{return true;}protected function isAlwaysDark(): bool{return true;}
Вкл/выкл тем оформления
Чтобы убрать переключатель тем и оставить только светлую тему, переопределите метод hasThemes() и верните false.
protected function hasThemes(): bool{return false;}protected function hasThemes(): bool{return false;}
Цвета
Каждый шаблон может иметь собственную цветовую схему.
Самый простой способ задать её — указать реализацию PaletteContract в свойстве $palette:
use App\MoonShine\Palettes\CorporatePalette;use MoonShine\Laravel\Layouts\AppLayout;final class MyLayout extends AppLayout{protected ?string $palette = CorporatePalette::class;}
use App\MoonShine\Palettes\CorporatePalette;use MoonShine\Laravel\Layouts\AppLayout;final class MyLayout extends AppLayout{protected ?string $palette = CorporatePalette::class;}
За более подробной информацией обратитесь в раздел Палитры.
Если оставить $palette равным null, будет использоваться значение из config('moonshine.palette').
Если требуется полное управление, переопределите метод colors():
use MoonShine\Laravel\Layouts\AppLayout;use MoonShine\Contracts\ColorManager\ColorManagerContract;final class MyLayout extends AppLayout{protected function colors(ColorManagerContract $colorManager): void{$colorManager->primary('oklch(65% 0.18 264)')->secondary('oklch(70% 0.14 230)')->bulkAssign(['theme' => ['body' => '0 0 0',50 => '0.99 0 0',100 => '0.98 0 0',900 => '0.90 0 0',],])->successBg('oklch(63.9% 0.218 142.495)')->warningBg('oklch(80.88% 0.170358 75.3501)')->errorBg('oklch(58.9% 0.214 26.855)')->infoBg('oklch(60.1% 0.219 257.63)');$colorManager->set('body', '0.2 0.0168 274.32', dark: true)->theme(['body' => '1 0 0','stroke' => '1 0 0 / 10%','default' => '0.24 0.0168 274.32',900 => '0.39 0.025 274.32',], dark: true)->successBg('0.639 0.218 142.495', dark: true)->warningBg('0.898 0.177 96.726', dark: true)->errorBg('0.589 0.214 26.855', dark: true)->infoBg('0.601 0.219 257.63', dark: true);}}
use MoonShine\Laravel\Layouts\AppLayout;use MoonShine\Contracts\ColorManager\ColorManagerContract;final class MyLayout extends AppLayout{protected function colors(ColorManagerContract $colorManager): void{$colorManager->primary('oklch(65% 0.18 264)')->secondary('oklch(70% 0.14 230)')->bulkAssign(['theme' => ['body' => '0 0 0',50 => '0.99 0 0',100 => '0.98 0 0',900 => '0.90 0 0',],])->successBg('oklch(63.9% 0.218 142.495)')->warningBg('oklch(80.88% 0.170358 75.3501)')->errorBg('oklch(58.9% 0.214 26.855)')->infoBg('oklch(60.1% 0.219 257.63)');$colorManager->set('body', '0.2 0.0168 274.32', dark: true)->theme(['body' => '1 0 0','stroke' => '1 0 0 / 10%','default' => '0.24 0.0168 274.32',900 => '0.39 0.025 274.32',], dark: true)->successBg('0.639 0.218 142.495', dark: true)->warningBg('0.898 0.177 96.726', dark: true)->errorBg('0.589 0.214 26.855', dark: true)->infoBg('0.601 0.219 257.63', dark: true);}}
За более подробной информацией обратитесь в раздел Цветовая схема.
Fragments
По умолчанию в базовом шаблоне отдельные его части находятся внутри компонентов Fragment.
За счёт этого вы можете обновлять их без перезагрузки страницы с помощью событий JSEvents.
Доступные фрагменты:
sidebar-top- Верхняя часть бокового меню (логотип и переключатель темы оформления),sidebar-content- Контентная часть бокового меню,topbar-logo- Логотип в верхнем меню,topbar-menu- Пункты верхнего меню,topbar-actions- Блок действий в верхнем меню,assets- Набор стилей и скриптов.
Пример обновления всех фрагментов шаблона из метода контроллера:
public function saveElement(CrudRequestContract $request): JsonResponse{//...return JsonResponse::make()->events([AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-top'),AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-content'),AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-logo'),AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-menu'),AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-actions'),AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'assets'),]);}public function saveElement(CrudRequestContract $request): JsonResponse{//...return JsonResponse::make()->events([AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-top'),AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'sidebar-content'),AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-logo'),AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-menu'),AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'topbar-actions'),AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'assets'),]);}
Blade
MoonShine позволяет создавать шаблоны напрямую через Blade.
Пример базового шаблона:
<x-moonshine::layout><x-moonshine::layout.html :with-alpine-js="true" :with-themes="true"><x-moonshine::layout.head><x-moonshine::layout.meta name="csrf-token" :content="csrf_token()"/><x-moonshine::layout.favicon /><x-moonshine::layout.assets>@vite(['resources/css/main.css','resources/js/app.js',], 'vendor/moonshine')</x-moonshine::layout.assets></x-moonshine::layout.head><x-moonshine::layout.body><x-moonshine::layout.wrapper><x-moonshine::layout.sidebar :collapsed="true"><x-moonshine::layout.div class="menu-header"><x-moonshine::layout.div class="menu-logo"><x-moonshine::layout.logo href="/" logo="/tableau.png" logo-small="/tableau.png" :minimized="true"/></x-moonshine::layout.div><x-moonshine::layout.div class="menu-actions"><x-moonshine::layout.theme-switcher/></x-moonshine::layout.div><x-moonshine::layout.div class="menu-burger"><x-moonshine::layout.burger/></x-moonshine::layout.div></x-moonshine::layout.div><x-moonshine::layout.div class="menu menu--vertical"><x-moonshine::layout.menu :elements="[['label' => 'Dashboard', 'url' => '/'], ['label' => 'Section', 'url' => '/section']]"/></x-moonshine::layout.div></x-moonshine::layout.sidebar><x-moonshine::layout.div class="layout-main"><x-moonshine::layout.div class="layout-page"><x-moonshine::layout.header><x-moonshine::layout.div class="menu-burger"><x-moonshine::layout.burger/></x-moonshine::layout.div><x-moonshine::breadcrumbs :items="['#' => 'Home']"/><x-moonshine::layout.search placeholder="Search" /><x-moonshine::layout.locales :locales="collect()"/></x-moonshine::layout.header><x-moonshine::layout.content><article class="article">Your content</article></x-moonshine::layout.content></x-moonshine::layout.div></x-moonshine::layout.div></x-moonshine::layout.wrapper></x-moonshine::layout.body></x-moonshine::layout.html></x-moonshine::layout><x-moonshine::layout><x-moonshine::layout.html :with-alpine-js="true" :with-themes="true"><x-moonshine::layout.head><x-moonshine::layout.meta name="csrf-token" :content="csrf_token()"/><x-moonshine::layout.favicon /><x-moonshine::layout.assets>@vite(['resources/css/main.css','resources/js/app.js',], 'vendor/moonshine')</x-moonshine::layout.assets></x-moonshine::layout.head><x-moonshine::layout.body><x-moonshine::layout.wrapper><x-moonshine::layout.sidebar :collapsed="true"><x-moonshine::layout.div class="menu-header"><x-moonshine::layout.div class="menu-logo"><x-moonshine::layout.logo href="/" logo="/tableau.png" logo-small="/tableau.png" :minimized="true"/></x-moonshine::layout.div><x-moonshine::layout.div class="menu-actions"><x-moonshine::layout.theme-switcher/></x-moonshine::layout.div><x-moonshine::layout.div class="menu-burger"><x-moonshine::layout.burger/></x-moonshine::layout.div></x-moonshine::layout.div><x-moonshine::layout.div class="menu menu--vertical"><x-moonshine::layout.menu :elements="[['label' => 'Dashboard', 'url' => '/'], ['label' => 'Section', 'url' => '/section']]"/></x-moonshine::layout.div></x-moonshine::layout.sidebar><x-moonshine::layout.div class="layout-main"><x-moonshine::layout.div class="layout-page"><x-moonshine::layout.header><x-moonshine::layout.div class="menu-burger"><x-moonshine::layout.burger/></x-moonshine::layout.div><x-moonshine::breadcrumbs :items="['#' => 'Home']"/><x-moonshine::layout.search placeholder="Search" /><x-moonshine::layout.locales :locales="collect()"/></x-moonshine::layout.header><x-moonshine::layout.content><article class="article">Your content</article></x-moonshine::layout.content></x-moonshine::layout.div></x-moonshine::layout.div></x-moonshine::layout.wrapper></x-moonshine::layout.body></x-moonshine::layout.html></x-moonshine::layout>