All checks were successful
Build, Test, and Publish (to Private NPM Registry) UI Components Library / publish (push) Successful in 58s
124 lines
No EOL
6.3 KiB
JavaScript
124 lines
No EOL
6.3 KiB
JavaScript
/**
|
|
* Abstract Base Class for Bridgeman Accessible Web Components
|
|
* Provides helper methods for easily initializing components, composing complex components, and managing nested custom elements.
|
|
*/
|
|
export class ComposableElement extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
}
|
|
|
|
/**
|
|
* Helper method to load and parse a JSON configuration ("Data Island") from a script tag within the Light DOM of the component.
|
|
* The script tag should have a `data-{name}-config` attribute where `{name}` is a unique identifier for the component (e.g., 'tablist' for 'ba-tablist').
|
|
*
|
|
* @param {string} name - The suffix of the component (e.g., 'tablist' for 'ba-tablist')
|
|
* @returns {Object|null} The parsed JSON configuration object, or null if not found
|
|
*/
|
|
loadDataIslandConfig(name) {
|
|
const configScript = this.querySelector(`script[data-${name}-config]`);
|
|
|
|
if (!configScript) {
|
|
console.error(`${name} configuration missing.`);
|
|
return null;
|
|
}
|
|
|
|
const config = JSON.parse(configScript.textContent);
|
|
|
|
return config;
|
|
}
|
|
|
|
/**
|
|
* Helper method to include the necessary stylesheets and scripts for a component based on its configuration.
|
|
* This method should be called within the `createTemplateInJS` of a component after loading it's "data island" configurations.
|
|
*
|
|
* @param {string} name - The suffix of the component (e.g., 'tablist' for 'ba-tablist')
|
|
* @param {Object} config - The JSON configuration object that includes the necessary information about styles and scripts to include
|
|
* @param {ShadowRoot} shadow - The shadow DOM root to append the link and script tags to
|
|
*/
|
|
includeStylesheetsAndScripts(name, config, shadow) {
|
|
if (config.componentsStyleHref) {
|
|
// Create and append the link tag for the components library CSS (including component specific styles)
|
|
const componentLibraryStyle = document.createElement('link');
|
|
componentLibraryStyle.rel = 'stylesheet';
|
|
componentLibraryStyle.type = 'text/css';
|
|
componentLibraryStyle.href = config.componentsStyleHref;
|
|
shadow.appendChild(componentLibraryStyle);
|
|
}
|
|
|
|
// Create additional link tags for any extra CSS files to include
|
|
const additionalStyles = config[`${name}ExtraStyles`] || [];
|
|
additionalStyles.forEach(style => {
|
|
const link = document.createElement('link');
|
|
link.rel = 'stylesheet';
|
|
link.type = 'text/css';
|
|
link.href = `/css/${style}.css`;
|
|
shadow.appendChild(link);
|
|
});
|
|
|
|
// Create and append script tags for any extra JavaScript files to include
|
|
const additionalScripts = config[`${name}ExtraScripts`] || [];
|
|
additionalScripts.forEach(script => {
|
|
const scriptElem = document.createElement('script');
|
|
scriptElem.type = typeof script === 'object' && typeof script.module === 'boolean' && script.module ? 'module' : typeof script === 'string' && script.endsWith('.mjs') ? 'module' : 'application/javascript';
|
|
scriptElem.src = typeof script === 'object' ? script.script.startsWith('http') ? script.script : `/js/${script.script.endsWith('.mjs') ? script.script : script.script + '.js'}` : script.startsWith('http') ? script : `/js/${script.endsWith('.mjs') ? script : script + '.js'}`;
|
|
shadow.appendChild(scriptElem);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Helper method to initialize a component by loading its configuration and including necessary stylesheets and scripts.
|
|
* This method should be called at the beginning of the `createTemplateInJS` of a component to set up everything needed for the component to function properly.
|
|
*
|
|
* @param {string} name - The suffix of the component (e.g., 'tablist' for 'ba-tablist')
|
|
* @param {ShadowRoot} shadow - The shadow DOM root to append the link and script tags to
|
|
* @param {{ includeStylesAndScripts: boolean, stylesAndScriptsNameOverride?: string, subComponents?: Array<{ name: string, configKey: string, slotNames: Array<string> }> }} options - Options for initializing the component
|
|
* @returns {Object|null} The parsed JSON configuration object, or null if not found
|
|
*/
|
|
initializeComponent(name, shadow, { includeStylesAndScripts = true, stylesAndScriptsNameOverride, subComponents = [] } = {}) {
|
|
const config = this.loadDataIslandConfig(name);
|
|
if(config == null) {
|
|
return null;
|
|
}
|
|
|
|
if (includeStylesAndScripts) {
|
|
this.includeStylesheetsAndScripts(typeof stylesAndScriptsNameOverride !== 'undefined' ? stylesAndScriptsNameOverride : name, config, shadow);
|
|
}
|
|
|
|
subComponents.forEach(subComp => {
|
|
const subCompElem = this.createSubComponent(subComp.name, config[subComp.configKey], subComp.slotNames);
|
|
shadow.appendChild(subCompElem);
|
|
});
|
|
|
|
return config;
|
|
}
|
|
|
|
/**
|
|
* Generates a sub-component, attaches its configuration, and forwards requested slots.
|
|
*
|
|
* @param {string} name - The suffix of the component (e.g., 'tablist' for 'ba-tablist')
|
|
* @param {Object} config - The JSON configuration object to pass to the sub-component
|
|
* @param {Array<string>} slotNames - An array of exact slot names to forward down
|
|
* @returns {HTMLElement} The fully constructed sub-component ready to be appended
|
|
*/
|
|
createSubComponent(name, config, slotNames = []) {
|
|
const customElem = document.createElement(`ba-${name}`);
|
|
|
|
// Attach the Configuration Data Island
|
|
const customElemConfig = document.createElement('script');
|
|
customElemConfig.type = 'application/json';
|
|
customElemConfig.setAttribute(`data-${name}-config`, '');
|
|
customElemConfig.textContent = JSON.stringify(config);
|
|
customElem.appendChild(customElemConfig);
|
|
|
|
// Forward the explicit slots
|
|
slotNames.forEach(slotName => {
|
|
const slotForwarder = document.createElement('slot');
|
|
slotForwarder.name = slotName;
|
|
slotForwarder.slot = slotName;
|
|
customElem.appendChild(slotForwarder);
|
|
});
|
|
|
|
// Return the element so the child class can append it exactly where it belongs in its specific DOM structure
|
|
return customElem;
|
|
}
|
|
} |