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