JezK
Edit File: object-side-merge-converter.php
<?php namespace Elementor\Modules\AtomicWidgets\CssConverter\Converters; use Elementor\Modules\AtomicWidgets\CssConverter\Conversion_Context; use Elementor\Modules\AtomicWidgets\CssConverter\Property_Converter_Base; use Elementor\Modules\AtomicWidgets\CssConverter\Css_Var_Token_Resolver; use Elementor\Modules\AtomicWidgets\CssConverter\ValueParsers\Size_Value_Parser; use Elementor\Modules\AtomicWidgets\PropTypes\Size_Prop_Type; use Elementor\Modules\Variables\Services\Variables_Service; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Converter for a single side/corner longhand that contributes a fragment to a multi-side object prop: * border-{side}-width -> a side of `border-width` (Border_Width), border-{corner}-radius -> a corner of * `border-radius` (Border_Radius). One instance per input property. * * The target object is accumulated in the shared context across sibling declarations: each instance * reads the current target prop and re-populates it. If the target is already this object it merges the * one side in; if it is the single Size member (e.g. a prior `border-width: 1px`) all sides are seeded * from that single before the override, so the CSS cascade stays faithful; otherwise a fresh object is * created. A value the Size parser rejects declines the declaration (-> custom_css) and leaves the * accumulated object untouched. */ class Object_Side_Merge_Converter extends Property_Converter_Base { const SIZE_TYPE = 'size'; private string $property; private string $target_property; private string $type_key; private string $side_key; /** * @var string[] */ private array $all_side_keys; /** * @var class-string */ private string $object_prop_type; private ?Variables_Service $variables_service; /** * @param string $property The input longhand this converter owns (e.g. border-top-width). * @param string $target_property The aggregate prop it contributes to (e.g. border-width). * @param string $type_key The aggregate object's $$type (e.g. border-width). * @param string $side_key The object key this longhand fills (e.g. block-start). * @param string[] $all_side_keys Every key of the object, used to seed from a single Size. * @param string $object_prop_type Object_Prop_Type class used to wrap the merged sides. * @param Variables_Service|null $variables_service When provided, a var-only value that resolves to a * known size variable is emitted as a variable PropValue * instead of a raw Size leaf. */ public function __construct( string $property, string $target_property, string $type_key, string $side_key, array $all_side_keys, string $object_prop_type, ?Variables_Service $variables_service = null ) { $this->property = $property; $this->target_property = $target_property; $this->type_key = $type_key; $this->side_key = $side_key; $this->all_side_keys = $all_side_keys; $this->object_prop_type = $object_prop_type; $this->variables_service = $variables_service; } protected function get_supported_properties(): array { return [ $this->property ]; } protected function convert_null( Conversion_Context $context, array $rule ): bool { $sides = $this->current_sides( $context->get_prop( $this->target_property ) ); $sides[ $this->side_key ] = null; $context->set_prop( $this->target_property, ( $this->object_prop_type )::generate( $sides ) ); return true; } protected function do_convert( Conversion_Context $context, array $rule ): bool { $value = trim( $rule['value'] ); $leaf = Size_Value_Parser::parse( $value ); if ( null === $leaf ) { return false; } $side_value = Css_Var_Token_Resolver::resolve_size_var_prop_value( $this->variables_service, $value ) ?? Size_Prop_Type::generate( $leaf ); $sides = $this->current_sides( $context->get_prop( $this->target_property ) ); $sides[ $this->side_key ] = $side_value; $context->set_prop( $this->target_property, ( $this->object_prop_type )::generate( $sides ) ); return true; } /** * @param mixed $existing The current target prop value, if any. * @return array<string, array> Side key -> Size PropValue. */ private function current_sides( $existing ): array { if ( ! is_array( $existing ) ) { return []; } $type = $existing['$$type'] ?? null; if ( $this->type_key === $type && is_array( $existing['value'] ?? null ) ) { return $existing['value']; } if ( self::SIZE_TYPE === $type ) { return array_fill_keys( $this->all_side_keys, $existing ); } return []; } }