From 4c04072edea2fe584a48a4bcd36afdcd7c681592 Mon Sep 17 00:00:00 2001 From: Tofandel Date: Mon, 4 Mar 2024 10:49:16 +0100 Subject: [PATCH] Add global order config --- config/block_editor.php | 7 +++ src/Facades/TwillBlocks.php | 3 +- src/Services/Blocks/Block.php | 5 -- src/Services/Forms/Fields/BlockEditor.php | 9 +++ src/TwillBlocks.php | 44 +++++++++++---- .../Components/Blocks/TwillBlockComponent.php | 5 -- src/View/Components/Fields/BlockEditor.php | 2 + tests/unit/Helpers/BlockHelpersTest.php | 56 +++++++++---------- 8 files changed, 82 insertions(+), 49 deletions(-) diff --git a/config/block_editor.php b/config/block_editor.php index 3e35e46e5..bc16faed5 100644 --- a/config/block_editor.php +++ b/config/block_editor.php @@ -95,4 +95,11 @@ 'repeaters' => resource_path('views/twill/repeaters'), ], ], + 'blocks' => [], + 'block_rules' => [ + // List of block identifiers to globally disallow + 'disable' => [], + // List of block identifiers to order blocks by if no order is provided + 'order' => [], + ] ]; diff --git a/src/Facades/TwillBlocks.php b/src/Facades/TwillBlocks.php index 1bcd35c3e..47a6eb3f3 100644 --- a/src/Facades/TwillBlocks.php +++ b/src/Facades/TwillBlocks.php @@ -9,11 +9,12 @@ /** * @method static void globallyExcludeBlocks(array|callable $blocks) * @method static array getGloballyExcludedBlocks + * @method static void setGloballyExcludedBlocks(array $exclude = []) * @method static CollectiongetBlocks * @method static CollectiongetSettingsBlocks * @method static CollectiongetRepeaters * @method static registerManualBlock(string $blockClass, string $source = Block::SOURCE_APP) - * @method static CollectiongenerateListOfAvailableBlocks(?array $blocks = null, ?array $groups = null, bool $settingsOnly = false, array|callable $excludeBlocks = []) + * @method static CollectiongenerateListOfAvailableBlocks(?array $blocks = null, ?array $groups = null, bool $settingsOnly = false, array|callable $excludeBlocks = [], bool $defaultOrder = false) */ class TwillBlocks extends Facade { diff --git a/src/Services/Blocks/Block.php b/src/Services/Blocks/Block.php index 6ba30ccc5..7ceaeefb5 100644 --- a/src/Services/Blocks/Block.php +++ b/src/Services/Blocks/Block.php @@ -206,11 +206,6 @@ public function newInstance(): static ); } - public function getPosition(): float|int|string - { - return $this->componentClass && is_callable([$this->componentClass, 'getPosition']) ? $this->componentClass::getPosition() : 0; - } - /** * Gets the first match being a block or repeater. */ diff --git a/src/Services/Forms/Fields/BlockEditor.php b/src/Services/Forms/Fields/BlockEditor.php index 09789f628..6dc893d3c 100644 --- a/src/Services/Forms/Fields/BlockEditor.php +++ b/src/Services/Forms/Fields/BlockEditor.php @@ -14,6 +14,7 @@ class BlockEditor extends BaseFormField protected bool $isSettings = false; protected bool $withoutSeparator = false; + protected bool $usingDefaultOrder = false; public static function make(): static { @@ -58,6 +59,14 @@ public function blocks(array $blocks): static return $this; } + + public function usingDefaultOrder(bool $usingDefaultOrder = true): static + { + $this->usingDefaultOrder = $usingDefaultOrder; + + return $this; + } + public function getBlocks(): array { return $this->blocks; diff --git a/src/TwillBlocks.php b/src/TwillBlocks.php index 3fe4b8018..906b339b3 100644 --- a/src/TwillBlocks.php +++ b/src/TwillBlocks.php @@ -139,6 +139,11 @@ public function globallyExcludeBlocks(array|callable $blocks): void $this->globallyExcludedBlocks[] = $blocks; } + public function setGloballyExcludedBlocks(array $exclude = []): void + { + $this->globallyExcludedBlocks = []; + } + public function getGloballyExcludedBlocks(): array { return $this->globallyExcludedBlocks; @@ -342,7 +347,7 @@ public function getAllCropConfigs(): array return $this->cropConfigs; } - public function generateListOfAllBlocks(bool $settingsOnly = false) + public function generateListOfAllBlocks(bool $settingsOnly = false): Collection { return once(function () use ($settingsOnly) { /** @var Collection $blockList */ @@ -352,11 +357,14 @@ public function generateListOfAllBlocks(bool $settingsOnly = false) $blockList = TwillBlocks::getBlocks(); } + $customOrder = array_flip(config('twill.block_editor.block_rules.order', [])); + $disabledBlocks = array_flip(config('twill.block_editor.block_rules.disable', [])); + $appBlocksList = $blockList->filter(function (Block $block) { return $block->source !== Block::SOURCE_TWILL; }); - return $blockList->filter(function (Block $block) use ($appBlocksList) { + return $blockList->filter(function (Block $block) use ($disabledBlocks, $appBlocksList) { if ($block->group === Block::SOURCE_TWILL) { if (!collect(config('twill.block_editor.use_twill_blocks'))->contains($block->name)) { return false; @@ -372,16 +380,21 @@ function ($appBlock) use ($block) { return false; } } - return true; - })->sortBy(function (Block $b) { - // Blocks are by default sorted by the order they have been found in directories, but we can allow individual blocks to override this behavior - return $b->getPosition(); - })->values(); + return isset($disabledBlocks[$block->name]) || isset($disabledBlocks[ltrim($block->componentClass, '\\')]); + })->sortBy(function (Block $b) use ($customOrder) { + // Sort blocks by custom order then by group and then by name + return ($customOrder[$b->name] ?? $customOrder[ltrim($b->componentClass, '\\')] ?? PHP_INT_MAX) . '-' . $b->group . '-' . $b->name; + }, SORT_NATURAL)->values(); }); } - public function generateListOfAvailableBlocks(?array $blocks = null, ?array $groups = null, bool $settingsOnly = false, array|callable $excludeBlocks = []): Collection - { + public function generateListOfAvailableBlocks( + ?array $blocks = null, + ?array $groups = null, + bool $settingsOnly = false, + array|callable $excludeBlocks = [], + bool $defaultOrder = false + ): Collection { $globalExcludeBlocks = TwillBlocks::getGloballyExcludedBlocks(); $matchBlock = function ($matcher, $block, $someFn = null) { @@ -393,7 +406,7 @@ public function generateListOfAvailableBlocks(?array $blocks = null, ?array $gro } return null; }; - return $this->generateListOfAllBlocks($settingsOnly)->filter( + $finalList = $this->generateListOfAllBlocks($settingsOnly)->filter( function (Block $block) use ($blocks, $groups, $excludeBlocks, $globalExcludeBlocks, $matchBlock) { if ($matchBlock($excludeBlocks, $block)) { return false; @@ -420,5 +433,16 @@ function (Block $block) use ($blocks, $groups, $excludeBlocks, $globalExcludeBlo return true; } ); + if (! $defaultOrder) { + if (! empty($blocks)) { + $blocks = array_flip($blocks); + $finalList = $finalList->sortBy(fn(Block $block) => $blocks[$block->name] ?? $blocks[ltrim($block->componentClass, '\\')] ?? PHP_INT_MAX, SORT_NUMERIC); + } + if (! empty($groups)) { + $groups = array_flip($groups); + $finalList = $finalList->sortBy(fn(Block $block) => $groups[$block->group] ?? PHP_INT_MAX, SORT_NUMERIC); + } + } + return $finalList; } } diff --git a/src/View/Components/Blocks/TwillBlockComponent.php b/src/View/Components/Blocks/TwillBlockComponent.php index db2d6a31b..9382b6763 100644 --- a/src/View/Components/Blocks/TwillBlockComponent.php +++ b/src/View/Components/Blocks/TwillBlockComponent.php @@ -112,11 +112,6 @@ public static function getBlockGroup(): string return Str::slug(Str::before(static::class, '\\')); } - public static function getPosition(): float|int|string - { - return 0; - } - public static function getBlockIcon(): string { return 'text'; diff --git a/src/View/Components/Fields/BlockEditor.php b/src/View/Components/Fields/BlockEditor.php index 0f66a764f..b4a888c3e 100644 --- a/src/View/Components/Fields/BlockEditor.php +++ b/src/View/Components/Fields/BlockEditor.php @@ -21,6 +21,7 @@ public function __construct( public ?string $group = null, public ?string $trigger = null, public bool $isSettings = false, + public bool $usingDefaultOrder = false, ) { parent::__construct( name: $name, @@ -46,6 +47,7 @@ public function getAllowedBlocks(): array $groups, $this->isSettings, $this->excludeBlocks ?? null, + $this->usingDefaultOrder )->pluck('name')->all(); } diff --git a/tests/unit/Helpers/BlockHelpersTest.php b/tests/unit/Helpers/BlockHelpersTest.php index 49daa9bd2..9fc699080 100644 --- a/tests/unit/Helpers/BlockHelpersTest.php +++ b/tests/unit/Helpers/BlockHelpersTest.php @@ -7,6 +7,7 @@ use A17\Twill\Services\Forms\Form; use A17\Twill\Tests\Unit\TestCase; use A17\Twill\View\Components\Blocks\TwillBlockComponent; +use Spatie\Once\Cache; class GroupBlock extends TwillBlockComponent { public static function getBlockGroup(): string @@ -35,11 +36,6 @@ public static function getBlockName(): string { return 'block2'; } - - public static function getPosition(): float|int|string - { - return 2; - } } class AppBlock extends GroupBlock @@ -48,11 +44,6 @@ public static function getBlockGroup(): string { return 'app'; } - - public static function getPosition(): float|int|string - { - return 1; - } } class BlockHelpersTest extends TestCase { @@ -66,36 +57,27 @@ public function testGenerateListOfAvailableBlocks() $this->assertContains(GroupBlock::class, $blockClasses); $this->assertContains(AppBlock::class, $blockClasses); - $available = TwillBlocks::generateListOfAvailableBlocks()->pluck('componentClass'); - - $this->assertGreaterThanOrEqual(3, count($available)); + $available = TwillBlocks::generateListOfAvailableBlocks()->pluck('componentClass')->filter(); + // Ensure correct order + $this->assertEquals([AppBlock::class, GroupBlock::class, GroupBlock2::class], $available->all()); + $available = TwillBlocks::generateListOfAvailableBlocks([GroupBlock2::class, 'app-block'])->pluck('componentClass'); // Ensure correct order + $this->assertEquals([GroupBlock2::class, AppBlock::class], $available->all()); - $this->assertEquals(GroupBlock2::class, $available->pop()); - $this->assertEquals(AppBlock::class, $available->pop()); + $available = TwillBlocks::generateListOfAvailableBlocks([GroupBlock2::class, AppBlock::class], ['group', 'app'])->pluck('componentClass'); + $this->assertEquals([GroupBlock2::class, GroupBlock::class, AppBlock::class], $available->all()); - $available = TwillBlocks::generateListOfAvailableBlocks(['app-block', GroupBlock2::class])->pluck('componentClass'); - $this->assertCount(2, $available); - $this->assertContains(AppBlock::class, $available); - $this->assertContains(GroupBlock2::class, $available); - - $available = TwillBlocks::generateListOfAvailableBlocks([GroupBlock2::class], ['group'])->pluck('componentClass'); - $this->assertCount(2, $available); - $this->assertContains(GroupBlock::class, $available); $available = TwillBlocks::generateListOfAvailableBlocks(['app-block', GroupBlock2::class], excludeBlocks: ['app-block'])->pluck('componentClass'); - - $this->assertCount(1, $available); - $this->assertContains(GroupBlock2::class, $available); + $this->assertEquals([GroupBlock2::class], $available->all()); $available = TwillBlocks::generateListOfAvailableBlocks( [AppBlock::class, GroupBlock::class, GroupBlock2::class], excludeBlocks: fn (Block $block) => $block->componentClass && $block->componentClass::getBlockName() == 'block' )->pluck('componentClass'); - $this->assertCount(1, $available); - $this->assertContains(GroupBlock2::class, $available); + $this->assertEquals([GroupBlock2::class], $available->all()); TwillBlocks::globallyExcludeBlocks([GroupBlock::class]); TwillBlocks::globallyExcludeBlocks(fn (Block $block) => $block->name == 'group-block2'); @@ -105,6 +87,24 @@ public function testGenerateListOfAvailableBlocks() )->pluck('componentClass'); $this->assertCount(1, $available); $this->assertContains(AppBlock::class, $available); + + TwillBlocks::setGloballyExcludedBlocks(); + + config(['twill.block_editor.block_rules.order' => ['group-block2', AppBlock::class, 'group-block']]); + + Cache::getInstance()->flush(); + + $available = TwillBlocks::generateListOfAvailableBlocks()->pluck('componentClass')->filter(); + // Ensure correct order + $this->assertEquals([GroupBlock2::class, AppBlock::class, GroupBlock::class], $available->all()); + + config(['twill.block_editor.block_rules.disable' => ['group-block2', AppBlock::class]]); + + Cache::getInstance()->flush(); + + $available = TwillBlocks::generateListOfAvailableBlocks()->pluck('componentClass')->filter(); + $this->assertEquals([GroupBlock::class], $available->all()); + } }