mirror of
https://github.com/filamentphp/filament.git
synced 2026-10-16 07:17:49 +08:00
922 lines
26 KiB
PHP
922 lines
26 KiB
PHP
<?php
|
|
|
|
namespace Filament\Forms\Components;
|
|
|
|
use Closure;
|
|
use Filament\Forms\ComponentContainer;
|
|
use Filament\Forms\Components\Actions\Action;
|
|
use Filament\Forms\Components\Builder\Block;
|
|
use Filament\Support\Enums\ActionSize;
|
|
use Filament\Support\Enums\MaxWidth;
|
|
use Filament\Support\Facades\FilamentIcon;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Str;
|
|
|
|
use function Filament\Forms\array_move_after;
|
|
use function Filament\Forms\array_move_before;
|
|
|
|
class Builder extends Field implements Contracts\CanConcealComponents, Contracts\HasExtraItemActions
|
|
{
|
|
use Concerns\CanBeCloned;
|
|
use Concerns\CanBeCollapsed;
|
|
use Concerns\CanGenerateUuids;
|
|
use Concerns\CanLimitItemsLength;
|
|
use Concerns\HasExtraItemActions;
|
|
|
|
/**
|
|
* @var view-string
|
|
*/
|
|
protected string $view = 'filament-forms::components.builder';
|
|
|
|
protected string | Closure | null $addBetweenActionLabel = null;
|
|
|
|
protected string | Closure | null $addActionLabel = null;
|
|
|
|
protected bool | Closure $isReorderable = true;
|
|
|
|
protected bool | Closure $isReorderableWithDragAndDrop = true;
|
|
|
|
protected bool | Closure $isReorderableWithButtons = false;
|
|
|
|
protected bool | Closure $isAddable = true;
|
|
|
|
protected bool | Closure $isDeletable = true;
|
|
|
|
protected bool | Closure $hasBlockLabels = true;
|
|
|
|
protected bool | Closure $hasBlockNumbers = true;
|
|
|
|
protected ?Closure $modifyAddActionUsing = null;
|
|
|
|
protected ?Closure $modifyAddBetweenActionUsing = null;
|
|
|
|
protected ?Closure $modifyCloneActionUsing = null;
|
|
|
|
protected ?Closure $modifyDeleteActionUsing = null;
|
|
|
|
protected ?Closure $modifyMoveDownActionUsing = null;
|
|
|
|
protected ?Closure $modifyMoveUpActionUsing = null;
|
|
|
|
protected ?Closure $modifyReorderActionUsing = null;
|
|
|
|
protected ?Closure $modifyCollapseActionUsing = null;
|
|
|
|
protected ?Closure $modifyExpandActionUsing = null;
|
|
|
|
protected ?Closure $modifyCollapseAllActionUsing = null;
|
|
|
|
protected ?Closure $modifyExpandAllActionUsing = null;
|
|
|
|
protected string | Closure | null $labelBetweenItems = null;
|
|
|
|
protected bool | Closure $isBlockLabelTruncated = true;
|
|
|
|
/**
|
|
* @var array<string, int | string | null> | null
|
|
*/
|
|
protected ?array $blockPickerColumns = [];
|
|
|
|
protected MaxWidth | string | Closure | null $blockPickerWidth = null;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->default([]);
|
|
|
|
$this->afterStateHydrated(static function (Builder $component, ?array $state): void {
|
|
$items = [];
|
|
|
|
foreach ($state ?? [] as $itemData) {
|
|
$items[$component->generateUuid()] = $itemData;
|
|
}
|
|
|
|
$component->state($items);
|
|
});
|
|
|
|
$this->registerActions([
|
|
fn (Builder $component): Action => $component->getAddAction(),
|
|
fn (Builder $component): Action => $component->getAddBetweenAction(),
|
|
fn (Builder $component): Action => $component->getCloneAction(),
|
|
fn (Builder $component): Action => $component->getCollapseAction(),
|
|
fn (Builder $component): Action => $component->getCollapseAllAction(),
|
|
fn (Builder $component): Action => $component->getDeleteAction(),
|
|
fn (Builder $component): Action => $component->getExpandAction(),
|
|
fn (Builder $component): Action => $component->getExpandAllAction(),
|
|
fn (Builder $component): Action => $component->getMoveDownAction(),
|
|
fn (Builder $component): Action => $component->getMoveUpAction(),
|
|
fn (Builder $component): Action => $component->getReorderAction(),
|
|
]);
|
|
|
|
$this->mutateDehydratedStateUsing(static function (?array $state): array {
|
|
return array_values($state ?? []);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param array<Block> | Closure $blocks
|
|
*/
|
|
public function blocks(array | Closure $blocks): static
|
|
{
|
|
$this->childComponents($blocks);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getAddAction(): Action
|
|
{
|
|
$action = Action::make($this->getAddActionName())
|
|
->label(fn (Builder $component) => $component->getAddActionLabel())
|
|
->color('gray')
|
|
->action(function (array $arguments, Builder $component): void {
|
|
$newUuid = $component->generateUuid();
|
|
|
|
$items = $component->getState();
|
|
$items[$newUuid] = [
|
|
'type' => $arguments['block'],
|
|
'data' => [],
|
|
];
|
|
|
|
$component->state($items);
|
|
|
|
$component->getChildComponentContainer($newUuid)->fill();
|
|
|
|
$component->collapsed(false, shouldMakeComponentCollapsible: false);
|
|
|
|
$component->callAfterStateUpdated();
|
|
})
|
|
->livewireClickHandlerEnabled(false)
|
|
->button()
|
|
->size(ActionSize::Small)
|
|
->visible(fn (Builder $component): bool => $component->isAddable());
|
|
|
|
if ($this->modifyAddActionUsing) {
|
|
$action = $this->evaluate($this->modifyAddActionUsing, [
|
|
'action' => $action,
|
|
]) ?? $action;
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
public function addAction(?Closure $callback): static
|
|
{
|
|
$this->modifyAddActionUsing = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getAddActionName(): string
|
|
{
|
|
return 'add';
|
|
}
|
|
|
|
public function getAddBetweenAction(): Action
|
|
{
|
|
$action = Action::make($this->getAddBetweenActionName())
|
|
->label(fn (Builder $component) => $component->getAddBetweenActionLabel())
|
|
->color('gray')
|
|
->action(function (array $arguments, Builder $component): void {
|
|
$newUuid = $component->generateUuid();
|
|
|
|
$items = [];
|
|
|
|
foreach ($component->getState() ?? [] as $uuid => $item) {
|
|
$items[$uuid] = $item;
|
|
|
|
if ($uuid === $arguments['afterItem']) {
|
|
$items[$newUuid] = [
|
|
'type' => $arguments['block'],
|
|
'data' => [],
|
|
];
|
|
}
|
|
}
|
|
|
|
$component->state($items);
|
|
|
|
$component->getChildComponentContainer($newUuid)->fill();
|
|
|
|
$component->collapsed(false, shouldMakeComponentCollapsible: false);
|
|
|
|
$component->callAfterStateUpdated();
|
|
})
|
|
->livewireClickHandlerEnabled(false)
|
|
->button()
|
|
->size(ActionSize::Small)
|
|
->visible(fn (Builder $component): bool => $component->isAddable());
|
|
|
|
if ($this->modifyAddBetweenActionUsing) {
|
|
$action = $this->evaluate($this->modifyAddBetweenActionUsing, [
|
|
'action' => $action,
|
|
]) ?? $action;
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
public function addBetweenAction(?Closure $callback): static
|
|
{
|
|
$this->modifyAddBetweenActionUsing = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getAddBetweenActionName(): string
|
|
{
|
|
return 'addBetween';
|
|
}
|
|
|
|
public function getCloneAction(): Action
|
|
{
|
|
$action = Action::make($this->getCloneActionName())
|
|
->label(__('filament-forms::components.builder.actions.clone.label'))
|
|
->icon(FilamentIcon::resolve('forms::components.builder.actions.clone') ?? 'heroicon-m-square-2-stack')
|
|
->color('gray')
|
|
->action(function (array $arguments, Builder $component): void {
|
|
$newUuid = $component->generateUuid();
|
|
|
|
$items = $component->getState();
|
|
$items[$newUuid] = $items[$arguments['item']];
|
|
|
|
$component->state($items);
|
|
|
|
$component->collapsed(false, shouldMakeComponentCollapsible: false);
|
|
|
|
$component->callAfterStateUpdated();
|
|
})
|
|
->iconButton()
|
|
->size(ActionSize::Small)
|
|
->visible(fn (Builder $component): bool => $component->isCloneable());
|
|
|
|
if ($this->modifyCloneActionUsing) {
|
|
$action = $this->evaluate($this->modifyCloneActionUsing, [
|
|
'action' => $action,
|
|
]) ?? $action;
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
public function cloneAction(?Closure $callback): static
|
|
{
|
|
$this->modifyCloneActionUsing = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getCloneActionName(): string
|
|
{
|
|
return 'clone';
|
|
}
|
|
|
|
public function getDeleteAction(): Action
|
|
{
|
|
$action = Action::make($this->getDeleteActionName())
|
|
->label(__('filament-forms::components.builder.actions.delete.label'))
|
|
->icon(FilamentIcon::resolve('forms::components.builder.actions.delete') ?? 'heroicon-m-trash')
|
|
->color('danger')
|
|
->action(function (array $arguments, Builder $component): void {
|
|
$items = $component->getState();
|
|
unset($items[$arguments['item']]);
|
|
|
|
$component->state($items);
|
|
|
|
$component->callAfterStateUpdated();
|
|
})
|
|
->iconButton()
|
|
->size(ActionSize::Small)
|
|
->visible(fn (Builder $component): bool => $component->isDeletable());
|
|
|
|
if ($this->modifyDeleteActionUsing) {
|
|
$action = $this->evaluate($this->modifyDeleteActionUsing, [
|
|
'action' => $action,
|
|
]) ?? $action;
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
public function deleteAction(?Closure $callback): static
|
|
{
|
|
$this->modifyDeleteActionUsing = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getDeleteActionName(): string
|
|
{
|
|
return 'delete';
|
|
}
|
|
|
|
public function getMoveDownAction(): Action
|
|
{
|
|
$action = Action::make($this->getMoveDownActionName())
|
|
->label(__('filament-forms::components.builder.actions.move_down.label'))
|
|
->icon(FilamentIcon::resolve('forms::components.builder.actions.move-down') ?? 'heroicon-m-arrow-down')
|
|
->color('gray')
|
|
->action(function (array $arguments, Builder $component): void {
|
|
$items = array_move_after($component->getState(), $arguments['item']);
|
|
|
|
$component->state($items);
|
|
|
|
$component->callAfterStateUpdated();
|
|
})
|
|
->iconButton()
|
|
->size(ActionSize::Small)
|
|
->visible(fn (Builder $component): bool => $component->isReorderable());
|
|
|
|
if ($this->modifyMoveDownActionUsing) {
|
|
$action = $this->evaluate($this->modifyMoveDownActionUsing, [
|
|
'action' => $action,
|
|
]) ?? $action;
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
public function moveDownAction(?Closure $callback): static
|
|
{
|
|
$this->modifyMoveDownActionUsing = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getMoveDownActionName(): string
|
|
{
|
|
return 'moveDown';
|
|
}
|
|
|
|
public function getMoveUpAction(): Action
|
|
{
|
|
$action = Action::make($this->getMoveUpActionName())
|
|
->label(__('filament-forms::components.builder.actions.move_up.label'))
|
|
->icon(FilamentIcon::resolve('forms::components.builder.actions.move-up') ?? 'heroicon-m-arrow-up')
|
|
->color('gray')
|
|
->action(function (array $arguments, Builder $component): void {
|
|
$items = array_move_before($component->getState(), $arguments['item']);
|
|
|
|
$component->state($items);
|
|
|
|
$component->callAfterStateUpdated();
|
|
})
|
|
->iconButton()
|
|
->size(ActionSize::Small)
|
|
->visible(fn (Builder $component): bool => $component->isReorderable());
|
|
|
|
if ($this->modifyMoveUpActionUsing) {
|
|
$action = $this->evaluate($this->modifyMoveUpActionUsing, [
|
|
'action' => $action,
|
|
]) ?? $action;
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
public function moveUpAction(?Closure $callback): static
|
|
{
|
|
$this->modifyMoveUpActionUsing = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function labelBetweenItems(string | Closure | null $label): static
|
|
{
|
|
$this->labelBetweenItems = $label;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getMoveUpActionName(): string
|
|
{
|
|
return 'moveUp';
|
|
}
|
|
|
|
public function getReorderAction(): Action
|
|
{
|
|
$action = Action::make($this->getReorderActionName())
|
|
->label(__('filament-forms::components.builder.actions.reorder.label'))
|
|
->icon(FilamentIcon::resolve('forms::components.builder.actions.reorder') ?? 'heroicon-m-arrows-up-down')
|
|
->color('gray')
|
|
->action(function (array $arguments, Builder $component): void {
|
|
$items = [
|
|
...array_flip($arguments['items']),
|
|
...$component->getState(),
|
|
];
|
|
|
|
$component->state($items);
|
|
|
|
$component->callAfterStateUpdated();
|
|
})
|
|
->livewireClickHandlerEnabled(false)
|
|
->iconButton()
|
|
->size(ActionSize::Small)
|
|
->visible(fn (Builder $component): bool => $component->isReorderableWithDragAndDrop());
|
|
|
|
if ($this->modifyReorderActionUsing) {
|
|
$action = $this->evaluate($this->modifyReorderActionUsing, [
|
|
'action' => $action,
|
|
]) ?? $action;
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
public function reorderAction(?Closure $callback): static
|
|
{
|
|
$this->modifyReorderActionUsing = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getReorderActionName(): string
|
|
{
|
|
return 'reorder';
|
|
}
|
|
|
|
public function getCollapseAction(): Action
|
|
{
|
|
$action = Action::make($this->getCollapseActionName())
|
|
->label(__('filament-forms::components.builder.actions.collapse.label'))
|
|
->icon(FilamentIcon::resolve('forms::components.builder.actions.collapse') ?? 'heroicon-m-chevron-up')
|
|
->color('gray')
|
|
->livewireClickHandlerEnabled(false)
|
|
->iconButton()
|
|
->size(ActionSize::Small);
|
|
|
|
if ($this->modifyCollapseActionUsing) {
|
|
$action = $this->evaluate($this->modifyCollapseActionUsing, [
|
|
'action' => $action,
|
|
]) ?? $action;
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
public function collapseAction(?Closure $callback): static
|
|
{
|
|
$this->modifyCollapseActionUsing = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getCollapseActionName(): string
|
|
{
|
|
return 'collapse';
|
|
}
|
|
|
|
public function getExpandAction(): Action
|
|
{
|
|
$action = Action::make($this->getExpandActionName())
|
|
->label(__('filament-forms::components.builder.actions.expand.label'))
|
|
->icon(FilamentIcon::resolve('forms::components.builder.actions.expand') ?? 'heroicon-m-chevron-down')
|
|
->color('gray')
|
|
->livewireClickHandlerEnabled(false)
|
|
->iconButton()
|
|
->size(ActionSize::Small);
|
|
|
|
if ($this->modifyExpandActionUsing) {
|
|
$action = $this->evaluate($this->modifyExpandActionUsing, [
|
|
'action' => $action,
|
|
]) ?? $action;
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
public function expandAction(?Closure $callback): static
|
|
{
|
|
$this->modifyExpandActionUsing = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getExpandActionName(): string
|
|
{
|
|
return 'expand';
|
|
}
|
|
|
|
public function getCollapseAllAction(): Action
|
|
{
|
|
$action = Action::make($this->getCollapseAllActionName())
|
|
->label(__('filament-forms::components.builder.actions.collapse_all.label'))
|
|
->color('gray')
|
|
->livewireClickHandlerEnabled(false)
|
|
->link()
|
|
->size(ActionSize::Small);
|
|
|
|
if ($this->modifyCollapseAllActionUsing) {
|
|
$action = $this->evaluate($this->modifyCollapseAllActionUsing, [
|
|
'action' => $action,
|
|
]) ?? $action;
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
public function collapseAllAction(?Closure $callback): static
|
|
{
|
|
$this->modifyCollapseAllActionUsing = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getCollapseAllActionName(): string
|
|
{
|
|
return 'collapseAll';
|
|
}
|
|
|
|
public function getExpandAllAction(): Action
|
|
{
|
|
$action = Action::make($this->getExpandAllActionName())
|
|
->label(__('filament-forms::components.builder.actions.expand_all.label'))
|
|
->color('gray')
|
|
->livewireClickHandlerEnabled(false)
|
|
->link()
|
|
->size(ActionSize::Small);
|
|
|
|
if ($this->modifyExpandAllActionUsing) {
|
|
$action = $this->evaluate($this->modifyExpandAllActionUsing, [
|
|
'action' => $action,
|
|
]) ?? $action;
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
public function expandAllAction(?Closure $callback): static
|
|
{
|
|
$this->modifyExpandAllActionUsing = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getExpandAllActionName(): string
|
|
{
|
|
return 'expandAll';
|
|
}
|
|
|
|
public function truncateBlockLabel(bool | Closure $condition = true): static
|
|
{
|
|
$this->isBlockLabelTruncated = $condition;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function addBetweenActionLabel(string | Closure | null $label): static
|
|
{
|
|
$this->addBetweenActionLabel = $label;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use `addBetweenActionLabel()` instead.
|
|
*/
|
|
public function createItemBetweenButtonLabel(string | Closure | null $label): static
|
|
{
|
|
$this->addBetweenActionLabel($label);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function addActionLabel(string | Closure | null $label): static
|
|
{
|
|
$this->addActionLabel = $label;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use `addActionLabel()` instead.
|
|
*/
|
|
public function createItemButtonLabel(string | Closure | null $label): static
|
|
{
|
|
$this->addActionLabel($label);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function addable(bool | Closure $condition = true): static
|
|
{
|
|
$this->isAddable = $condition;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function deletable(bool | Closure $condition = true): static
|
|
{
|
|
$this->isDeletable = $condition;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function reorderable(bool | Closure $condition = true): static
|
|
{
|
|
$this->isReorderable = $condition;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use `addable()` instead.
|
|
*/
|
|
public function disableItemCreation(bool | Closure $condition = true): static
|
|
{
|
|
$this->addable(fn (Builder $component): bool => ! $this->evaluate($condition));
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use `deletable()` instead.
|
|
*/
|
|
public function disableItemDeletion(bool | Closure $condition = true): static
|
|
{
|
|
$this->deletable(fn (Builder $component): bool => ! $this->evaluate($condition));
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use `reorderable()` instead.
|
|
*/
|
|
public function disableItemMovement(bool | Closure $condition = true): static
|
|
{
|
|
$this->reorderable(fn (Builder $component): bool => ! $this->evaluate($condition));
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @deprecated No longer part of the design system.
|
|
*/
|
|
public function inset(bool | Closure $condition = true): static
|
|
{
|
|
return $this;
|
|
}
|
|
|
|
public function reorderableWithDragAndDrop(bool | Closure $condition = true): static
|
|
{
|
|
$this->isReorderableWithDragAndDrop = $condition;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function reorderableWithButtons(bool | Closure $condition = true): static
|
|
{
|
|
$this->isReorderableWithButtons = $condition;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use `blockLabels()` instead.
|
|
*/
|
|
public function showBlockLabels(bool | Closure $condition = true): static
|
|
{
|
|
$this->withBlockLabels($condition);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use `blockLabels()` instead.
|
|
*/
|
|
public function withBlockLabels(bool | Closure $condition = true): static
|
|
{
|
|
$this->blockLabels($condition);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use `blockNumbers()` instead.
|
|
*/
|
|
public function withBlockNumbers(bool | Closure $condition = true): static
|
|
{
|
|
$this->blockNumbers($condition);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function blockLabels(bool | Closure $condition = true): static
|
|
{
|
|
$this->hasBlockLabels = $condition;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function blockNumbers(bool | Closure $condition = true): static
|
|
{
|
|
$this->hasBlockNumbers = $condition;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getBlock(string $name): ?Block
|
|
{
|
|
return Arr::first(
|
|
$this->getBlocks(),
|
|
fn (Block $block): bool => $block->getName() === $name,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return array<Component>
|
|
*/
|
|
public function getBlocks(): array
|
|
{
|
|
return $this->getChildComponentContainer()->getComponents();
|
|
}
|
|
|
|
public function getChildComponentContainers(bool $withHidden = false): array
|
|
{
|
|
if ((! $withHidden) && $this->isHidden()) {
|
|
return [];
|
|
}
|
|
|
|
return collect($this->getState())
|
|
->filter(fn (array $itemData): bool => $this->hasBlock($itemData['type']))
|
|
->map(
|
|
fn (array $itemData, $itemIndex): ComponentContainer => $this
|
|
->getBlock($itemData['type'])
|
|
->getChildComponentContainer()
|
|
->statePath("{$itemIndex}.data")
|
|
->inlineLabel(false)
|
|
->getClone(),
|
|
)
|
|
->all();
|
|
}
|
|
|
|
public function getAddBetweenActionLabel(): string
|
|
{
|
|
return $this->evaluate($this->addBetweenActionLabel) ?? __('filament-forms::components.builder.actions.add_between.label');
|
|
}
|
|
|
|
public function getAddActionLabel(): string
|
|
{
|
|
return $this->evaluate($this->addActionLabel) ?? __('filament-forms::components.builder.actions.add.label', [
|
|
'label' => Str::lcfirst($this->getLabel()),
|
|
]);
|
|
}
|
|
|
|
public function hasBlock(string $name): bool
|
|
{
|
|
return (bool) $this->getBlock($name);
|
|
}
|
|
|
|
public function isReorderable(): bool
|
|
{
|
|
if ($this->isDisabled()) {
|
|
return false;
|
|
}
|
|
|
|
return (bool) $this->evaluate($this->isReorderable);
|
|
}
|
|
|
|
public function isReorderableWithDragAndDrop(): bool
|
|
{
|
|
return $this->evaluate($this->isReorderableWithDragAndDrop) && $this->isReorderable();
|
|
}
|
|
|
|
public function isReorderableWithButtons(): bool
|
|
{
|
|
return $this->evaluate($this->isReorderableWithButtons) && $this->isReorderable();
|
|
}
|
|
|
|
public function isAddable(): bool
|
|
{
|
|
if ($this->isDisabled()) {
|
|
return false;
|
|
}
|
|
|
|
if (filled($this->getMaxItems()) && ($this->getMaxItems() <= $this->getItemsCount())) {
|
|
return false;
|
|
}
|
|
|
|
return (bool) $this->evaluate($this->isAddable);
|
|
}
|
|
|
|
public function isDeletable(): bool
|
|
{
|
|
if ($this->isDisabled()) {
|
|
return false;
|
|
}
|
|
|
|
return (bool) $this->evaluate($this->isDeletable);
|
|
}
|
|
|
|
public function hasBlockLabels(): bool
|
|
{
|
|
return (bool) $this->evaluate($this->hasBlockLabels);
|
|
}
|
|
|
|
public function hasBlockNumbers(): bool
|
|
{
|
|
return (bool) $this->evaluate($this->hasBlockNumbers);
|
|
}
|
|
|
|
public function canConcealComponents(): bool
|
|
{
|
|
return $this->isCollapsible();
|
|
}
|
|
|
|
public function getLabelBetweenItems(): ?string
|
|
{
|
|
return $this->evaluate($this->labelBetweenItems);
|
|
}
|
|
|
|
public function isBlockLabelTruncated(): bool
|
|
{
|
|
return (bool) $this->evaluate($this->isBlockLabelTruncated);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, int | string | null> | int | string | null $columns
|
|
*/
|
|
public function blockPickerColumns(array | int | string | null $columns = 2): static
|
|
{
|
|
if (! is_array($columns)) {
|
|
$columns = [
|
|
'lg' => $columns,
|
|
];
|
|
}
|
|
|
|
$this->blockPickerColumns = [
|
|
...($this->blockPickerColumns ?? []),
|
|
...$columns,
|
|
];
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, int | string | null> | int | string | null
|
|
*/
|
|
public function getBlockPickerColumns(?string $breakpoint = null): array | int | string | null
|
|
{
|
|
$columns = $this->blockPickerColumns ?? [
|
|
'default' => 1,
|
|
'sm' => null,
|
|
'md' => null,
|
|
'lg' => null,
|
|
'xl' => null,
|
|
'2xl' => null,
|
|
];
|
|
|
|
if ($breakpoint !== null) {
|
|
return $columns[$breakpoint] ?? null;
|
|
}
|
|
|
|
return $columns;
|
|
}
|
|
|
|
public function blockPickerWidth(MaxWidth | string | Closure | null $width): static
|
|
{
|
|
$this->blockPickerWidth = $width;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getBlockPickerWidth(): MaxWidth | string | null
|
|
{
|
|
$width = $this->evaluate($this->blockPickerWidth);
|
|
|
|
if (filled($width)) {
|
|
return $width;
|
|
}
|
|
|
|
$columns = $this->getBlockPickerColumns();
|
|
|
|
if (empty($columns)) {
|
|
return null;
|
|
}
|
|
|
|
return match (max($columns)) {
|
|
2 => 'md',
|
|
3 => '2xl',
|
|
4 => '4xl',
|
|
5 => '6xl',
|
|
6 => '7xl',
|
|
default => null,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function getItemState(string $uuid): array
|
|
{
|
|
return $this->getChildComponentContainer($uuid)->getState(shouldCallHooksBefore: false);
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function getRawItemState(string $uuid): array
|
|
{
|
|
return $this->getChildComponentContainer($uuid)->getRawState();
|
|
}
|
|
}
|