PluginProbe
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder / 2.13.1
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder v2.13.1
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 / style-background.php

style-background.php in aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder 2.13.1, at includes/classes/style-background.php

160 lines 4.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 * Background overlay layers -> CSS.
10 *
11 * Mirror of the compile half of `src/blocks/atomic-shared/background.js`.
12 * `background-image` is a list — several images and gradients stacked, first one
13 * on top — with `background-position` / `-size` / `-repeat` / `-attachment`
14 * carrying one entry per layer, so the whole group compiles together here.
15 *
16 * The editor computes a style hash over compiled output, so this formatter must
17 * match the JS one character for character. The parity harness asserts exactly
18 * that.
19 */
20 class StyleBackground {
21
22 /** Per-layer fallbacks, used when ANY layer sets that property. */
23 const LAYER_FALLBACK = [
24 'position' => 'center center',
25 'size' => 'auto',
26 'repeat' => 'repeat',
27 'attachment' => 'scroll',
28 ];
29
30 /** Member -> CSS property, in emission order. */
31 const LAYER_PROPERTIES = [
32 'position' => 'background-position',
33 'size' => 'background-size',
34 'repeat' => 'background-repeat',
35 'attachment' => 'background-attachment',
36 ];
37
38 /**
39 * A numeric member, falling back when absent or unparseable.
40 *
41 * @param mixed $value Raw stored value.
42 * @param float $fallback Fallback.
43 * @return float|int The number.
44 */
45 private static function num( $value, $fallback = 0 ) {
46 if ( is_numeric( $value ) ) {
47 return $value + 0;
48 }
49 return $fallback;
50 }
51
52 /**
53 * Trim a float the way JS number-to-string does (1.0 -> "1").
54 *
55 * @param mixed $value Number to format.
56 * @return string Formatted number.
57 */
58 private static function n( $value ) {
59 $value = (float) $value;
60 return ( floor( $value ) === $value ) ? (string) (int) $value : (string) $value;
61 }
62
63 /**
64 * One layer -> its `background-image` fragment, or '' when it has nothing to
65 * paint (an image layer with no file yet).
66 *
67 * @param array $e Layer entry.
68 * @return string CSS fragment.
69 */
70 public static function layer_image( $e ) {
71 if ( ! is_array( $e ) ) {
72 return '';
73 }
74
75 if ( isset( $e['type'] ) && 'image' === $e['type'] ) {
76 return ! empty( $e['url'] ) ? 'url("' . $e['url'] . '")' : '';
77 }
78
79 $from = ( ! empty( $e['from'] ) ? $e['from'] : '#000000' ) . ' ' . self::n( self::num( $e['fromStop'] ?? 0 ) ) . '%';
80 $to = ( ! empty( $e['to'] ) ? $e['to'] : 'rgba(0, 0, 0, 0)' ) . ' ' . self::n( self::num( $e['toStop'] ?? 100, 100 ) ) . '%';
81
82 if ( isset( $e['gradientType'] ) && 'radial' === $e['gradientType'] ) {
83 // Radial centre. Deliberately NOT `position`: that member is the
84 // layer's background-position, and sharing one key made picking a
85 // radial centre also pin background-position on the whole stack.
86 $at = ! empty( $e['radialPosition'] ) ? $e['radialPosition'] : 'center center';
87 return 'radial-gradient(circle at ' . $at . ', ' . $from . ', ' . $to . ')';
88 }
89
90 return 'linear-gradient(' . self::n( self::num( $e['angle'] ?? 180, 180 ) ) . 'deg, ' . $from . ', ' . $to . ')';
91 }
92
93 /**
94 * A list of layers -> the background declarations they compile to.
95 *
96 * The four positional properties are emitted only when at least one layer
97 * sets them, and when they are, every layer supplies its own entry so the
98 * comma lists stay index-aligned with the image list.
99 *
100 * @param mixed $entries Stored layers.
101 * @return array CSS declarations keyed by property, in emission order.
102 */
103 public static function to_declarations( $entries ) {
104 if ( ! is_array( $entries ) || empty( $entries ) ) {
105 return [];
106 }
107
108 $layers = [];
109 foreach ( $entries as $entry ) {
110 if ( ! is_array( $entry ) ) {
111 continue;
112 }
113 // Mirrors the `! e.disabled` filter in background.js, and the same
114 // check StyleEffects::to_css() already made for the other
115 // repeatable lists — the inspector's per-entry eye toggle has to
116 // mean something here too.
117 if ( ! empty( $entry['disabled'] ) ) {
118 continue;
119 }
120 $image = self::layer_image( $entry );
121 if ( '' === $image ) {
122 continue;
123 }
124 $layers[] = [
125 'entry' => $entry,
126 'image' => $image
127 ];
128 }
129
130 if ( empty( $layers ) ) {
131 return [];
132 }
133
134 $css = [ 'background-image' => implode( ', ', wp_list_pluck( $layers, 'image' ) ) ];
135
136 foreach ( self::LAYER_PROPERTIES as $member => $property ) {
137 $any_set = false;
138 foreach ( $layers as $layer ) {
139 if ( ! empty( $layer['entry'][ $member ] ) ) {
140 $any_set = true;
141 break;
142 }
143 }
144 if ( ! $any_set ) {
145 continue;
146 }
147
148 $values = [];
149 foreach ( $layers as $layer ) {
150 $values[] = ! empty( $layer['entry'][ $member ] )
151 ? $layer['entry'][ $member ]
152 : self::LAYER_FALLBACK[ $member ];
153 }
154 $css[ $property ] = implode( ', ', $values );
155 }
156
157 return $css;
158 }
159 }
160