Поддержка
Слоты

Серверные слоты

Управление слотами с бэкенда через Slot и SlotComponentRegistry

Серверные слоты позволяют управлять содержимым интерфейса из PHP. Бэкенд указывает какой компонент показать и с какими props, а фронтенд резолвит компонент по имени и рендерит его.

Регистрация на бэкенде

app/Providers/AppServiceProvider.php
use Frame\Core\Enums\SlotLocation;
use Frame\Core\Frame;
use Frame\Core\Slot;

Frame::registerSlots([
    SlotLocation::LOGIN_BEFORE_FORM->value => Slot::make('Banner', [
        'text' => 'Плановые работы с 20:00 до 22:00',
        'variant' => 'warning',
    ]),
    SlotLocation::LOGIN_SIDEBAR->value => Slot::make('LoginSidebar'),
]);

Можно регистрировать несколько слотов для одного location:

Frame::registerSlots([
    SlotLocation::LOGIN_BEFORE_FORM->value => [
        Slot::make('Banner', ['text' => 'Первый баннер', 'variant' => 'info']),
        Slot::make('Banner', ['text' => 'Второй баннер', 'variant' => 'warning']),
    ],
]);

Slot

Slot::make(string $component, ?array $props = [], mixed $content = null)
component
string
Имя компонента, зарегистрированного на фронте через SlotComponentRegistry.
props
array
Пропсы для компонента. Любые JSON-сериализуемые значения, включая HTML-строки.
content
mixed
Дополнительный контент (опционально).

Регистрация компонента на фронтенде

Бэкенд передаёт имя компонента как строку. Фронтенд должен зарегистрировать Vue-компонент под этим именем:

resources/js/frame.js
import { SlotComponentRegistry } from '@frame/core'
import Banner from './components/Banner.vue'
import LoginSidebar from './components/LoginSidebar.vue'

SlotComponentRegistry.register('Banner', Banner)
SlotComponentRegistry.register('LoginSidebar', LoginSidebar)

Методы SlotComponentRegistry

import { SlotComponentRegistry } from '@frame/core'

SlotComponentRegistry.register('Banner', BannerComponent)
SlotComponentRegistry.registerMany({
  Banner: BannerComponent,
  LoginSidebar: LoginSidebarComponent,
})

SlotComponentRegistry.get('Banner')    // Component | undefined
SlotComponentRegistry.has('Banner')    // boolean
SlotComponentRegistry.unregister('Banner')
SlotComponentRegistry.clear()

Как данные попадают на фронт

Серверные слоты доставляются двумя путями:

Авторизованные страницы

Слоты приходят в ответе GET /meta вместе с модулями и дашбордами:

{
  "dashboards": [...],
  "modules": [...],
  "slots": {
    "resource-page-actions": [
      { "component": "MyWidget", "props": { "title": "Hello" } }
    ]
  }
}

Публичные страницы (login)

Для locations с isPublic() = true данные приходят через GET /slots — без авторизации:

{
  "slots": {
    "login-before-form": [
      { "component": "Banner", "props": { "text": "...", "variant": "warning" } }
    ]
  }
}

AppSlotRenderer автоматически выбирает источник — если пользователь авторизован, берёт из /meta, иначе запрашивает /slots.

SlotLocation

Enum Frame\Core\Enums\SlotLocation определяет все доступные locations:

SlotLocation::LOGIN_BEFORE_FORM    // публичный
SlotLocation::LOGIN_SIDEBAR        // публичный
SlotLocation::RESOURCE_PAGE_ACTIONS // защищённый

Каждый case имеет метод isPublic():

SlotLocation::LOGIN_BEFORE_FORM->isPublic()    // true
SlotLocation::RESOURCE_PAGE_ACTIONS->isPublic() // false

// Все публичные locations
SlotLocation::publicValues() // ['login-before-form', 'login-after-form', ...]

Пример: баннер на странице входа

Бэкенд:

Frame::registerSlots([
    SlotLocation::LOGIN_BEFORE_FORM->value => Slot::make('Banner', [
        'text' => 'Система обновлена до версии 2.0.<br>Подробности в документации.',
        'variant' => 'info',
        'showIcon' => true,
    ]),
]);

Фронтенд — регистрация:

SlotComponentRegistry.register('Banner', BannerComponent)

Фронтенд — компонент:

Banner.vue
<template>
  <div class="mb-4 rounded-lg px-4 py-3 text-sm" :class="variantClasses">
    <span v-html="text" />
  </div>
</template>

<script lang="ts" setup>
const props = withDefaults(defineProps<{
  text: string
  variant?: 'info' | 'warning' | 'success' | 'danger'
  showIcon?: boolean
}>(), {
  variant: 'info',
  showIcon: true,
})

// ...стили по variant
</script>

Props передаются с бэкенда — text, variant, showIcon управляются из PHP.

Slot в формах ресурсов

Slot также можно использовать в массиве fields() ресурса — наравне с полями. На фронтенде он рендерится как произвольный компонент внутри формы:

app/Frame/Resources/PostResource.php
use Frame\Core\Slot;

public function fields(): array
{
    return [
        ID::make('id'),
        Text::make('title'),

        Slot::make('UserSalesComponent', [
            'chartType' => 'bar',
        ]),

        BelongsTo::make('author', UserResource::class),
    ];
}

Компонент должен быть зарегистрирован глобально через app.component():

app.component('UserSalesComponent', UserSalesComponent)

В форме компонент получает пропсы resource, formContext и всё что передано в props.