Skip to content

Commit 540d54b

Browse files
committed
Add support for tree toolbar actions
Introduces a new mechanism for defining and rendering toolbar actions in tree components. Adds a getTreeToolbarActions method to code generators, a toolbarActions property and related methods to the Tree component, and updates the Blade view to display toolbar actions. Refactors tree action classes to use a shared TreeActionTrait for consistency.
1 parent f38aae5 commit 540d54b

File tree

17 files changed

+338
-338
lines changed

17 files changed

+338
-338
lines changed

resources/dist/filament-tree.css

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/views/components/tree/index.blade.php

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
$containerKey = 'filament_tree_container_' . $this->getId();
33
$maxDepth = $getMaxDepth() ?? 1;
44
$records = collect($this->getRootLayerRecords() ?? []);
5-
5+
$toolbarActions = $tree->getToolbarActions() ?? [];
66
@endphp
77

88
<div class="filament-tree-component"
@@ -14,25 +14,36 @@
1414
containerKey: {{ $containerKey }},
1515
maxDepth: {{ $maxDepth }}
1616
})">
17-
<x-filament::section :heading="($this->displayTreeTitle() ?? false) ? $this->getTreeTitle() : null">
17+
<x-filament::section>
18+
<x-slot name="heading">
19+
{{ ($this->displayTreeTitle() ?? false) ? $this->getTreeTitle() : null }}
20+
</x-slot>
1821
<menu class="nestable-menu" id="nestable-menu">
19-
<div class="btn-group">
20-
<x-filament::button color="gray" tag="button" data-action="expand-all" x-on:click="expandAll()" wire:loading.attr="disabled" wire:loading.class="cursor-wait opacity-70">
21-
{{ __('filament-tree::filament-tree.button.expand_all') }}
22-
</x-filament::button>
23-
<x-filament::button color="gray" tag="button" data-action="collapse-all" x-on:click="collapseAll()" wire:loading.attr="disabled" wire:loading.class="cursor-wait opacity-70">
24-
{{ __('filament-tree::filament-tree.button.collapse_all') }}
25-
</x-filament::button>
22+
<div class="toolbar-btns main">
23+
<div class="btn-group">
24+
<x-filament::button color="gray" tag="button" data-action="expand-all" x-on:click="expandAll()" wire:loading.attr="disabled" wire:loading.class="cursor-wait opacity-70">
25+
{{ __('filament-tree::filament-tree.button.expand_all') }}
26+
</x-filament::button>
27+
<x-filament::button color="gray" tag="button" data-action="collapse-all" x-on:click="collapseAll()" wire:loading.attr="disabled" wire:loading.class="cursor-wait opacity-70">
28+
{{ __('filament-tree::filament-tree.button.collapse_all') }}
29+
</x-filament::button>
30+
</div>
31+
<div class="btn-group">
32+
<x-filament::button tag="button" data-action="save" x-on:click="save()" wire:loading.attr="disabled" wire:loading.class="cursor-wait opacity-70">
33+
<x-filament::loading-indicator class="h-4 w-4" wire:loading wire:target="updateTree"/>
34+
<span wire:loading.remove wire:target="updateTree">
35+
{{ __('filament-tree::filament-tree.button.save') }}
36+
</span>
37+
</x-filament::button>
38+
</div>
2639
</div>
27-
<div class="btn-group">
28-
<x-filament::button tag="button" data-action="save" x-on:click="save()" wire:loading.attr="disabled" wire:loading.class="cursor-wait opacity-70">
29-
<x-filament::loading-indicator class="h-4 w-4" wire:loading wire:target="updateTree"/>
30-
<span wire:loading.remove wire:target="updateTree">
31-
{{ __('filament-tree::filament-tree.button.save') }}
32-
</span>
3340

34-
</x-filament::button>
35-
</div>
41+
@if (is_array($toolbarActions) && count($toolbarActions))
42+
<x-filament::actions
43+
class="toolbar-btns"
44+
:actions="$toolbarActions"
45+
/>
46+
@endif
3647
</menu>
3748
<div class="filament-tree dd" id="{{ $containerKey }}" x-ref="treeContainer">
3849
<x-filament-tree::tree.list :records="$records" :containerKey="$containerKey" :tree="$tree"/>

src/Actions/Action.php

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3,81 +3,12 @@
33
namespace SolutionForest\FilamentTree\Actions;
44

55
use Filament\Actions\Action as BaseAction;
6-
use Illuminate\Database\Eloquent\Model;
76
use SolutionForest\FilamentTree\Concern\Actions\HasTree;
7+
use SolutionForest\FilamentTree\Concern\Actions\TreeActionTrait;
88
use SolutionForest\FilamentTree\Concern\BelongsToTree;
99

1010
class Action extends BaseAction implements HasTree
1111
{
1212
use BelongsToTree;
13-
14-
public function getLivewireClickHandler(): ?string
15-
{
16-
if (! $this->isLivewireClickHandlerEnabled()) {
17-
return null;
18-
}
19-
20-
if (is_string($this->action)) {
21-
return $this->action;
22-
}
23-
24-
if ($record = $this->getRecord()) {
25-
$recordKey = $this->getLivewire()->getRecordKey($record);
26-
27-
return "mountTreeAction('{$this->getName()}', '{$recordKey}')";
28-
}
29-
30-
return "mountTreeAction('{$this->getName()}')";
31-
}
32-
33-
/**
34-
* @return array<mixed>
35-
*/
36-
protected function resolveDefaultClosureDependencyForEvaluationByName(string $parameterName): array
37-
{
38-
return match ($parameterName) {
39-
'tree' => [$this->getTree()],
40-
default => parent::resolveDefaultClosureDependencyForEvaluationByName($parameterName),
41-
};
42-
}
43-
44-
public function getRecordTitle(?Model $record = null): string
45-
{
46-
$record ??= $this->getRecord();
47-
48-
return $this->getCustomRecordTitle($record) ?? $this->getLivewire()->getTreeRecordTitle($record);
49-
}
50-
51-
public function getRecordTitleAttribute(): ?string
52-
{
53-
return $this->getCustomRecordTitleAttribute() ?? $this->getTree()->getRecordTitleAttribute();
54-
}
55-
56-
public function getModelLabel(): string
57-
{
58-
return $this->getCustomModelLabel() ?? $this->getTree()->getModelLabel();
59-
}
60-
61-
public function getPluralModelLabel(): string
62-
{
63-
return $this->getCustomPluralModelLabel() ?? $this->getTree()->getPluralModelLabel();
64-
}
65-
66-
public function getModel(bool $withDefault = true): ?string
67-
{
68-
return $this->getCustomModel() ?? $this->getLivewire()->getModel();
69-
}
70-
71-
public function prepareModalAction(BaseAction $action): BaseAction
72-
{
73-
$action = parent::prepareModalAction($action);
74-
75-
if (! $action instanceof Action) {
76-
return $action;
77-
}
78-
79-
return $action
80-
->tree($this->getTree())
81-
->record($this->getRecord());
82-
}
13+
use TreeActionTrait;
8314
}

src/Actions/CreateAction.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace SolutionForest\FilamentTree\Actions;
4+
5+
use Filament\Actions\CreateAction as BaseCreateAction;
6+
use SolutionForest\FilamentTree\Concern\Actions\TreeActionTrait;
7+
8+
class CreateAction extends BaseCreateAction
9+
{
10+
use TreeActionTrait;
11+
}

src/Actions/DeleteAction.php

Lines changed: 4 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,66 +2,10 @@
22

33
namespace SolutionForest\FilamentTree\Actions;
44

5-
use Filament\Actions\Concerns\CanCustomizeProcess;
6-
use Illuminate\Database\Eloquent\Model;
5+
use Filament\Actions\DeleteAction as BaseDeleteAction;
6+
use SolutionForest\FilamentTree\Concern\Actions\TreeActionTrait;
77

8-
class DeleteAction extends Action
8+
class DeleteAction extends BaseDeleteAction
99
{
10-
use CanCustomizeProcess;
11-
12-
public static function getDefaultName(): ?string
13-
{
14-
return 'delete';
15-
}
16-
17-
protected function setUp(): void
18-
{
19-
parent::setUp();
20-
21-
$this->label(__('filament-actions::delete.single.label'));
22-
23-
$this->modalHeading(fn (): string => __('filament-actions::delete.single.modal.heading', ['label' => $this->getRecordTitle()]));
24-
25-
$this->modalSubmitActionLabel(__('filament-actions::delete.single.modal.actions.delete.label'));
26-
27-
$this->successNotificationTitle(__('filament-actions::delete.single.notifications.deleted.title'));
28-
29-
$this->color('danger');
30-
31-
$this->icon('heroicon-m-trash');
32-
33-
$this->requiresConfirmation();
34-
35-
$this->modalSubheading(function (Model $record) {
36-
if (collect($record->children)->isNotEmpty()) {
37-
return __('filament-tree::filament-tree.actions.delete.confirmation.with_children');
38-
39-
} else {
40-
return __('filament-actions::modal.confirmation');
41-
42-
}
43-
});
44-
45-
$this->modalIcon('heroicon-o-trash');
46-
47-
$this->hidden(static function (Model $record): bool {
48-
if (! method_exists($record, 'trashed')) {
49-
return false;
50-
}
51-
52-
return $record->trashed();
53-
});
54-
55-
$this->action(function (): void {
56-
$result = $this->process(static fn (Model $record) => $record->delete());
57-
58-
if (! $result) {
59-
$this->failure();
60-
61-
return;
62-
}
63-
64-
$this->success();
65-
});
66-
}
10+
use TreeActionTrait;
6711
}

src/Actions/EditAction.php

Lines changed: 4 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2,81 +2,10 @@
22

33
namespace SolutionForest\FilamentTree\Actions;
44

5-
use Closure;
6-
use Filament\Actions\Concerns\CanCustomizeProcess;
7-
use Illuminate\Database\Eloquent\Model;
8-
use SolutionForest\FilamentTree\Components\Tree;
5+
use Filament\Actions\EditAction as BaseEditAction;
6+
use SolutionForest\FilamentTree\Concern\Actions\TreeActionTrait;
97

10-
class EditAction extends Action
8+
class EditAction extends BaseEditAction
119
{
12-
use CanCustomizeProcess;
13-
14-
protected ?Closure $mutateRecordDataUsing = null;
15-
16-
protected ?Closure $mutateFormDataBeforeSaveUsing = null;
17-
18-
public static function getDefaultName(): ?string
19-
{
20-
return 'edit';
21-
}
22-
23-
protected function setUp(): void
24-
{
25-
parent::setUp();
26-
27-
$this->label(__('filament-actions::edit.single.label'));
28-
29-
$this->modalHeading(fn (): string => __('filament-actions::edit.single.modal.heading', ['label' => $this->getRecordTitle()]));
30-
31-
$this->modalSubmitActionLabel(__('filament-actions::edit.single.modal.actions.save.label'));
32-
33-
$this->successNotificationTitle(__('filament-actions::edit.single.notifications.saved.title'));
34-
35-
$this->icon('heroicon-m-pencil-square');
36-
37-
$this->fillForm(function (Model $record, Tree $tree): array {
38-
if ($translatableContentDriver = $tree->makeFilamentTranslatableContentDriver()) {
39-
$data = $translatableContentDriver->getRecordAttributesToArray($record);
40-
} else {
41-
$data = $record->attributesToArray();
42-
}
43-
44-
if ($this->mutateRecordDataUsing) {
45-
$data = $this->evaluate($this->mutateRecordDataUsing, ['data' => $data, 'record' => $record]);
46-
}
47-
48-
return $data;
49-
});
50-
51-
$this->action(function (): void {
52-
$this->process(function (array $data, Model $record, Tree $tree) {
53-
if ($translatableContentDriver = $tree->makeFilamentTranslatableContentDriver()) {
54-
$translatableContentDriver->updateRecord($record, $data);
55-
} else {
56-
$record->update($data);
57-
}
58-
});
59-
60-
$this->success();
61-
});
62-
}
63-
64-
public function mutateRecordDataUsing(?Closure $callback): static
65-
{
66-
$this->mutateRecordDataUsing = $callback;
67-
68-
return $this;
69-
}
70-
71-
public function mutateFormDataBeforeSaveUsing(?Closure $callback): static
72-
{
73-
$this->mutateFormDataBeforeSaveUsing = $callback;
74-
75-
return $this;
76-
}
77-
78-
public function getMutateFormDataBeforeSave(): ?Closure
79-
{
80-
return $this->mutateFormDataBeforeSaveUsing;
81-
}
10+
use TreeActionTrait;
8211
}

src/Actions/ViewAction.php

Lines changed: 4 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,10 @@
22

33
namespace SolutionForest\FilamentTree\Actions;
44

5-
use Closure;
6-
use Illuminate\Database\Eloquent\Model;
7-
use SolutionForest\FilamentTree\Components\Tree;
5+
use Filament\Actions\ViewAction as BaseViewAction;
6+
use SolutionForest\FilamentTree\Concern\Actions\TreeActionTrait;
87

9-
class ViewAction extends Action
8+
class ViewAction extends BaseViewAction
109
{
11-
protected ?Closure $mutateRecordDataUsing = null;
12-
13-
public static function getDefaultName(): ?string
14-
{
15-
return 'view';
16-
}
17-
18-
protected function setUp(): void
19-
{
20-
parent::setUp();
21-
22-
$this->label(__('filament-actions::view.single.label'));
23-
24-
$this->modalHeading(fn (): string => __('filament-actions::view.single.modal.heading', ['label' => $this->getRecordTitle()]));
25-
26-
$this->modalSubmitAction(false);
27-
$this->modalCancelAction(fn (\Filament\Actions\Action $action) => $action->label(__('filament-actions::view.single.modal.actions.close.label')));
28-
29-
$this->color('gray');
30-
31-
$this->icon('heroicon-m-eye');
32-
33-
$this->disabledForm();
34-
35-
$this->fillForm(function (Model $record, Tree $tree): array {
36-
if ($translatableContentDriver = $tree->makeFilamentTranslatableContentDriver()) {
37-
$data = $translatableContentDriver->getRecordAttributesToArray($record);
38-
} else {
39-
$data = $record->attributesToArray();
40-
}
41-
42-
if ($this->mutateRecordDataUsing) {
43-
$data = $this->evaluate($this->mutateRecordDataUsing, ['data' => $data, 'record' => $record]);
44-
}
45-
46-
return $data;
47-
});
48-
49-
$this->action(static function (): void {});
50-
}
51-
52-
public function mutateRecordDataUsing(?Closure $callback): static
53-
{
54-
$this->mutateRecordDataUsing = $callback;
55-
56-
return $this;
57-
}
10+
use TreeActionTrait;
5811
}

0 commit comments

Comments
 (0)