Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 164 |
|
0.00% |
0 / 18 |
CRAP | |
0.00% |
0 / 1 |
HtmxEvents | |
0.00% |
0 / 164 |
|
0.00% |
0 / 18 |
702 | |
0.00% |
0 / 1 |
onClickDelete | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
onClickSavePreset | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
onClickPaste | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
2 | |||
onClickDuplicate | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
2 | |||
onRootDrop | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
onSlotDrop | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
onInstanceClick | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
2 | |||
onInstanceFormChange | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
12 | |||
onInstanceUpdateButtonClick | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
42 | |||
onThirdPartyFormChange | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
onUndo | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
onRedo | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
onReset | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
onRevert | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
onClear | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
onSave | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
setHtmxAttributes | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
setHtmxAttributesOnSubKey | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace Drupal\display_builder; |
6 | |
7 | use Drupal\Component\Render\MarkupInterface; |
8 | use Drupal\Core\Url; |
9 | |
10 | /** |
11 | * The HTMX Events class. |
12 | */ |
13 | class HtmxEvents { |
14 | |
15 | use HtmxTrait; |
16 | |
17 | /** |
18 | * Delete on click. |
19 | * |
20 | * @param array $build |
21 | * The render array. |
22 | * @param string $builder_id |
23 | * The instance entity ID. |
24 | * @param string $node_id |
25 | * The node id of the source. |
26 | * |
27 | * @return array |
28 | * The render array. |
29 | */ |
30 | public function onClickDelete(array $build, string $builder_id, string $node_id): array { |
31 | $url = new Url( |
32 | 'display_builder.api_delete', |
33 | [ |
34 | 'builder' => $builder_id, |
35 | 'node_id' => $node_id, |
36 | ] |
37 | ); |
38 | |
39 | return $this->setHtmxAttributes($build, $url, 'click consume', 'delete'); |
40 | } |
41 | |
42 | /** |
43 | * Save as preset on click. |
44 | * |
45 | * @param array $build |
46 | * The render array. |
47 | * @param string $builder_id |
48 | * The instance entity ID. |
49 | * @param string $node_id |
50 | * The node id of the source. |
51 | * @param string|\Drupal\Component\Render\MarkupInterface $prompt |
52 | * The prompt before save. |
53 | * |
54 | * @return array |
55 | * The render array. |
56 | */ |
57 | public function onClickSavePreset(array $build, string $builder_id, string $node_id, MarkupInterface|string $prompt): array { |
58 | $url = new Url( |
59 | 'display_builder.api_save_preset', |
60 | [ |
61 | 'builder' => $builder_id, |
62 | 'node_id' => $node_id, |
63 | ] |
64 | ); |
65 | |
66 | return $this->setHtmxAttributes($build, $url, 'click consume', 'post', ['hx-prompt' => $prompt]); |
67 | } |
68 | |
69 | /** |
70 | * Paste on click. |
71 | * |
72 | * @param array $build |
73 | * The render array. |
74 | * @param string $builder_id |
75 | * The instance entity ID. |
76 | * @param string $node_id |
77 | * The node id to copy. |
78 | * @param string $parent_id |
79 | * The instance id target. |
80 | * @param string $slot_id |
81 | * The instance target slot id. |
82 | * @param string $slot_position |
83 | * The slot position. |
84 | * |
85 | * @return array |
86 | * The render array. |
87 | */ |
88 | public function onClickPaste(array $build, string $builder_id, string $node_id, string $parent_id, string $slot_id, string $slot_position): array { |
89 | $url = new Url( |
90 | 'display_builder.api_paste', |
91 | [ |
92 | 'builder' => $builder_id, |
93 | 'node_id' => $node_id, |
94 | 'parent_id' => $parent_id, |
95 | 'slot_id' => $slot_id, |
96 | 'slot_position' => $slot_position, |
97 | ] |
98 | ); |
99 | |
100 | return $this->setHtmxAttributes($build, $url, 'click consume', 'post'); |
101 | } |
102 | |
103 | /** |
104 | * Duplicate on placeholder click. |
105 | * |
106 | * @param array $build |
107 | * The render array. |
108 | * @param string $builder_id |
109 | * The instance entity ID. |
110 | * @param string $node_id |
111 | * The node id to copy. |
112 | * @param string $parent_id |
113 | * The instance id target. |
114 | * @param string $slot_id |
115 | * The instance target slot id. |
116 | * @param string $slot_position |
117 | * The slot position. |
118 | * |
119 | * @return array |
120 | * The render array. |
121 | */ |
122 | public function onClickDuplicate(array $build, string $builder_id, string $node_id, string $parent_id, string $slot_id, string $slot_position): array { |
123 | $url = new Url( |
124 | 'display_builder.api_duplicate', |
125 | [ |
126 | 'builder' => $builder_id, |
127 | 'node_id' => $node_id, |
128 | 'parent_id' => $parent_id, |
129 | 'slot_id' => $slot_id, |
130 | 'slot_position' => $slot_position, |
131 | ] |
132 | ); |
133 | |
134 | return $this->setHtmxAttributes($build, $url, 'click consume', 'post'); |
135 | } |
136 | |
137 | /** |
138 | * Drop a component_id, a block_id, or an instance_id, to the root dropzone. |
139 | * |
140 | * @param array $build |
141 | * The render array. |
142 | * @param string $builder_id |
143 | * The instance entity ID. |
144 | * @param string $island_id |
145 | * The island initiating the event. |
146 | * |
147 | * @return array |
148 | * The render array. |
149 | */ |
150 | public function onRootDrop(array $build, string $builder_id, string $island_id): array { |
151 | $url = new Url( |
152 | 'display_builder.api_root_attach', |
153 | [ |
154 | 'builder' => $builder_id, |
155 | 'from' => $island_id, |
156 | ] |
157 | ); |
158 | |
159 | return $this->setHtmxAttributes($build, $url, 'dragend consume', 'post'); |
160 | } |
161 | |
162 | /** |
163 | * Drop a component_id, a block_id, or an instance_id, to a component slot. |
164 | * |
165 | * @param array $build |
166 | * The render array. |
167 | * @param string $builder_id |
168 | * The instance entity ID. |
169 | * @param string $island_id |
170 | * The island initiating the event. |
171 | * @param string $node_id |
172 | * The node id of the source. |
173 | * @param string $slot |
174 | * The slot. |
175 | * |
176 | * @return array |
177 | * The render array. |
178 | */ |
179 | public function onSlotDrop(array $build, string $builder_id, string $island_id, string $node_id, string $slot): array { |
180 | $url = new Url( |
181 | 'display_builder.api_slot_attach', |
182 | [ |
183 | 'builder' => $builder_id, |
184 | 'node_id' => $node_id, |
185 | 'slot' => $slot, |
186 | 'from' => $island_id, |
187 | ] |
188 | ); |
189 | |
190 | return $this->setHtmxAttributes($build, $url, 'dragend consume', 'post'); |
191 | } |
192 | |
193 | /** |
194 | * When a component or block is clicked. |
195 | * |
196 | * @param array $build |
197 | * The render array. |
198 | * @param string $builder_id |
199 | * The instance entity ID. |
200 | * @param string $node_id |
201 | * The node id of the source. |
202 | * @param string $title |
203 | * The instance title. |
204 | * @param int $index |
205 | * The instance index. |
206 | * |
207 | * @return array |
208 | * The render array. |
209 | */ |
210 | public function onInstanceClick(array $build, string $builder_id, string $node_id, string $title, int $index): array { |
211 | $url = new Url( |
212 | 'display_builder.api_get', |
213 | [ |
214 | 'builder' => $builder_id, |
215 | 'node_id' => $node_id, |
216 | ] |
217 | ); |
218 | |
219 | // Only for icon case, remove suffix without loading label. |
220 | $label = \ucfirst(\trim(\str_replace(['renderable', '_'], ['', ' '], $title))); |
221 | |
222 | $attributes = [ |
223 | 'tabindex' => '0', |
224 | 'data-node-id' => $node_id, |
225 | // Data used for contextual menu or drawer name. |
226 | 'data-node-title' => $label, |
227 | 'data-slot-position' => $index, |
228 | 'hx-on::after-swap' => \sprintf('Drupal.displayBuilder.handleSecondDrawer(%s, this)', $builder_id), |
229 | 'hx-on:click' => \sprintf('Drupal.displayBuilder.handleSecondDrawer(%s, this)', $builder_id), |
230 | ]; |
231 | |
232 | return $this->setHtmxAttributes($build, $url, 'click consume', 'get', $attributes); |
233 | } |
234 | |
235 | /** |
236 | * When a value is changed in an instance island form. |
237 | * |
238 | * @param array $build |
239 | * The render array. |
240 | * @param string $builder_id |
241 | * The instance entity ID. |
242 | * @param string $island_id |
243 | * The island initiating the event. |
244 | * @param string $node_id |
245 | * The node id of the source. |
246 | * |
247 | * @return array |
248 | * The render array. |
249 | */ |
250 | public function onInstanceFormChange(array $build, string $builder_id, string $island_id, string $node_id): array { |
251 | $url = new Url( |
252 | 'display_builder.api_update', |
253 | [ |
254 | 'builder' => $builder_id, |
255 | 'node_id' => $node_id, |
256 | 'from' => $island_id, |
257 | ] |
258 | ); |
259 | |
260 | $extra_attr = []; |
261 | |
262 | // Specific Wysiwyg extra code to make it work. |
263 | if (isset($build['source']['value']['#type']) && $build['source']['value']['#type'] === 'text_format') { |
264 | $extra_attr['hx-on:htmx:config-request'] = 'fixWysiwygUpdate(this, event)'; |
265 | $build['#attached']['library'][] = 'display_builder/wysiwyg_fixes.js'; |
266 | } |
267 | |
268 | return $this->setHtmxAttributesOnSubKey($build, $url, 'change consume', 'put', $extra_attr, 'source'); |
269 | } |
270 | |
271 | /** |
272 | * When the update button is clicked in an instance island form. |
273 | * |
274 | * @param array $build |
275 | * The render array. |
276 | * @param string $builder_id |
277 | * The instance entity ID. |
278 | * @param string $island_id |
279 | * The island initiating the event. |
280 | * @param string $node_id |
281 | * The node id of the source. |
282 | * |
283 | * @return array |
284 | * The render array. |
285 | */ |
286 | public function onInstanceUpdateButtonClick(array $build, string $builder_id, string $island_id, string $node_id): array { |
287 | if (!isset($build['update']) || !isset($build['source']) || !isset($build['source']['#id'])) { |
288 | return $build; |
289 | } |
290 | $url = new Url( |
291 | 'display_builder.api_update', |
292 | [ |
293 | 'builder' => $builder_id, |
294 | 'node_id' => $node_id, |
295 | 'from' => $island_id, |
296 | ] |
297 | ); |
298 | |
299 | $extra_attr = [ |
300 | 'hx-include' => '#' . $build['source']['#id'], |
301 | ]; |
302 | |
303 | // Specific Wysiwyg extra code to make it work. |
304 | if (isset($build['source']['value']['#type']) && $build['source']['value']['#type'] === 'text_format') { |
305 | $extra_attr['hx-on:htmx:config-request'] = 'fixWysiwygUpdate(this, event)'; |
306 | $build['#attached']['library'][] = 'display_builder/wysiwyg_fixes.js'; |
307 | } |
308 | |
309 | return $this->setHtmxAttributesOnSubKey($build, $url, 'click consume', 'put', $extra_attr, 'update'); |
310 | } |
311 | |
312 | /** |
313 | * When a value is changed in a third party island. |
314 | * |
315 | * @param array $build |
316 | * The render array. |
317 | * @param string $builder_id |
318 | * The instance entity ID. |
319 | * @param string $node_id |
320 | * The node id of the source. |
321 | * @param string $island_id |
322 | * The island id. |
323 | * |
324 | * @return array |
325 | * The render array. |
326 | */ |
327 | public function onThirdPartyFormChange(array $build, string $builder_id, string $node_id, string $island_id): array { |
328 | $url = new Url( |
329 | 'display_builder.api_third_party_settings_update', |
330 | [ |
331 | 'builder' => $builder_id, |
332 | 'node_id' => $node_id, |
333 | 'island_id' => $island_id, |
334 | ] |
335 | ); |
336 | |
337 | return $this->setHtmxAttributes($build, $url, 'change', 'put'); |
338 | } |
339 | |
340 | /** |
341 | * When the undo button is clicked. |
342 | * |
343 | * @param array $build |
344 | * The render array. |
345 | * @param string $builder_id |
346 | * The instance entity ID. |
347 | * |
348 | * @return array |
349 | * The render array. |
350 | */ |
351 | public function onUndo(array $build, string $builder_id): array { |
352 | $url = new Url( |
353 | 'display_builder.api_undo', |
354 | [ |
355 | 'builder' => $builder_id, |
356 | ] |
357 | ); |
358 | |
359 | return $this->setHtmxAttributes($build, $url, 'click consume', 'post'); |
360 | } |
361 | |
362 | /** |
363 | * When the undo button is clicked. |
364 | * |
365 | * @param array $build |
366 | * The render array. |
367 | * @param string $builder_id |
368 | * The instance entity ID. |
369 | * |
370 | * @return array |
371 | * The render array. |
372 | */ |
373 | public function onRedo(array $build, string $builder_id): array { |
374 | $url = new Url( |
375 | 'display_builder.api_redo', |
376 | [ |
377 | 'builder' => $builder_id, |
378 | ] |
379 | ); |
380 | |
381 | return $this->setHtmxAttributes($build, $url, 'click consume', 'post'); |
382 | } |
383 | |
384 | /** |
385 | * When the restore button is clicked. |
386 | * |
387 | * @param array $build |
388 | * The render array. |
389 | * @param string $builder_id |
390 | * The instance entity ID. |
391 | * |
392 | * @return array |
393 | * The render array. |
394 | */ |
395 | public function onReset(array $build, string $builder_id): array { |
396 | $url = new Url( |
397 | 'display_builder.api_restore', |
398 | [ |
399 | 'builder' => $builder_id, |
400 | ] |
401 | ); |
402 | |
403 | return $this->setHtmxAttributes($build, $url, 'click consume', 'post'); |
404 | } |
405 | |
406 | /** |
407 | * When the revert button is clicked. |
408 | * |
409 | * @param array $build |
410 | * The render array. |
411 | * @param string $builder_id |
412 | * The instance entity ID. |
413 | * |
414 | * @return array |
415 | * The render array. |
416 | */ |
417 | public function onRevert(array $build, string $builder_id): array { |
418 | $url = new Url( |
419 | 'display_builder.api_revert', |
420 | [ |
421 | 'builder' => $builder_id, |
422 | ] |
423 | ); |
424 | |
425 | return $this->setHtmxAttributes($build, $url, 'click consume', 'post'); |
426 | } |
427 | |
428 | /** |
429 | * When the history clear button is clicked. |
430 | * |
431 | * @param array $build |
432 | * The render array. |
433 | * @param string $builder_id |
434 | * The instance entity ID. |
435 | * |
436 | * @return array |
437 | * The render array. |
438 | */ |
439 | public function onClear(array $build, string $builder_id): array { |
440 | $url = new Url( |
441 | 'display_builder.api_clear', |
442 | [ |
443 | 'builder' => $builder_id, |
444 | ] |
445 | ); |
446 | |
447 | return $this->setHtmxAttributes($build, $url, 'click consume', 'post'); |
448 | } |
449 | |
450 | /** |
451 | * When the save button is clicked. |
452 | * |
453 | * @param array $build |
454 | * The render array. |
455 | * @param string $builder_id |
456 | * The instance entity ID. |
457 | * |
458 | * @return array |
459 | * The render array. |
460 | */ |
461 | public function onSave(array $build, string $builder_id): array { |
462 | $url = new Url( |
463 | 'display_builder.api_save', |
464 | [ |
465 | 'builder' => $builder_id, |
466 | ] |
467 | ); |
468 | |
469 | return $this->setHtmxAttributes($build, $url, 'click consume', 'post'); |
470 | } |
471 | |
472 | /** |
473 | * Sets HTMX attributes for a given URL, trigger, and method. |
474 | * |
475 | * @param array $build |
476 | * The render array to modify. |
477 | * @param \Drupal\Core\Url $url |
478 | * The URL for the HTMX request. |
479 | * @param string $trigger |
480 | * The HTMX trigger. |
481 | * @param string $method |
482 | * The HTTP method. |
483 | * @param array $extra_attr |
484 | * (Optional) Extra attributes to add. |
485 | * |
486 | * @return array |
487 | * The modified render array. |
488 | */ |
489 | private function setHtmxAttributes(array $build, Url $url, string $trigger, string $method, array $extra_attr = []): array { |
490 | $attr = $this->setTrigger($trigger, $method, $url); |
491 | $attr = \array_merge($attr, $extra_attr); |
492 | $build['#attributes'] = \array_merge($build['#attributes'] ?? [], $attr); |
493 | |
494 | return $build; |
495 | } |
496 | |
497 | /** |
498 | * Sets HTMX attributes for a given URL, trigger, and method on a subkey. |
499 | * |
500 | * @param array $build |
501 | * The render array to modify. |
502 | * @param \Drupal\Core\Url $url |
503 | * The URL for the HTMX request. |
504 | * @param string $trigger |
505 | * The HTMX trigger. |
506 | * @param string $method |
507 | * The HTTP method. |
508 | * @param array $extra_attr |
509 | * (Optional) Extra attributes to add. |
510 | * @param string $source_key |
511 | * The name of the key to modify, example : update, source. |
512 | * |
513 | * @return array |
514 | * The modified render array. |
515 | */ |
516 | private function setHtmxAttributesOnSubKey(array $build, Url $url, string $trigger, string $method, array $extra_attr, string $source_key): array { |
517 | if (!isset($build[$source_key])) { |
518 | return $build; |
519 | } |
520 | $attr = $this->setTrigger($trigger, $method, $url); |
521 | $attr = \array_merge($attr, $extra_attr); |
522 | $build[$source_key]['#attributes'] = \array_merge($build[$source_key]['#attributes'] ?? [], $attr); |
523 | |
524 | return $build; |
525 | } |
526 | |
527 | } |