Difficulties building a custom image block plugin

I’m trying to create a custom image block plugin that adds a handful of fields to the image blueprint and controls the behaviour of the image block preview in the Panel.

I followed this Cookbook lesson to split out the parts of the default Image.vue file into the index.js and index.css files but I’m struggling with a couple of challenges.

1. Import/extend script affecting other plugins
The first section of script that imports and extends Block from "./Default.vue"; seems to be causing some problems.

  • If I leave that code out, no image preview appears in the Panel (of course)
  • If I include it, I get problems with other plugins, for example…
    • The Clear Cache page is completely empty
    • The Panel preview for my custom button block disappears

My questions are…

  • Should that script section go before or after the line panel.plugin("brandsistency/image-block", {? If after, I get code errors that I can’t resolve. Either way, it impacts the other plugins.
  • Or should I be building in actual code from Default.vue?

2. Adding new field classes to the preview <template>
One of the additional fields I’m adding to the blueprint is an alignment setting where the user can set the image to be centred or right-aligned. It works perfectly in the front-end via the snippet but I want to be able to centre the Panel preview as well. What I can’t figure out is how to ‘mix’ the existing classes and my dynamic class. I know that having both class and :class is wrong but I can’t figure out how to combine them.


This is my index.js code as it stands. What am I doing wrong?

import Block from "./Default.vue";
/** 
* @displayName BlockTypeImage
*/
export default {
  extends: Block,
  data() {
    return {
      back: this.onBack() ?? "white"
    };
  }
};

panel.plugin("brandsistency/image-block", {
  blocks: {
    image: {
      computed: {
        captionMarks() {
          return this.field("caption", { marks: true }).marks;
        },
        crop() {
          return this.content.crop ?? false;
        },
        src() {
          if (this.content.location === "web") {
            return this.content.src;
          }
          if (this.content.image?.[0]?.url) {
            return this.content.image[0].url;
          }
          return false;
        },
        ratio() {
          return this.content.ratio ?? false;
        },
        className() {
          let value = this.content.alignment;
          let className = value === 'center' ? 'txt-c' : value === 'right' ? 'txt-r' : ''; return className;
        },		
      },
      methods: {
        onBack(value) {
          const id = `kirby.imageBlock.${this.endpoints.field}.${this.id}`;
          if (value !== undefined) {
            this.back = value;
            sessionStorage.setItem(id, value);
          } else {
            return sessionStorage.getItem(id);
          }
        }
      },
      template: `
        <k-block-figure
          :back="back"
          :caption="content.caption"
          :caption-marks="captionMarks"
          :empty-text="$t('field.blocks.image.placeholder') + ' …'"
          :disabled="disabled"
          :is-empty="!src"
          empty-icon="image"
          @open="open"
          @update="update"
        >
          <template v-if="src">
            <k-image-frame
              v-if="ratio"
              :ratio="ratio"
              :cover="crop"
              :alt="content.alt"
              class="k-block-type-image-auto"
              :class="className"
              :src="src"
            />
            <img
              v-else
              :alt="content.alt"
              class="k-block-type-image-auto"
              :class="className"
              :src="src"
            />
            <k-block-background-dropdown :value="back" @input="onBack" />
          </template>
        </k-block-figure>
      `
    }
  }
});

That whole import/export stuff should not be there, and in any case not at the top of the file.

Thanks, Sonja. But if I remove that section completely, the image blocks do not appear at all in the Panel. It’s still there in the front-end but not in the Panel. I can add new image blocks but, as soon I’ve finished filling in the fields in the modal, the block disappears from view.

Here is a screenshot of the Panel with that import section included in index.js. You can see some of the extra fields I’ve added, including the alignment setting. The image blocks appear in the preview. (I’ve set one of them to be centred but it is not centred in the Panel because I can’t figure that bit out.) Other plugins are affected in this case.

Without the import section, all the image block previews disappear…

But everything is still there in the front-end because the snippet works. (You can see the centred image.)

I’m struggling to work out how to get my index.js to work properly.

Could you post the complete code or do you have it in a repo?

It’s not in a repo yet but I’ll add one. Link to follow.

Sonja, I’ve created a new repo – kirby-image-block – with the full code in its current state. I look forward to feedback. Thanks.

I think the problem here is that you will have to give your custom block a new name. Then move the data part inside the block definition:

panel.plugin("brandsistency/image-block", {
	blocks: {
		myimage: {
			data() {
				return {
					back: this.onBack() ?? "white"
				};
			},
			computed: {
				captionMarks() {
					return this.field("caption", { marks: true }).marks;
				},
				crop() {
					return this.content.crop ?? false;
				},
				src() {
					if (this.content.location === "web") {
						return this.content.src;
					}
					if (this.content.image?.[0]?.url) {
						return this.content.image[0].url;
					}
					return false;
				},
//...
Kirby::plugin('brandsistency/image-block', [
	'blueprints' => [
		'blocks/myimage' => __DIR__ . '/blueprints/blocks/myimage.yml'
	],
	'snippets' => [
		'blocks/myimage' => __DIR__ . '/snippets/blocks/myimage.php'
	]
]);

myimage is probably a stupid name, but you get the idea. And everything will work as expected.

Note that export/import statements only every work in single file components that require a build system. But it doesn’t really make sense to require this default.vue here.

Thank you. That’s worked in general but I’m still struggling with addition of the classes in the Panel preview, which is the main reason for creating a plugin rather than just a custom blueprint and snippet.

With my custom button block plugin everything works perfectly. If I set the alignment to centred, the button is centred in the Panel as well as the front-end. That’s because the class txt-c is added in the Panel, which I’ve then been able to style…

panel.plugin("brandsistency/button-block", {
  blocks: {
    button: {
      computed: {
        placeholder() {
          return "Button text...";
        },
        className() {
          let value = this.content.alignment;
          let className = value === 'center' ? 'txt-c' : value === 'right' ? 'txt-r' : ''; return className;
        }
      },
      template: `
        <button :class="className" type="button" @click="open">{{ content.text }}</button>
      `
    }
  }
});

I’ve included exactly the same className code in the relevant places in the custom image block index.js file but it isn’t working the same way. It does not add the txt-c class in the Panel. In fact, it’s adding the class k-block-type-image-auto even though I temporarily removed that.

As a test, I also tried overriding some of the styling in the index.css file but that has no impact.

Could it still be picking up ‘instructions’ from the standard image block Image.vue file? Do I need to do something to bypass that?

Did you also set the preview to the new block name and the block type, see Custom block examples | Kirby CMS

k-block-type-nameOfBlock

I’ve replaced instances of k-block-type-image with k-block-type-myimage in what I think are all the relevant places. I’ve cleared both the Kirby and the browser cache. But still the Panel preview seems to be picking up the ‘old’ image template.

I can’t figure out what I’m missing.

I’ve updated the GitHub repo with the latest version of my code and I’d be grateful if you’re able to spot any other errors.

Your new myimage block blueprint is still using the image block preview, not your myimage: kirby-image-block/blueprints/blocks/myimage.yml at main · brandsis/kirby-image-block · GitHub

Thanks, Nico. That solved it. It had to be something obvious, didn’t it! :wink: