Skip to content

Commit aeefba0

Browse files
committed
feat(core): add support for reusable BlockPreset and PresetChild classes
1 parent 288dc0a commit aeefba0

File tree

5 files changed

+1115
-9
lines changed

5 files changed

+1115
-9
lines changed

packages/core/src/Data/BlockPreset.php

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,58 @@ class BlockPreset implements JsonSerializable
2929
/**
3030
* Create a new block preset instance.
3131
*/
32-
public function __construct(string $name)
32+
public function __construct(?string $name = null)
3333
{
34-
$this->name = $name;
34+
$this->build();
35+
36+
// Set name with priority: constructor param > build() set value > getName()
37+
$this->name = $name ?? $this->name ?? $this->getName();
3538
}
3639

3740
/**
3841
* Create a new block preset.
3942
*/
40-
public static function make(string $name): static
43+
public static function make(?string $name = null): static
4144
{
4245
return new static($name);
4346
}
4447

48+
/**
49+
* Get default preset name.
50+
* Can be overridden in subclasses to provide a custom name.
51+
*/
52+
protected function getName(): string
53+
{
54+
$class = (new \ReflectionClass(static::class))->getShortName();
55+
56+
// Remove "Preset" suffix if present
57+
$class = preg_replace('/Preset$/', '', $class);
58+
59+
// Convert PascalCase to spaces (e.g., "RichText" -> "Rich Text")
60+
$words = preg_replace('/(?<!^)[A-Z]/', ' $0', $class);
61+
62+
return trim($words);
63+
}
64+
65+
/**
66+
* Build the preset configuration.
67+
* Override this method in subclasses to define preset structure.
68+
*/
69+
protected function build(): void
70+
{
71+
// Base implementation does nothing
72+
}
73+
74+
/**
75+
* Set the preset name.
76+
*/
77+
public function name(string $name): static
78+
{
79+
$this->name = $name;
80+
81+
return $this;
82+
}
83+
4584
/**
4685
* Set the preset description.
4786
*/
@@ -97,7 +136,63 @@ public function properties(array $properties): static
97136
*/
98137
public function blocks(array $children): static
99138
{
100-
$this->children = $children;
139+
$this->children = static::normalizeChildren($children);
140+
141+
return $this;
142+
}
143+
144+
/**
145+
* Normalize children array by converting class names to instances.
146+
*
147+
* @param array $children Raw children array (may contain class names, instances, or arrays)
148+
* @return array Normalized children array (instances and arrays)
149+
*/
150+
protected static function normalizeChildren(array $children): array
151+
{
152+
return array_map(function ($item) {
153+
// If it's a class string that exists and extends PresetChild
154+
if (is_string($item) && class_exists($item)) {
155+
if (is_subclass_of($item, PresetChild::class)) {
156+
// Call ::make() to instantiate (type will be auto-derived)
157+
return $item::make();
158+
}
159+
}
160+
161+
// Otherwise return as-is (PresetChild instance or array)
162+
return $item;
163+
}, $children);
164+
}
165+
166+
/**
167+
* Append a single child block to this preset.
168+
*/
169+
public function addBlock(PresetChild|array|string $block): static
170+
{
171+
$normalized = static::normalizeChildren([$block]);
172+
$this->children[] = $normalized[0];
173+
174+
return $this;
175+
}
176+
177+
/**
178+
* Append multiple child blocks to this preset.
179+
*/
180+
public function addBlocks(array $blocks): static
181+
{
182+
$normalized = static::normalizeChildren($blocks);
183+
foreach ($normalized as $block) {
184+
$this->children[] = $block;
185+
}
186+
187+
return $this;
188+
}
189+
190+
/**
191+
* Merge additional properties into existing properties.
192+
*/
193+
public function mergeProperties(array $properties): static
194+
{
195+
$this->properties = array_merge($this->properties, $properties);
101196

102197
return $this;
103198
}

packages/core/src/Data/BlockSchema.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public function __construct(
6262
$this->accepts = static::normalizeAccepts($accepts);
6363
$this->wrapper = $wrapper;
6464
$this->previewImageUrl = $previewImageUrl;
65-
$this->presets = $presets;
65+
$this->presets = static::normalizePresets($presets);
6666
$this->private = $private;
6767
}
6868

@@ -88,6 +88,28 @@ protected static function normalizeAccepts(array $accepts): array
8888
}, $accepts);
8989
}
9090

91+
/**
92+
* Normalize presets array by converting class names to instances.
93+
*
94+
* @param array $presets Raw presets array (may contain class names, instances, or arrays)
95+
* @return array Normalized presets array (instances and arrays)
96+
*/
97+
protected static function normalizePresets(array $presets): array
98+
{
99+
return array_map(function ($item) {
100+
// If it's a class string that exists and extends BlockPreset
101+
if (is_string($item) && class_exists($item)) {
102+
if (is_subclass_of($item, BlockPreset::class)) {
103+
// Call ::make() to instantiate (name will be auto-derived)
104+
return $item::make();
105+
}
106+
}
107+
108+
// Otherwise return as-is (BlockPreset instance or array)
109+
return $item;
110+
}, $presets);
111+
}
112+
91113
/**
92114
* Create schema from BlockInterface class.
93115
*/

packages/core/src/Data/PresetChild.php

Lines changed: 116 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,59 @@ class PresetChild implements JsonSerializable
2727
/**
2828
* Create a new preset child instance.
2929
*/
30-
public function __construct(string $type)
30+
public function __construct(?string $type = null)
3131
{
32-
$this->type = $type;
32+
// Call build() first to allow subclass configuration
33+
$this->build();
34+
35+
// Set type with priority: constructor param > build() set value > getType()
36+
$this->type = $type ?? $this->type ?? $this->getType();
3337
}
3438

3539
/**
3640
* Create a new preset child.
3741
*/
38-
public static function make(string $type): static
42+
public static function make(?string $type = null): static
3943
{
4044
return new static($type);
4145
}
4246

47+
/**
48+
* Get default preset child type.
49+
* Can be overridden in subclasses to provide a custom type.
50+
*/
51+
protected function getType(): string
52+
{
53+
$class = (new \ReflectionClass(static::class))->getShortName();
54+
55+
// Remove "PresetChild" or "Child" suffix if present
56+
$class = preg_replace('/(PresetChild|Child)$/', '', $class);
57+
58+
// Convert PascalCase to kebab-case (e.g., "Paragraph" -> "paragraph")
59+
$kebab = strtolower(preg_replace('/(?<!^)[A-Z]/', '-$0', $class));
60+
61+
return $kebab;
62+
}
63+
64+
/**
65+
* Build the preset child configuration.
66+
* Override this method in subclasses to define structure.
67+
*/
68+
protected function build(): void
69+
{
70+
// Base implementation does nothing
71+
}
72+
73+
/**
74+
* Set the block type.
75+
*/
76+
public function type(string $type): static
77+
{
78+
$this->type = $type;
79+
80+
return $this;
81+
}
82+
4383
/**
4484
* Set the block's semantic ID.
4585
*/
@@ -85,7 +125,7 @@ public function static(bool $static = true): static
85125
*/
86126
public function blocks(array $children): static
87127
{
88-
$this->children = $children;
128+
$this->children = static::normalizeChildren($children);
89129

90130
return $this;
91131
}
@@ -98,6 +138,78 @@ public function children(array $children): static
98138
return $this->blocks($children);
99139
}
100140

141+
/**
142+
* Normalize children array by converting class names to instances.
143+
*
144+
* @param array $children Raw children array (may contain class names, instances, or arrays)
145+
* @return array Normalized children array (instances and arrays)
146+
*/
147+
protected static function normalizeChildren(array $children): array
148+
{
149+
return array_map(function ($item) {
150+
// If it's a class string that exists and extends PresetChild
151+
if (is_string($item) && class_exists($item)) {
152+
if (is_subclass_of($item, self::class)) {
153+
// Call ::make() to instantiate (type will be auto-derived)
154+
return $item::make();
155+
}
156+
}
157+
158+
// Otherwise return as-is (PresetChild instance or array)
159+
return $item;
160+
}, $children);
161+
}
162+
163+
/**
164+
* Append a single child block to this preset child.
165+
*/
166+
public function addChild(self|array|string $child): static
167+
{
168+
$normalized = static::normalizeChildren([$child]);
169+
$this->children[] = $normalized[0];
170+
171+
return $this;
172+
}
173+
174+
/**
175+
* Append multiple child blocks to this preset child.
176+
*/
177+
public function addChildren(array $children): static
178+
{
179+
$normalized = static::normalizeChildren($children);
180+
foreach ($normalized as $child) {
181+
$this->children[] = $child;
182+
}
183+
184+
return $this;
185+
}
186+
187+
/**
188+
* Append a single child block (alias for addChild()).
189+
*/
190+
public function addBlock(self|array|string $child): static
191+
{
192+
return $this->addChild($child);
193+
}
194+
195+
/**
196+
* Append multiple child blocks (alias for addChildren()).
197+
*/
198+
public function addBlocks(array $children): static
199+
{
200+
return $this->addChildren($children);
201+
}
202+
203+
/**
204+
* Merge additional properties into existing properties.
205+
*/
206+
public function mergeProperties(array $properties): static
207+
{
208+
$this->properties = array_merge($this->properties, $properties);
209+
210+
return $this;
211+
}
212+
101213
/**
102214
* Convert to array representation.
103215
*/

0 commit comments

Comments
 (0)