Writer Field Custom Mark for Remix Icons

Hi,

I would like to create a custom mark for the writer field to insert Remix Icons into the text. The idea is to apply the mark to some text, for example, ri-arrow-up-line, and have Writer save it as:

<i class=\"ri-arrow-up-line\"><span class=\"ri-name\">ri-arrow-up-line</span></i>

I used the Kirby Reference as a template and tried to adapt the commands() from the default marks. However, no matter what I tried, either the code isn’t saved or the custom mark doesn’t appear at all.

Does anyone have a tip? Thanks a lot!

Here is the base code:

window.panel.plugin("sb/remixicon", {
  writerMarks: {
    remixicon: {

      get button() {
        return {
          icon: "circle",
          label: "Remix Icon",
        };
      },

      commands() {
        return () => this.toggle();
      },

      get name() {
        return "remixicon";
      },

      get schema() {
        return {

          parseDOM: [

            {
              tag: "i",
              priority: 51
            }

          ],

          toDOM: () => ["i", 0],

        };
      },

    },
  },
});

Not an expert in this area but this is how you add a class name to the i tag. Feel free to add the priority back if it’s still needed

get schema() {
	return {
		parseDOM: [
			{
				tag: 'i',
				getAttrs: () => {
					return { class: 'ri-arrow-up-line' }
				}
			}
		],
		toDOM: () => ['i', { class: 'ri-arrow-up-line' }, 0]
	}
}

Not an expert in this area but this is how you add a class name to the i tag. Feel free to add the priority back if it’s still needed

Thanks for the reply!

You are correct, this adds the class. But I don’t get the syntax right that adds the marked text as a class (“ri-arrow-up-line” was just an example).

And also, how to add the <span> inside the <i> that I need:

<i class=\"ri-arrow-up-line\"><span class=\"ri-name\">ri-arrow-up-line</span></i>

Unfortunately I only know how to add an element around the selected text. What you want is to insert an icon without selecting the text? I am clueless how to do that myself. Perhaps have a read of the Prose documentation as that’s the underlying library used by the Writer field.

https://prosemirror.net/docs/ref/#model.MarkSpec

I hope someone else with more knowledge can assist you. Maybe @texnixe

1 Like

What you want is to insert an icon without selecting the text?

No, I want to select a text, for example „ri-arrow-up-line“, then click on the mark (since Remix Icon uses the <i> tag, the text will be in italic in the editor), and then I want the writer field to save the text like this:

<i class=\"ri-arrow-up-line\"><span class=\"ri-name\">ri-arrow-up-line</span></i>

The problem is that I don’t know how to transfer the selected text to this code correctly.

Why a <span> inside the <i>?

Remix Icon uses this markup:

<i class="ri-arrow-up-line"></i>

So the reason for the <span> inside the <i> is that you can still read the text in the editor. In the frontend the <span> will be set to display: none;

I figured it out :slight_smile:

This should work

panel.plugin("sb/remixicon", {
	writerMarks: {
		remixicon: {
			get button() {
				return {
					icon: "circle",
					label: window.panel.$t("remixicon"),
				}
			},

			commands() {
				return () => this.toggle();
			},

			get name() {
				return "remixicon"
			},

			get schema() {
				return {
					parseDOM: [
					{
						tag: 'i',
						priority: 51,
						getAttrs: () => {
							return { class: 'ri-arrow-up-line' }
						}
					}
					],
					toDOM: () => ['i', { class: 'ri-arrow-up-line' }, ["span", { class: "ri-name" }, 0]]
				}
			},

		},
	}
});

I noticed your original code had some semicolons in odd places that probably weren’t helping you. Did the icon even appear in the toolbar?

@saschabregenhorn I solved it with a simple plugin. However, I use FontAwesome icons. But the concept can easily be adapted for Remix icons. This is my solution:

site/plugins/fa-icons/index.php

<?php
Kirby::plugin('yourname/fa-icons', [
    'hooks' => [],
    'tags' => [],
    'components' => [],
]);

function replaceIcons($text) {
    return preg_replace_callback('/\[fa="([^"]+)"\]/', function($matches) {
        $iconName = htmlspecialchars($matches[1], ENT_QUOTES, 'UTF-8');
        return '<i class="fas fa-' . $iconName . '"></i>';
    }, $text);
}

site/blueprints/pages/default.yml

textwithicon:
  type: writer

And in the writer field, you use a placeholder:

Here is an example text with an icon [fa=“star”]

site/templates/default.php

<?= replaceIcons($page->textwithicon()->kt()) ?>

This is what the output looks like in the source code:

<p>Here is an example text with an icon <i class="fas fa-star"></i></p>

This should work

Thanks for your help! Yes, this adds the <span> inside the <i>. Still, my main problem is that I don’t know how to correctly transfer the selected text to the saved code. ‘ri-arrow-up-line’ is just an example.

Did the icon even appear in the toolbar?

Yes. You’re right, the return statements don’t need semicolons.

Oh wow, that’s a much better idea. Thanks a lot!

I see. I think best to use the code provided by @GB_DESIGN. Unfortunately (and I just tested it) using the selected text as the class name is way too buggy that it’s not reliable. Someone else with more experience might have been able to get it to work though

1 Like