From 1c49c3107e476e7f0754500aa17b7618a2e0e6ee Mon Sep 17 00:00:00 2001 From: Saade Date: Wed, 14 Feb 2024 15:00:24 -0300 Subject: [PATCH] feat: indent and dedent action --- resources/lang/en/adjacency-list.php | 8 +++ resources/lang/pt_BR/adjacency-list.php | 8 +++ resources/views/builder.blade.php | 6 +- resources/views/components/item.blade.php | 14 ++-- src/Forms/Components/Actions/DedentAction.php | 59 ++++++++++++++++ src/Forms/Components/Actions/IndentAction.php | 69 +++++++++++++++++++ src/Forms/Components/Component.php | 2 + src/Forms/Components/Concerns/HasActions.php | 64 +++++++++++++++++ 8 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 src/Forms/Components/Actions/DedentAction.php create mode 100644 src/Forms/Components/Actions/IndentAction.php diff --git a/resources/lang/en/adjacency-list.php b/resources/lang/en/adjacency-list.php index f8ae2c7..fc5764b 100644 --- a/resources/lang/en/adjacency-list.php +++ b/resources/lang/en/adjacency-list.php @@ -49,6 +49,14 @@ 'reorder' => [ 'label' => 'Click and drag to reorder', ], + + 'indent' => [ + 'label' => 'Indent', + ], + + 'dedent' => [ + 'label' => 'Dedent', + ], ], 'items' => [ diff --git a/resources/lang/pt_BR/adjacency-list.php b/resources/lang/pt_BR/adjacency-list.php index 3ab0786..6a0778a 100644 --- a/resources/lang/pt_BR/adjacency-list.php +++ b/resources/lang/pt_BR/adjacency-list.php @@ -49,6 +49,14 @@ 'reorder' => [ 'label' => 'Clique e arraste para reordenar', ], + + 'indent' => [ + 'label' => 'Indentar', + ], + + 'dedent' => [ + 'label' => 'Desindentar', + ], ], 'items' => [ diff --git a/resources/views/builder.blade.php b/resources/views/builder.blade.php index d6f6955..4551be4 100644 --- a/resources/views/builder.blade.php +++ b/resources/views/builder.blade.php @@ -17,6 +17,7 @@ class="filament-navigation" $isDeletable = $isDeletable(); $isDisabled = $isDisabled(); $isEditable = $isEditable(); + $isIndentable = $isIndentable(); $isReorderable = $isReorderable(); $isCollapsible = $isCollapsible(); $isCollapsed = $isCollapsed(); @@ -24,7 +25,7 @@ class="filament-navigation" $maxDepth = $getMaxDepth(); $addAction = $getAction('add'); - $itemActions = [$getAction('addChild'), $getAction('delete'), $getAction('edit'), $getAction('reorder')]; + $itemActions = [$getAction('addChild'), $getAction('delete'), $getAction('edit'), $getAction('reorder'), $getAction('indent'), $getAction('dedent')]; @endphp
@@ -47,12 +48,15 @@ class="filament-navigation" :actions="$itemActions" :addable="$isAddable" :children-key="$getChildrenKey()" + :dedentable="$isIndentable && false" :deletable="$isDeletable" :disabled="$isDisabled" :editable="$isEditable" + :indentable="$isIndentable && (!$loop->first && $loop->count > 1)" :has-rulers="$hasRulers" :is-collapsed="$isCollapsed" :is-collapsible="$isCollapsible" + :is-indentable="$isIndentable" :item="$item" :item-state-path="$getStatePath() . '.' . $uuid" :label-key="$getLabelKey()" diff --git a/resources/views/components/item.blade.php b/resources/views/components/item.blade.php index bb0b9ac..625923e 100644 --- a/resources/views/components/item.blade.php +++ b/resources/views/components/item.blade.php @@ -1,4 +1,4 @@ -@props(['uuid', 'treeId', 'actions', 'addable', 'childrenKey', 'hasRulers', 'isCollapsible', 'isCollapsed', 'deletable', 'disabled', 'editable', 'item', 'itemStatePath', 'labelKey', 'reorderable', 'statePath']) +@props(['uuid', 'treeId', 'actions', 'addable', 'childrenKey', 'dedentable', 'hasRulers', 'indentable', 'isCollapsible', 'isCollapsed', 'isIndentable', 'deletable', 'disabled', 'editable', 'item', 'itemStatePath', 'labelKey', 'reorderable', 'statePath'])
@php - [$addChildAction, $deleteAction, $editAction, $reorderAction] = $actions; + [$addChildAction, $deleteAction, $editAction, $reorderAction, $indentAction, $dedentAction] = $actions; $hasChildren = count($item[$childrenKey] ?? []) > 0; @@ -63,8 +63,11 @@ class="px-2 text-gray-500 appearance-none" @if ($addable) {{ $addChildAction($mountArgs) }} @endif - @if ($editable) - {{ $editAction($mountArgs) }} + @if ($dedentable) + {{ $dedentAction($mountArgs) }} + @endif + @if ($indentable) + {{ $indentAction($mountArgs) }} @endif @if ($deletable) {{ $deleteAction($mountArgs) }} @@ -100,10 +103,13 @@ class="px-2 text-gray-500 appearance-none" :children-key="$childrenKey" :deletable="$deletable" :disabled="$disabled" + :dedentable="$isIndentable && true" :editable="$editable" + :indentable="$isIndentable && (!$loop->first && $loop->count > 1)" :has-rulers="$hasRulers" :is-collapsed="$isCollapsed" :is-collapsible="$isCollapsible" + :is-indentable="$isIndentable" :item="$child" :item-state-path="$itemStatePath . '.' . $childrenKey . '.' . $uuid" :label-key="$labelKey" diff --git a/src/Forms/Components/Actions/DedentAction.php b/src/Forms/Components/Actions/DedentAction.php new file mode 100644 index 0000000..4f899f1 --- /dev/null +++ b/src/Forms/Components/Actions/DedentAction.php @@ -0,0 +1,59 @@ +iconButton()->icon('heroicon-o-arrow-left')->color('gray'); + + $this->label(fn (): string => __('filament-adjacency-list::adjacency-list.actions.dedent.label')); + + $this->size(ActionSize::ExtraSmall); + + $this->action( + function (Component $component, array $arguments): void { + $statePath = $component->getRelativeStatePath($arguments['statePath']); + $state = $component->getState(); + + $item = data_get($state, $statePath); + $uuid = (string) Str::afterLast($statePath, '.'); + + $parentPath = (string) Str::beforeLast($statePath, '.'); + $parent = data_get($state, $parentPath); + + $pathToMoveInto = (string) Str::of($statePath)->beforeLast('.')->rtrim('.children')->beforeLast('.'); + $pathToMoveIntoData = data_get($state, $pathToMoveInto); + + if (array_key_exists($pathToMoveInto, $state) || ! str_contains($pathToMoveInto, '.children')) { + data_set($state, $uuid, $item); + } else { + $pathToMoveIntoData[$uuid] = $item; + data_set($state, $pathToMoveInto, $pathToMoveIntoData); + } + + data_set($state, $parentPath, Arr::except($parent, $uuid)); + + $component->state($state); + } + ); + + $this->visible( + fn (Component $component): bool => $component->isIndentable() + ); + } +} diff --git a/src/Forms/Components/Actions/IndentAction.php b/src/Forms/Components/Actions/IndentAction.php new file mode 100644 index 0000000..9ca06de --- /dev/null +++ b/src/Forms/Components/Actions/IndentAction.php @@ -0,0 +1,69 @@ +iconButton()->icon('heroicon-o-arrow-right')->color('gray'); + + $this->label(fn (): string => __('filament-adjacency-list::adjacency-list.actions.indent.label')); + + $this->size(ActionSize::ExtraSmall); + + $this->action( + function (Component $component, array $arguments): void { + $statePath = $component->getRelativeStatePath($arguments['statePath']); + $state = $component->getState(); + + $item = data_get($state, $statePath); + $uuid = Str::afterLast($statePath, '.'); + + $parentPath = Str::beforeLast($statePath, '.'); + $parent = data_get($state, $parentPath); + + if ($parentPath === $uuid) { + $parent = $state; + } + + $keys = array_keys($parent); + $position = array_search($uuid, $keys); + + $previous = $parent[$keys[$position - 1]]; + + if (! isset($previous['children'])) { + $previous['children'] = []; + } + + $previous['children'][$uuid] = $item; + $parent[$keys[$position - 1]] = $previous; + + if ($parentPath === $uuid) { + $state = Arr::except($parent, $uuid); + } else { + data_set($state, $parentPath, Arr::except($parent, $uuid)); + } + + $component->state($state); + } + ); + + $this->visible( + fn (Component $component): bool => $component->isIndentable() + ); + } +} diff --git a/src/Forms/Components/Component.php b/src/Forms/Components/Component.php index f5d6f56..f711aed 100644 --- a/src/Forms/Components/Component.php +++ b/src/Forms/Components/Component.php @@ -41,6 +41,8 @@ protected function setUp(): void fn (Component $component): Action => $component->getDeleteAction(), fn (Component $component): Action => $component->getEditAction(), fn (Component $component): Action => $component->getReorderAction(), + fn (Component $component): Action => $component->getIndentAction(), + fn (Component $component): Action => $component->getDedentAction(), ]); $this->registerListeners([ diff --git a/src/Forms/Components/Concerns/HasActions.php b/src/Forms/Components/Concerns/HasActions.php index db287ce..cf31e84 100644 --- a/src/Forms/Components/Concerns/HasActions.php +++ b/src/Forms/Components/Concerns/HasActions.php @@ -6,8 +6,10 @@ use Filament\Forms\Components\Actions\Action; use Saade\FilamentAdjacencyList\Forms\Components\Actions\AddAction; use Saade\FilamentAdjacencyList\Forms\Components\Actions\AddChildAction; +use Saade\FilamentAdjacencyList\Forms\Components\Actions\DedentAction; use Saade\FilamentAdjacencyList\Forms\Components\Actions\DeleteAction; use Saade\FilamentAdjacencyList\Forms\Components\Actions\EditAction; +use Saade\FilamentAdjacencyList\Forms\Components\Actions\IndentAction; use Saade\FilamentAdjacencyList\Forms\Components\Actions\ReorderAction; trait HasActions @@ -20,6 +22,8 @@ trait HasActions protected bool | Closure $isReorderable = true; + protected bool | Closure $isIndentable = true; + protected ?Closure $modifyAddActionUsing = null; protected ?Closure $modifyAddChildActionUsing = null; @@ -30,6 +34,10 @@ trait HasActions protected ?Closure $modifyReorderActionUsing = null; + protected ?Closure $modifyIndentActionUsing = null; + + protected ?Closure $modifyDedentActionUsing = null; + public function getAddAction(): Action { $action = AddAction::make(); @@ -135,6 +143,46 @@ public function reorderAction(?Closure $callback): static return $this; } + public function getIndentAction(): Action + { + $action = IndentAction::make(); + + if ($this->modifyIndentActionUsing) { + $action = $this->evaluate($this->modifyIndentActionUsing, [ + 'action' => $action, + ]) ?? $action; + } + + return $action; + } + + public function indentAction(?Closure $callback): static + { + $this->modifyIndentActionUsing = $callback; + + return $this; + } + + public function getDedentAction(): Action + { + $action = DedentAction::make(); + + if ($this->modifyDedentActionUsing) { + $action = $this->evaluate($this->modifyDedentActionUsing, [ + 'action' => $action, + ]) ?? $action; + } + + return $action; + } + + public function dedentAction(?Closure $callback): static + { + $this->modifyDedentActionUsing = $callback; + + return $this; + } + public function addable(bool | Closure $condition = true): static { $this->isAddable = $condition; @@ -198,4 +246,20 @@ public function isReorderable(): bool return (bool) $this->evaluate($this->isReorderable); } + + public function indentable(bool | Closure $condition = true): static + { + $this->isIndentable = $condition; + + return $this; + } + + public function isIndentable(): bool + { + if ($this->isDisabled()) { + return false; + } + + return (bool) $this->evaluate($this->isIndentable); + } }