Plugin Template

I just created a custom block plugin following the screencast Block Plugin Crash Course. Everything works so far but I have a problem with the template. The block consists of a text field used for a heading and a textarea for a text.

panel.plugin("tillcremer/index-blocks", {
    blocks: {
        textblock: {
            methods: {
                editHeadline(event) {
                    this.update({
                        headline: event.target.value
                    });
                },
                editText(event) {
                    this.update({
                        text: event.target.value
                    });
                }
            },
            template: `
                <div>
                    <input
                        type="text"
                        :value="content.headline"
                        @input="editHeadline"
                    />         
                    <input
                        height="3rem"
                        type="text"
                        :value="content.text"
                        @input="editText"
                    />
                </div>
            `
        }    
    }
});

When I delete the div element, the text field disapperas in the panel.

            template: `
                    <input
                        type="text"
                        :value="content.headline"
                        @input="editHeadline"
                    />         
                    <input
                        height="3rem"
                        type="text"
                        :value="content.text"
                        @input="editText"
                    />
            `

I do not understand why the div element is necessary here. The same happens when I use a div element for each input element:

            template: `
                <div>
                    <input
                        type="text"
                        :value="content.headline"
                        @input="editHeadline"
                    />
                </div>
                <div>         
                    <input
                        height="3rem"
                        type="text"
                        :value="content.text"
                        @input="editText"
                    />
                </div>
            `

Vue components (which such a Panel plugin is basically rendered as) need a single DOM element as their template. This will change once we migrate to Vue 3 in Kirby 6 (likely), but for the moment, your template code needs to be a single node, not two parallel ones.

Thank you. So it is not possible to create a block type including several elements like a headline and a text?

Yes, it is possible, if you wrap everything into an outer container, you described that that worked, so why did you remove it?

In my opinion, it was superfluous code. I like it tidy. I will think about it. Thank you!

I agree that it would be great without it but there we just need to follow what Vue requires. Don’t try to fight it, it’s not worth it and while I understand the desire for tidiness this is fine - your efforts will be more worthwhile on other aspects :slight_smile:

Well, finally it is all fine. My aim is create blocks that follow HTML semantics. Therefore, a section element works perfect as a container for it.

panel.plugin("tillcremer/index-theme-blocks", {
    blocks: {
        textblock: {
            methods: {
                editHeading(event) {
                    this.update({
                        heading: event.target.value
                    });
                },
                editToggle(event) {
                    this.update({
                        toggle: event.target.value
                    });
                },
                editText(event) {
                    this.update({
                        text: event.target.value
                    });
                }
            },
            template: `
                <section>
                    <input
                        type="text"
                        :placeholder="field('heading').placeholder"
                        :value="content.heading"
                        @input="editHeading"
                    />      
                    <input
                        type="checkbox"
                        data-variant="toggle"
                        :value="content.toggle"  
                        @input="editToggle"
                    />
                    <input
                        type="text"
                        :placeholder="field('text').placeholder"
                        :value="content.text"
                        @input="editText"
                    />                    
                </section>
            `
        }    
    }
});

Now, I have another problem. I would like to have the same panel styles and structures for the elements. How do I achieve this? I know about lab.getkirby.com.

Using the components documented on https://lab.getkirby.com will also provide the styling, e.g. using <k-text-input> etc. - or for the full field.

On most Lab examples you can switch between preview and code view to see how they’re used:

Thanks! The styling is fine now but the sync does not work anymore. Why does this happen? If I edit the fields in the regular block editing section the contents change but not vice versa.

            template: `
                <section>                    
                    <k-writer-input
                        placeholder="field('heading').placeholder"
                        :value="content.heading"
                        @input="editHeading"
                    />     
                    <k-toggle-input 
                        name="toggle" 
                        :value="content.toggle" 
                        @input="editToggle" 
                    />   
                    <k-writer-input
                        placeholder="field('text').placeholder"
                        :value="content.text"
                        @input="editText"
                    />                 
                </section>
            `

Moreover, how do I assign the set field width to my custom layout?

Our component directly pass the value via the input event. I assume you are still doing event.target.value in your methods though.

Yes, what do I have to change? No methods needed?

You still need the methods, but instead of receiving event and updating event.target.value, your methods receive the value directly and can use it to update.

panel.plugin("tillcremer/index-theme-blocks", {
    blocks: {
        textblock: {
            methods: {
                editToggle(event) {
                    this.update({
                        toggle: event.target.value
                    });
                }
            },
            template: `
                <section>                    
                    <k-toggle-input 
                        name="toggle" 
                        :value="value" 
                        @input="value = $event"
                    />                
                </section>
            `
        }    
    }
});

Well, I am not a JS expert. With what should I replace toggle: event.target.value and $event?

And how do I assign my panal layout? For instance, the field width.

No, as I wrote, you keep the method call as is (@input="editToggle") but your method receives value instead of event and also uses value for the update call:

editToggle(value) {
  this.update({
    toggle: value
  })
}

I don’t know what you refer to with this.

This does not work:

panel.plugin("tillcremer/index-theme-blocks", {
    blocks: {
        textblock: {
            methods: {
                editToggle(value) {
                    this.update({
                        toggle: value
                    });
                }
            },
            template: `
                <section>                     
                    <k-toggle-input 
                        name="toggle" 
                        :value="value" 
                        @input="editToggle"
                    />               
                </section>
            `
        }    
    }
});

Why :value="value" and not :value="content.toggle" anymore?

Thanks. Now the fields sync but the placeholder does not sync with the blueprint anymore. Why? I used, for instance, placeholder="field('text').placeholder" to achieve this.

The blueprint places a toggle next to the heading. This is not the case in my custom form. How can I adapt the layout of my form to that of the blueprint?

name: Text Block with Headline
type: textblock
icon: text

fields:
  heading:
    label: Heading
    type: writer
    inline: true
    width: 3/4
    required: true
    placeholder: Heading
  toggle:
    label: false
    type: toggle                
    text: 
      - hide
      - show
    default: true  
    width: 1/4
  text:
    label: Text
    type: writer
    inline: true
    size: medium
    required: true
    placeholder: Text
  

Question: are you just trying to recreate 1:1 the fields from the drawer in your block preview? If so, I’d recommend to rather go with Blocks | Kirby CMS

Yes, I do. I plan to create some structure-based blocks as well. As far as I know, these do not work with wysiwyg. That is why I try to create such a workaround.

Please see the screenshots. I am planning an accordion <details> block with a heading that can be hidden by display: none. The heading is important for the semantic structure and appears in the source code even if it is not to be displayed. I am also planning a table block with a heading that can be hidden. Hence the toggle field next to the heading fields.

It would be nice if the input mask appeared just as minimally in the panel as Kirby’s own blocks.

Each block will be a <section> of an <article>. Accroding to W3Schools, section and article elements require a heading element. My HTML semantics will be structured like this:

<header>
  <h1></h1>
</header
<article>
  <h2></h2>
  <section>
    <h3></h3>
  </section>
</article>

My plugin works well so far. At the moment, I just do not know how to apply the width and the placeholder defined in the blueprints. Is there an easy way like you showed me with the single fields?

Should I apply some grids code from here?