Внешний вид

Layout

Основы

Layout в MoonShine представляет собой набор компонентов, формирующих структуру страницы административной панели. Каждый элемент страницы, включая HTML-теги, является компонентом MoonShine. Это обеспечивает высокую степень гибкости и возможность кастомизации.

MoonShine предлагает два готовых шаблона:

  • AppLayout - базовый шаблон,
  • CompactLayout - компактный шаблон.

При установке MoonShine вы выбираете один из этих шаблонов по умолчанию. Выбранный шаблон публикуется в директорию app/MoonShine/Layouts и указывается в конфигурационном файле moonshine.layout.

Вы можете:

  • Модифицировать существующий шаблон,
  • Создать новый шаблон,
  • Применять разные шаблоны для различных страниц.

Пример возможного шаблона вашего приложения:

 namespaces
namespace App\MoonShine\Layouts;
 
use App\MoonShine\Resources\PackageCategoryResource;
use App\MoonShine\Resources\PackageResource;
use App\MoonShine\Resources\UserResource;
use MoonShine\ColorManager\ColorManager;
use MoonShine\Contracts\ColorManager\ColorManagerContract;
use MoonShine\Laravel\Components\Layout\{Locales, Notifications, Profile, Search};
use MoonShine\Laravel\Layouts\CompactLayout;
use MoonShine\MenuManager\MenuGroup;
use MoonShine\MenuManager\MenuItem;
use MoonShine\UI\Components\{Breadcrumbs,
Components,
Layout\Assets,
Layout\Div,
Layout\Body,
Layout\Burger,
Layout\Content,
Layout\Favicon,
Layout\Flash,
Layout\Footer,
Layout\Head,
Layout\Header,
Layout\Html,
Layout\Layout,
Layout\Logo,
Layout\Menu,
Layout\Meta,
Layout\Sidebar,
Layout\ThemeSwitcher,
Layout\Wrapper,
When};
 
final class MoonShineLayout extends CompactLayout
{
// ...
 
public function build(): Layout
{
return Layout::make([
Html::make([
Head::make([
Meta::make()->customAttributes([
'name' => 'csrf-token',
'content' => csrf_token(),
]),
Favicon::make()->bodyColor($this->getColorManager()->get('body')),
Assets::make(),
])
->bodyColor($this->getColorManager()->get('body'))
->title($this->getPage()->getTitle()),
Body::make([
Wrapper::make([
Sidebar::make([
Div::make([
Div::make([
Logo::make(
$this->getHomeUrl(),
$this->getLogo(),
$this->getLogo(small: true),
)->minimized(),
])->class('menu-heading-logo'),
 
Div::make([
Div::make([
ThemeSwitcher::make(),
])->class('menu-heading-mode'),
 
Div::make([
Burger::make(),
])->class('menu-heading-burger'),
])->class('menu-heading-actions'),
])->class('menu-heading'),
 
Div::make([
Menu::make(),
When::make(
fn(): bool => $this->isAuthEnabled(),
static fn(): array => [Profile::make(withBorder: true)],
),
])->customAttributes([
'class' => 'menu',
':class' => "asideMenuOpen && '_is-opened'",
]),
])->collapsed(),
 
Div::make([
Flash::make(),
Header::make([
Breadcrumbs::make($this->getPage()->getBreadcrumbs())->prepend(
$this->getHomeUrl(),
icon: 'home',
),
Search::make(),
When::make(
fn(): bool => $this->isUseNotifications(),
static fn(): array => [Notifications::make()],
),
Locales::make(),
]),
 
Content::make([
Components::make(
$this->getPage()->getComponents(),
),
]),
 
Footer::make()
->copyright(static fn(): string
=> sprintf(
<<<'HTML'
&copy; 2021-%d Made with ❤️ by
<a href="https://cutcode.dev"
class="font-semibold text-primary hover:text-secondary"
target="_blank"
>
CutCode
</a>
HTML,
now()->year,
))
->menu([
'https://moonshine-laravel.com/docs' => 'Documentation',
]),
])->class('layout-page'),
]),
])->class('theme-minimalistic'),
])
->customAttributes([
'lang' => $this->getHeadLang(),
])
->withAlpineJs()
->withThemes(),
]);
}
}

Как видите, начиная от тега HTML всё в MoonShine является компонентами, что дает огромную свободу кастомизации вашей админ-панели.

Полный список компонентов ищите в разделе Компоненты.

Как можно заметить, компонентов огромное количество, и для удобства мы объединили их в группы, чтобы вы могли удобно переопределять только те группы, которые требуются.

 namespaces
use MoonShine\Laravel\Layouts\CompactLayout;
 
final class MoonShineLayout extends CompactLayout
{
// ...
 
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(): Head

Переопределить компонент Logo

protected function getLogoComponent(): Logo

Переопределить компонент Sidebar

protected function getSidebarComponent(): Sidebar

Переопределить компонент Header

protected function getHeaderComponent(): Header

Переопределить или интегрировать компонент TopBar

protected function getTopBarComponent(): Topbar

Переопределить компонент Footer

protected function getFooterComponent(): Footer

Переопределить компонент Profile

protected function getProfileComponent(bool $sidebar = false): Profile

Переопределить содержимое компонента Content

protected function getContentComponents(): array
Content::make(
$this->getContentComponents()
)

Путь до логотипа

protected function getLogo(bool $small = false): string

URL главной страницы

protected function getHomeUrl(): string

Вы также можете создать собственный шаблон со своим набором удобных методов для дальнейшего удобного взаимодействия.

Создание шаблона

Чтобы создать еще один шаблон, воспользуйтесь командой:

php artisan moonshine:layout

О всех поддерживаемых опциях можно узнать в разделе Команды.

Изменение шаблона страницы

По умолчанию страницы используют шаблон отображения AppLayout или CompactLayout. Но вы можете изменить на собственный шаблон, просто заменив значение свойства $layout.

Подробнее про страницы читайте в разделе Страница.

 namespaces
use App\MoonShine\Layouts\MyLayout;
use MoonShine\Laravel\Pages\Page;
 
class CustomPage extends Page
{
protected ?string $layout = MyLayout::class;
 
// ...
}

Assets

Каждый шаблон может иметь свой набор стилей и скриптов, определяемых через метод assets():

 namespaces
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.

Компактная тема с rounded radius

protected function assets(): array
{
return [
...parent::assets(),
InlineCss::make(<<<'Style'
:root {
--radius: 0.15rem;
--radius-sm: 0.075rem;
--radius-md: 0.275rem;
--radius-lg: 0.3rem;
--radius-xl: 0.4rem;
--radius-2xl: 0.5rem;
--radius-3xl: 1rem;
--radius-full: 9999px;
}
Style),
];
}

Favicons

Вы можете заменить набор favicons в шаблоне через переопределение метода getFaviconComponent():

 namespaces
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',
'web-manifest' => 'favicon_path',
]);
}
}

Для каждого шаблона можно объявить список пунктов меню через метод menu(), которые автоматически будут переданы в компонент Menu.

 namespaces
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\MenuManager\MenuItem;
 
final class MyLayout extends AppLayout
{
// ...
 
protected function menu(): array
{
return [
...parent::menu(),
MenuItem::make('Articles', ArticleResource::class),
];
}
}

За более подробной информацией обратитесь в раздел Меню.

Вы также можете не пользоваться методом menu, а передать список вручную в компонент Menu.

Верхнее меню

По умолчанию MoonShine имеет компонент верхнего меню, который можно использовать вместо Sidebar или совместно с ним. Давайте посмотрим, как заменить Sidebar на TopBar в Layout.

 namespaces
use MoonShine\Laravel\Layouts\CompactLayout;
 
final class MoonShineLayout extends CompactLayout
{
// ...
 
public function build(): Layout
{
return Layout::make([
Html::make([
$this->getHeadComponent(),
Body::make([
Wrapper::make([
$this->getTopBarComponent(),
//$this->getSidebarComponent(),
Div::make([
Flash::make(),
$this->getHeaderComponent(),
 
Content::make([
Components::make(
$this->getPage()->getComponents()
),
]),
 
$this->getFooterComponent(),
])->class('layout-page'),
]),
])->class('theme-minimalistic'),
])
->customAttributes([
'lang' => $this->getHeadLang(),
])
->withAlpineJs()
->withThemes(),
]);
}
}

Если вы хотите оставить и Sidebar и TopBar одновременно, то обязательно соблюдайте очередность, первым должен идти TopBar.

Цвета

Каждый шаблон может иметь собственную цветовую схему, определяемую в методе colors.

 namespaces
use MoonShine\Laravel\Layouts\AppLayout;
use MoonShine\Contracts\ColorManager\ColorManagerContract;
 
final class MyLayout extends AppLayout
{
// ...
 
/**
* @param ColorManager $colorManager
*/
protected function colors(ColorManagerContract $colorManager): void
{
$colorManager
->primary('#1E96FC')
->secondary('#1D8A99')
->body('249, 250, 251')
->dark('30, 31, 67', 'DEFAULT')
->dark('249, 250, 251', 50)
->dark('243, 244, 246', 100)
->dark('229, 231, 235', 200)
->dark('209, 213, 219', 300)
->dark('156, 163, 175', 400)
->dark('107, 114, 128', 500)
->dark('75, 85, 99', 600)
->dark('55, 65, 81', 700)
->dark('31, 41, 55', 800)
->dark('17, 24, 39', 900)
->successBg('209, 255, 209')
->successText('15, 99, 15')
->warningBg('255, 246, 207')
->warningText('92, 77, 6')
->errorBg('255, 224, 224')
->errorText('81, 20, 20')
->infoBg('196, 224, 255')
->infoText('34, 65, 124');
 
$colorManager
->body('27, 37, 59', dark: true)
->dark('83, 103, 132', 50, dark: true)
->dark('74, 90, 121', 100, dark: true)
->dark('65, 81, 114', 200, dark: true)
->dark('53, 69, 103', 300, dark: true)
->dark('48, 61, 93', 400, dark: true)
->dark('41, 53, 82', 500, dark: true)
->dark('40, 51, 78', 600, dark: true)
->dark('39, 45, 69', 700, dark: true)
->dark('27, 37, 59', 800, dark: true)
->dark('15, 23, 42', 900, dark: true)
->successBg('17, 157, 17', dark: true)
->successText('178, 255, 178', dark: true)
->warningBg('225, 169, 0', dark: true)
->warningText('255, 255, 199', dark: true)
->errorBg('190, 10, 10', dark: true)
->errorText('255, 197, 197', dark: true)
->infoBg('38, 93, 205', dark: true)
->infoText('179, 220, 255', dark: true);
}
}

За более подробной информацией обратитесь в раздел Цветовая схема.

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-heading">
<x-moonshine::layout.div class="menu-heading-logo">
<x-moonshine::layout.logo href="/" logo="/logo.png" :minimized="true"/>
</x-moonshine::layout.div>
 
<x-moonshine::layout.div class="menu-heading-actions">
<x-moonshine::layout.div class="menu-heading-mode">
<x-moonshine::layout.theme-switcher/>
</x-moonshine::layout.div>
<x-moonshine::layout.div class="menu-heading-burger">
<x-moonshine::layout.burger/>
</x-moonshine::layout.div>
</x-moonshine::layout.div>
 
</x-moonshine::layout.div>
 
<x-moonshine::layout.div class="menu" ::class="asideMenuOpen && '_is-opened'">
<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-page">
<x-moonshine::layout.header>
<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.wrapper>
</x-moonshine::layout.body>
</x-moonshine::layout.html>
</x-moonshine::layout>