first commit

This commit is contained in:
Roman Pyrih
2026-03-10 09:50:10 +01:00
commit 64c4a90405
7289 changed files with 2645777 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver;
use Elementor\Modules\AtomicWidgets\PropTypes\Contracts\Prop_Type;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Import_Export_Props_Resolver extends Props_Resolver {
const CONTEXT_IMPORT = 'import';
const CONTEXT_EXPORT = 'export';
public static function for_import() {
return static::instance( self::CONTEXT_IMPORT );
}
public static function for_export() {
return static::instance( self::CONTEXT_EXPORT );
}
public function resolve( array $schema, array $props ): array {
$resolved = [];
foreach ( $schema as $key => $prop_type ) {
if ( ! ( $prop_type instanceof Prop_Type ) ) {
continue;
}
$value = $this->resolve_item( $props[ $key ] ?? null, $key, $prop_type );
if ( null === $value ) {
continue;
}
$resolved[ $key ] = $value;
}
return $resolved;
}
protected function resolve_item( $value, $key, Prop_Type $prop_type ) {
if ( null === $value ) {
return null;
}
if ( ! $this->is_transformable( $value ) ) {
return $value;
}
return $this->transform( $value, $key, $prop_type );
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Multi_Props {
public static function is( $value ) {
return (
! empty( $value['$$multi-props'] ) &&
true === $value['$$multi-props'] &&
array_key_exists( 'value', $value )
);
}
public static function generate( $value ) {
return [
'$$multi-props' => true,
'value' => $value,
];
}
public static function get_value( $value ) {
return $value['value'] ?? null;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver;
use Elementor\Modules\AtomicWidgets\PropTypes\Contracts\Transformable_Prop_Type;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Props_Resolver_Context {
private ?string $key = null;
private ?Transformable_Prop_Type $prop_type;
private bool $disabled = false;
public static function make(): self {
return new static();
}
public function set_key( ?string $key ): self {
$this->key = $key;
return $this;
}
public function set_disabled( bool $disabled ): self {
$this->disabled = $disabled;
return $this;
}
public function set_prop_type( Transformable_Prop_Type $prop_type ): self {
$this->prop_type = $prop_type;
return $this;
}
public function get_key(): ?string {
return $this->key;
}
public function is_disabled(): bool {
return $this->disabled;
}
public function get_prop_type(): ?Transformable_Prop_Type {
return $this->prop_type;
}
}

View File

@@ -0,0 +1,111 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver;
use Elementor\Modules\AtomicWidgets\PropTypes\Base\Array_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Base\Object_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Contracts\Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Union_Prop_Type;
use Exception;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
abstract class Props_Resolver {
protected static array $instances = [];
protected Transformers_Registry $transformers_registry;
protected function __construct( Transformers_Registry $transformers_registry ) {
$this->transformers_registry = $transformers_registry;
}
protected static function instance( string $context ) {
if ( ! isset( static::$instances[ $context ] ) ) {
$instance = new static( new Transformers_Registry() );
static::$instances[ $context ] = $instance;
do_action(
"elementor/atomic-widgets/$context/transformers/register",
$instance->get_transformers_registry(),
$instance
);
}
return static::$instances[ $context ];
}
public static function reset(): void {
static::$instances = [];
}
public function get_transformers_registry(): Transformers_Registry {
return $this->transformers_registry;
}
protected function transform( $value, $key, Prop_Type $prop_type ) {
if ( $prop_type instanceof Union_Prop_Type ) {
$prop_type = $prop_type->get_prop_type( $value['$$type'] );
if ( ! $prop_type ) {
return null;
}
}
if ( $value['$$type'] !== $prop_type::get_key() ) {
return null;
}
if ( $prop_type instanceof Object_Prop_Type ) {
if ( ! is_array( $value['value'] ) ) {
return null;
}
$value['value'] = $this->resolve(
$prop_type->get_shape(),
$value['value']
);
}
if ( $prop_type instanceof Array_Prop_Type ) {
if ( ! is_array( $value['value'] ) ) {
return null;
}
$value['value'] = array_map(
fn( $item ) => $this->resolve_item( $item, null, $prop_type->get_item_type() ),
$value['value']
);
}
$transformer = $this->transformers_registry->get( $value['$$type'] );
if ( ! ( $transformer instanceof Transformer_Base ) ) {
return null;
}
try {
$context = Props_Resolver_Context::make()
->set_key( $key )
->set_disabled( (bool) ( $value['disabled'] ?? false ) )
->set_prop_type( $prop_type );
return $transformer->transform( $value['value'], $context );
} catch ( Exception $e ) {
return null;
}
}
protected function is_transformable( $value ): bool {
return (
! empty( $value['$$type'] ) &&
array_key_exists( 'value', $value )
);
}
abstract public function resolve( array $schema, array $props ): array;
abstract protected function resolve_item( $value, $key, Prop_Type $prop_type );
}

View File

@@ -0,0 +1,103 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver;
use Elementor\Modules\AtomicWidgets\DynamicTags\Dynamic_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Base\Array_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Base\Object_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Contracts\Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Union_Prop_Type;
use Elementor\Plugin;
use Exception;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Render_Props_Resolver extends Props_Resolver {
/**
* Each transformer can return a value that is also a transformable value,
* which means that it can be transformed again by another transformer.
* This constant defines the maximum depth of transformations to avoid infinite loops.
*/
const TRANSFORM_DEPTH_LIMIT = 3;
const CONTEXT_SETTINGS = 'settings';
const CONTEXT_STYLES = 'styles';
public static function for_styles(): self {
return static::instance( self::CONTEXT_STYLES );
}
public static function for_settings(): self {
return static::instance( self::CONTEXT_SETTINGS );
}
public function resolve( array $schema, array $props ): array {
$resolved = [];
foreach ( $schema as $key => $prop_type ) {
if ( ! ( $prop_type instanceof Prop_Type ) ) {
continue;
}
$prop_value = $props[ $key ] ?? null;
$actual_value = $this->get_validated_value( $prop_type, $prop_value );
$transformed = $this->resolve_item(
$actual_value,
$key,
$prop_type
);
if ( Multi_Props::is( $transformed ) ) {
$resolved = array_merge( $resolved, Multi_Props::get_value( $transformed ) );
continue;
}
$resolved[ $key ] = $transformed;
}
return $resolved;
}
protected function resolve_item( $value, $key, Prop_Type $prop_type, int $depth = 0 ) {
if ( null === $value ) {
return null;
}
if ( ! $this->is_transformable( $value ) ) {
return $value;
}
if ( $depth >= self::TRANSFORM_DEPTH_LIMIT ) {
return null;
}
if ( isset( $value['disabled'] ) && true === $value['disabled'] ) {
return null;
}
$transformed = $this->transform( $value, $key, $prop_type );
return $this->resolve_item( $transformed, $key, $prop_type, $depth + 1 );
}
private function get_validated_value( Prop_Type $prop_type, $prop_value ) {
$default = $prop_type->get_default() ?? null;
if ( null === $prop_value ) {
return $default;
}
if ( ! Dynamic_Prop_Type::is_dynamic_prop_value( $prop_value ) ) {
return $prop_value;
}
$tag_name = $prop_value['value']['name'] ?? null;
$tag = Plugin::$instance->dynamic_tags->get_tag_info( $tag_name );
return ! $tag ? $default : $prop_value;
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
abstract class Transformer_Base {
abstract public function transform( $value, Props_Resolver_Context $context );
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver;
use Elementor\Core\Utils\Collection;
use Elementor\Utils;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Transformers_Registry extends Collection {
private ?Transformer_Base $fallback = null;
public function register( string $key, Transformer_Base $transformer ): self {
$this->items[ $key ] = $transformer;
return $this;
}
public function register_fallback( Transformer_Base $transformer ): self {
$this->fallback = $transformer;
return $this;
}
public function get( $key, $fallback = null ) {
return parent::get( $key, $fallback ?? $this->fallback );
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Array_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
if ( ! is_array( $value ) ) {
return null;
}
return array_filter( $value );
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Combine_Array_Transformer extends Transformer_Base {
private string $separator;
public function __construct( string $separator ) {
$this->separator = $separator;
}
public function transform( $value, Props_Resolver_Context $context ) {
if ( ! is_array( $value ) ) {
return null;
}
return implode( $this->separator, array_filter( $value ) );
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Export;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
use Elementor\Modules\AtomicWidgets\PropTypes\Image_Src_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Url_Prop_Type;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Image_Src_Export_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ): ?array {
if ( ! empty( $value['url'] ) ) {
return Image_Src_Prop_Type::generate( [
'id' => null,
'url' => $value['url'],
], $context->is_disabled() );
}
if ( ! empty( $value['id'] ) && ! empty( $value['id']['value'] ) ) {
$image = wp_get_attachment_image_src( $value['id']['value'], 'full' );
if ( ! $image ) {
return null;
}
[ $src ] = $image;
return Image_Src_Prop_Type::generate( [
'id' => $value['id'],
'url' => Url_Prop_Type::generate( $src ),
], $context->is_disabled() );
}
return null;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Image_Src_Transformer extends Transformer_Base {
/**
* This transformer (or rather this prop type) exists only to support dynamic images.
* Currently, the dynamic tags that return images return it with id & url no matter
* what, so we need to keep the same structure in the props.
*/
public function transform( $value, Props_Resolver_Context $context ) {
return [
'id' => isset( $value['id'] ) ? (int) $value['id'] : null,
'url' => $value['url'] ?? null,
];
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Image_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
if ( ! empty( $value['src']['id'] ) ) {
$image_src = wp_get_attachment_image_src(
(int) $value['src']['id'],
$value['size'] ?? 'full'
);
if ( ! $image_src ) {
throw new \Exception( 'Cannot get image src.' );
}
[ $src, $width, $height ] = $image_src;
return [
'id' => $value['src']['id'],
'src' => $src,
'width' => (int) $width,
'height' => (int) $height,
'srcset' => wp_get_attachment_image_srcset( $value['src']['id'], $value['size'] ),
'alt' => get_post_meta( $value['src']['id'], '_wp_attachment_image_alt', true ),
];
}
if ( empty( $value['src']['url'] ) ) {
throw new \Exception( 'Invalid image URL.' );
}
return [
'src' => $value['src']['url'],
];
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Import_Export_Plain_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
$prop_type = $context->get_prop_type();
return $prop_type::generate( $value, $context->is_disabled() );
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Import;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
use Elementor\Modules\AtomicWidgets\PropTypes\Image_Attachment_Id_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Image_Src_Prop_Type;
use Elementor\Plugin;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Image_Src_Import_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
if ( empty( $value['url']['value'] ) ) {
return null;
}
$uploaded = Plugin::$instance->templates_manager->get_import_images_instance()->import( [
'id' => $value['id']['value'] ?? null,
'url' => $value['url']['value'],
] );
if ( ! $uploaded ) {
return null;
}
return Image_Src_Prop_Type::generate( [
'id' => Image_Attachment_Id_Prop_Type::generate( $uploaded['id'] ),
'url' => null,
] );
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Plain_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
return $value;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Settings;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Attributes_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
return null;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Settings;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Classes_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
if ( ! is_array( $value ) ) {
return null;
}
$value = apply_filters( 'elementor/atomic-widgets/settings/transformers/classes', $value, $context );
return array_filter( $value );
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Settings;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Date_Time_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
if ( ! is_array( $value ) ) {
return null;
}
$date = isset( $value['date'] ) ? trim( $value['date'] ) : '';
$time = isset( $value['time'] ) ? trim( $value['time'] ) : '';
if ( '' === $date && '' === $time ) {
return '';
}
$result = trim( $date . ' ' . $time );
return esc_attr( $result );
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Settings;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Link_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ): ?array {
$url = $this->extract_url( $value );
$link_attrs = [
'href' => $url,
'target' => $value['isTargetBlank'] ? '_blank' : '_self',
'tag' => $url && 'button' === $value['tag'] ? 'button' : 'a',
];
return array_filter( $link_attrs );
}
private function extract_url( $value ): ?string {
$destination = $value['destination'];
$post = is_numeric( $destination ) ? get_post( $destination ) : null;
return $post ? get_permalink( $post ) : $destination;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Settings;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Query_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
return $value['id'] ?? null;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Background_Color_Overlay_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
$color = $value['color'] ?? '';
if ( ! $color ) {
return null;
}
return "linear-gradient($color, $color)";
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Background_Gradient_Overlay_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ): string {
$type = $value['type'];
$angle = $value['angle'];
$positions = $value['positions'];
$stops = $value['stops'];
if ( 'radial' === $type ) {
return sprintf( 'radial-gradient(circle at %s, %s)', $positions, $stops );
}
return sprintf( 'linear-gradient(%ddeg, %s)', $angle, $stops );
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Background_Image_Overlay_Size_Scale_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ): string {
$default_custom_size = 'auto';
$width = $value['width'] ?? $default_custom_size;
$height = $value['height'] ?? $default_custom_size;
return $width . ' ' . $height;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Background_Image_Overlay_Transformer extends Transformer_Base {
const DEFAULT_IMAGE = 'none';
const DEFAULT_REPEAT = 'repeat';
const DEFAULT_ATTACHMENT = 'scroll';
const DEFAULT_SIZE = 'auto auto';
const DEFAULT_POSITION = '0% 0%';
public function transform( $value, Props_Resolver_Context $context ) {
if ( ! isset( $value['image'] ) ) {
return '';
}
$image_url = $value['image']['src'];
return [
'src' => "url(\"$image_url\")",
'repeat' => $value['repeat'] ?? null,
'attachment' => $value['attachment'] ?? null,
'size' => $value['size'] ?? null,
'position' => $value['position'] ?? null,
];
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Multi_Props;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Background_Overlay_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
$normalized_values = $this->normalize_overlay_values( $value );
if ( empty( $normalized_values ) ) {
return null;
}
return [
'background-image' => $this->get_values_string( $normalized_values, 'src', Background_Image_Overlay_Transformer::DEFAULT_IMAGE, true ),
'background-repeat' => $this->get_values_string( $normalized_values, 'repeat', Background_Image_Overlay_Transformer::DEFAULT_REPEAT ),
'background-attachment' => $this->get_values_string( $normalized_values, 'attachment', Background_Image_Overlay_Transformer::DEFAULT_ATTACHMENT ),
'background-size' => $this->get_values_string( $normalized_values, 'size', Background_Image_Overlay_Transformer::DEFAULT_SIZE ),
'background-position' => $this->get_values_string( $normalized_values, 'position', Background_Image_Overlay_Transformer::DEFAULT_POSITION ),
];
}
private function normalize_overlay_values( $overlays ): array {
$mapped_values = array_map( function( $value ) {
if ( is_string( $value ) ) {
return [
'src' => $value,
'repeat' => null,
'attachment' => null,
'size' => null,
'position' => null,
];
}
return $value;
}, $overlays );
return array_filter( $mapped_values, function( $value ) {
return is_array( $value ) && ! empty( $value['src'] );
} );
}
private function get_values_string( $value, string $prop, string $default_value, bool $prevent_unification = false ) {
$is_empty = empty( array_filter( $value, function ( array $item ) use ( $prop ) {
return isset( $item[ $prop ] ) && ! is_null( $item[ $prop ] );
} ) );
if ( $is_empty ) {
return $default_value;
}
$formatted_values = array_map( function ( $item ) use ( $prop, $default_value ) {
if ( is_string( $item ) ) {
return $default_value;
}
if ( ! is_array( $item ) ) {
return $default_value;
}
return $item[ $prop ] ?? $default_value;
}, $value );
if ( ! $prevent_unification ) {
$all_same = count( array_unique( $formatted_values ) ) === 1;
if ( $all_same ) {
return $formatted_values[0];
}
}
return implode( ',', $formatted_values );
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Multi_Props;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Background_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
$overlay = $value['background-overlay'] ?? [];
$color = $value['color'] ?? null;
$clip = $value['clip'] ?? null;
return Multi_Props::generate( array_merge( $overlay, [
'background-color' => $color,
'background-clip' => $clip,
] ) );
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Color_Stop_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ): string {
$color = $value['color'];
$offset = $value['offset'] . '%';
return $color . ' ' . $offset;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Filter_Transformer extends Transformer_Base {
public function transform( $filters, Props_Resolver_Context $context ) {
$filter_strings = array_map( [ $this, 'map_to_filter_string' ], $filters );
return implode( ' ', $filter_strings );
}
private function map_to_filter_string( $filter ): string {
$func = $filter['func'];
$args = $filter['args'];
if ( 'drop-shadow' === $func ) {
$x_axis = $args['xAxis'] ?? '0px';
$y_axis = $args['yAxis'] ?? '0px';
$blur = $args['blur'] ?? '10px';
$color = $args['color'] ?? 'transparent';
return "drop-shadow({$x_axis} {$y_axis} {$blur} {$color})";
}
return $func . '(' . $args['size'] . ')';
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Flex_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
$grow = $value['flexGrow'] ?? null;
$shrink = $value['flexShrink'] ?? null;
$basis = $value['flexBasis'] ?? null;
$has_grow = null !== $grow && '' !== $grow;
$has_shrink = null !== $shrink && '' !== $shrink;
$has_basis = null !== $basis && '' !== $basis;
if ( ! $has_grow && ! $has_shrink && ! $has_basis ) {
return null;
}
$basis_value = $this->transform_basis_value( $basis );
if ( $has_grow && $has_shrink && $has_basis ) {
return "{$grow} {$shrink} {$basis_value}";
}
if ( $has_grow && $has_shrink && ! $has_basis ) {
return "{$grow} {$shrink}";
}
if ( $has_grow && ! $has_shrink && $has_basis ) {
return "{$grow} 1 {$basis_value}";
}
if ( ! $has_grow && $has_shrink && $has_basis ) {
return "0 {$shrink} {$basis_value}";
}
if ( $has_grow && ! $has_shrink && ! $has_basis ) {
return "{$grow}";
}
if ( ! $has_grow && $has_shrink && ! $has_basis ) {
return "0 {$shrink}";
}
if ( ! $has_grow && ! $has_shrink && $has_basis ) {
return "0 1 {$basis_value}";
}
return null;
}
/**
* Transform basis value to string format
*
* @param mixed $basis The basis value
* @return string
*/
private function transform_basis_value( $basis ) {
if ( is_array( $basis ) && isset( $basis['size'] ) ) {
$unit = $basis['unit'] ?? '';
return $basis['size'] . $unit;
}
return (string) $basis;
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Core\Utils\Collection;
use Elementor\Modules\AtomicWidgets\PropsResolver\Multi_Props;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
use phpDocumentor\Reflection\Types\Callable_;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Multi_Props_Transformer extends Transformer_Base {
private $key_generator;
private array $keys;
public function __construct( array $keys, callable $key_generator ) {
$this->keys = $keys;
$this->key_generator = $key_generator;
}
public function transform( $value, Props_Resolver_Context $context ) {
$values = Collection::make( $this->keys )
->filter( fn ( $key ) => isset( $value[ $key ] ) )
->map_with_keys( function( $key ) use ( $value, $context ) {
$new_key = call_user_func( $this->key_generator, $context->get_key(), $key );
$new_value = $value[ $key ];
return [ $new_key => $new_value ];
} )
->all();
return Multi_Props::generate( $values );
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Perspective_Origin_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ): string {
$default_move = '0px';
$x = $value['x'] ?? $default_move;
$y = $value['y'] ?? $default_move;
return "$x $y";
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Position_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ): string {
return ( $value['x'] ?? '0px' ) . ' ' . ( $value['y'] ?? '0px' );
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Shadow_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
$val = array_filter( [
$value['hOffset'],
$value['vOffset'],
$value['blur'],
$value['spread'],
$value['color'],
$value['position'] ?? '',
] );
return implode( ' ', $val );
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Size_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
$size = $value['size'];
$unit = $value['unit'];
if ( 'custom' === $unit ) {
return $size;
}
if ( 'auto' === $unit ) {
return 'auto';
}
return +$size . $unit;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Multi_Props;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Stroke_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ) {
return Multi_Props::generate( [
'-webkit-text-stroke' => $value['width'] . ' ' . $value['color'],
'stroke' => $value['color'],
'stroke-width' => $value['width'],
] );
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Transform_Functions_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ): string {
return implode( ' ', $value );
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Transform_Move_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ): string {
$default_move = '0px';
return sprintf( 'translate3d(%s, %s, %s)', $value['x'] ?? $default_move, $value['y'] ?? $default_move, $value['z'] ?? $default_move );
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Transform_Origin_Transformer extends Transformer_Base {
private string $default_origin = '0px';
private string $default_xy = '50%';
private function get_val( ?string $val ): string {
return $val ?? $this->default_origin;
}
public function transform( $value, Props_Resolver_Context $context ) {
$x = $this->get_val( $value['x'] );
$y = $this->get_val( $value['y'] );
$z = $this->get_val( $value['z'] );
if ( $x === $this->default_xy && $y === $this->default_xy && $z === $this->default_origin ) {
return null;
}
return sprintf( '%s %s %s', $x, $y, $z );
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Transform_Rotate_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ): string {
$default_rotate = '0deg';
return sprintf(
'rotateX(%s) rotateY(%s) rotateZ(%s)',
$value['x'] ?? $default_rotate,
$value['y'] ?? $default_rotate,
$value['z'] ?? $default_rotate
);
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Transform_Scale_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ): string {
return sprintf( 'scale3d(%s, %s, %s)', $value['x'] ?? 1, $value['y'] ?? 1, $value['z'] ?? 1 );
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Transform_Skew_Transformer extends Transformer_Base {
public function transform( $value, Props_Resolver_Context $context ): string {
$default_skew = '0deg';
return sprintf(
'skew(%s, %s)',
$value['x'] ?? $default_skew,
$value['y'] ?? $default_skew
);
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Elementor\Modules\AtomicWidgets\PropsResolver\Transformers\Styles;
use Elementor\Modules\AtomicWidgets\PropsResolver\Props_Resolver_Context;
use Elementor\Modules\AtomicWidgets\PropsResolver\Transformer_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Transition_Transformer extends Transformer_Base {
const EMPTY_STRING = '';
private function get_allowed_properties(): array {
$core_properties = [ 'all' ];
return apply_filters(
'elementor/atomic-widgets/styles/transitions/allowed-properties',
$core_properties
);
}
public function transform( $transitions, Props_Resolver_Context $context ) {
if ( ! is_array( $transitions ) ) {
return self::EMPTY_STRING;
}
$allowed_properties = $this->get_allowed_properties();
$transition_strings = array_map(
function( $transition ) use ( $allowed_properties ) {
return $this->map_to_transition_string( $transition, $allowed_properties );
},
$transitions
);
$valid_transitions = array_filter( $transition_strings );
return implode( ', ', $valid_transitions );
}
private function map_to_transition_string( $transition, array $allowed_properties ): string {
if ( empty( $transition['selection'] ) || empty( $transition['size'] ) ) {
return self::EMPTY_STRING;
}
$selection = $transition['selection'];
$size = $transition['size'];
if ( empty( $selection['value'] ) ) {
return self::EMPTY_STRING;
}
$property = $selection['value'];
if ( ! in_array( $property, $allowed_properties, true ) ) {
return self::EMPTY_STRING;
}
return trim( "{$property} {$size}" );
}
}