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);
+ }
}