@onenexus/polymorph

Style Synergy modules/BEM DOM elements using JavaScript

Usage no npm install needed!

<script type="module">
  import onenexusPolymorph from 'https://cdn.skypack.dev/@onenexus/polymorph';
</script>

README

GitHub license Inline docs Build Status npm version npm downloads codecov

Style Synergy modules/BEM DOM elements using JavaScript

Overview

Polymorph is used for styling DOM elements that follow the Synergy naming convention.

Learn how to integrate with React components

View a live demo using React + Polymorph

Example Module Markup
<div class="accordion">
    <div class="accordion_panel">
        <div class="accordion_title">foo</div>
        <div class="accordion_content">bar</div>
    </div>
    <div class="accordion_panel-active">
        <div class="accordion_title">fizz</div>
        <div class="accordion_content">buzz</div>
    </div>
</div>

Learn more about Synergy modules

Style all accordion modules
document.querySelectorAll('.accordion').forEach(element => {
    polymorph(element, {
        'position': 'relative'
    });
});
Style panel components

Learn more about Synergy Components

document.querySelectorAll('.accordion').forEach(element => {
    polymorph(element, {
        panel: {
            'display': 'block'
        }
    });
});
Style panel components with active modifier
document.querySelectorAll('.accordion').forEach(element => {
    polymorph(element, {
        panel: {
            'color': 'white',

            'modifier(active)': {
                'color': 'blue'
            }
        }
    }
});
Alternatively
document.querySelectorAll('.accordion').forEach(element => {
    polymorph(element, {
        panel: panel => ({
            'color': panel.matches('.accordion_panel-active') ? 'blue' : 'white'
        })
    }
});
Using In-Built modifier Method

This ensures no class names are hard coded

Learn more about the modifier method

document.querySelectorAll('.accordion').forEach(element => {
    polymorph(element, {
        panel: panel => ({
            'color': polymorph.modifier(panel, 'active') ? 'blue' : 'white'
        })
    }
});
Using sQuery (Recommended)

Learn more about sQuery

sQuery('accordion', element => {
    polymorph(element, {
        panel: panel => ({
            'color': panel.modifier('active') ? 'blue' : 'white'
        })
    }
});
Style title components when parent panel component has active modifier
document.querySelectorAll('.accordion').forEach(element => {
    polymorph(element, {
        panel: {
            'modifier(active)': {
                title: {
                    'color': 'red'
                }
            }
        },
        title: {
            'color': 'blue'
        }
    }
});
Alternatively
document.querySelectorAll('.accordion').forEach(element => {
    polymorph(element, {
        title: title => ({
            'color': title.closest('.accordion_panel-active') ? 'red' : 'blue'
        })
    }
});
Using sQuery

Learn more about sQuery

sQuery('accordion', element => {
    polymorph(element, {
        title: title => ({
            'color': title.parent('panel').is('active') ? 'red' : 'blue'
        })
    }
});

Installation & Setup

npm install --save @onenexus/polymorph
import 'polymorph' from '@onenexus/polymorph';

polymorph(document.getElementById('someElement'), someConfigurationObject);

Using BEM? Checkout the Working With BEM page

API

Polymorph.modifier()

Determine if an HTML element has the specified modifier

polymorph.modifier(element, modifier)
Param Type Info
element HTMLElement The HTML element of interest
modifier String The modifier of interest
Example
<div class="accordion">
    <div class="accordion_panel">
        <div class="accordion_title">foo</div>
        <div class="accordion_content">bar</div>
    </div>
    <div class="accordion_panel-active">
        <div class="accordion_title">fizz</div>
        <div class="accordion_content">buzz</div>
    </div>
</div>
document.querySelectorAll('.accordion').forEach(element => {
    polymorph(element, {
        panel: panel => ({
            'color': polymorph.modifier(panel, 'active') ? 'blue' : 'white'
        })
    }
});
Result
<div class="accordion">
    <div class="accordion_panel" style="color: white;">
        ...
    </div>
    <div class="accordion_panel-active" style="color: blue;">
        ...
    </div>
</div>

Element.repaint()

Repaint the module by re-applying the style rules

element.repaint()

This method is attached directly to the DOM element after the initial polymorph call

This is useful for updating the styles after an event that modifies the DOM, such as a click event which adds an active modifier to an element. In order to repaint the element, you should call the repaint() method in the same place you handle the event.

Example
<div class="accordion" id="alpha">
    <div class="accordion_panel">
        <div class="accordion_title">foo</div>
        <div class="accordion_content">bar</div>
    </div>
    <div class="accordion_panel">
        <div class="accordion_title">fizz</div>
        <div class="accordion_content">buzz</div>
    </div>
</div>
polymorph(document.getElementById('alpha'), {
    panel: {
        'background': 'red';

        'modifier(active)': {
            'background': 'blue'
        }
    }
});

// `#alpha` element and all targeted child components
//  will now have a `repaint()` method
document.querySelectorAll('.accordion').forEach(accordion => {
    accordion.querySelectorAll('.accordion_panel').forEach(panel => {
      panel.querySelector('.accordion_title').addEventListener('click', () => {
        // do event handling...
        panel.classList.toggle('accordion_panel-active');
        
        // repaint the accordion panel
        panel.repaint();
      });
    }
});
Using sQuery
sQuery('accordion').getComponents('panel').forEach(PANEL => {
    sQuery(PANEL).getComponent('title').addEventListener('click', () => {
        // the `repaint` method is called automatically
        // when using the sQuery API
        sQuery(PANEL).toggleModifier('visible');
    });
});
Using Lucid
// By passing a styles function/object to the `styles` prop of `<Module>`,
// `repaint()` will be called on the approprate rendered DOM elements
//  in the `componentDidUpdate` lifecycle method
<Module name='myModule' styles={styles}>...</Module>

Use With sQuery

sQuery is a JavaScript library for interacting with Synergy modules

sQuery is perfect for interacting with Polymorph, and isn't included by default to keep bundle size down (as it isn't strictly required for functionality).

Once installed, you can use sQuery to style your modules and components much more easily using the provided API.

Without sQuery
document.querySelectorAll('.accordion').forEach(element => {
    polymorph(element, {
        title: title => ({
            'color': title.closest('.accordion_panel-active') ? 'red' : 'blue'
        })
    }
});
With sQuery
sQuery('accordion', element => polymorph(element, {
    title: title => ({
        'color': title.parent('panel').is('active') ? 'red' : 'blue'
    })
}));

Use With Lucid

Lucid is a set of higher-order React components for rendering Synergy modules

Lucid and Polymorph were built in tandem as part of the Synergy framework, so go hand-in-hand together. If you are using Lucid, the easiest way to use Polymorph as the styleParser function is to attach it to the window.Synergy.styleParser property:

import { Module, Component } from '@onenexus/lucid';
import Polymorph from '@onenexus/polymorph';

window.Synergy = {
    styleParser: Polymorph
}

// start using Lucid components (Module, Component)...

Learn more about using Polymorph with Lucid