Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.67% covered (success)
96.67%
58 / 60
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
BlockChoices
96.67% covered (success)
96.67%
58 / 60
50.00% covered (danger)
50.00%
1 / 2
31
0.00% covered (danger)
0.00%
0 / 1
 __construct
96.49% covered (success)
96.49%
55 / 57
0.00% covered (danger)
0.00%
0 / 1
26
 describe
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Panel;
6
7use Cosray\Block;
8use Cosray\Exception\RuntimeException;
9use Cosray\Field\Blocks;
10
11final 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}