Skip to content

Commit 0a6ff51

Browse files
Merge pull request #60 from CodeWithDennis/feature/create-option-form
[Feature] `createOptionForm`
2 parents c81ff3f + 670fc3f commit 0a6ff51

File tree

1 file changed

+139
-0
lines changed

1 file changed

+139
-0
lines changed

src/SelectTree.php

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@
33
namespace CodeWithDennis\FilamentSelectTree;
44

55
use Closure;
6+
use Exception;
7+
use Filament\Forms\ComponentContainer;
8+
use Filament\Forms\Components\Actions\Action;
69
use Filament\Forms\Components\Concerns\CanBeDisabled;
710
use Filament\Forms\Components\Concerns\CanBeSearchable;
811
use Filament\Forms\Components\Concerns\HasActions;
912
use Filament\Forms\Components\Concerns\HasAffixes;
1013
use Filament\Forms\Components\Concerns\HasPlaceholder;
1114
use Filament\Forms\Components\Contracts\HasAffixActions;
1215
use Filament\Forms\Components\Field;
16+
use Filament\Forms\Form;
17+
use Filament\Support\Facades\FilamentIcon;
1318
use Illuminate\Database\Eloquent\Relations\BelongsTo;
1419
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
1520
use Illuminate\Support\Collection;
@@ -58,6 +63,16 @@ class SelectTree extends Field implements HasAffixActions
5863

5964
protected Closure|array $hiddenOptions = [];
6065

66+
protected array|Closure|null $createOptionActionForm = null;
67+
68+
protected string|Closure|null $createOptionModalHeading = null;
69+
70+
protected ?Closure $modifyCreateOptionActionUsing = null;
71+
72+
protected ?Closure $modifyManageOptionActionsUsing = null;
73+
74+
protected ?Closure $createOptionUsing = null;
75+
6176
protected function setUp(): void
6277
{
6378
// Load the state from relationships using a callback function.
@@ -88,6 +103,20 @@ protected function setUp(): void
88103
$component->getRelationship()->sync($state->toArray());
89104
}
90105
});
106+
107+
$this->createOptionUsing(static function (SelectTree $component, array $data, Form $form) {
108+
$record = $component->getRelationship()->getRelated();
109+
$record->fill($data);
110+
$record->save();
111+
112+
$form->model($record)->saveRelationships();
113+
114+
return $record->getKey();
115+
});
116+
117+
$this->suffixActions([
118+
static fn (SelectTree $component): ?Action => $component->getCreateOptionAction(),
119+
]);
91120
}
92121

93122
private function buildTree(): Collection
@@ -369,4 +398,114 @@ public function getHiddenOptions(): array
369398
{
370399
return $this->evaluate($this->hiddenOptions);
371400
}
401+
402+
public function getCreateOptionActionForm(Form $form): array|Form|null
403+
{
404+
return $this->evaluate($this->createOptionActionForm, ['form' => $form]);
405+
}
406+
407+
public function hasCreateOptionActionFormSchema(): bool
408+
{
409+
return (bool) $this->createOptionActionForm;
410+
}
411+
412+
public function getCreateOptionModalHeading(): ?string
413+
{
414+
return $this->evaluate($this->createOptionModalHeading);
415+
}
416+
417+
public function createOptionForm(array|Closure|null $schema): static
418+
{
419+
$this->createOptionActionForm = $schema;
420+
421+
return $this;
422+
}
423+
424+
public function getCreateOptionActionName(): string
425+
{
426+
return 'createOption';
427+
}
428+
429+
public function getCreateOptionUsing(): ?Closure
430+
{
431+
return $this->createOptionUsing;
432+
}
433+
434+
public function createOptionUsing(Closure $callback): static
435+
{
436+
$this->createOptionUsing = $callback;
437+
438+
return $this;
439+
}
440+
441+
public function getCreateOptionAction(): ?Action
442+
{
443+
if ($this->isDisabled()) {
444+
return null;
445+
}
446+
447+
if (! $this->hasCreateOptionActionFormSchema()) {
448+
return null;
449+
}
450+
451+
$action = Action::make($this->getCreateOptionActionName())
452+
->form(function (SelectTree $component, Form $form): array|Form|null {
453+
return $component->getCreateOptionActionForm($form->model(
454+
$component->getRelationship() ? $component->getRelationship()->getModel()::class : null,
455+
));
456+
})
457+
->action(static function (Action $action, array $arguments, SelectTree $component, array $data, ComponentContainer $form) {
458+
if (! $component->getCreateOptionUsing()) {
459+
throw new Exception("Select field [{$component->getStatePath()}] must have a [createOptionUsing()] closure set.");
460+
}
461+
462+
$createdOptionKey = $component->evaluate($component->getCreateOptionUsing(), [
463+
'data' => $data,
464+
'form' => $form,
465+
]);
466+
467+
$state = $component->getMultiple()
468+
? [
469+
...$component->getState() ?? [],
470+
$createdOptionKey,
471+
]
472+
: $createdOptionKey;
473+
474+
$component->state($state);
475+
$component->callAfterStateUpdated();
476+
477+
if (! ($arguments['another'] ?? false)) {
478+
return;
479+
}
480+
481+
$action->callAfter();
482+
483+
$form->fill();
484+
485+
$action->halt();
486+
})
487+
->color('gray')
488+
->icon(FilamentIcon::resolve('forms::components.select.actions.create-option') ?? 'heroicon-m-plus')
489+
->iconButton()
490+
->modalHeading($this->getCreateOptionModalHeading() ?? __('filament-forms::components.select.actions.create_option.modal.heading'))
491+
->modalSubmitActionLabel(__('filament-forms::components.select.actions.create_option.modal.actions.create.label'))
492+
->extraModalFooterActions(fn (Action $action, SelectTree $component): array => $component->getMultiple() ? [
493+
$action->makeModalSubmitAction('createAnother', arguments: ['another' => true])
494+
->label(__('filament-forms::components.select.actions.create_option.modal.actions.create_another.label')),
495+
] : []);
496+
497+
if ($this->modifyManageOptionActionsUsing) {
498+
$action = $this->evaluate($this->modifyManageOptionActionsUsing, [
499+
'action' => $action,
500+
]) ?? $action;
501+
}
502+
503+
if ($this->modifyCreateOptionActionUsing) {
504+
$action = $this->evaluate($this->modifyCreateOptionActionUsing, [
505+
'action' => $action,
506+
]) ?? $action;
507+
}
508+
509+
return $action;
510+
}
372511
}

0 commit comments

Comments
 (0)