PluginProbe
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder / 2.13.0
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder v2.13.0
2.13.0 2.13.1 2.12.0 2.11.1 2.11.0 2.10.0 2.9.0 2.7.4 2.7.5 2.7.6 2.7.7 2.8.0 2.8.1 2.9.1 trunk 1.0 1.0-beta1 1.0-beta2 1.0-beta3 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 All 80 releases
ablocks / includes / classes / styles-schema.php

styles-schema.php in aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder 2.13.0, at includes/classes/styles-schema.php

224 lines 9.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace ABlocks\Classes;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * The single prop -> CSS descriptor for the atomic style system.
10 *
11 * Both compilers read this instead of keeping their own prop lists: the PHP
12 * frontend compiler (AtomicStyles) and the JS editor compiler (the mirror in
13 * src/blocks/atomic-shared/styles-schema.js). Adding a style prop means one
14 * entry here and one in the mirror — nothing else enumerates props.
15 *
16 * The array order IS the canonical emission order. Declarations are emitted in
17 * this order on both sides so the two compilers serialise identically, which is
18 * what lets a block's style hash be computed in the editor and matched on the
19 * front end.
20 *
21 * Each entry is [ prop, css, kind ] where kind is one of:
22 * typography - the `typography` object, compiled by Controls\Typography
23 * color - a scalar resolved through Controls\Color (supports globals)
24 * scalar - a scalar emitted as-is
25 * range - an aBlocks Range object ({ value, valueUnit }) -> "<value><unit>"
26 * border - the border group (width/style/colour/radius + its fallbacks)
27 * dimensions - padding / margin, compiled by Controls\Dimensions
28 * effect - a repeatable list, compiled by StyleEffects
29 * overlay - background overlay layers, compiled by StyleBackground
30 * clip - background-clip plus its -webkit- companion
31 */
32 class StylesSchema {
33
34 /**
35 * The border group's members, in the order they are emitted. Declared here
36 * rather than inline in the compiler so the JS mirror can assert the same
37 * set.
38 *
39 * The per-side widths and per-corner radii are optional overrides on top of
40 * the uniform value — a block that sets none of them compiles exactly as it
41 * did before they existed, which matters because the style class is a hash
42 * of compiled output.
43 */
44 const BORDER_SIDES = [ 'Top', 'Right', 'Bottom', 'Left' ];
45
46 /** Corner prop suffix -> CSS property, in CSS shorthand order. */
47 const BORDER_CORNERS = [
48 'TopLeft' => 'border-top-left-radius',
49 'TopRight' => 'border-top-right-radius',
50 'BottomRight' => 'border-bottom-right-radius',
51 'BottomLeft' => 'border-bottom-left-radius',
52 ];
53
54 const BORDER_MEMBERS = [
55 'borderWidth',
56 'borderWidthTop',
57 'borderWidthRight',
58 'borderWidthBottom',
59 'borderWidthLeft',
60 'borderStyle',
61 'borderColor',
62 'borderRadius',
63 'borderRadiusTopLeft',
64 'borderRadiusTopRight',
65 'borderRadiusBottomRight',
66 'borderRadiusBottomLeft',
67 ];
68
69 /** Ordered descriptor. */
70 public static function props() {
71 return [
72 [ 'prop' => 'typography', 'css' => '', 'kind' => 'typography' ],
73
74 [ 'prop' => 'textColor', 'css' => 'color', 'kind' => 'color' ],
75 [ 'prop' => 'backgroundColor', 'css' => 'background-color', 'kind' => 'color' ],
76 [ 'prop' => 'backgroundGradient', 'css' => 'background-image', 'kind' => 'scalar' ],
77 // Overlay layers expand to background-image + the four positional
78 // props at once, so they emit as a group (like border / dimensions).
79 [ 'prop' => 'backgroundOverlay', 'css' => '', 'kind' => 'overlay' ],
80
81 [ 'prop' => 'border', 'css' => '', 'kind' => 'border' ],
82
83 // Size.
84 [ 'prop' => 'width', 'css' => 'width', 'kind' => 'range' ],
85 [ 'prop' => 'maxWidth', 'css' => 'max-width', 'kind' => 'range' ],
86 [ 'prop' => 'minWidth', 'css' => 'min-width', 'kind' => 'range' ],
87 [ 'prop' => 'height', 'css' => 'height', 'kind' => 'range' ],
88 [ 'prop' => 'minHeight', 'css' => 'min-height', 'kind' => 'range' ],
89 [ 'prop' => 'maxHeight', 'css' => 'max-height', 'kind' => 'range' ],
90 [ 'prop' => 'aspectRatio', 'css' => 'aspect-ratio', 'kind' => 'scalar' ],
91
92 // Gaps.
93 [ 'prop' => 'gap', 'css' => 'gap', 'kind' => 'range' ],
94 [ 'prop' => 'rowGap', 'css' => 'row-gap', 'kind' => 'range' ],
95 [ 'prop' => 'columnGap', 'css' => 'column-gap', 'kind' => 'range' ],
96 [ 'prop' => 'strokeWidth', 'css' => 'stroke-width', 'kind' => 'range' ],
97
98 // Position offsets.
99 [ 'prop' => 'top', 'css' => 'top', 'kind' => 'range' ],
100 [ 'prop' => 'right', 'css' => 'right', 'kind' => 'range' ],
101 [ 'prop' => 'bottom', 'css' => 'bottom', 'kind' => 'range' ],
102 [ 'prop' => 'left', 'css' => 'left', 'kind' => 'range' ],
103 // Elementor's "Anchor offset": how far above the element an in-page
104 // anchor link should stop, so a fixed header does not cover it.
105 [ 'prop' => 'anchorOffset', 'css' => 'scroll-margin-top', 'kind' => 'range' ],
106
107 // Layout.
108 [ 'prop' => 'display', 'css' => 'display', 'kind' => 'scalar' ],
109 [ 'prop' => 'flexDirection', 'css' => 'flex-direction', 'kind' => 'scalar' ],
110 [ 'prop' => 'flexWrap', 'css' => 'flex-wrap', 'kind' => 'scalar' ],
111 [ 'prop' => 'justifyContent', 'css' => 'justify-content', 'kind' => 'scalar' ],
112 [ 'prop' => 'alignItems', 'css' => 'align-items', 'kind' => 'scalar' ],
113 [ 'prop' => 'alignContent', 'css' => 'align-content', 'kind' => 'scalar' ],
114 [ 'prop' => 'gridTemplateColumns', 'css' => 'grid-template-columns', 'kind' => 'scalar' ],
115 [ 'prop' => 'gridTemplateRows', 'css' => 'grid-template-rows', 'kind' => 'scalar' ],
116 [ 'prop' => 'gridAutoFlow', 'css' => 'grid-auto-flow', 'kind' => 'scalar' ],
117 [ 'prop' => 'gridAutoColumns', 'css' => 'grid-auto-columns', 'kind' => 'scalar' ],
118 [ 'prop' => 'gridAutoRows', 'css' => 'grid-auto-rows', 'kind' => 'scalar' ],
119
120 // Flex-item behaviour (when placed inside an atomic flex/grid parent).
121 [ 'prop' => 'flexBasis', 'css' => 'flex-basis', 'kind' => 'scalar' ],
122 [ 'prop' => 'flexGrow', 'css' => 'flex-grow', 'kind' => 'scalar' ],
123 [ 'prop' => 'flexShrink', 'css' => 'flex-shrink', 'kind' => 'scalar' ],
124 [ 'prop' => 'order', 'css' => 'order', 'kind' => 'scalar' ],
125 [ 'prop' => 'alignSelf', 'css' => 'align-self', 'kind' => 'scalar' ],
126
127 // Media (image / svg).
128 [ 'prop' => 'objectFit', 'css' => 'object-fit', 'kind' => 'scalar' ],
129 [ 'prop' => 'objectPosition', 'css' => 'object-position', 'kind' => 'scalar' ],
130 [ 'prop' => 'overflow', 'css' => 'overflow', 'kind' => 'scalar' ],
131 [ 'prop' => 'fill', 'css' => 'fill', 'kind' => 'scalar' ],
132 [ 'prop' => 'stroke', 'css' => 'stroke', 'kind' => 'scalar' ],
133
134 [ 'prop' => 'textAlign', 'css' => 'text-align', 'kind' => 'scalar' ],
135
136 // Position.
137 [ 'prop' => 'position', 'css' => 'position', 'kind' => 'scalar' ],
138 [ 'prop' => 'zIndex', 'css' => 'z-index', 'kind' => 'scalar' ],
139
140 // Effects.
141 [ 'prop' => 'transform', 'css' => 'transform', 'kind' => 'effect' ],
142 [ 'prop' => 'transformOrigin', 'css' => 'transform-origin', 'kind' => 'scalar' ],
143 [ 'prop' => 'filter', 'css' => 'filter', 'kind' => 'effect' ],
144 [ 'prop' => 'backdropFilter', 'css' => 'backdrop-filter', 'kind' => 'effect' ],
145 [ 'prop' => 'mixBlendMode', 'css' => 'mix-blend-mode', 'kind' => 'scalar' ],
146 // `isolation: isolate` starts a stacking context, which is what
147 // confines a CHILD's blend mode to this element instead of letting it
148 // blend with whatever the page happens to paint behind it.
149 [ 'prop' => 'isolation', 'css' => 'isolation', 'kind' => 'scalar' ],
150 [ 'prop' => 'opacity', 'css' => 'opacity', 'kind' => 'scalar' ],
151 [ 'prop' => 'boxShadow', 'css' => 'box-shadow', 'kind' => 'effect' ],
152 [ 'prop' => 'textShadow', 'css' => 'text-shadow', 'kind' => 'effect' ],
153 [ 'prop' => 'cursor', 'css' => 'cursor', 'kind' => 'scalar' ],
154 [ 'prop' => 'transition', 'css' => 'transition', 'kind' => 'effect' ],
155
156 // Background (image layer settings; the colour/gradient are above).
157 [ 'prop' => 'backgroundRepeat', 'css' => 'background-repeat', 'kind' => 'scalar' ],
158 [ 'prop' => 'backgroundSize', 'css' => 'background-size', 'kind' => 'scalar' ],
159 [ 'prop' => 'backgroundPosition', 'css' => 'background-position', 'kind' => 'scalar' ],
160 [ 'prop' => 'backgroundAttachment', 'css' => 'background-attachment', 'kind' => 'scalar' ],
161 // `background-clip: text` needs the -webkit- prefix to paint anywhere
162 // but the newest engines, so this kind emits the pair.
163 [ 'prop' => 'backgroundClip', 'css' => 'background-clip', 'kind' => 'clip' ],
164
165 // Spacing.
166 [ 'prop' => 'padding', 'css' => 'padding', 'kind' => 'dimensions' ],
167 [ 'prop' => 'margin', 'css' => 'margin', 'kind' => 'dimensions' ],
168 ];
169 }
170
171 /**
172 * Reorder a declarations map into canonical order, so both compilers
173 * serialise the same declaration set identically — which is what makes the
174 * style hash reproducible across the editor and the front end.
175 *
176 * Some descriptor entries emit CSS properties the descriptor does not name
177 * one-by-one: `typography` expands to font-family/font-size/…, `dimensions`
178 * to padding-top/padding-right/…, and the border group to its longhands. An
179 * unnamed property therefore inherits the rank of the last named property
180 * before it, keeping it adjacent to the group that produced it. Compiler
181 * output is already in descriptor order, so this is a stable no-op there and
182 * a normaliser for maps assembled anywhere else.
183 */
184 /**
185 * @param array $declarations CSS declarations keyed by property.
186 * @return array The same declarations in canonical order.
187 */
188 public static function canonical_order( $declarations ) {
189 if ( ! is_array( $declarations ) || empty( $declarations ) ) {
190 return $declarations;
191 }
192
193 $rank = [];
194 foreach ( self::props() as $i => $entry ) {
195 if ( '' !== $entry['css'] ) {
196 $rank[ $entry['css'] ] = $i;
197 }
198 }
199
200 $indexed = [];
201 $n = 0;
202 $last_rank = 0;
203 foreach ( $declarations as $property => $value ) {
204 if ( isset( $rank[ $property ] ) ) {
205 $last_rank = $rank[ $property ];
206 }
207 $indexed[] = [ $last_rank, $n++, $property, $value ];
208 }
209
210 usort(
211 $indexed,
212 function ( $a, $b ) {
213 return $a[0] === $b[0] ? $a[1] - $b[1] : $a[0] - $b[0];
214 }
215 );
216
217 $out = [];
218 foreach ( $indexed as $row ) {
219 $out[ $row[2] ] = $row[3];
220 }
221 return $out;
222 }
223 }
224