meta( 'is_container', true ); } public static function get_type() { return 'e-background-video'; } public static function get_element_type(): string { return 'e-background-video'; } public function get_title() { return esc_html__( 'Background Video', 'elementor' ); } public function get_keywords() { return [ 'ato', 'atom', 'atoms', 'atomic', 'video', 'background', 'bg', 'media', 'autoplay', 'hero', 'film', 'mp4' ]; } public function get_icon() { return 'eicon-background-video'; } public static function get_computed_html_tag( array $settings ): string { return Utils::validate_html_tag( Html_Tag_Computer::compute( $settings, 'div' ) ); } protected static function define_props_schema(): array { // The States control is design-time only and directly tied to the controls. A prop dependency // describes when the control is *shown* (the editor hides it when the dependency is not met), // so show States only while Show Controls is on; the value is preserved when hidden (no reset). $state_dependencies = Dependency_Manager::make() ->where( [ 'operator' => 'eq', 'path' => [ 'show_controls' ], 'value' => true, 'effect' => 'hide', ] ) ->get(); return [ 'classes' => Classes_Prop_Type::make()->default( [] ), 'tag' => String_Prop_Type::make() ->enum( [ 'div', 'header', 'section', 'article', 'aside', 'footer', 'main', 'nav' ] ) ->default( 'div' ) ->description( 'The HTML tag for the background video container. Could be div, header, section, article, aside, footer, main, or nav.' ), 'source' => Video_Src_Prop_Type::make()->alias( 'video', 'src' ), 'start_time' => Number_Prop_Type::make() ->default( null ) ->meta( Dynamic_Prop_Type::ignore() ) ->meta( 'suffix', 'SEC' ), 'end_time' => Number_Prop_Type::make() ->default( null ) ->meta( 'suffix', 'SEC' ) ->meta( Dynamic_Prop_Type::ignore() ), 'autoplay' => Boolean_Prop_Type::make()->default( true ), 'mute' => Boolean_Prop_Type::make()->default( true ), 'loop' => Boolean_Prop_Type::make()->default( true ), 'show_controls' => Boolean_Prop_Type::make()->default( true ), 'state' => String_Prop_Type::make() ->enum( [ 'playing', 'paused' ] ) ->default( 'playing' ) ->set_dependencies( $state_dependencies ) ->meta( Overridable_Prop_Type::ignore() ), 'attributes' => Attributes_Prop_Type::make()->meta( Overridable_Prop_Type::ignore() ), ]; } protected function define_atomic_controls(): array { return [ Section::make() ->set_label( __( 'Content', 'elementor' ) ) ->set_id( 'content' ) ->set_items( [ Video_Control::bind_to( 'source' ) ->set_label( esc_html__( 'Video', 'elementor' ) ), Number_Control::bind_to( 'start_time' ) ->set_label( esc_html__( 'Start Time', 'elementor' ) ) ->set_min( 0 ) ->set_max( 10000 ), Number_Control::bind_to( 'end_time' ) ->set_label( esc_html__( 'End Time', 'elementor' ) ) ->set_min( 0 ) ->set_max( 10000 ), Switch_Control::bind_to( 'autoplay' ) ->set_label( esc_html__( 'Autoplay', 'elementor' ) ), Switch_Control::bind_to( 'mute' ) ->set_label( esc_html__( 'Mute', 'elementor' ) ), Switch_Control::bind_to( 'loop' ) ->set_label( esc_html__( 'Loop', 'elementor' ) ), Switch_Control::bind_to( 'show_controls' ) ->set_label( esc_html__( 'Show Controls', 'elementor' ) ), Toggle_Control::bind_to( 'state' ) ->set_label( esc_html__( 'States', 'elementor' ) ) ->add_options( [ 'playing' => [ 'title' => esc_html__( 'Play', 'elementor' ) ], 'paused' => [ 'title' => esc_html__( 'Pause', 'elementor' ) ], ] ) ->set_exclusive( true ) ->set_allow_empty( true ) ->set_convert_options( true ) ->set_size( 'tiny' ) ->set_full_width( true ), ] ), Section::make() ->set_label( __( 'Settings', 'elementor' ) ) ->set_id( 'settings' ) ->set_items( [ Html_Tag_Control::bind_to( 'tag' ) ->set_options( [ [ 'value' => 'div', 'label' => 'Div', ], [ 'value' => 'header', 'label' => 'Header', ], [ 'value' => 'section', 'label' => 'Section', ], [ 'value' => 'article', 'label' => 'Article', ], [ 'value' => 'aside', 'label' => 'Aside', ], [ 'value' => 'footer', 'label' => 'Footer', ], [ 'value' => 'main', 'label' => 'Main', ], [ 'value' => 'nav', 'label' => 'Nav', ], ] ) ->set_label( esc_html__( 'HTML Tag', 'elementor' ) ), Text_Control::bind_to( '_cssid' ) ->set_label( __( 'ID', 'elementor' ) ) ->set_meta( $this->get_css_id_control_meta() ), ] ), ]; } protected function define_base_styles(): array { return [ static::BASE_STYLE_KEY => Style_Definition::make() ->add_variant( Style_Variant::make() ->add_props( [ 'display' => String_Prop_Type::generate( 'flex' ), 'flex-direction' => String_Prop_Type::generate( 'column' ), 'position' => String_Prop_Type::generate( 'relative' ), 'overflow' => String_Prop_Type::generate( 'hidden' ), // Match Flexbox/container default padding so the empty-state outline and // stacking gaps match other layout elements. Absolute video still covers // the full padding box (abspos containing block = padding edge). 'padding' => Size_Prop_Type::generate( [ 'size' => 10, 'unit' => 'px', ] ), 'width' => Size_Prop_Type::generate( [ 'size' => 100, 'unit' => '%', ] ), ] ) ), ]; } protected function define_default_children() { $content = Atomic_Background_Video_Content::generate() ->meta( [ 'required' => true ] ) ->editor_settings( [ 'title' => esc_html__( 'Content Area', 'elementor' ), ] ) ->build(); $controls = Atomic_Background_Video_Controls::generate() ->meta( [ 'required' => true ] ) ->editor_settings( [ 'title' => esc_html__( 'Controls', 'elementor' ), ] ) ->children( [ Atomic_Background_Video_Play::build_default_element( true ), Atomic_Background_Video_Pause::build_default_element( true ), ] ) ->build(); return [ $content, $controls, ]; } protected function define_children_dependencies(): array { return [ Child_Dependency::for( self::ELEMENT_TYPE_CONTROLS ) ->when( Dependency_Manager::make()->where( [ 'operator' => 'ne', 'path' => [ 'show_controls' ], 'value' => false, ] ) ) ->position( Element_Position::last() ) ->stash( true ) ->default_model( Element_Builder::make( self::ELEMENT_TYPE_CONTROLS ) ->is_locked( true ) ->hydrate_default_children( true ) ->children( [ Element_Builder::make( self::ELEMENT_TYPE_PLAY ) ->hydrate_default_children( true ) ->build(), Element_Builder::make( self::ELEMENT_TYPE_PAUSE ) ->hydrate_default_children( true ) ->build(), ] ) ->build() ), ]; } protected function define_allowed_child_types() { return [ self::ELEMENT_TYPE_CONTENT, self::ELEMENT_TYPE_CONTROLS, ]; } public function get_script_depends() { $global_depends = parent::get_script_depends(); if ( Plugin::$instance->preview->is_preview_mode() ) { return array_merge( $global_depends, [ 'elementor-background-video-handler', 'elementor-background-video-preview-handler' ] ); } return array_merge( $global_depends, [ 'elementor-background-video-handler' ] ); } public function register_frontend_handlers() { $assets_url = ELEMENTOR_ASSETS_URL; $min_suffix = ( Utils::is_script_debug() || Utils::is_elementor_tests() ) ? '' : '.min'; wp_register_script( 'elementor-background-video-handler', "{$assets_url}js/background-video-handler{$min_suffix}.js", [ Frontend_Assets_Loader::FRONTEND_HANDLERS_HANDLE, Frontend_Assets_Loader::ALPINEJS_HANDLE ], ELEMENTOR_VERSION, true ); wp_register_script( 'elementor-background-video-preview-handler', "{$assets_url}js/background-video-preview-handler{$min_suffix}.js", [ Frontend_Assets_Loader::FRONTEND_HANDLERS_HANDLE, Frontend_Assets_Loader::ALPINEJS_HANDLE ], ELEMENTOR_VERSION, true ); } protected function build_template_context(): array { $settings = $this->get_atomic_settings(); $video_start_time = $settings['start_time'] ?? null; $video_end_time = $settings['end_time'] ?? null; $video_timings = '#t=0'; if ( ! empty( $video_start_time ) ) { $video_timings = '#t=' . $video_start_time; } if ( ! empty( $video_end_time ) && $video_end_time > ( $video_start_time ?? 0 ) ) { $video_timings .= ',' . $video_end_time; } return array_merge( $this->build_base_template_context(), [ 'video_timings' => $video_timings, ] ); } protected function get_templates(): array { return [ 'elementor/elements/atomic-background-video' => __DIR__ . '/atomic-background-video.html.twig', ]; } }