Virtual Happy Hour this month, Jun 28, we'll be getting a sneak preview at our soon to launch SaaS CMS!

Try our conversational search powered by Generative AI!

Jon Sexton
Jun 9, 2021
  3946
(2 votes)

More on custom Style Formats in TinyMCE

One of the problems we come across, when adding in our custom style formats for TinyMCE in the EPiServer CMS, is that this overrides the default settings.  For example, a client may require the following style format options:

  • Headings
    • Heading 1
    • Heading 2
    • etc...
  • Inline
    • Bold
    • Italic
    • Underline
    • Strikethrough
    • Superscript
    • Subscript
    • Code
  • Blocks
    • Paragraph
    • Blockquote
    • Div
    • Pre
  • Alignment
    • Left
    • Center
    • Right
    • Justify
  • [a new font to select from]

All standard, out-of-the-box stuff, for TinyMCE, however, when we add a custom style format (in this case for the new font option the client requires) in our TinyMCE intialisation class, and as described in a previous post: https://world.episerver.com/blogs/jon-sexton/dates/2021/2/how-to-set-primary-default-and-secondary-optional-fonts-for-tinymce-in-episerver-cms-11/, all these default style formatting options are blasted away, great!

This blog will show how to put them back in.

Firstly, there appears to be no functionality available to us to simply append a new style format option to the existing set of default options, well, none that I could find nor get working.  Unfortunately we're going to have to add these in manually; here's how...

In our TinyMCE initialisation class let's start by adding in the Headings and Headings sub-menu:

        public void ConfigureContainer(ServiceConfigurationContext context)
        {
            context.Services.Configure<TinyMceConfiguration>(config =>
            {
                config.Default()
                    .AddEpiserverSupport()
                    .AddSetting("paste_as_text", true)
                    .AddSetting("paste_webkit_styles", "none")
                    .AddSetting("paste_merge_formats", true)
                    .AddSetting("paste_remove_styles_if_webkit", true)
                    .AddPlugin(
                        "epi-link epi-dnd-processor epi-personalized-content help image fullscreen lists searchreplace hr table paste media code")
                    .Toolbar(
                        "bold italic superscript subscript quote citation bullist numlist undo redo | styleselect formatselect removeformat",
                        "epi-link epi-unlink anchor | cut copy paste pastetext pasteword | table | code | image epi-image-editor | epi-personalized-content | fullscreen ")
                    .ContentCss("/ClientResources/Styles/editorStyles.css")
                    .BodyClass("solomon")
                    .StyleFormats(
                        // Headings
                        new
                        {
                            title = "Headings", items = new[]
                            {
                                new { title = "Heading 1", block = "h1" },
                                new { title = "Heading 2", block = "h2" },
                                new { title = "Heading 3", block = "h3" },
                                new { title = "Heading 4", block = "h4" },
                                new { title = "Heading 5", block = "h5" },
                                new { title = "Heading 6", block = "h6" }
                            }
                        })
                    .BlockFormats(
                        "Paragraph=p;Header 1=h1;Header 2=h2;Header 3=h3;Header 4=h4;Header 5=h5;Header 6=h6;");
            });
        }

Pretty easy really and we only need to remember that each sub-item in an item we create, "Headings" in this case, needs to have the same format as the other sub-items in the set.

Now we need to add the 'Inline' options:

                    .StyleFormats(
                        // Headings
                        new
                        {
                            title = "Headings", items = new[]
                            {
                                new { title = "Heading 1", block = "h1" },
                                new { title = "Heading 2", block = "h2" },
                                new { title = "Heading 3", block = "h3" },
                                new { title = "Heading 4", block = "h4" },
                                new { title = "Heading 5", block = "h5" },
                                new { title = "Heading 6", block = "h6" }
                            }
                        },
                        // Inline (formats selected text)
                        new
                        {
                            title = "Inline", items = new[]
                            {
                                new { title = "Bold", inline = "span", classes = "boldText" },
                                new { title = "Italic", inline = "span", classes = "italicText" },
                                new { title = "Underline", inline = "span", classes = "underlineText" },
                                new { title = "Strikethough", inline = "span", classes = "strikethrough" },
                                new { title = "Superscript", inline = "span", classes = "superscript" },
                                new { title = "Subscript", inline = "span", classes = "subscript" },
                                new { title = "Code", inline = "span", classes = "code" }
                            }
                        })

The trick with these format options lies in using the 'inline = "span"' and 'classes = "[CSS CLASSNAME]"' attributes.  This ensures that only the selected text is formatted as required, not the entire block of text the cursor is on.  By defining a CSS classname (more on this later) we can have more than one style element, e.g. the "superscript' CSS class contains 'vertical-align: super; font-size: smaller' in order to mimic the HTML <sup> tag.

Now let's add in our 'Blocks' options:

                    .StyleFormats(
                        // Headings
                        new
                        {
                            title = "Headings", items = new[]
                            {
                                new { title = "Heading 1", block = "h1" },
                                new { title = "Heading 2", block = "h2" },
                                new { title = "Heading 3", block = "h3" },
                                new { title = "Heading 4", block = "h4" },
                                new { title = "Heading 5", block = "h5" },
                                new { title = "Heading 6", block = "h6" }
                            }
                        },
                        // Inline (formats selected text)
                        new
                        {
                            title = "Inline", items = new[]
                            {
                                new { title = "Bold", inline = "span", classes = "boldText" },
                                new { title = "Italic", inline = "span", classes = "italicText" },
                                new { title = "Underline", inline = "span", classes = "underlineText" },
                                new { title = "Strikethough", inline = "span", classes = "strikethrough" },
                                new { title = "Superscript", inline = "span", classes = "superscript" },
                                new { title = "Subscript", inline = "span", classes = "subscript" },
                                new { title = "Code", inline = "span", classes = "code" }
                            }
                        },
                        // Blocks (applies HTML tags)
                        new
                        {
                            title = "Blocks", items = new[]
                            {
                                new { title = "Paragraph", block = "p" },
                                new { title = "Blockquote", block = "blockquote" },
                                new { title = "Div", block = "div" },
                                new { title = "Pre", block = "pre" }
                            }
                        })

The difference here is that we're applying HTML tags around the block of text in scope, for <p>, <blockquote>, <div> and <pre> respectively.

Now let's add in our 'Alignments' section:

                    .StyleFormats(
                        // Headings
                        new
                        {
                            title = "Headings", items = new[]
                            {
                                new { title = "Heading 1", block = "h1" },
                                new { title = "Heading 2", block = "h2" },
                                new { title = "Heading 3", block = "h3" },
                                new { title = "Heading 4", block = "h4" },
                                new { title = "Heading 5", block = "h5" },
                                new { title = "Heading 6", block = "h6" }
                            }
                        },
                        // Inline (formats selected text)
                        new
                        {
                            title = "Inline", items = new[]
                            {
                                new { title = "Bold", inline = "span", classes = "boldText" },
                                new { title = "Italic", inline = "span", classes = "italicText" },
                                new { title = "Underline", inline = "span", classes = "underlineText" },
                                new { title = "Strikethough", inline = "span", classes = "strikethrough" },
                                new { title = "Superscript", inline = "span", classes = "superscript" },
                                new { title = "Subscript", inline = "span", classes = "subscript" },
                                new { title = "Code", inline = "span", classes = "code" }
                            }
                        },
                        // Blocks (applies HTML tags)
                        new
                        {
                            title = "Blocks", items = new[]
                            {
                                new { title = "Paragraph", block = "p" },
                                new { title = "Blockquote", block = "blockquote" },
                                new { title = "Div", block = "div" },
                                new { title = "Pre", block = "pre" }
                            }
                        },
                        // Text alignment (applies to all block formats)
                        new
                        {
                            title = "Alignment", items = new[]
                            {
                                new { title = "Left", selector = "p,h1,h2,h3,h4,h5,h6", classes = "leftAlign" },
                                new { title = "Center", selector = "p,h1,h2,h3,h4,h5,h6", classes = "centerAlign" },
                                new { title = "Right", selector = "p,h1,h2,h3,h4,h5,h6", classes = "rightAlign" },
                                new { title = "Justify", selector = "p,h1,h2,h3,h4,h5,h6", classes = "justifyAlign" }
                            }
                        })

Note that we've ensured that the options are applied to all block formats.

Now we can add in our new custom item, in this case a specific font:

                    .StyleFormats(
                        // Headings
                        new
                        {
                            title = "Headings", items = new[]
                            {
                                new { title = "Heading 1", block = "h1" },
                                new { title = "Heading 2", block = "h2" },
                                new { title = "Heading 3", block = "h3" },
                                new { title = "Heading 4", block = "h4" },
                                new { title = "Heading 5", block = "h5" },
                                new { title = "Heading 6", block = "h6" }
                            }
                        },
                        // Inline (formats selected text)
                        new
                        {
                            title = "Inline", items = new[]
                            {
                                new { title = "Bold", inline = "span", classes = "boldText" },
                                new { title = "Italic", inline = "span", classes = "italicText" },
                                new { title = "Underline", inline = "span", classes = "underlineText" },
                                new { title = "Strikethough", inline = "span", classes = "strikethrough" },
                                new { title = "Superscript", inline = "span", classes = "superscript" },
                                new { title = "Subscript", inline = "span", classes = "subscript" },
                                new { title = "Code", inline = "span", classes = "code" }
                            }
                        },
                        // Blocks (applies HTML tags)
                        new
                        {
                            title = "Blocks", items = new[]
                            {
                                new { title = "Paragraph", block = "p" },
                                new { title = "Blockquote", block = "blockquote" },
                                new { title = "Div", block = "div" },
                                new { title = "Pre", block = "pre" }
                            }
                        },
                        // Text alignment (applies to all block formats)
                        new
                        {
                            title = "Alignment", items = new[]
                            {
                                new { title = "Left", selector = "p,h1,h2,h3,h4,h5,h6", classes = "leftAlign" },
                                new { title = "Center", selector = "p,h1,h2,h3,h4,h5,h6", classes = "centerAlign" },
                                new { title = "Right", selector = "p,h1,h2,h3,h4,h5,h6", classes = "rightAlign" },
                                new { title = "Justify", selector = "p,h1,h2,h3,h4,h5,h6", classes = "justifyAlign" }
                            }
                        },
                        // BornReady font (applies to all block formats)
                        new
                        {
                            title = "BornReady font", selector = "p,h1,h2,h3,h4,h5,h6", classes = "bornready"
                        })

And that's it for ensuring that our default options are still available even thouigh we've added in a new style format (for the specific font).

Finally, a quick look at the CSS where we've defined our classes:

@font-face {
    font-family: BornReady;
    font-style: normal;
    font-weight: 400;
    font-stretch: normal;
    src: url(/assets/main/fonts/bornreadyupright/normal_normal_normal.woff2) format("woff2"), 
         url(/assets/main/fonts/bornreadyupright/normal_normal_normal.woff) format("woff")
}

.bornready {
    font-family: BornReady,sans-serif,Arial,Verdana,"Trebuchet MS";
}
.solomon {
    font-family: Solomon,sans-serif,Arial,Verdana,"Trebuchet MS";
}
.boldText {
    font-weight: bold;
}
.italicText {
    font-style: italic;
}
.underlineText {
    text-decoration: underline
}
.strikethrough {
    text-decoration: line-through;
}
.superscript {
    vertical-align: super;
    font-size: smaller;
}
.subscript {
    vertical-align: sub;
    font-size: smaller;
}
.code {
    font-family: monospace;
}
.leftAlign {
    text-align: left;
}
.centerAlign {
    text-align: center;
}
.rightAlign {
    text-align: right;
}
.justifyAlign {
    text-align: justify;
}

Summary

Yes, it would be so much easier if we had the facility to copy our default style format options, then append our new one to them.  Unfortunately we don't have that option so I hope what I have written will be useful to you all, when presented with the same scenario.

Comments and thoughts always welcomed.

Jun 09, 2021

Comments

Zach Graceffa
Zach Graceffa Feb 2, 2023 07:58 PM

Great post! I ran into this issue and this post really helped me out. The only thing I had to update were the names of the toolbar items. In TinyMCE 5+ the "styleselect" toolbar item has been renamed "styles" and the "formatselect" toolbar item has been renamed "blocks" as noted here: https://www.tiny.cloud/docs/tinymce/6/migration-from-5x/#things-we-renamed

Twan Baten
Twan Baten Jul 1, 2023 07:26 AM

For future reference, it is (now) possible to use append style formats using:

.AddSetting("style_formats_merge", true);

Please login to comment.
Latest blogs
Copying property values part 2

After publishing my last article about copying property values to other language versions, I received constructive feedback on how could I change t...

Grzegorz Wiecheć | Jun 18, 2024 | Syndicated blog

Enhancing online shopping through Optimizely's personalized product recommendations

In this blog, I have summarized my experience of using and learning product recommendation feature of Optimizely Personalization Artificial...

Hetaxi | Jun 18, 2024

New Series: Building a .NET Core headless site on Optimizely Graph and SaaS CMS

Welcome to this new multi-post series where you can follow along as I indulge in yet another crazy experiment: Can we make our beloved Alloy site r...

Allan Thraen | Jun 14, 2024 | Syndicated blog

Inspect In Index is finally back

EPiCode.InspectInIndex was released 9 years ago . The Search and Navigation addon is now finally upgraded to support Optimizely CMS 12....

Haakon Peder Haugsten | Jun 14, 2024