first commit

This commit is contained in:
Roman Pyrih
2025-07-11 12:34:24 +02:00
commit 296b13244b
10181 changed files with 3916595 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
<?php
namespace Elementor\Modules\AtomicWidgets\Styles;
use Elementor\Core\Files\CSS\Post as Post_CSS;
use Elementor\Core\Utils\Collection;
use Elementor\Modules\AtomicWidgets\Utils;
use Elementor\Plugin;
class Atomic_Widget_Base_Styles {
public function register_hooks() {
add_action( 'elementor/css-file/post/parse', fn( Post_CSS $post ) => $this->inject_elements_base_styles( $post ), 10 );
}
private function inject_elements_base_styles( Post_CSS $post ) {
if ( ! Plugin::$instance->kits_manager->is_kit( $post->get_post_id() ) ) {
return;
}
$elements = Plugin::$instance->elements_manager->get_element_types();
$widgets = Plugin::$instance->widgets_manager->get_widget_types();
$base_styles = Collection::make( $elements )
->merge( $widgets )
->filter( fn( $element ) => Utils::is_atomic( $element ) )
->map( fn( $element ) => $element->get_base_styles() )
->flatten()
->all();
$css = Styles_Renderer::make(
Plugin::$instance->breakpoints->get_breakpoints_config()
)->on_prop_transform( function( $key, $value ) use ( &$post ) {
if ( 'font-family' !== $key ) {
return;
}
$post->add_font( $value );
} )->render( $base_styles );
$post->get_stylesheet()->add_raw_css( $css );
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Elementor\Modules\AtomicWidgets\Styles;
use Elementor\Core\Files\CSS\Post as Post_CSS;
use Elementor\Element_Base;
use Elementor\Modules\AtomicWidgets\Utils;
use Elementor\Plugin;
class Atomic_Widget_Styles {
public function register_hooks() {
add_action( 'elementor/element/parse_css', fn( Post_CSS $post, Element_Base $element ) => $this->parse_element_style( $post, $element ), 10, 2 );
}
private function parse_element_style( Post_CSS $post, Element_Base $element ) {
if ( ! Utils::is_atomic( $element ) ) {
return;
}
$styles = $element->get_raw_data()['styles'];
if ( empty( $styles ) ) {
return;
}
$css = Styles_Renderer::make(
Plugin::$instance->breakpoints->get_breakpoints_config()
)->on_prop_transform( function( $key, $value ) use ( &$post ) {
if ( 'font-family' !== $key ) {
return;
}
$post->add_font( $value );
} )->render( $styles );
$post->get_stylesheet()->add_raw_css( $css );
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Elementor\Modules\AtomicWidgets\Styles;
class Style_Definition {
private string $type = 'class';
private string $label = '';
/** @var Style_Variant[] */
private array $variants = [];
public static function make(): self {
return new self();
}
public function set_type( string $type ): self {
$this->type = $type;
return $this;
}
public function set_label( string $label ): self {
$this->label = $label;
return $this;
}
public function add_variant( Style_Variant $variant ): self {
$this->variants[] = $variant->build();
return $this;
}
public function build( string $id ): array {
return [
'id' => $id,
'type' => $this->type,
'label' => $this->label,
'variants' => $this->variants,
];
}
}

View File

@@ -0,0 +1,292 @@
<?php
namespace Elementor\Modules\AtomicWidgets\Styles;
use Elementor\Modules\AtomicWidgets\PropTypes\Background_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Box_Shadow_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Border_Radius_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Border_Width_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Color_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Dimensions_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Layout_Direction_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\Number_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Size_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\String_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Stroke_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Union_Prop_Type;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Style_Schema {
public static function get() {
return apply_filters( 'elementor/atomic-widgets/styles/schema', static::get_style_schema() );
}
public static function get_style_schema(): array {
return array_merge(
self::get_size_props(),
self::get_position_props(),
self::get_typography_props(),
self::get_spacing_props(),
self::get_border_props(),
self::get_background_props(),
self::get_effects_props(),
self::get_layout_props(),
self::get_alignment_props(),
);
}
private static function get_size_props() {
return [
'width' => Size_Prop_Type::make(),
'height' => Size_Prop_Type::make(),
'min-width' => Size_Prop_Type::make(),
'min-height' => Size_Prop_Type::make(),
'max-width' => Size_Prop_Type::make(),
'max-height' => Size_Prop_Type::make(),
'overflow' => String_Prop_Type::make()->enum( [
'visible',
'hidden',
'auto',
] ),
'aspect-ratio' => String_Prop_Type::make(),
'object-fit' => String_Prop_Type::make()->enum( [
'fill',
'cover',
'contain',
'none',
'scale-down',
] ),
'object-position' => String_Prop_Type::make()->enum( [
'center center',
'center left',
'center right',
'top center',
'top left',
'top right',
'bottom center',
'bottom left',
'bottom right',
] ),
];
}
private static function get_position_props() {
return [
'position' => String_Prop_Type::make()->enum( [
'static',
'relative',
'absolute',
'fixed',
'sticky',
] ),
'inset-block-start' => Size_Prop_Type::make(),
'inset-inline-end' => Size_Prop_Type::make(),
'inset-block-end' => Size_Prop_Type::make(),
'inset-inline-start' => Size_Prop_Type::make(),
'z-index' => Number_Prop_Type::make(),
'scroll-margin-top' => Size_Prop_Type::make(),
];
}
private static function get_typography_props() {
return [
'font-family' => String_Prop_Type::make(),
'font-weight' => String_Prop_Type::make()->enum( [
'100',
'200',
'300',
'400',
'500',
'600',
'700',
'800',
'900',
'normal',
'bold',
'bolder',
'lighter',
] ),
'font-size' => Size_Prop_Type::make(),
'color' => Color_Prop_Type::make(),
'letter-spacing' => Size_Prop_Type::make(),
'word-spacing' => Size_Prop_Type::make(),
'column-count' => Number_Prop_Type::make(),
'column-gap' => Size_Prop_Type::make(),
'line-height' => Size_Prop_Type::make(),
'text-align' => String_Prop_Type::make()->enum( [
'start',
'center',
'end',
'justify',
] ),
'font-style' => String_Prop_Type::make()->enum( [
'normal',
'italic',
'oblique',
] ),
// TODO: validate text-decoration in more specific way [EDS-524]
'text-decoration' => String_Prop_Type::make(),
'text-transform' => String_Prop_Type::make()->enum( [
'none',
'capitalize',
'uppercase',
'lowercase',
] ),
'direction' => String_Prop_Type::make()->enum( [
'ltr',
'rtl',
] ),
'stroke' => Stroke_Prop_Type::make(),
'all' => String_Prop_Type::make()->enum( [
'initial',
'inherit',
'unset',
'revert',
'revert-layer',
] ),
'cursor' => String_Prop_Type::make()->enum( [
'pointer',
] ),
];
}
private static function get_spacing_props() {
return [
'padding' => Union_Prop_Type::make()
->add_prop_type( Dimensions_Prop_Type::make() )
->add_prop_type( Size_Prop_Type::make() ),
'margin' => Union_Prop_Type::make()
->add_prop_type( Dimensions_Prop_Type::make() )
->add_prop_type( Size_Prop_Type::make() ),
];
}
private static function get_border_props() {
return [
'border-radius' => Union_Prop_Type::make()
->add_prop_type( Size_Prop_Type::make() )
->add_prop_type( Border_Radius_Prop_Type::make() ),
'border-width' => Union_Prop_Type::make()
->add_prop_type( Size_Prop_Type::make() )
->add_prop_type( Border_Width_Prop_Type::make() ),
'border-color' => Color_Prop_Type::make(),
'border-style' => String_Prop_Type::make()->enum( [
'none',
'hidden',
'dotted',
'dashed',
'solid',
'double',
'groove',
'ridge',
'inset',
'outset',
] ),
];
}
private static function get_background_props() {
return [
'background' => Background_Prop_Type::make(),
];
}
private static function get_effects_props() {
return [
'box-shadow' => Box_Shadow_Prop_Type::make(),
];
}
private static function get_layout_props() {
return [
'display' => String_Prop_Type::make()->enum( [
'block',
'inline',
'inline-block',
'flex',
'inline-flex',
'grid',
'inline-grid',
'flow-root',
'none',
'contents',
] ),
'flex-direction' => String_Prop_Type::make()->enum( [
'row',
'row-reverse',
'column',
'column-reverse',
] ),
'gap' => Union_Prop_Type::make()
->add_prop_type( Layout_Direction_Prop_Type::make() )
->add_prop_type( Size_Prop_Type::make() ),
'flex-wrap' => String_Prop_Type::make()->enum( [
'wrap',
'nowrap',
'wrap-reverse',
] ),
'flex-grow' => Number_Prop_Type::make(),
'flex-shrink' => Number_Prop_Type::make(),
'flex-basis' => Size_Prop_Type::make(),
];
}
private static function get_alignment_props() {
return [
'justify-content' => String_Prop_Type::make()->enum( [
'center',
'start',
'end',
'flex-start',
'flex-end',
'left',
'right',
'normal',
'space-between',
'space-around',
'space-evenly',
'stretch',
] ),
'align-content' => String_Prop_Type::make()->enum( [
'center',
'start',
'end',
'space-between',
'space-around',
'space-evenly',
] ),
'align-items' => String_Prop_Type::make()->enum( [
'normal',
'stretch',
'center',
'start',
'end',
'flex-start',
'flex-end',
'self-start',
'self-end',
'anchor-center',
] ),
'align-self' => String_Prop_Type::make()->enum( [
'auto',
'normal',
'center',
'start',
'end',
'self-start',
'self-end',
'flex-start',
'flex-end',
'anchor-center',
'baseline',
'first baseline',
'last baseline',
'stretch',
] ),
'order' => Number_Prop_Type::make(),
];
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Elementor\Modules\AtomicWidgets\Styles;
class Style_Variant {
private ?string $breakpoint = null;
private ?string $state = null;
/** @var array<string, array> */
private array $props = [];
public static function make(): self {
return new self();
}
public function set_breakpoint( string $breakpoint ): self {
$this->breakpoint = $breakpoint;
return $this;
}
public function set_state( string $state ): self {
$this->state = $state;
return $this;
}
public function add_prop( string $key, $value ): self {
$this->props[ $key ] = $value;
return $this;
}
public function build(): array {
return [
'meta' => [
'breakpoint' => $this->breakpoint,
'state' => $this->state,
],
'props' => $this->props,
];
}
}

View File

@@ -0,0 +1,168 @@
<?php
namespace Elementor\Modules\AtomicWidgets\Styles;
use Elementor\Core\Utils\Collection;
use Elementor\Modules\AtomicWidgets\PropsResolver\Render_Props_Resolver;
class Styles_Renderer {
const DEFAULT_SELECTOR_PREFIX = '.elementor';
/**
* @var array<string, array{direction: 'min' | 'max', value: int, is_enabled: boolean}>
*/
private array $breakpoints;
private $on_prop_transform;
private string $selector_prefix;
/**
* @param array<string, array{direction: 'min' | 'max', value: int, is_enabled: boolean}> $breakpoints
* @param string $selector_prefix
*/
private function __construct( array $breakpoints, string $selector_prefix = self::DEFAULT_SELECTOR_PREFIX ) {
$this->breakpoints = $breakpoints;
$this->selector_prefix = $selector_prefix;
}
public static function make( array $breakpoints, string $selector_prefix = self::DEFAULT_SELECTOR_PREFIX ): self {
return new self( $breakpoints, $selector_prefix );
}
/**
* Render the styles to a CSS string.
*
* Styles format:
* array<int, array{
* id: string,
* type: string,
* cssName: string | null,
* variants: array<int, array{
* props: array<string, mixed>,
* meta: array<string, mixed>
* }>
* }>
*
* @param array $styles Array of style definitions.
*
* @return string Rendered CSS string.
*/
public function render( array $styles ): string {
$css_style = [];
foreach ( $styles as $style_def ) {
$style = $this->style_definition_to_css_string( $style_def );
$css_style[] = $style;
}
return implode( '', $css_style );
}
public function on_prop_transform( callable $callback ): self {
$this->on_prop_transform = $callback;
return $this;
}
private function style_definition_to_css_string( array $style ): string {
$base_selector = $this->get_base_selector( $style );
if ( ! $base_selector ) {
return '';
}
$stylesheet = [];
foreach ( $style['variants'] as $variant ) {
$style_declaration = $this->variant_to_css_string( $base_selector, $variant );
if ( $style_declaration ) {
$stylesheet[] = $style_declaration;
}
}
return implode( '', $stylesheet );
}
private function get_base_selector( array $style_def ): ?string {
$map = [
'class' => '.',
];
if (
isset( $style_def['type'] ) &&
isset( $style_def['id'] ) &&
isset( $map[ $style_def['type'] ] ) &&
$style_def['id']
) {
$type = $map[ $style_def['type'] ];
$name = $style_def['cssName'] ?? $style_def['id'];
$selector_parts = array_filter( [
$this->selector_prefix,
"{$type}{$name}",
] );
return implode( ' ', $selector_parts );
}
return null;
}
private function variant_to_css_string( string $base_selector, array $variant ): string {
$css = $this->props_to_css_string( $variant['props'] );
if ( ! $css ) {
return '';
}
$state = isset( $variant['meta']['state'] ) ? ':' . $variant['meta']['state'] : '';
$selector = $base_selector . $state;
$style_declaration = $selector . '{' . $css . '}';
if ( isset( $variant['meta']['breakpoint'] ) ) {
$style_declaration = $this->wrap_with_media_query( $variant['meta']['breakpoint'], $style_declaration );
}
return $style_declaration;
}
private function props_to_css_string( array $props ): string {
$schema = Style_Schema::get();
return Collection::make( Render_Props_Resolver::for_styles()->resolve( $schema, $props ) )
->filter()
->map( function ( $value, $prop ) {
if ( $this->on_prop_transform ) {
call_user_func( $this->on_prop_transform, $prop, $value );
}
return $prop . ':' . $value . ';';
} )
->implode( '' );
}
private function wrap_with_media_query( string $breakpoint_id, string $css ): string {
if ( ! isset( $this->breakpoints[ $breakpoint_id ] ) ) {
return $css;
}
$breakpoint = $this->breakpoints[ $breakpoint_id ];
if ( isset( $breakpoint['is_enabled'] ) && ! $breakpoint['is_enabled'] ) {
return '';
}
$size = $this->get_breakpoint_size( $this->breakpoints[ $breakpoint_id ] );
return $size ? '@media(' . $size . '){' . $css . '}' : $css;
}
private function get_breakpoint_size( array $breakpoint ): ?string {
$bound = 'min' === $breakpoint['direction'] ? 'min-width' : 'max-width';
$width = $breakpoint['value'] . 'px';
return "{$bound}:{$width}";
}
}