Mugentoki
Mugentoki

Reputation: 1616

Dynamic Block - How to create dynamic stylesheet on post save / load

I've created a working Gutenberg Block with Create Guten Block (https://github.com/ahmadawais/create-guten-block). Currently it's only working with inline-styles, but as a requirement I have to avoid them.

Therefore I want to create a post/page stylesheet when the post is saved including the style settings for my blocks (for example background-color, color, font-size...)

My block's current save function (block.js)

save: function( props ) {
        const { attributes: { typetext, infotext, linktext, background_color, background_button_color, text_color, text_color_button }} = props;
        return (
            <div id="cgb-infoblock" className="cgb-infoblock">
                <div className="cgb-infoblock-body" style={{
                    backgroundColor: background_color,
                    color: text_color,
                }}>
                    <div className="cgb-infoblock-type">
                        <p>
                            <span className="cgb-infoblock-icon"><i>i</i></span>
                            { typetext && !! typetext.length && (
                                <RichText.Content
                                    tagName="span"
                                    className={ classnames(
                                        'cgb-infoblock-type-text'
                                    ) }
                                    style={ {
                                        color: text_color
                                    } }
                                    value={ typetext }
                                />
                            )}
                        </p>
                    </div>
                    <div className="cgb-infoblock-text">
                        { infotext && !! infotext.length && (
                            <RichText.Content
                                tagName="p"
                                style={ {
                                    color: text_color
                                } }
                                value={ infotext }
                            />
                        )}
                    </div>
                </div>
                <div className="cgb-infoblock-button" style={{
                    backgroundColor: background_button_color,
                    color: text_color_button,
                }}>
                    { linktext && !! linktext.length && (
                        <RichText.Content
                            tagName="p"
                            style={ {
                                color: text_color_button
                            } }
                            value={ linktext }
                        />
                    )}
                </div>
            </div>
        );
    },

The best solution would be some sort of stylesheet generation for a whole page/post with all settings from all blocks.

Best way would be if the stylesheet generation is happening on page save, but it would be also ok if it is happening on page-load. Since those posts are not going to be large, the performance shouldn't be that much of a problem.

Upvotes: 2

Views: 1229

Answers (1)

Mugentoki
Mugentoki

Reputation: 1616

So after digging around I've figured it out myself. Just in case someone else got this problem here's the solution:

First of all, attributes have to be defined in registerBlockTypefunction

registerBlockType( 'cgb/your-block-type', {
title: __( 'Your Block Name' ),
icon: 'shield',
category: 'maybe-a-category',
keywords: [
    __( 'some keywords' ),
],

attributes: {
    background_color: {
        type: 'string',
        default: 'default' //we will use the "default"-value later
    },
},

So now Wordpress knows which attributes you wanna save. Problem now, as long as the "default" value is not overwritten, Wordpress won't save the value into the block object's attributes. To solve this we'll use the save function from registerBlockType. (Quick note on this: This will not trigger the default value for the editor widget, so you allways have to change your background_color's value to see it the first time you insert your widget into gutenberg editor. To fix this, use saveDefaultValue(this.props) right at the beginning of your render() function.)

    save: function( props ) {

    saveDefaultValues(props);

    const { attributes: {background_color}} = props;
    return (
        //... here's your html that's beeing saved
    );
},

function saveDefaultValues(props) {
    if(props.attributes.background_color === 'default'){
        props.attributes.background_color = '#f1f6fb';
    }
}

With this we are forcing wordpress to save our default value. Pretty sure there's a better solution for this, but since I just started with react / Gutenberg, this is the only thing that got it working for me.

Ok, now we can save our Attributes into the Block-object. Now we wanna create our dynamic stylesheet. For this we are creating a new .php file in the following directory /plugin-dir/src/since we are using create-guten-block. The name doesn't matter, but I named it the same way like my stylesheet. `gutenberg-styles.css.php``

The gutenberg-styles.css.php will later create a gutenberg-styles.cssfile, everytime someone is visiting the post. But first we are looking into the plugin.phpfile. Add the following code:

function create_dynamic_gutenberg_stylesheet() {
    global $post;
    require_once plugin_dir_path( __FILE__ ) . 'src/gutenberg-styles.css.php';

    wp_enqueue_style('cgb/gutenberg-styles', plugins_url( 'src/gutenberg-styles.css',  __FILE__ ));
}
add_action('wp_head', 'create_dynamic_gutenberg_stylesheet', 5, 0);

This code accesses the global $post variable, we need it to get all the gutenberg-blocks from the current visited post. After that we require our own gutenberg-styles.css.php which will automatically create our stylesheet, which will be enqueued in the next line. Now hook it up to wp_head(you could probably hook it up to wordpress save action as well, but you will have to do more work for enqueuing the stylesheet)

Finally a look into our gutenberg-styles.css.php:

$styleSheetPath = plugin_dir_path( __FILE__ ) . 'gutenberg-styles.css';
$styleSheet = '';
$blocks = parse_blocks($post->post_content);

//loop over all blocks and create styles
foreach($blocks as $block) {
    $blockType = $block['blockName'];
    $blockAttributes = $block['attrs']; //these are the attributes we've forced to saved in our block's save function

    //switch case so you can target different blocks
    switch ($blockType) {
    case 'cgb/your-block-type':
        $styleSheet .= '.your-block-class {'.PHP_EOL
        $styleSheet .= 'background-color: '.$blockAttributes['background_color'].';'.PHP_EOL
        $styleSheet .= '}'.PHP_EOL
        break;
    }
}

file_put_contents($styleSheetPath, $styleSheet); //write css styles to stylesheet (creates file if it not exists)

I've added PHP_EOL at each line to generate line breaks, you don't have to do this. But now you can visit a page with your custom block and will see the gutenberg-styles.css is loaded and applied to your blocks.

Upvotes: 3

Related Questions