Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
96.61% |
57 / 59 |
|
50.00% |
1 / 2 |
CRAP | |
0.00% |
0 / 1 |
| BlockChoices | |
96.61% |
57 / 59 |
|
50.00% |
1 / 2 |
30 | |
0.00% |
0 / 1 |
| __construct | |
96.43% |
54 / 56 |
|
0.00% |
0 / 1 |
25 | |||
| describe | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
5 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Cosray\Panel; |
| 6 | |
| 7 | use Cosray\Block; |
| 8 | use Cosray\Exception\RuntimeException; |
| 9 | use Cosray\Field\Blocks; |
| 10 | |
| 11 | final class BlockChoices |
| 12 | { |
| 13 | /** @var array<string, array<array-key, mixed>> */ |
| 14 | public readonly array $types; |
| 15 | /** @var array<string, array{type: string, handle: string, label: string, icon: string}> */ |
| 16 | public readonly array $all; |
| 17 | /** @var list<array{type: string, handle: string, label: string, icon: string}> */ |
| 18 | public readonly array $common; |
| 19 | |
| 20 | /** @param array<string, mixed> $props */ |
| 21 | public function __construct(string $field, array $props, ?callable $renderIcon = null) |
| 22 | { |
| 23 | $descriptors = $props['blockTypes'] ?? []; |
| 24 | if (!is_array($descriptors)) { |
| 25 | throw new RuntimeException( |
| 26 | "Blocks field '{$field}' blockTypes must be a list of block type descriptors, " |
| 27 | . self::describe($descriptors) |
| 28 | . ' given', |
| 29 | ); |
| 30 | } |
| 31 | $types = []; |
| 32 | foreach ($descriptors as $index => $type) { |
| 33 | if (!is_array($type) || !is_string($type['type'] ?? null) || $type['type'] === '') { |
| 34 | throw new RuntimeException( |
| 35 | "Blocks field '{$field}' block type descriptor {$index} needs a non-empty string 'type', " |
| 36 | . self::describe(is_array($type) ? $type['type'] ?? null : $type) |
| 37 | . ' given', |
| 38 | ); |
| 39 | } |
| 40 | $types[$type['type']] = $type; |
| 41 | } |
| 42 | $this->types = $types; |
| 43 | $common = array_key_exists('commonTypes', $props) ? $props['commonTypes'] : []; |
| 44 | if (!is_array($common) || !array_is_list($common)) { |
| 45 | throw new RuntimeException( |
| 46 | "Blocks field '{$field}' commonTypes must be a list of block type IDs, " |
| 47 | . self::describe($common) |
| 48 | . ' given', |
| 49 | ); |
| 50 | } |
| 51 | foreach ($common as $index => $type) { |
| 52 | if (!is_string($type)) { |
| 53 | throw new RuntimeException( |
| 54 | "Blocks field '{$field}' common type {$index} must be a block type ID, " |
| 55 | . self::describe($type) |
| 56 | . ' given', |
| 57 | ); |
| 58 | } |
| 59 | } |
| 60 | $common = Blocks::commonSelection($common, array_keys($types), $field); |
| 61 | |
| 62 | $choices = []; |
| 63 | foreach ($types as $id => $type) { |
| 64 | $custom = $type['icon'] ?? null; |
| 65 | $svg = is_array($custom) && $renderIcon !== null ? $renderIcon($custom) : ''; |
| 66 | $panelIcon = $custom !== null |
| 67 | ? 'square' |
| 68 | : match ($id) { |
| 69 | Block\RichText::class => 'body-text', |
| 70 | Block\Heading::class => 'heading', |
| 71 | Block\Image::class => 'image', |
| 72 | Block\Images::class => 'images', |
| 73 | Block\Video::class => 'film', |
| 74 | Block\Youtube::class => 'play-btn', |
| 75 | Block\Iframe::class => 'window', |
| 76 | default => 'square', |
| 77 | }; |
| 78 | $choices[$id] = [ |
| 79 | 'type' => $id, |
| 80 | 'handle' => (string) ($type['handle'] ?? $id), |
| 81 | 'label' => (string) ($type['label'] ?? __('field:block')), |
| 82 | // Debug providers can return only a diagnostic HTML comment. |
| 83 | 'icon' => preg_match('/<svg\b/i', $svg) === 1 ? $svg : Icon::render($panelIcon), |
| 84 | ]; |
| 85 | } |
| 86 | $this->all = $choices; |
| 87 | $this->common = array_map(static fn(string $type): array => $choices[$type], $common); |
| 88 | } |
| 89 | |
| 90 | private static function describe(mixed $value): string |
| 91 | { |
| 92 | return match (true) { |
| 93 | is_scalar($value) => var_export($value, true), |
| 94 | is_array($value) && !array_is_list($value) => 'keyed array', |
| 95 | default => get_debug_type($value), |
| 96 | }; |
| 97 | } |
| 98 | } |