This Statamic addon allows you to modify the tags rendered by the Bard fieldtype, giving you full control over the final HTML.

Overview

Statamic Packagist version License

Bard Mutator

This Statamic addon allows you to modify the tags rendered by the Bard fieldtype, giving you full control over the final HTML. You can add, remove and modify attributes, wrap tags and content, or rename and replace tags entirely.

Installation

You can search for this addon in the Tools > Addons section of the Statamic control panel and click install, or run the following command from your project root:

composer require jacksleight/statamic-bard-mutator

Examples

First of all let's see what this can do with some examples!

Add a class to all bullet lists:

use JackSleight\StatamicBardMutator\Facades\Mutator;

Mutator::tag('bullet_list', function ($tag) {
    $tag[0]['attrs']['class'] = 'list';
    return $tag;
});

Add noopener to all external links:

use JackSleight\StatamicBardMutator\Facades\Mutator;
use Statamic\Facades\URL;

Mutator::tag('link', function ($tag) {
    if (URL::isExternal($tag[0]['attrs']['href'])) {
        $tag[0]['attrs']['rel'] = 'noopener';
    }
    return $tag;
});

Add an auto-generated ID to all level 2 headings:

use JackSleight\StatamicBardMutator\Facades\Mutator;

Mutator::tag('heading', function ($tag, $data) {
    if ($data->attrs->level === 2) {
        $tag[0]['attrs']['id'] = str_slug(collect($data->content)->implode('text', ''));
    }
    return $tag;
});

Add a wrapper div around all tables:

use JackSleight\StatamicBardMutator\Facades\Mutator;

Mutator::tag('table', function ($tag) {
    array_unshift($tag, [
        'tag' => 'div',
        'attrs' => ['class' => 'table-wrapper'],
    ]);
    return $tag;
});

Add a wrapper span around all list item content:

use JackSleight\StatamicBardMutator\Facades\Mutator;

Mutator::tag('list_item', function ($tag) {
    array_push($tag, 'span');
    return $tag;
});

Convert all images to a custom element:

use JackSleight\StatamicBardMutator\Facades\Mutator;

Mutator::tag('image', function ($tag) {
    $tag[0]['tag'] = 'fancy-image';
    return $tag;
});

Creating Mutators

You should add mutators in your service provider's boot() method. They will receive two arguments:

  • tag (array): The standard tag value
  • data (object): The raw node or mark data

You should return a tag value.

You can add multiple mutators for the same node or mark, they'll be executed in the order they were added.

How it Works

Rendering Process

This is roughly how ProseMirror (the magic behind Bard's content format and rendering) handles the rendering process:

  1. The raw content is stored as a ProseMirror document in your entries, these documents consist of nodes and marks
  2. ProseMirror converts the raw node and mark data to their standard tag values
  3. Bard Mutator jumps in here, allowing you to modify the tag values
  4. ProseMirror renders the tag values to an HTML string

Tag Values

In its fully expanded format a tag value looks like this:

[
    [
        'tag' => 'a',
        'attrs' => [
            'href' => 'http://...'
        ]
    ]
]

A fully expanded tag value is an array of items where each item is an associative array with two keys:

  • tag (string): The name of the tag
  • attrs (array): A name/value array of attributes

An array with multiple items will render nested tags, with the content placed within the innermost (last) tag.

If there are no attributes the item can just be the tag name:

[
    ['p']
]

And if there's only one item with no attributes the entire tag value can just be the tag name:

'p'

The built-in node and mark classes return a mixture of these formats, but for ease and consistency Bard Mutator normalizes them to the fully expanded format before passing to your mutators. You can return any format you like.

Important: A tag value that's a single associative array is not supported:

// Don't do this, it won't work! Wrap it in another array.
return [
    'tag' => 'a',
    'attrs' => [
        'href' => 'http://...'
    ]
];

Avaliable Nodes & Marks

Bard Mutator will replace all of the built-in node and mark classes with extended versions that support mutation, except for Statamic's Set node. These are:

  • Nodes
    • blockquote
    • bullet_list
    • code_block
    • hard_break
    • heading
    • horizontal_rule
    • image
    • list_item
    • ordered_list
    • paragraph
    • table
    • table_cell
    • table_header
    • table_row
  • Marks
    • bold
    • code
    • italic
    • link
    • subscript
    • underline
    • strike
    • superscript

🚨 Other Addons & Potential Incompatibilites

Because of the way ProseMirror works Bard Mutator has to replace all of the built-in node and mark classes with its own. And it can only do that reliably if there are no other addons (or user code) trying to do the same thing.

My other Bard addon (Bard Paragraph Style) is fully compatible with this. If Bard Mutator is installed it uses that under the hood instead of it's own node class.

However, if you have other addons (or user code) that replace any of the built-in node or mark classes Bard Mutator probably won't work properly. Unfortunately I don’t think there’s any way I can fix that.

This does not affect custom nodes and marks.

You might also like...
Spin up a working Statamic instance quickly & easily with Docker

Spin Up Statamic Allows you to create your own self-contained Statamic project complete site config, Antlers/Blade/Twig template files, assets, and de

A custom twig extension to truncate text while preserving HTML tags.

TwigTruncateExtension A custom twig extension to truncate text while preserving HTML tags. Installation Add the library to your app's composer.json:

Hi everyone! This is our repository for our final project in college. We're sorry if so many bug or error. Thank You

About The Project Hi everyone! This is our repository for our final project in college. We're sorry if so many bug or error. Thank You About Laravel L

PHP Japanese string helper functions for converting Japanese strings from full-width to half-width and reverse. Laravel Rule for validation Japanese string only full-width or only half-width.

Japanese String Helpers PHP Japanese string helper functions for converting Japanese strings from full-width to half-width and reverse. Laravel Rule f

Music website developed as a final group project for our Webpage Development class.

double-drummer Music listening website developed as final group project for our Webpage Development course. All parts that disclosed any information r

Repositorio del TP final de la materia de Introduccion a la programacion

tateti Repositorio del TP final de la materia de Introduccion a la programacion Materia TECNICATURA UNIVERSITARIA EN DESARROLLO WEB INTRODUCCION A LA

Meu projeto de final do curso, utilizando laravel.

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

A&D challenge for AIS3 EOF CTF 2022 Final.

A&D challenge for AIS3 EOF CTF 2022 Final.

Comments
  • List_item not working

    List_item not working

    Mutator::data('list_item', function ($data) {
        ...
    });
    

    It seems like the list_item data mutator is not getting triggered. All mutator:tags work like aspected.

    opened by pitcher-maarten 6
  • Mutator runs twice

    Mutator runs twice

    Hi,

    I'm using the mutator to add an id to some headings, which works fine just like in the provided example. However, a heading can realistically exist twice in a document, so would then need to have id="heading" and id="heading-2. I have kinda solved this, but for some reason the mutator runs twice for each heading so I get id="heading and id="heading-3".

    I have solved it like this

    Mutator::tag('heading', function ($tag, $data, $meta) {
                if ($data->attrs->level === 2 || $data->attrs->level === 3) {
                    $slug = str_slug(collect($data->content)->implode('text', ''));
                    $this->slugs->push($slug);
                    $occurences = $this->slugs->filter(fn ($value) => $value === $slug)->count();
                    
                    if ($occurences > 1) {
                        $slug = $slug . "-" . $occurences;
                    }
                    info($slug . " - " . $occurences);
                    
                    $tag[0]['attrs']['id'] = $slug;
                }
    
                return $tag;
            });
    

    If I give it this content

    <h2>Heading</h2>
    <h2>Other</h2>
    <h2>Heading</h2>
    

    The results will be

    <h2 id="heading">Heading</h2>
    <h2 id="other">Other</h2>
    <h2 id="heading-3">Heading</h2>
    

    And the log will show

    heading - 1  
    heading-2 - 2  
    other - 1  
    other-2 - 2  
    heading-3 - 3  
    heading-4 - 4  
    

    Is there any reason for running it twice, or have I maybe missed something?

    opened by danielbrodin 5
  • bts_span doesn't have

    bts_span doesn't have "content"

    Hi Jack,

    first of all: awesome plugin you've got there! This makes it so much easier to extend bard.

    I'm using your Bard Textstyle plugin to create a bts-span with a class footnote. Then I want to wrap these elements in another span and give them a data-attribute like shown below. But $data->content is always empty. Is this a bug because of the custom bts-span element or is this because of the order of how Bard Textstyle and Bard Mutator work? Or maybe something else entirely?

    and btw: would it also be possible for you to extend the plugin to prepend/append tags to tags instead of wrapping them?

    Mutator::tag('bts_span', function ($tag, $data) {
        if ($data->attrs->class === "footnote") {
            $tag[0]['attrs']['data-footnote'] = collect($data->content)->implode('text', '');
        }
    
        array_push($tag, 'span');
    
        return $tag;
    });
    
    opened by el-schneider 2
  • Only run tag mutators once

    Only run tag mutators once

    Closes https://github.com/jacksleight/statamic-bard-mutator/issues/8

    This change prevents tag mutators running twice. Instead of mutating the tag data both times ProseMirror generates it we instead cache the first value and return that to ProseMirror the second time.

    opened by jacksleight 0
Releases(1.1.2)
This extensions makes it possible to modify the TCA of container children elements

This extensions makes it possible to modify the TCA of container children elements

Georg Ringer 6 Oct 24, 2022
Enables developers to modify Magento installations (configuration, data) based on the given environment using n98-magerun.

Enables developers to modify Magento installations (configuration, data) based on the given environment using n98-magerun.

LimeSoda Interactive Marketing GmbH 73 Apr 1, 2022
Keep control over the complexity of your methods by checking that they do not have too many arguments.

php arguments detector The ideal number of arguments for a function is zero. ~ Robert C. Martin Keep control over the complexity of your methods by ch

DeGraciaMathieu 13 Dec 26, 2022
A SilverStripe module for conveniently injecting JSON-LD metadata into the header of each rendered page in SilverStripe

A SilverStripe module for conveniently injecting JSON-LD metadata into the header of each rendered page in Silver

null 4 Apr 20, 2022
HTMX example app that demonstrates how to use HTMX to add javascript interactivity to a serverside rendered PHP app

HTMX examle app This demo app demonstrates how to use HTMX to transform a server side rendered PHP app into a more 'interactive' app with AJAX request

Alexander Morland 3 Dec 11, 2022
This AddOn allows you to search for module names when adding a block

Modulsuche und Modulvorschau für REDAXO 5 Dieses AddOn ermöglicht die Suche nach Modulnamen, wenn man einen Block hinzufügt. Dies kann sehr hilfreich

Friends Of REDAXO 15 Nov 30, 2022
A Zend_Cache backend for Redis with full support for tags (works great with Magento)

Zend_Cache backend using Redis with full support for tags This Zend_Cache backend allows you to use a Redis server as a central cache storage. Tags ar

Colin Mollenhour 389 Jan 4, 2023
Minimalist PHP frame for Core-Library, for Developing PHP application that gives you the full control of your application.

LazyPHP lightweight Pre-Made Frame for Core-library Install Run the below command in your terminal $ composer create-project ryzen/lazyphp my-first-pr

Ry-Zen 7 Aug 21, 2022
An Inertia.js adapter for Statamic.

Inertia.js adapter for Statamic Statamic server side adapter for Inertia.js to build single-page apps, without building an API. Installation You can i

Adam Campbell 52 Dec 31, 2022
A widget that displays a Bible verse every day in the Statamic dashboard

Statamic Widget: Verse of the Day What is it A widget that displays a Bible verse every day in the Statamic dashboard. How to install it Install via c

Michael 2 Jan 27, 2022