| @@ -171,5 +171,88 @@ | ||
| 171 | 171 | return []; |
| 172 | 172 | } |
| 173 | 173 | return $level[ $state ]; |
| 174 | 174 | } |
| 175 | + | |
| 176 | + /** | |
| 177 | + * What a device/state inherits for one prop: the nearest value up the | |
| 178 | + * cascade, not counting the bucket itself, stepping over value-less | |
| 179 | + * remnants (a Range cleared down to its unit). Mirror of the JS | |
| 180 | + * `inheritedProp()`. | |
| 181 | + * | |
| 182 | + * @param array $styles Stored styles array. | |
| 183 | + * @param string $bucket Breakpoint bucket key ('' for base). | |
| 184 | + * @param string $state State key. | |
| 185 | + * @param string $prop Prop name. | |
| 186 | + * @param array $devices Devices applying here, widest-first. | |
| 187 | + * @return mixed The inherited value, or null. | |
| 188 | + */ | |
| 189 | + public static function inherited_prop( $styles, $bucket, $state, $prop, $devices ) { | |
| 190 | + foreach ( self::resolve_buckets( $bucket, $devices ) as $b ) { | |
| 191 | + foreach ( '' === $state ? [ '' ] : [ $state, '' ] as $s ) { | |
| 192 | + if ( $b === $bucket && $s === $state ) { | |
| 193 | + continue; | |
| 194 | + } | |
| 195 | + $props = self::read_bucket( $styles, $b, $s ); | |
| 196 | + $value = isset( $props[ $prop ] ) ? $props[ $prop ] : null; | |
| 197 | + if ( self::has_value( $value ) ) { | |
| 198 | + return $value; | |
| 199 | + } | |
| 200 | + } | |
| 201 | + } | |
| 202 | + return null; | |
| 203 | + } | |
| 204 | + | |
| 205 | + /** | |
| 206 | + * The buckets to consult for one device, nearest first. Mirror of the JS | |
| 207 | + * `resolveBuckets()`. | |
| 208 | + * | |
| 209 | + * @param string $bucket The bucket being resolved for. | |
| 210 | + * @param array $devices Devices applying at that one, widest-first. | |
| 211 | + * @return string[] Bucket keys, nearest first. | |
| 212 | + */ | |
| 213 | + private static function resolve_buckets( $bucket, $devices ) { | |
| 214 | + if ( empty( $devices ) ) { | |
| 215 | + return '' === $bucket ? [ '' ] : [ $bucket, '' ]; | |
| 216 | + } | |
| 217 | + $out = [ $bucket ]; | |
| 218 | + foreach ( array_reverse( $devices ) as $device ) { | |
| 219 | + $key = self::device_bucket_key( $device ); | |
| 220 | + if ( ! in_array( $key, $out, true ) ) { | |
| 221 | + $out[] = $key; | |
| 222 | + } | |
| 223 | + } | |
| 224 | + if ( ! in_array( '', $out, true ) ) { | |
| 225 | + $out[] = ''; | |
| 226 | + } | |
| 227 | + return $out; | |
| 228 | + } | |
| 229 | + | |
| 230 | + /** | |
| 231 | + * Whether a stored value counts as set. Mirror of the JS `hasValue()`: | |
| 232 | + * unit / isLinked bookkeeping members are not values. | |
| 233 | + * | |
| 234 | + * @param mixed $value Stored value. | |
| 235 | + * @return bool Whether anything was actually entered. | |
| 236 | + */ | |
| 237 | + private static function has_value( $value ) { | |
| 238 | + if ( null === $value || '' === $value || false === $value ) { | |
| 239 | + return false; | |
| 240 | + } | |
| 241 | + if ( is_array( $value ) ) { | |
| 242 | + if ( array_keys( $value ) === range( 0, count( $value ) - 1 ) ) { | |
| 243 | + return count( $value ) > 0; | |
| 244 | + } | |
| 245 | + foreach ( $value as $key => $member ) { | |
| 246 | + $key = (string) $key; | |
| 247 | + if ( 'isLinked' === $key || 'unit' === $key || 'Unit' === substr( $key, -4 ) ) { | |
| 248 | + continue; | |
| 249 | + } | |
| 250 | + if ( self::has_value( $member ) ) { | |
| 251 | + return true; | |
| 252 | + } | |
| 253 | + } | |
| 254 | + return false; | |
| 255 | + } | |
| 256 | + return true; | |
| 257 | + } | |
| 175 | 258 | } |