33namespace ipl \Html \FormElement ;
44
55use InvalidArgumentException ;
6+ use ipl \Html \Contract \DefaultFormElementDecoration ;
67use ipl \Html \Contract \FormElement ;
78use ipl \Html \Contract \FormElementDecorator ;
89use ipl \Html \Contract \ValueCandidates ;
@@ -29,12 +30,53 @@ trait FormElements
2930 /** @var bool Whether the default element loader has been registered */
3031 protected $ defaultElementLoaderRegistered = false ;
3132
33+ /**
34+ * Custom Element decorator loader paths
35+ *
36+ * @var array<int, array<string|int, string> Override this property to add custom decorator loader paths
37+ *
38+ * Each entry must be an array, where the key is the decorator name postfix (if any) and the value is the path
39+ */
40+ protected array $ elementDecoratorLoaderPaths = [];
41+
42+ /**
43+ * Default element decorators
44+ *
45+ * Override this property to change the default decorators of the elements.
46+ * The default decorators will be applied to all elements that do not have their own decorators.
47+ *
48+ * The order of the decorators is important, as it determines the rendering order.
49+ *
50+ * @var ?array<string|int, string|array<string, string>>
51+ */
52+ protected ?array $ defaultElementDecorators = [];
53+
3254 /** @var FormElement[] */
3355 private $ elements = [];
3456
3557 /** @var array<string, array<int, mixed>> */
3658 private $ populatedValues = [];
3759
60+ public function getDefaultElementDecorators (): ?array
61+ {
62+ if (empty ($ this ->defaultElementDecorators )) {
63+ return null ;
64+ }
65+
66+ return $ this ->defaultElementDecorators ;
67+ }
68+
69+ public function setDefaultElementDecorators (?array $ defaultElementDecorators ): self
70+ {
71+ if (empty ($ defaultElementDecorators )) {
72+ $ defaultElementDecorators = null ;
73+ }
74+
75+ $ this ->defaultElementDecorators = $ defaultElementDecorators ;
76+
77+ return $ this ;
78+ }
79+
3880 /**
3981 * Get all elements
4082 *
@@ -130,6 +172,23 @@ public function addElement($typeOrElement, $name = null, $options = null)
130172 return $ this ;
131173 }
132174
175+ /**
176+ * Add custom element decorator loader paths for the elements
177+ *
178+ * Call this method before creating any element OR override the {@see self::$elementDecoratorLoaderPaths} property,
179+ * to ensure that your custom decorator loader paths are applied
180+ *
181+ * @param array $loaderPaths
182+ *
183+ * @return $this
184+ */
185+ public function addElementDecoratorLoaderPaths (array $ loaderPaths ): self
186+ {
187+ $ this ->elementDecoratorLoaderPaths = $ loaderPaths ;
188+
189+ return $ this ;
190+ }
191+
133192 /**
134193 * Create an element
135194 *
@@ -157,6 +216,33 @@ public function createElement($type, $name, $options = null)
157216 /** @var FormElement $element */
158217 $ element = new $ class ($ name );
159218
219+ $ customDecoratorPaths = $ this ->elementDecoratorLoaderPaths ;
220+ if (! empty ($ customDecoratorPaths )) {
221+ if ($ element instanceof DefaultFormElementDecoration) {
222+ $ element ->addElementDecoratorLoaderPaths ($ customDecoratorPaths );
223+ }
224+
225+ $ chain = $ element ->getDecorators ();
226+ foreach ($ customDecoratorPaths as $ paths ) {
227+ foreach ($ paths as $ postfix => $ path ) {
228+ if (is_int ($ postfix )) {
229+ $ postfix = '' ;
230+ }
231+
232+ $ chain ->addDecoratorLoader ($ path , $ postfix );
233+ }
234+ }
235+ }
236+
237+ $ defaultDecorators = $ this ->getDefaultElementDecorators ();
238+ if ($ defaultDecorators !== null && ! $ this ->hasDefaultElementDecorator ()) {
239+ if ($ element instanceof DefaultFormElementDecoration) {
240+ $ element ->setDefaultElementDecorators ($ defaultDecorators );
241+ }
242+
243+ $ element ->setDecorators ($ defaultDecorators );
244+ }
245+
160246 if ($ options !== null ) {
161247 $ element ->addAttributes ($ options );
162248 }
@@ -454,6 +540,11 @@ protected function ensureDefaultElementLoaderRegistered()
454540 */
455541 protected function decorate (FormElement $ element )
456542 {
543+ if ($ element ->hasDecorators ()) {
544+ // new decorator implementation in use
545+ return $ this ;
546+ }
547+
457548 if ($ this ->hasDefaultElementDecorator ()) {
458549 $ decorator = $ this ->getDefaultElementDecorator ();
459550
0 commit comments