Viewing 10 posts - 1 through 10 (of 10 total)
  • Author
    Posts
  • #44652
    R B ATTENBOROUGH
    Participant

    http://jsfiddle.net/mofle/tZVsS/

    I’ve been inspired to write a Gutenberg block to help show syntax highlighted code snippets in blog posts.
    I’m starting a blog and would like to display snippets like you see on CSS Tricks (for example: https://css-tricks.com/keep-pixelated-images-pixelated-as-they-scale/)

    I’ve got quite far with this. I can display a code editor as a block on the backend and I can then display the code in a read-only version on the front end (which is the intended behaviour). However, although I can specify the type of code on the frontend (by using some JS), I cannot see how to set the “mode” of the code editor component on the back end.

    You can see the entire project git hub repo here.

    The code for the block itself is here:

    
    /**
     * Block dependencies
     */
    import icon from './icon';
    import './style.scss';
    const { CodeEditor } = wp.components;
    
    /**
     * Internal block libraries
     */
    const { __ } = wp.i18n;
    const { registerBlockType, PlainText } = wp.blocks;
    
    /**
     * Register block
     */
    export default registerBlockType(
        'jsforwpblocks/code',
        {
            title: __('Example - Code', 'jsforwpblocks'),
            description: __('How to use the Code component for building your own editable blocks.', 'jsforwpblocks'),
            category: 'common',
            icon: icon,
            keywords: [
                __('Banner', 'jsforwpblocks'),
                __('Call to Action', 'jsforwpblocks'),
                __('Message', 'jsforwpblocks'),
            ],
            attributes: {
                content: {
                    type: 'string',
                },
            },
            edit: props => {
                const { attributes: { content }, className, setAttributes } = props;
                return (
                    <CodeEditor
                        value={ content }
                        onChange={ ( content ) => setAttributes( { content } ) }
                    />
                );
            },
            save: props => {
                const { attributes: { content } } = props;
                return (
                    <div>
                        <div class="message-body">
                            <p>Code below:</p>
                            <div className="code">{ content }</div>
                        </div>
                    </div>
                );
            }
        }
    );
    

    On the front end I’m enqueuing the Code Mirror assets and have an app.js file which looks for instances of the code class and turns them into a read only code editor.

    My intention is to include some options on the block for specifying the code type and then setting this (perhaps as a data attribute) on the code class. Then the correct code highlighting can take place.

    But I’d really like this code highlighting on the backend as well.

    NOTE: I’m still a rookie so I know the code will be shaky at the moment.

    #44663
    Zac Gordon
    Keymaster

    Could you use a selector like a radio button in the inspector control that let’s you assign the type of code block on the backend? Then I’m guessing some class or something passed as from an attribute could let the frontend know the kind of code you’re working with. Would that work or address the problem?

    #44689
    R B ATTENBOROUGH
    Participant

    The problem is the CodeEditor component on the backend. When I’ve setup buttons to select the code mode I don’t know how to communicate this to the component:

    
    <CodeEditor
        value={ content }
        onChange={ ( content ) => setAttributes( { content } ) }
    />
    

    As you can see I can pass the content prop to the element and update the content on change. But I don’t understand how to communicate the type or mode of code to the element so that it shows the appropriate syntax highlighting.
    On the front end I’m not using the component but instead creating a div with the class of code where I inject the content (as a string). I then use JS to convert this div it a code editor. I suppose I could do a similar thing on the backend as well but really seeing as WP has added Code Mirror to core it would be nice to use it rather than pulling in the whole library twice…

    #44698
    Zac Gordon
    Keymaster

    Okay so I may still not be following everything completely, but couldn’t you add the attribute you need to the save function as a data attribute or class?

    I’m also curious what happens when you use the CodeEditor component in the save method (I haven’t experimented with it yet).

    Not sure if it relates, but blocks were not designed to be interactive on the frontend. So if you want a block to have JS run on the frontend you do need to load it separately in a script loaded to the frontend like you’re describing.

    Hope this helps a bit? Let me know how the experimentation goes!

    #45557
    R B ATTENBOROUGH
    Participant

    Hi Zac good to catch up with you at WordCamp London!

    It’s not the front end display I’m struggling with but rather the backend. I want to create a code mirror block which will allow me to edit text in a specified language. So I need to have a way to tell the <CodeEditor> component on the backend what language it should be using. I’ve tried looking at the CodeMirror docs and the Gutenberg docs but I’m scratching my head because the <CodeEditor> component doesn’t seem to have a property for it’s language mode: https://github.com/WordPress/gutenberg/tree/master/components/code-editor

    Possibly this element is only intended for html, css and js?

    Anyway I’ll have a play around with it.

    #45621
    R B ATTENBOROUGH
    Participant

    Actually, it looks like they’ve extended the component now to accept settings!

    https://github.com/WordPress/gutenberg/tree/master/components/code-editor

    Looking forward to checking this out.

    #45633
    Zac Gordon
    Keymaster

    Nice! Thanks for posting updates about this! Hope you can get it easily working now ::

    #45790
    R B ATTENBOROUGH
    Participant

    Okay, very nearly there now. The code editor now accepts settings, so the block now looks like this:

    
    return [
        isSelected && <Inspector { ...{setAttributes, ...props} } />,
        <div>
            <div>
                <h4>Language: {language}</h4>
            </div>
            <CodeEditor
                value={ content }
                settings={Object.assign(  {
                    codemirror: {
                    mode: language,
                    lint: true
                } }) }
                onChange={ ( content ) => setAttributes( { content } ) }
            />
        </div>
    ];
    

    I’m providing the language via an inspector block and currently the user can choose between html, javascript and css. When I update the language option this is reflected in the language title changing. However the CodeEditor component doesn’t change. Should I be adding something to the onChange method?

    There’s some documentation here: https://github.com/WordPress/gutenberg/tree/master/components/code-editor which suggests editorRef and this.editorInstance.setOption( 'mode', 'css' ); can be used to change modes, but I’m not quite sure how to a) use the editorRef or b) how to change the language for a specific CodeEditor instance (user may want different editors using different languages on the same post).

    No worries if you can’t help. I appreciate this component is pretty new! (By the way my code on https://github.com/BenAttenborough/rba-codeblock will only work with the Gutenberg taken direct from github).

    #46111
    R B ATTENBOROUGH
    Participant

    Managed to fix it 🙂 !

    I had to pass the instance to the Inspector controls:

    edit: props => {
                const { attributes: { content, language, lineNumbers }, isSelected, setAttributes } = props;
                return [
                    isSelected && (<Inspector { ...{setAttributes, ...props, ...{editor: this}}  } />),
                    isSelected && (<Controls { ...{setAttributes, ...props, ...{editor: this}}  } />),
                    <div>
                        <div>
                            <h4>Language: {language}</h4>
                        </div>
                        <CodeEditor editorRef={ ( ref ) => this.editorInstance = ref }
                                    value={ content }
                                    settings={Object.assign(  {
                                        codemirror: {
                                        mode: language,
                                        lint: true,
                                        lineNumbers: lineNumbers,
                                    } },
                                        window._wpGutenbergCodeEditorSetting
    
                                    ) }
                                    onChange={ ( content, language, lineNumbers ) => setAttributes( { content }, {language}, {lineNumbers}) }
                        />
                    </div>
                ];
            },

    And then within the inspector control I used the editor prop to change the editor instance:

    export default class Inspector extends Component {
    
        constructor() {
            super( ...arguments );
        }
    
        render() {
            const { attributes: { language }, setAttributes, editor } = this.props;
    
            function onChangeLanguage (language) {
                setAttributes( { language } );
                editor.editorInstance.setOption( 'mode', language );
            }
    
            return (
                <InspectorControls>
                    <PanelBody
                        title={ __( 'Snippet settings', 'rba-codeblock' ) }
                    >
                    </PanelBody>
    
                    <PanelBody>
                        <RadioControl
                            label={ __( 'Language', 'rba-codeblock' ) }
                            selected={ language }
                            options={ [
                                { label: 'HTML', value: 'htmlmixed' },
                                { label: 'CSS', value: 'css' },
                                { label: 'JS', value: 'javascript' }
                            ] }
                            onChange={ onChangeLanguage }
                        />
                    </PanelBody>
    
                </InspectorControls>
            );
        }
    }

    So it finally all works! You can check out the github repo here: https://github.com/BenAttenborough/rba-codeblock
    Be warned that this only works with the Gutenberg production code at the moment.

    #46971
    R B ATTENBOROUGH
    Participant

    Hey finally got the code snippet block working!
    It’s available at: https://github.com/BenAttenborough/rba-codeblock
    There still seems to be a bit of a strange bug. For some reason when editing a page with multiple code snippet blocks changing the language or line number settings changes the wrong editor. However upon saving and updating the correct settings should show. Does not affect the front end.
    I think this may be because the code block is being updated directly rather than through the React component. Anyway let me know what you think of the block. 🙂

Viewing 10 posts - 1 through 10 (of 10 total)
  • You must be logged in to reply to this topic.