PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.0-beta2
Elementor Website Builder – more than just a page builder v4.3.0-beta2
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / modules / components / utils / reconcile-component-instance-elements.php

reconcile-component-instance-elements.php in Elementor Website Builder – more than just a page builder 4.3.0-beta2, at modules/components/utils/reconcile-component-instance-elements.php

145 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Modules\Components\Utils;
4
5 use Elementor\Modules\AtomicWidgets\ChildrenDependencies\Children_Dependency_Evaluator;
6 use Elementor\Modules\AtomicWidgets\Elements\Base\Atomic_Element_Base;
7 use Elementor\Modules\AtomicWidgets\Elements\Base\Atomic_Widget_Base;
8 use Elementor\Modules\AtomicWidgets\PropsResolver\Render_Props_Resolver;
9 use Elementor\Modules\AtomicWidgets\Utils\Format_Element_Ids;
10 use Elementor\Modules\AtomicWidgets\Utils\Utils;
11 use Elementor\Plugin;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit; // Exit if accessed directly.
15 }
16
17 /**
18 * Applies `children_dependencies` rules to a component's element tree at render
19 * time, mirroring what the editor does on the canvas via `reconcileInitialChildren`.
20 *
21 * Must run *before* {@see Format_Component_Elements_Id::format()} so that children
22 * inserted here are included in the instance-scoped id hashing.
23 */
24 class Reconcile_Component_Instance_Elements {
25 const ELEMENT_ID_LENGTH = 7;
26
27 public static function apply( array $elements ): array {
28 return array_map( [ self::class, 'reconcile_element' ], $elements );
29 }
30
31 private static function reconcile_element( array $element ): array {
32 $children = self::resolve_children( $element );
33
34 $element['elements'] = array_map( [ self::class, 'reconcile_element' ], $children );
35
36 return $element;
37 }
38
39 private static function resolve_children( array $element ): array {
40 $children = $element['elements'] ?? [];
41 $instance = Plugin::$instance->elements_manager->create_element_instance( $element );
42
43 if ( ! $instance || ! Utils::is_atomic( $instance ) ) {
44 return $children;
45 }
46
47 /** @var Atomic_Element_Base|Atomic_Widget_Base $instance */
48 $dependencies = $instance->get_config()['children_dependencies'] ?? [];
49
50 if ( empty( $dependencies ) ) {
51 return $children;
52 }
53
54 $resolved_settings = Render_Props_Resolver::for_settings()->resolve(
55 $instance::get_props_schema(),
56 $instance->get_settings()
57 );
58
59 foreach ( $dependencies as $rule ) {
60 $child_type = $rule['child_type'];
61 $is_met = Children_Dependency_Evaluator::is_met( $rule['when'] ?? null, $resolved_settings );
62 $index = self::find_child_index( $children, $child_type );
63 $is_present = $index >= 0;
64
65 if ( $is_met && ! $is_present ) {
66 $model = self::derive_ids(
67 $rule['default_model'] ?? [ 'elType' => $child_type ],
68 ( $element['id'] ?? '' ) . '_' . $child_type
69 );
70
71 array_splice( $children, self::resolve_insert_index( $rule['position'] ?? [], $children ), 0, [ $model ] );
72
73 continue;
74 }
75
76 if ( ! $is_met && $is_present ) {
77 array_splice( $children, $index, 1 );
78 }
79 }
80
81 return $children;
82 }
83
84 /**
85 * `default_model` payloads are static configuration and carry no ids, so every
86 * inserted node gets one derived from the parent id and its position in the subtree.
87 * Keep in sync with `deriveIds()` in the editor's
88 * `reconcile-component-instance-elements.ts` so ids match between render and canvas.
89 */
90 private static function derive_ids( array $element, string $seed ): array {
91 $element['id'] = Format_Element_Ids::hash_string( $seed, self::ELEMENT_ID_LENGTH );
92
93 if ( ! empty( $element['elements'] ) ) {
94 $children = array_values( $element['elements'] );
95
96 foreach ( $children as $index => $child ) {
97 $children[ $index ] = self::derive_ids( $child, $element['id'] . '_' . $index );
98 }
99
100 $element['elements'] = $children;
101 }
102
103 return $element;
104 }
105
106 private static function find_child_index( array $children, string $child_type ): int {
107 foreach ( $children as $index => $child ) {
108 if ( ( $child['elType'] ?? null ) === $child_type ) {
109 return $index;
110 }
111 }
112
113 return -1;
114 }
115
116 private static function resolve_insert_index( array $position, array $children ): int {
117 $last_index = count( $children );
118 $kind = $position['kind'] ?? 'last';
119
120 switch ( $kind ) {
121 case 'first':
122 return 0;
123
124 case 'index':
125 $index = isset( $position['value'] ) ? (int) $position['value'] : $last_index;
126
127 return max( 0, min( $index, $last_index ) );
128
129 case 'after_type':
130 $anchor = self::find_child_index( $children, (string) ( $position['value'] ?? '' ) );
131
132 return $anchor >= 0 ? $anchor + 1 : $last_index;
133
134 case 'before_type':
135 $anchor = self::find_child_index( $children, (string) ( $position['value'] ?? '' ) );
136
137 return $anchor >= 0 ? $anchor : $last_index;
138
139 case 'last':
140 default:
141 return $last_index;
142 }
143 }
144 }
145