mirror of
https://github.com/filamentphp/filament.git
synced 2026-09-24 15:42:09 +08:00
fix: prevent duplicate toolbar buttons when rich editor plugin enables already-present buttons (#19666)
* feat: implement applyEnableToolbarButtonsModification for dynamic button enabling * fix: remove commented-out code for enabling toolbar buttons * test: add tests for toolbar button duplication in RichEditor plugin * fix: improve toolbar button detection logic in RichEditor plugin
This commit is contained in:
@@ -87,7 +87,7 @@ trait InteractsWithToolbarButtons
|
||||
$buttons = match ($modification['type']) {
|
||||
'disableAll' => [],
|
||||
'disable' => $this->applyDisableToolbarButtonsModification($buttons, $modification['buttons']),
|
||||
'enable' => [...$buttons, ...$modification['buttons']],
|
||||
'enable' => $this->applyEnableToolbarButtonsModification($buttons, $modification['buttons']),
|
||||
default => throw new Exception('Unknown toolbar buttons modification type: [' . $modification['type'] . '].'),
|
||||
};
|
||||
}
|
||||
@@ -170,6 +170,87 @@ trait InteractsWithToolbarButtons
|
||||
return $modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string | object | array<int, string | object>> $buttons
|
||||
* @param array<int, string | object | array<int, string | object>> $buttonsToEnable
|
||||
* @return array<int, string | object | array<int, string | object>>
|
||||
*/
|
||||
protected function applyEnableToolbarButtonsModification(array $buttons, array $buttonsToEnable): array
|
||||
{
|
||||
$modified = $buttons;
|
||||
|
||||
foreach ($buttonsToEnable as $button) {
|
||||
if (is_object($button)) {
|
||||
$modified[] = $button;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_array($button)) {
|
||||
$filteredGroup = [];
|
||||
|
||||
foreach ($button as $item) {
|
||||
if (is_object($item)) {
|
||||
$filteredGroup[] = $item;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->hasToolbarButtonInArray($modified, $item) || in_array($item, $filteredGroup)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filteredGroup[] = $item;
|
||||
}
|
||||
|
||||
if (filled($filteredGroup)) {
|
||||
$modified[] = $filteredGroup;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->hasToolbarButtonInArray($modified, $button)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$modified[] = $button;
|
||||
}
|
||||
|
||||
return $modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string | object | array<int, string | object>> $buttons
|
||||
*/
|
||||
protected function hasToolbarButtonInArray(array $buttons, string $button): bool
|
||||
{
|
||||
foreach ($buttons as $item) {
|
||||
if (is_array($item)) {
|
||||
if ($this->hasToolbarButtonInArray($item, $button)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_string($item) && ($item === $button)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_object($item) && $this->isToolbarButtonInObject($item, $button)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function isToolbarButtonInObject(object $item, string $button): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<array{type: string, buttons?: array<string | array<string | array<string>>>}>
|
||||
*/
|
||||
|
||||
@@ -828,6 +828,15 @@ class RichEditor extends Field implements Contracts\CanBeLengthConstrained
|
||||
return $modifications;
|
||||
}
|
||||
|
||||
protected function isToolbarButtonInObject(object $item, string $button): bool
|
||||
{
|
||||
if ($item instanceof ToolbarButtonGroup) {
|
||||
return in_array($button, $item->getButtons());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string | array<string>>
|
||||
*/
|
||||
|
||||
@@ -432,6 +432,48 @@ describe('plugins', function (): void {
|
||||
->toContain('bold');
|
||||
});
|
||||
|
||||
test('plugin implementing `HasToolbarButtons` does not duplicate toolbar buttons already returned by `toolbarButtons()`', function (): void {
|
||||
$buttons = Schema::make(Livewire::make())
|
||||
->statePath('data')
|
||||
->components([
|
||||
RichEditor::make('content')
|
||||
->toolbarButtons([
|
||||
['bold', 'highlight'],
|
||||
['undo', 'redo'],
|
||||
])
|
||||
->plugins([new TestRichContentPlugin(enabledButtons: ['highlight'])]),
|
||||
])
|
||||
->getComponents()[0]
|
||||
->getToolbarButtons();
|
||||
|
||||
$flatButtons = array_merge(...$buttons);
|
||||
$highlightButtons = array_values(array_filter($flatButtons, fn (mixed $button): bool => $button === 'highlight'));
|
||||
|
||||
expect($buttons)
|
||||
->toHaveCount(2)
|
||||
->and($buttons[0])->toEqual(['bold', 'highlight'])
|
||||
->and($buttons[1])->toEqual(['undo', 'redo'])
|
||||
->and($highlightButtons)->toHaveCount(1);
|
||||
});
|
||||
|
||||
test('plugin implementing `HasToolbarButtons` does not duplicate toolbar buttons already enabled by user `enableToolbarButtons()`', function (): void {
|
||||
$schema = Schema::make(Livewire::make())
|
||||
->statePath('data')
|
||||
->components([
|
||||
RichEditor::make('content')
|
||||
->plugins([new TestRichContentPlugin(enabledButtons: ['highlight'])]),
|
||||
]);
|
||||
|
||||
$richEditor = $schema->getComponents()[0];
|
||||
$richEditor->enableToolbarButtons(['highlight']);
|
||||
|
||||
$flatButtons = array_merge(...$richEditor->getToolbarButtons());
|
||||
$highlightButtons = array_values(array_filter($flatButtons, fn (mixed $button): bool => $button === 'highlight'));
|
||||
|
||||
expect($highlightButtons)
|
||||
->toHaveCount(1);
|
||||
});
|
||||
|
||||
test('plugin implementing `HasToolbarButtons` can disable toolbar buttons', function (): void {
|
||||
$richEditor = Schema::make(Livewire::make())
|
||||
->statePath('data')
|
||||
|
||||
Reference in New Issue
Block a user