soflyy / breakdance-bugs

Bug reports from Breakdance users.
42 stars 6 forks source link

Developer API Framework #730

Open ghost opened 1 year ago

ghost commented 1 year ago

It would be nice to have a simple Developer API Framework like Typerocket to create Custom Elements and Custom Fieldtypes. Also a Utility Class could help to validate and specify $propertiesData in %%SSR%% and build Custom Elements quickly.

So instead of use: $id = $propertiesData['content']['section']['field'];

The Utility Class could be look like:

$id = $this->getAsInt( 'content/section', 'id', 0 ); The last parameter is a default Value;

Or:

$text = $this->getAsString( 'content/section', 'text', 'Hello World', $PREPEND_IF_HAS_VALUE, $APPEND_IF_HAS_VALUE );

Or:

$isVisible = $this->getAsBool( 'design/section', 'isVisible', true );

The API could look like (for example a simple Video Element):

BDC::add_element('video',` 'Video', 'Medien', 'ICON')
    ->register_style( 'video', BD_PUBLIC_VC_URI.'/video.css' )
    ->register_script( 'video', BD_PUBLIC_VC_URI.'/video.js')
    ->register_style( 'vendor-plyr', BD_PUBLIC_VENDOR_URI.'/plyr/dist/plyr.css')
    ->register_script( vendor-plyr', BD_PUBLIC_ASSETS_URI.'/js/plyr/plyr.min.js')
    ->add_content_params( [
        BDF::add_section( 'Content', 'content', [
            BDF::add_select( 'type', 'Playtype', [ '---' => '', 'Player' => 'player', 'Viewport Autoplay' => 'viewport-autoplay' ] ),
            BDF::add_file( 'mp4', 'MP4' ),
            BDF::add_image( 'poster', 'Image' ),
        ], $ARGS),
    ])
->add_design_params( [
        BDF::add_section( 'Look & Feel', 'look', [], $ARGS),
    ])
    ->render(
        function() {
            /* @var $this BDU */
            $mp4 = '';
            $poster = '';
            $player_args = '';
            $type = $this->get_as_string( 'content/content', 'type', 'player' );
            if( $type == 'viewport-autoplay' ) {
                $player_args = ' muted playsinline autoplay loop preload="none"';
            }
            $mp4 = $this->get_as_file_url( 'content/content', 'mp4', $mp4 );
            $poster = $this->get_as_image_url( 'content/content', poster',  $poster );
            $this->render_container_start( null, null, [ 'data-type' => $type ] );
            ?>
            <div class="wrapper">
                <video class="video" id="player" preload="auto" <?php echo $player_args ?> poster="<?php echo $poster ?>" data-poster="<?php echo $poster ?>">
                    <source src="<?php echo $mp4 ?>" type="video/mp4" />
                </video>
            </div>
            <?php
            $this->render_container_end( );
        }
    );

Also a JS Framework would be cool, like a Factory Class, so Loops to match these elements don't need to be self coded. For example what if I have a Custom Element and this element needs some JS (not inline). When i add these elements for example 3 times, I need to loop and self identify these elements and match them.

A custom Factory Class could be like:

window.BD_FACTORY_ELEMENT_CLASS = class BD_FACTORY_ELEMENT {

    static BD_EVENT_FACTORY_REINIT = 'bd.event.factory.reinit';

    constructor() {

        this.elements = [];

    }

    static init() {

        if( typeof window.BD_FACTORY_ELEMENT === 'undefined' || window.BD_FACTORY_ELEMENT === null ) {
            window.BD_FACTORY_ELEMENT = new BD_FACTORY_ELEMENT();
            window.addEventListener( window.BD_EVENT_FACTORY_REINIT, window.BD_FACTORY_ELEMENT_CLASS .reinit );
        }

    }

    static reinit() {

        for ( let i = 0; i < window.BD_FACTORY_ELEMENT.elements.length; i++ ) {
            let element = window.BD_FACTORY_ELEMENT.elements[i];
            element.init();
        }

    }

    static add( element_class, element_selector, init_event ) {

        window.WPB_VC.elements.push( new BD_ELEMENT( element_class, element_selector, init_event ) );

    }

}

class BD_ELEMENT {

    constructor( element_class, element_root, init_event ) {

        this.elements = [];
        this.element_class = element_class;
        this.element_selector = element_selector;
        this.d_elements = document.querySelectorAll('.' +  this.element_selector + ':not(.bd-element-loaded)' );
        if( event !== null ) {
//Event like Document Ready
           event = window.BD_EVENT_DOM_READY;
        }
        window.addEventListener( event, this.init.bind(this));

    }

    init() {

        for ( let i = 0; i < this.d_elements.length; i++ ) {
            let d_element = this.d_elements[ i ];
            d_element.classList.add( 'bd-element-loaded' );
            this.elements.push( new this.element_class( d_element ) );
        }

    }

}

And for a Element like Video...


class Video {

    constructor( d_root ) {

        this.d_root = d_root;
        this.d_video = this.d_root.querySelectorAll( '.video' );
        this.id = this.d_root.getAttribute( 'id' );

        if( this.d_video.length > 0 ) {
            this.d_video = this.d_video[0];
            this.poster = this.d_root.hasAttribute( 'data-poster' ) ? this.d_root.getAttribute( 'data-poster' ) : '';
            this.type = this.d_root.getAttribute( 'data-type' );
            this.init();
        }

    }

    init() {

        let args = {
            fullscreen: {
                iosNative: true
            },
            resetOnEnd: true
        };

        if( this.type === 'viewport-autoplay' ) {
            args.hideControls = true;
            args.clickToPlay = false;
            args.muted = true;
            args.displayDuration = false;
            args.fullscreen = { enabled:false };
            args.loop = { active: true };
            args.resetOnEnd = false;*/
        } else {
            this.plyr = new Plyr(
                this.d_video,
                args
            );

            if (this.poster !== '') {
                setTimeout(() => {
                    this.plyr.poster = this.poster;
                }, 1);
            }

            this.plyr.on('play', () => {
                this.pause_all();
            });
        }

        if( this.type === 'viewport-autoplay' ) {

            window.ScrollTrigger.create( {
                trigger: this.d_root,
                start: "top bottom",
                end: "bottom top",
                onToggle: (e)  => {
                    try {
                        if (e.isActive) {
                            this.d_video.play();
                        } else {
                            this.d_video.pause();
                        }
                    } catch(e){}
                },
            } )

        }

    }

    stop() {

        this.plyr.stop();

    }

    play() {

        this.plyr.play();

    }

}

window.BD_FACTORY_ELEMENT_CLASS.add(  Video, 'video' );

So the developer only needs only register the Element by Selector and Class.

window.BD_FACTORY_ELEMENT_CLASS.add( Video, 'video' );

a-dubiel commented 1 year ago

Did you try using Element Studio?

https://breakdance.com/documentation/developers/element-studio/

Edit: Looks like you did. I am wondering about your thoughts about current developer experience.

In terms of elements JS framework we do have some improvements already planned for Element Studio. Very similar to factory solution you proposed. I passed this internally for the reference.

ghost commented 1 year ago

Yes, but a API for Power Developer should be also great. Element Studio is really cool, but I think many Developer missing some features like transpiing, autocompletion, Copilot, SCSS (or other) npm and other stuff.

This can be only managed by an IDE and therefore a simple Framework has none of the other Page Builders (the only one is WPBackery).

Am 22.03.2023 um 11:22 schrieb Andrzej Dubiel @.***>:

Did you try using Element Studio?

https://breakdance.com/documentation/developers/element-studio/ https://breakdance.com/documentation/developers/element-studio/ — Reply to this email directly, view it on GitHub https://github.com/soflyy/breakdance-bugs/issues/730#issuecomment-1479287369, or unsubscribe https://github.com/notifications/unsubscribe-auth/ATZY3PBU2DNQ2JUZ26PBKWDW5LHGRANCNFSM6AAAAAAWCZDZWE. You are receiving this because you authored the thread.

ghost commented 1 year ago

As I said. Element Studio is really great. But I am missing scss, webpack and so on - but these can also be my personal impressions. I can live with Element Studio ;-) . Factory classes sounds great. It would be also great, if it were possible to transfer the elements not just to a separate plugin. I mostly use my own themes. I just like having everything in one place. That's why it would be great if Element Studio also offered the option of transferring the created elements to your own theme and still having the option of expanding them in Element Studio.

I'm trying to write my own little framework. For example, to quickly create own dynamic fields. I will try to implement this with dynamic classes... let's see if this works ;-).

At the moment I could only set some simple Options like:

WPB_Breakdance::disable_google_fonts();
WPB_Breakdance::enable_core_features();
WPB_Breakdance::register_category( 'WPB', 'wpb' );

core_features are like:

 if ( function_exists( 'pll_the_languages' ) ) {
            add_action("breakdance_register_template_types_and_conditions", function () {
                if (function_exists("pll_languages_list")) {
                    $lang_list = \pll_languages_list();
                    \Breakdance\ConditionsAPI\register([
                        "supports" => ["element_display", "templating"],
                        "slug" => "wpb-breakdance-polylang-condition",
                        "label" => "Language",
                        "category" => "Polylang",
                        "allowMultiselect" => false,
                        "operands" => ["equals", "not equals"],
                        "values" => function () use ($lang_list) {
                            return [
                                [
                                    "label" => "Language",
                                    "items" => array_map(function ($lang_list) {
                                        return [
                                            "text" => $lang_list,
                                            "value" => $lang_list,
                                        ];
                                    }, $lang_list),
                                ],
                            ];
                        },
                        "callback" => function (string $operand, $value) {
                            $myVal = \pll_current_language();
                            if ($operand === "equals") {
                                return $val === $value;
                            }
                            if ($operand === "not equals") {
                                return $val !== $value;
                            }
                            return false;
                        },
                    ]);
                }
            });
        }
ghost commented 1 year ago

Is it possible to unregister Icons (FontAwesome and so on) and unregister Elements to hide some for Customers?

ghost commented 1 year ago

Yes it works with anonymous functions ^^:

That's the only, I need to write to add a dynamic field. So that's really cool to fast implement custom dynamic Fields. Parameters: Slug, Label, Category There is one last Parameter optional to specify if it is a StringField (string).

You see, less code to write.


WPB_Breakdance::register_dynamic_field( 'test', 'Test', 'Test' )
    ->set_return_types( [ 'string' ] )
    ->set_handler(
        function( $attributes ) {
            return 'test';
        }
    );