From 0a763348fb34abca2446122bea522e800eb5f08a Mon Sep 17 00:00:00 2001 From: Florin Raducan Date: Wed, 5 Jun 2024 17:27:43 +0300 Subject: [PATCH 1/2] Add metaTag config and fixes --- blocks/footer/footer.js | 7 ++-- blocks/header/header.js | 7 ++-- blocks/theming/theming.js | 42 +++++++------------ scripts/component-base.js | 33 +++++++++------ scripts/component-loader.js | 3 +- scripts/init.js | 11 ++--- scripts/libs.js | 82 +++++++++++++++++++++++++++++++++---- 7 files changed, 124 insertions(+), 61 deletions(-) diff --git a/blocks/footer/footer.js b/blocks/footer/footer.js index 452e5037..1a873156 100644 --- a/blocks/footer/footer.js +++ b/blocks/footer/footer.js @@ -1,7 +1,8 @@ import ComponentBase from '../../scripts/component-base.js'; -import { getMeta } from '../../scripts/libs.js'; +import { getMeta, metaTags } from '../../scripts/libs.js'; -const metaFooter = getMeta('footer'); +const { metaName, fallbackContent } = metaTags.footer; +const metaFooter = getMeta(metaName); const metaFragment = !!metaFooter && `${metaFooter}.plain.html`; export default class Footer extends ComponentBase { static loaderConfig = { @@ -11,7 +12,7 @@ export default class Footer extends ComponentBase { }, }; - fragmentPath = metaFragment || 'footer.plain.html'; + fragmentPath = metaFragment || fallbackContent; extendConfig() { return [ diff --git a/blocks/header/header.js b/blocks/header/header.js index 0a9209e3..4727ff7f 100644 --- a/blocks/header/header.js +++ b/blocks/header/header.js @@ -1,7 +1,8 @@ import ComponentBase from '../../scripts/component-base.js'; -import { eagerImage, getMeta } from '../../scripts/libs.js'; +import { eagerImage, getMeta, metaTags } from '../../scripts/libs.js'; -const metaHeader = getMeta('header'); +const { metaName, fallbackContent } = metaTags.header; +const metaHeader = getMeta(metaName); const metaFragment = !!metaHeader && `${metaHeader}.plain.html`; export default class Header extends ComponentBase { static loaderConfig = { @@ -11,7 +12,7 @@ export default class Header extends ComponentBase { }, }; - fragmentPath = metaFragment || 'header.plain.html'; + fragmentPath = metaFragment || fallbackContent; dependencies = ['navigation', 'image']; diff --git a/blocks/theming/theming.js b/blocks/theming/theming.js index 607c005b..c01e2155 100644 --- a/blocks/theming/theming.js +++ b/blocks/theming/theming.js @@ -1,28 +1,21 @@ import ComponentBase from '../../scripts/component-base.js'; -import { globalConfig, getMeta } from '../../scripts/libs.js'; -// minify alias -const metaTheming = getMeta('theming'); +import { globalConfig, metaTags, getMeta } from '../../scripts/libs.js'; + +const { theming, theme } = metaTags; +const metaTheming = getMeta(theming.metaName); const metaFragment = metaTheming && `${metaTheming}.json`; const k = Object.keys; export default class Theming extends ComponentBase { - nestedComponentsConfig = {}; setDefaults() { super.setDefaults(); this.scapeDiv = document.createElement('div'); // keep as it is - this.fragmentPath = metaFragment || 'theming.json'; + this.fragmentPath = metaFragment || theming.fallbackContent; this.skip = ['tags']; - this.toTags = [ - 'font-size', - 'font-weight', - 'font-family', - 'line-height', - 'font-style', - 'font-margin-block', - ]; + this.toTags = ['font-size', 'font-weight', 'font-family', 'line-height', 'font-style', 'font-margin-block']; this.transform = { 'font-margin-block': 'margin-block' }; this.tags = ''; this.fontFace = ''; @@ -58,10 +51,7 @@ export default class Theming extends ComponentBase { return k(values).map((value) => { const val = values[value]; return `${tag} {${k(val) - .map( - (v) => - `${this.getKey(v)}: var(--scope-${this.getKey(v)}, ${val[v]});`, - ) + .map((v) => `${this.getKey(v)}: var(--scope-${this.getKey(v)}, ${val[v]});`) .join('')}}`; }); } @@ -82,12 +72,10 @@ export default class Theming extends ComponentBase { if (key === 'font-face') { this.fontFace += this.fontFaceTemplate(value); } else { - variable = `\n--raqn-${this.getKey(key)}-${row}: ${this.escapeHtml( - value, - ).trim()};`; - this.atomic += `body .${this.getKey(key)}-${row} {--scope-${this.getKey( + variable = `\n--raqn-${this.getKey(key)}-${row}: ${this.escapeHtml(value).trim()};`; + this.atomic += `body .${this.getKey(key)}-${row} {--scope-${this.getKey(key)}: var(--raqn-${this.getKey( key, - )}: var(--raqn-${this.getKey(key)}-${row});}\n`; + )}-${row});}\n`; } } return variable; @@ -120,11 +108,9 @@ export default class Theming extends ComponentBase { // full scoped theme classes this.themes = this.themesKeys .map( - (theme) => `.theme-${theme} {${k(t) + (themeItem) => `.theme-${themeItem} {${k(t) .filter((key) => ![...this.skip, ...this.toTags].includes(key)) - .map((key) => - t[key][theme] ? `--scope-${key}: var(--raqn-${key}-${theme});` : '', - ) + .map((key) => (t[key][themeItem] ? `--scope-${key}: var(--raqn-${key}-${themeItem});` : '')) .filter((v) => v !== '') .join('')} }`, @@ -147,8 +133,8 @@ export default class Theming extends ComponentBase { style.classList.add(cssSegment); document.head.appendChild(style); }); - const themeMeta = getMeta('theme'); - document.body.classList.add(themeMeta || 'theme-default'); + const themeMeta = getMeta(theme.metaName); + document.body.classList.add(themeMeta || theme.fallbackContent); } async processFragment(response) { diff --git a/scripts/component-base.js b/scripts/component-base.js index fe575a1f..929f0590 100644 --- a/scripts/component-base.js +++ b/scripts/component-base.js @@ -41,6 +41,8 @@ export default class ComponentBase extends HTMLElement { this.fragmentPath = null; this.dependencies = []; this.attributesValues = {}; + this.initOptions = {}; + this.externalOptions = {}; this.childComponents = { // using the nested feature nestedComponents: [], @@ -55,6 +57,7 @@ export default class ComponentBase extends HTMLElement { // use the this.extendConfig() method to extend the default config this.config = { + listenBreakpoints: false, hideOnInitError: true, hideOnChildrenError: false, addToTargetMethod: 'replaceWith', @@ -72,13 +75,6 @@ export default class ComponentBase extends HTMLElement { button: { componentName: 'button', }, - columns: { - componentName: 'columns', - active: false, - loaderConfig: { - targetsAsContainers: false, - }, - }, }; } @@ -134,6 +130,7 @@ export default class ComponentBase extends HTMLElement { async connectComponent() { const { uuid } = this; + if (!this.initOptions.target) return this; this.setAttribute('isloading', ''); const initialized = new Promise((resolve, reject) => { const initListener = async (event) => { @@ -147,7 +144,7 @@ export default class ComponentBase extends HTMLElement { }; this.addEventListener(`initialized:${uuid}`, initListener); }); - const { targetsAsContainers } = deepMerge({}, this.Handler.loaderConfig, this.loaderConfig); + const { targetsAsContainers } = this.initOptions.loaderConfig; const conf = this.config; const addToTargetMethod = targetsAsContainers ? conf.targetsAsContainers.addToTargetMethod : conf.addToTargetMethod; this.initOptions.target[addToTargetMethod](this); @@ -182,6 +179,7 @@ export default class ComponentBase extends HTMLElement { } mergeConfigs() { + this.initOptions.loaderConfig = deepMerge({}, this.Handler.loaderConfig, this.initOptions.loaderConfig); this.props = deepMerge({}, this.initOptions.props, this.externalOptions.props); this.config = deepMerge({}, this.config, this.initOptions.componentConfig, this.externalOptions.config); @@ -204,9 +202,9 @@ export default class ComponentBase extends HTMLElement { this[prop] = value; }); // Set attributes based on attributesValues - Object.entries(this.attributesValues).forEach(([attr, attrValues]) => { + this.sortedAttributes.forEach(([attr, attrValues]) => { const isClass = attr === 'class'; - const val = (attrValues[this.breakpoints.active.name] ?? attrValues.all); + const val = attrValues[this.breakpoints.active.name] ?? attrValues.all; if (isClass) { const classes = (attrValues.all ? `${attrValues.all} ` : '') + (attrValues[this.breakpoints.active.name] ?? ''); const classesArr = classes.split(' ').flatMap((cls) => { @@ -221,6 +219,15 @@ export default class ComponentBase extends HTMLElement { }); } + get sortedAttributes() { + const knownAttr = this.Handler.observedAttributes; + // Sometimes the order in which the attributes are set matters. + // Control the order by using the order of the observedAttributes. + return Object.entries(this.attributesValues).sort( + (a, b) => knownAttr.indexOf(`data-${a}`) - knownAttr.indexOf(`data-${b}`), + ); + } + addDefaultsToNestedConfig() { Object.keys(this.nestedComponentsConfig).forEach((key) => { const defaults = { @@ -249,7 +256,7 @@ export default class ComponentBase extends HTMLElement { } setBreakpointAttributesValues(e) { - Object.entries(this.attributesValues).forEach(([attribute, breakpointsValues]) => { + this.sortedAttributes.forEach(([attribute, breakpointsValues]) => { const isAttribute = attribute !== 'class'; if (isAttribute) { const newValue = breakpointsValues[e.raqnBreakpoint.name] ?? breakpointsValues.all; @@ -300,7 +307,9 @@ export default class ComponentBase extends HTMLElement { } addListeners() { - listenBreakpointChange(this.onBreakpointChange); + if (this.externalOptions.hasBreakpointsValues || this.config.listenBreakpoints) { + listenBreakpointChange(this.onBreakpointChange); + } } async initChildComponents() { diff --git a/scripts/component-loader.js b/scripts/component-loader.js index 98676370..eaeaa4a7 100644 --- a/scripts/component-loader.js +++ b/scripts/component-loader.js @@ -130,7 +130,8 @@ export default class ComponentLoader { throwInitError: true, target, container, - configByClasses: !container ? mergeUniqueArrays(this.configByClasses, target.classList) : this.configByClasses, + configByClasses: + !container && target ? mergeUniqueArrays(this.configByClasses, target.classList) : this.configByClasses, props: this.props, componentConfig: this.componentConfig, externalConfigName: this.externalConfigName, diff --git a/scripts/init.js b/scripts/init.js index a8ec1420..dde41ad7 100644 --- a/scripts/init.js +++ b/scripts/init.js @@ -1,5 +1,5 @@ import ComponentLoader from './component-loader.js'; -import { globalConfig, eagerImage, getMeta, getMetaGroup, mergeUniqueArrays } from './libs.js'; +import { globalConfig, metaTags, eagerImage, getMeta, getMetaGroup, mergeUniqueArrays } from './libs.js'; const component = { async init(settings) { @@ -124,8 +124,9 @@ const onLoadComponents = { }, setLcp() { - const lcpMeta = getMeta('lcp', { getArray: true }); - const defaultLcp = ['theming', 'header', 'breadcrumbs']; + const { metaName, fallbackContent } = metaTags.lcp; + const lcpMeta = getMeta(metaName, { getArray: true }); + const defaultLcp = fallbackContent; const lcp = lcpMeta?.length ? lcpMeta : defaultLcp; // theming must be in LCP to prevent CLS this.lcp = mergeUniqueArrays(lcp, ['theming']).map((componentName) => ({ @@ -134,7 +135,7 @@ const onLoadComponents = { }, setStructure() { - const structureComponents = getMetaGroup('structure'); + const structureComponents = getMetaGroup(metaTags.structure.metaNamePrefix); this.structureComponents = structureComponents.flatMap(({ name, content }) => { if (content !== true) return []; return { @@ -180,7 +181,7 @@ const globalInit = { }, initEagerImages() { - const eagerImages = getMeta('eager-images'); + const eagerImages = getMeta(metaTags.eagerImage.metaName); if (eagerImages) { const length = parseInt(eagerImages, 10); eagerImage(document.body, length); diff --git a/scripts/libs.js b/scripts/libs.js index 2265173f..2bcaf113 100644 --- a/scripts/libs.js +++ b/scripts/libs.js @@ -15,6 +15,51 @@ export const globalConfig = { }, }; +export const metaTags = { + componentsConfig: { + metaName: 'components-config', + fallbackContent: 'components-config.json', + // contentType: 'path without extension', + }, + header: { + metaName: 'header', + fallbackContent: 'header.plain.html', + // contentType: 'path without extension', + }, + footer: { + metaName: 'footer', + fallbackContent: 'footer.plain.html', + // contentType: 'path without extension', + }, + structure: { + metaNamePrefix: 'structure', + // contentType: 'boolean string', + }, + lcp: { + metaName: 'lcp', + fallbackContent: ['theming', 'header', 'breadcrumbs'], + // contentType: 'string of comma separated component names', + }, + eagerImage: { + metaName: 'eager-images', + // contentType: 'number string', + }, + basePath: { + metaName: 'basePath', + // contentType: 'path without extension', + }, + theming: { + metaName: 'theming', + fallbackContent: 'theming.json', + // contentType: 'path without extension', + }, + theme: { + metaName: 'theme', + fallbackContent: 'theme-default', + // contentType: 'string theme name', + }, +}; + export const camelCaseAttr = (val) => val.replace(/-([a-z])/g, (k) => k[1].toUpperCase()); export const capitalizeCaseAttr = (val) => camelCaseAttr(val.replace(/^[a-z]/g, (k) => k.toUpperCase())); @@ -193,7 +238,14 @@ export function deepMerge(origin, ...toMerge) { export const externalConfig = { defaultConfig(rawConfig = []) { - return { attributesValues: {}, nestedComponentsConfig: {}, props: {}, config: {}, rawConfig }; + return { + attributesValues: {}, + nestedComponentsConfig: {}, + props: {}, + config: {}, + rawConfig, + hasBreakpointsValues: false, + }; }, async getConfig(componentName, configName, knownAttributes) { @@ -218,9 +270,9 @@ export const externalConfig = { async loadConfig() { window.raqnComponentsConfig ??= (async () => { - const metaConfigPath = getMeta('component-config'); - const defaultConfig = 'components-config.json'; - const configPath = (!!metaConfigPath && `${metaConfigPath}.json`) || defaultConfig; + const { metaName, fallbackContent } = metaTags.componentsConfig; + const metaConfigPath = getMeta(metaName); + const configPath = (!!metaConfigPath && `${metaConfigPath}.json`) || fallbackContent; let result = null; try { const response = await fetch(`${configPath}`); @@ -246,6 +298,8 @@ export const externalConfig = { Object.entries(breakpointConfig).forEach(([key, val]) => { if (val.trim() === '') return; + if (![...Object.keys(globalConfig.breakpoints), 'all'].includes(breakpoint)) return; + if (!isMainConfig) acc.hasBreakpointsValues = true; const parsedVal = stringToJsVal(val, { trim: true }); @@ -271,8 +325,9 @@ export const externalConfig = { parseAttrValues(parsedVal, acc, key, breakpoint) { const keyProp = key.replace(/^data-/, ''); - acc.attributesValues[keyProp] ??= {}; - acc.attributesValues[keyProp][breakpoint] = parsedVal; + const camelAttr = camelCaseAttr(keyProp); + acc.attributesValues[camelAttr] ??= {}; + acc.attributesValues[camelAttr][breakpoint] = parsedVal; }, parseConfig(parsedVal, acc, key, configPrefix) { @@ -303,10 +358,15 @@ export const externalConfig = { export const configFromClasses = { getConfig(componentName, configByClasses, knownAttributes) { const nestedComponentsConfig = this.nestedConfigFromClasses(configByClasses); - const attributesValues = this.attributeValuesFromClasses(componentName, configByClasses, knownAttributes); + const { attributesValues, hasBreakpointsValues } = this.attributeValuesFromClasses( + componentName, + configByClasses, + knownAttributes, + ); return { attributesValues, nestedComponentsConfig, + hasBreakpointsValues, }; }, @@ -344,6 +404,7 @@ export const configFromClasses = { }, attributeValuesFromClasses(componentName, configByClasses, knownAttributes) { + let hasBreakpointsValues = false; const nestedComponentsNames = this.nestedComponentsNames(configByClasses); const onlyKnownAttributes = knownAttributes.filter((a) => a !== 'class'); const attributesValues = configByClasses @@ -355,7 +416,10 @@ export const configFromClasses = { const classBreakpoint = this.classBreakpoint(c); const isBreakpoint = this.isBreakpoint(classBreakpoint); - if (isBreakpoint) value = value.slice(classBreakpoint.length + 1); + if (isBreakpoint) { + hasBreakpointsValues = true; + value = value.slice(classBreakpoint.length + 1); + } const excludeNested = nestedComponentsNames.find((prefix) => value.startsWith(prefix)); if (excludeNested) return acc; @@ -387,7 +451,7 @@ export const configFromClasses = { return acc; }, {}); - return attributesValues; + return { attributesValues, hasBreakpointsValues }; }, classBreakpoint(c) { return Object.keys(globalConfig.breakpoints).find((b) => c.startsWith(`${b}-`)) || 'all'; From 2e3a57c89926f7d0994006e462fae064c205d0e4 Mon Sep 17 00:00:00 2001 From: Florin Raducan Date: Thu, 6 Jun 2024 10:01:47 +0300 Subject: [PATCH 2/2] Remove extension from meta fallbackContent --- blocks/footer/footer.js | 2 +- blocks/header/header.js | 2 +- scripts/libs.js | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/blocks/footer/footer.js b/blocks/footer/footer.js index 1a873156..a5cbcf9f 100644 --- a/blocks/footer/footer.js +++ b/blocks/footer/footer.js @@ -12,7 +12,7 @@ export default class Footer extends ComponentBase { }, }; - fragmentPath = metaFragment || fallbackContent; + fragmentPath = metaFragment || `${fallbackContent}.plain.html`; extendConfig() { return [ diff --git a/blocks/header/header.js b/blocks/header/header.js index 4727ff7f..3f15faf1 100644 --- a/blocks/header/header.js +++ b/blocks/header/header.js @@ -12,7 +12,7 @@ export default class Header extends ComponentBase { }, }; - fragmentPath = metaFragment || fallbackContent; + fragmentPath = metaFragment || `${fallbackContent}.plain.html`; dependencies = ['navigation', 'image']; diff --git a/scripts/libs.js b/scripts/libs.js index 2bcaf113..e74c74c2 100644 --- a/scripts/libs.js +++ b/scripts/libs.js @@ -18,17 +18,17 @@ export const globalConfig = { export const metaTags = { componentsConfig: { metaName: 'components-config', - fallbackContent: 'components-config.json', + fallbackContent: 'components-config', // contentType: 'path without extension', }, header: { metaName: 'header', - fallbackContent: 'header.plain.html', + fallbackContent: 'header', // contentType: 'path without extension', }, footer: { metaName: 'footer', - fallbackContent: 'footer.plain.html', + fallbackContent: 'footer', // contentType: 'path without extension', }, structure: { @@ -272,7 +272,7 @@ export const externalConfig = { window.raqnComponentsConfig ??= (async () => { const { metaName, fallbackContent } = metaTags.componentsConfig; const metaConfigPath = getMeta(metaName); - const configPath = (!!metaConfigPath && `${metaConfigPath}.json`) || fallbackContent; + const configPath = (!!metaConfigPath && `${metaConfigPath}.json`) || `${fallbackContent}.json`; let result = null; try { const response = await fetch(`${configPath}`);