diff --git a/.eslintrc.js b/.eslintrc.js
index 76f220db..deac7e89 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,6 +1,6 @@
module.exports = {
root: true,
- extends: 'airbnb-base',
+ extends: ['airbnb-base', 'prettier'],
env: {
browser: true,
},
@@ -11,11 +11,18 @@ module.exports = {
requireConfigFile: false,
},
rules: {
+ 'max-len': [2, 160, 2, { ignoreUrls: true }],
+ 'import/no-unresolved': [2, { commonjs: true }],
+ 'array-callback-return': 'off', // due to prettier
+ 'class-methods-use-this': 'off', // due to prettier
// allow reassigning param
'no-param-reassign': [2, { props: false }],
'linebreak-style': ['error', 'unix'],
- 'import/extensions': ['error', {
- js: 'always',
- }],
+ 'import/extensions': [
+ 'error',
+ {
+ js: 'always',
+ },
+ ],
},
};
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 00000000..75fac8e1
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
+#!/usr/bin/env sh
+. "$(dirname -- "$0")/_/husky.sh"
+
+npm run lint
diff --git a/.stylelintrc.json b/.stylelintrc.json
index 17c74ed1..b70de4de 100644
--- a/.stylelintrc.json
+++ b/.stylelintrc.json
@@ -1,3 +1,6 @@
{
- "extends": ["stylelint-config-standard"]
+ "extends": ["stylelint-config-standard"],
+ "rules": {
+ "no-descending-specificity": null
+ }
}
\ No newline at end of file
diff --git a/README.md b/README.md
index 80fdb9d9..903379e0 100644
--- a/README.md
+++ b/README.md
@@ -23,3 +23,7 @@ npm run lint:fix
1. Install the [AEM CLI](https://github.com/adobe/helix-cli): `npm install -g @adobe/aem-cli`
1. Start AEM EDS Proxy: `aem up` (opens your browser at `http://localhost:3000`)
1. Open the `henkel-raqn-guide` directory in your favorite IDE and start coding :)
+
+## Documentation
+
+[Documentation](docs/readme.md)
\ No newline at end of file
diff --git a/blocks/accordion/accordion.css b/blocks/accordion/accordion.css
new file mode 100644
index 00000000..9774cf74
--- /dev/null
+++ b/blocks/accordion/accordion.css
@@ -0,0 +1,78 @@
+raqn-accordion {
+ --scope-icon-size: 1em;
+ --accordion-background: var(--scope-background, black);
+ --accordion-color: var(--scope-color, white);
+
+ background: var(--accordion-background);
+ color: var(--accordion-color);
+ margin: var(--scope-margin, 0);
+ padding: var(--scope-padding, 0);
+ display: grid;
+}
+
+raqn-accordion raqn-icon {
+ align-self: end;
+ transform: rotate(90deg);
+ transition: transform 0.2s ease-in-out;
+}
+
+raqn-accordion accordion-control.active raqn-icon {
+ transform: rotate(270deg);
+}
+
+.accordion-control {
+ border-block-start: var(--scope-border-block-start, none);
+ border-inline-start: var(--scope-border-inline-start, none);
+ border-inline-end: var(--scope-border-inline-end, none);
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: start;
+ width: 100%;
+}
+
+.accordion-control:first-child {
+ border-block-start: none;
+}
+
+.accordion-control > * {
+ --scope-headings-color: var(--scope-color, black);
+ --scope-hover-color: var(--scope-accent-color, gray);
+
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+ min-width: 100%;
+}
+
+.accordion-control:hover {
+ --scope-color: var(--scope-headings-color);
+}
+
+.accordion-content {
+ display: grid;
+ max-height: 0;
+ overflow: hidden;
+ opacity: 0;
+ border-block-end: var(--scope-border-block-end, none);
+ border-block-start: var(--scope-border-block-start, none);
+ margin-block-end: -1px;
+ transition:
+ max-height 0.5s ease-in-out,
+ opacity 0.5s ease-in-out;
+}
+
+.accordion-content:last-child {
+ border-block-end: none;
+}
+
+.accordion-content.active {
+ opacity: 1;
+ grid-template-rows: 1fr;
+ max-height: 100vw;
+}
+
+.accordion-content-wrapper {
+ margin-block: 1em;
+ display: grid;
+}
diff --git a/blocks/accordion/accordion.js b/blocks/accordion/accordion.js
new file mode 100644
index 00000000..f3b0b0eb
--- /dev/null
+++ b/blocks/accordion/accordion.js
@@ -0,0 +1,81 @@
+import Column from '../column/column.js';
+
+export default class Accordion extends Column {
+ dependencies = ['icon'];
+
+ ready() {
+ this.setAttribute('role', 'navigation');
+ let children = Array.from(this.children);
+ children = children.map((child) => {
+ if (child.tagName !== 'DIV') {
+ const div = document.createElement('div');
+ div.append(child);
+ this.append(div);
+ return div;
+ }
+ return child;
+ });
+ // console.log(children)
+ this.setupControls(children.filter((_, ind) => ind % 2 === 0));
+ this.setupContent(children.filter((_, ind) => ind % 2 === 1));
+ }
+
+ setupControls(controls) {
+ controls.forEach((control, index) => {
+ const icon = document.createElement('raqn-icon');
+ icon.setAttribute('icon', 'chevron-right');
+ const children = Array.from(control.children);
+ if (children.length === 0) {
+ const child = document.createElement('span');
+ child.textContent = control.textContent;
+ control.innerHTML = '';
+ control.append(child);
+ }
+ control.children[0].append(icon);
+ control.setAttribute('role', 'button');
+ control.setAttribute('aria-expanded', 'false');
+ control.setAttribute('tabindex', '0');
+ control.classList.add('accordion-control');
+ control.id = `accordion-${this.id}-${index}`;
+ control.addEventListener('click', () => this.toggleControl(control));
+ control.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ this.toggleControl(control);
+ }
+ });
+ });
+ }
+
+ toggleControl(control) {
+ const content = control.nextElementSibling;
+ if (content) {
+ content.classList.toggle('active');
+ control.classList.toggle('active');
+ control.setAttribute(
+ 'aria-expanded',
+ content.classList.contains('active'),
+ );
+ content.setAttribute(
+ 'aria-hidden',
+ !content.classList.contains('active'),
+ );
+ }
+ }
+
+ setupContent(contents) {
+ contents.forEach((content) => {
+ const internal = content.children;
+ const wrapper = document.createElement('div');
+ wrapper.classList.add('accordion-content-wrapper');
+ wrapper.append(...internal);
+ content.append(wrapper);
+ content.setAttribute('role', 'region');
+ content.setAttribute('aria-hidden', true);
+ content.classList.add('accordion-content');
+ content.setAttribute(
+ 'aria-labelledby',
+ content.previousElementSibling.id,
+ );
+ });
+ }
+}
diff --git a/blocks/breadcrumbs/breadcrumbs.css b/blocks/breadcrumbs/breadcrumbs.css
new file mode 100644
index 00000000..f53ea6f9
--- /dev/null
+++ b/blocks/breadcrumbs/breadcrumbs.css
@@ -0,0 +1,40 @@
+raqn-breadcrumbs {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 10px;
+ align-items: center;
+ padding: 10px 0;
+ background: var(--scope-background, transparent);
+ color: var(--scope-color, #000);
+}
+
+raqn-breadcrumbs ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: flex;
+}
+
+raqn-breadcrumbs ul li {
+ margin-inline-end: 1em;
+ font-weight: bold;
+}
+
+raqn-breadcrumbs ul li a {
+ color: var(--scope-color);
+ font-weight: normal;
+}
+
+@media screen and (max-width: 768px) {
+ raqn-breadcrumbs ul li {
+ display: none;
+ }
+
+ raqn-breadcrumbs ul li.separator:has(+ li:last-child) {
+ display: block;
+ }
+
+ raqn-breadcrumbs ul li:last-child {
+ display: block;
+ }
+}
diff --git a/blocks/breadcrumbs/breadcrumbs.js b/blocks/breadcrumbs/breadcrumbs.js
new file mode 100644
index 00000000..fe3b3083
--- /dev/null
+++ b/blocks/breadcrumbs/breadcrumbs.js
@@ -0,0 +1,31 @@
+import ComponentBase from '../../scripts/component-base.js';
+
+export default class BreadCrumbs extends ComponentBase {
+ capitalize(string) {
+ return string
+ .split('-')
+ .map((str) => str.charAt(0).toUpperCase() + str.slice(1))
+ .join(' ');
+ }
+
+ ready() {
+ this.classList.add('full-width');
+ this.classList.add('breadcrumbs');
+ this.path = window.location.pathname.split('/');
+ this.innerHTML = `
+
+ ${this.path
+ .map((path, index) => {
+ if (path === '') {
+ return `- Home
`;
+ }
+ if (index === this.path.length - 1) {
+ return `- ${this.capitalize(path)}
`;
+ }
+ const href = this.path.slice(0, index + 1).join('/');
+ return `- ${this.capitalize(path)}
`;
+ })
+ .join('- ›
')}
+
`;
+ }
+}
diff --git a/blocks/button/button.css b/blocks/button/button.css
new file mode 100644
index 00000000..b3b31b2d
--- /dev/null
+++ b/blocks/button/button.css
@@ -0,0 +1,38 @@
+raqn-button {
+ width: 100%;
+ display: grid;
+ align-content: center;
+ justify-content: center;
+ align-items: center;
+ justify-items: var(--scope-justify, start);
+}
+
+raqn-button > * {
+ background: var(--scope-accent-background, #000);
+ color: var(--scope-accent-color, #fff);
+ text-transform: none;
+ border-radius: var(--scope-border-radius, 0);
+ border-block-start: var(--scope-border-block-start, 1px solid transparent);
+ border-block-end: var(--scope-border-block-end, 1px solid transparent);
+ border-inline-start: var(--scope-border-inline-start, 1px solid transparent);
+ border-inline-end: var(--scope-border-inline-end, 1px solid transparent);
+ overflow: hidden;
+}
+
+raqn-button > *:hover {
+ background: var(--scope-accent-background-hover, #fff);
+ color: var(--scope-accent-color-hover, #fff);
+ border-color: currentcolor;
+}
+
+raqn-button a {
+ color: var(--scope-accent-color, currentcolor);
+ padding: 10px 20px;
+ text-decoration: none;
+}
+
+raqn-button a:hover,
+raqn-button a:visited,
+raqn-button a:active {
+ text-decoration: none;
+}
diff --git a/blocks/button/button.js b/blocks/button/button.js
new file mode 100644
index 00000000..227717d3
--- /dev/null
+++ b/blocks/button/button.js
@@ -0,0 +1,8 @@
+import Column from '../column/column.js';
+
+export default class Button extends Column {
+ render() {
+ this.setAttribute('role', 'button');
+ this.setAttribute('tabindex', '0');
+ }
+}
diff --git a/blocks/card/card.css b/blocks/card/card.css
new file mode 100644
index 00000000..b3f2c32f
--- /dev/null
+++ b/blocks/card/card.css
@@ -0,0 +1,63 @@
+raqn-card {
+ background: var(--scope-background, transparent);
+ color: var(--scope-color, #fff);
+ display: grid;
+ position: relative;
+ grid-template-columns: var(--card-columns, 1fr);
+ gap: var(--scope-gap, 20px);
+ padding: var(--scope-padding, 20px 0);
+}
+
+raqn-card a {
+ font-size: var(--raqn-font-size-3, 1.2em);
+ font-weight: bold;
+}
+
+raqn-card p a {
+ margin-block: 0;
+}
+
+raqn-card > picture {
+ grid-column: span var(--card-columns, 1fr);
+}
+
+raqn-card > div {
+ position: relative;
+ background: var(--scope-inner-background, transparent);
+ padding: var(--scope-inner-padding, 20px);
+ border-block-start: var(--scope-border-block-start, none);
+ border-block-end: var(--scope-border-block-end, none);
+ border-inline-start: var(--scope-border-inline-start, none);
+ border-inline-end: var(--scope-border-inline-end, none);
+}
+
+raqn-card > div div:last-child > a {
+ position: absolute;
+ inset-inline-start: 0;
+ inset-block-start: 0;
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+ text-indent: -10000px;
+ margin: 0;
+ padding: 0;
+}
+
+raqn-card > div:has(raqn-icon) {
+ padding-block-end: 0;
+ padding-block-end: var(--scope-icon-size, 20px);
+}
+
+raqn-card p:has(raqn-icon) {
+ display: inline-grid;
+}
+
+raqn-card raqn-icon {
+ width: 100%;
+ display: flex;
+ position: absolute;
+ inset-block-end: 0;
+ inset-inline-end: 0;
+ box-sizing: border-box;
+ padding: var(--scope-inner-padding, 20px);
+}
diff --git a/blocks/card/card.js b/blocks/card/card.js
new file mode 100644
index 00000000..1933582d
--- /dev/null
+++ b/blocks/card/card.js
@@ -0,0 +1,42 @@
+import ComponentBase from '../../scripts/component-base.js';
+import { eagerImage } from '../../scripts/libs.js';
+
+export default class Card extends ComponentBase {
+ static get observedAttributes() {
+ return ['columns', 'ratio', 'eager', 'background', 'button'];
+ }
+
+ ready() {
+ if (this.getAttribute('button') === 'true') {
+ Array.from(this.querySelectorAll('a')).forEach((a) =>
+ this.convertLink(a),
+ );
+ }
+ this.eager = parseInt(this.getAttribute('eager') || 0, 10);
+ this.ratio = this.getAttribute('ratio') || '4/3';
+ this.style.setProperty('--card-ratio', this.ratio);
+ this.classList.add('inner');
+ this.setupColumns(this.getAttribute('columns'));
+ if (this.eager) {
+ eagerImage(this, this.eager);
+ }
+ }
+
+ convertLink(a) {
+ const button = document.createElement('raqn-button');
+ const content = a.outerHTML;
+ button.innerHTML = content;
+ a.replaceWith(button);
+ }
+
+ setupColumns(columns) {
+ if (!columns) {
+ return;
+ }
+ this.columns = parseInt(columns, 10);
+ this.area = Array.from(Array(parseInt(this.columns, 10)))
+ .map(() => '1fr')
+ .join(' ');
+ this.style.setProperty('--card-columns', this.area);
+ }
+}
diff --git a/blocks/cards/cards.css b/blocks/cards/cards.css
deleted file mode 100644
index a0315ede..00000000
--- a/blocks/cards/cards.css
+++ /dev/null
@@ -1,31 +0,0 @@
-.cards > ul {
- list-style: none;
- margin: 0;
- padding: 0;
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
- grid-gap: 16px;
-}
-
-.cards > ul > li {
- border: 1px solid var(--highlight-background-color);
- background-color: var(--background-color)
-}
-
-.cards .cards-card-body {
- margin: 16px;
-}
-
-.cards .cards-card-image {
- line-height: 0;
-}
-
-.cards .cards-card-body > *:first-child {
- margin-top: 0;
-}
-
-.cards > ul > li img {
- width: 100%;
- aspect-ratio: 4 / 3;
- object-fit: cover;
-}
diff --git a/blocks/cards/cards.js b/blocks/cards/cards.js
deleted file mode 100644
index 717ec302..00000000
--- a/blocks/cards/cards.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { createOptimizedPicture } from '../../scripts/lib-franklin.js';
-
-export default function decorate(block) {
- /* change to ul, li */
- const ul = document.createElement('ul');
- [...block.children].forEach((row) => {
- const li = document.createElement('li');
- while (row.firstElementChild) li.append(row.firstElementChild);
- [...li.children].forEach((div) => {
- if (div.children.length === 1 && div.querySelector('picture')) div.className = 'cards-card-image';
- else div.className = 'cards-card-body';
- });
- ul.append(li);
- });
- ul.querySelectorAll('img').forEach((img) => img.closest('picture').replaceWith(createOptimizedPicture(img.src, img.alt, false, [{ width: '750' }])));
- block.textContent = '';
- block.append(ul);
-}
diff --git a/blocks/column/column.css b/blocks/column/column.css
new file mode 100644
index 00000000..1879b40e
--- /dev/null
+++ b/blocks/column/column.css
@@ -0,0 +1,3 @@
+raqn-column {
+ margin: var(--scope-margin, 0);
+}
diff --git a/blocks/column/column.js b/blocks/column/column.js
new file mode 100644
index 00000000..d4a72505
--- /dev/null
+++ b/blocks/column/column.js
@@ -0,0 +1,84 @@
+import ComponentBase from '../../scripts/component-base.js';
+
+export default class Column extends ComponentBase {
+ static observedAttributes() {
+ return ['position', 'size'];
+ }
+
+ connected() {
+ const content = this.querySelectorAll('div > div');
+ // clean up dom structure (div div div div div div) and save the content
+ this.contentChildren = Array.from(content).map((child) => {
+ const {children} = child;
+ const parent = child.parentNode;
+ if (children.length > 0) {
+ child.replaceWith(...children);
+ }
+ return parent;
+ })
+ this.calculateGridTemplateColumns();
+ }
+
+ calculateGridTemplateColumns() {
+ this.position = parseInt(this.getAttribute('position'), 10);
+ this.size = this.getAttribute('size');
+ this.justify = this.getAttribute('justify') || 'stretch';
+ if (this.justify) {
+ this.style.justifyContent = this.justify;
+ }
+ if (this.position) {
+ const parent = this.parentElement;
+ const children = Array.from(parent.children);
+ this.parentElement.classList.add('raqn-grid');
+ let parentGridTemplateColumns = parent.style.getPropertyValue(
+ '--grid-template-columns',
+ );
+ if (!parentGridTemplateColumns) {
+ // we have no grid template columns yet
+ parentGridTemplateColumns = children
+ .map((child, index) => {
+ if (this.position === index + 1) {
+ return this.size || 'auto';
+ }
+ return 'auto';
+ })
+ .join(' ');
+ // set the new grid template columns
+ parent.style.setProperty(
+ '--grid-template-columns',
+ parentGridTemplateColumns,
+ );
+ } else {
+ const { position } = this;
+ const prio = children.indexOf(this) + 1;
+ parentGridTemplateColumns = parentGridTemplateColumns
+ .split(' ')
+ .map((size, i) => {
+ // we have a non standard value for this position
+ const hasValue = size !== 'auto';
+ // we are at the position
+ const isPosition = i + 1 === position;
+ // we are at a position before the prio
+ const isBeforePrio = i + 1 <= prio;
+ // we have a non standard value for this position and we are at the position
+ if (!hasValue && isPosition) {
+ return this.size || 'auto';
+ }
+ // we have a non standard value for this position and we are at a position before the prio
+ if (hasValue && isPosition && isBeforePrio) {
+ return this.size || size;
+ }
+ return size;
+ })
+ .join(' ');
+ // set the new grid template columns
+ parent.style.setProperty(
+ '--grid-template-columns',
+ parentGridTemplateColumns,
+ );
+ }
+ this.style.gridColumn = this.position;
+ this.style.gridRow = 1;
+ }
+ }
+}
diff --git a/blocks/columns/columns.css b/blocks/columns/columns.css
deleted file mode 100644
index 4b09aeea..00000000
--- a/blocks/columns/columns.css
+++ /dev/null
@@ -1,65 +0,0 @@
-.columns > div {
- display: flex;
- flex-direction: column;
-}
-
-.columns img {
- width: 100%;
-}
-
-@media (min-width: 900px) {
- .columns > div {
- align-items: center;
- flex-direction: unset;
- gap: var(--padding-vertical) var(--padding-horizontal);
- padding: var(--padding-vertical) var(--padding-horizontal);
- }
-
- .columns > div > div:nth-child(1) {
- flex: var(--column0-flex, 0);
- }
-
- .columns > div > div:nth-child(2) {
- flex: var(--column1-flex, 0);
- }
-
- .columns > div > div:nth-child(3) {
- flex: var(--column2-flex, 0);
- }
-
- .columns > div > div:nth-child(4) {
- flex: var(--column3-flex, 0);
- }
-
- .columns > div > div:nth-child(5) {
- flex: var(--column4-flex, 0);
- }
-
- .columns > div > div:nth-child(6) {
- flex: var(--column5-flex, 0);
- }
-
- .columns > div > div:nth-child(7) {
- flex: var(--column6-flex, 0);
- }
-
- .columns > div > div:nth-child(8) {
- flex: var(--column7-flex, 0);
- }
-
- .columns > div > div:nth-child(9) {
- flex: var(--column8-flex, 0);
- }
-
- .columns > div > div:nth-child(10) {
- flex: var(--column9-flex, 0);
- }
-
- .columns > div > div:nth-child(11) {
- flex: var(--column10-flex, 0);
- }
-
- .columns > div > div:nth-child(12) {
- flex: var(--column11-flex, 0);
- }
-}
diff --git a/blocks/columns/columns.js b/blocks/columns/columns.js
deleted file mode 100644
index 823c6c18..00000000
--- a/blocks/columns/columns.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import {
- addCssVariables,
-} from '../../scripts/lib-franklin.js';
-
-export default function decorate(block) {
- const columns = block.querySelectorAll(':scope > div > div');
- const columnCount = columns.length;
- // following line regex matches partition sizes separated by dashes like 1-2-3
- const columnPartionRegex = /^\d{1,}(?:-\d{1,})*$/;
- const columnPartions = [...block.classList].find((c) => columnPartionRegex.test(c))?.split('-') || [];
-
- const variables = {};
- for (let i = 0; i < columnCount; i += 1) {
- const partition = columnPartions.length > i ? columnPartions[i] : 1;
- variables[`column${i}-flex`] = partition;
- }
- addCssVariables(block, variables);
-}
diff --git a/blocks/external/external.css b/blocks/external/external.css
new file mode 100644
index 00000000..cfa763db
--- /dev/null
+++ b/blocks/external/external.css
@@ -0,0 +1,7 @@
+raqn-external {
+ display: none;
+}
+
+raqn-external[initialized='true'] {
+ display: block;
+}
diff --git a/blocks/external/external.js b/blocks/external/external.js
new file mode 100644
index 00000000..d7f24168
--- /dev/null
+++ b/blocks/external/external.js
@@ -0,0 +1,23 @@
+import ComponentBase from '../../scripts/component-base.js';
+
+export default class External extends ComponentBase {
+ static get observedAttributes() {
+ return ['external', 'folder'];
+ }
+
+ link = false;
+
+ get external() {
+ const link = this.querySelector('a');
+ if (link) {
+ this.link = link.href;
+ }
+ return this.link;
+ }
+
+ set external(value) {
+ if (value !== '') {
+ this.link = value;
+ }
+ }
+}
diff --git a/blocks/footer/footer.css b/blocks/footer/footer.css
index 19fc5607..7aa0fb17 100644
--- a/blocks/footer/footer.css
+++ b/blocks/footer/footer.css
@@ -1,14 +1,51 @@
footer {
- padding: 2rem;
- background-color: var(--overlay-background-color);
- font-size: var(--body-font-size-s);
+ background: var(--scope-background-color);
+ width: var(--scope-max-width);
+ margin: 0 auto;
}
-footer .footer {
- max-width: 1200px;
- margin: auto;
+raqn-footer {
+ background: var(--scope-background-color);
+ border-top: 1px solid var(--scope-color);
}
-footer .footer p {
- margin: 0;
-}
\ No newline at end of file
+raqn-footer ul {
+ list-style: none;
+ padding: 0;
+ margin: 20px 0;
+ min-width: 100%;
+}
+
+raqn-footer ul li a {
+ color: var(--scope-color);
+}
+
+@media screen and (min-width: 1024px) {
+ raqn-footer {
+ display: grid;
+ grid-template-columns: auto 20vw;
+ }
+
+ raqn-footer ul li a {
+ padding: 10px 1.2em;
+ border-inline-end: 1px solid var(--scope-color);
+ }
+
+ raqn-footer ul {
+ display: flex;
+ margin: 0;
+ }
+
+ raqn-footer ul li {
+ margin: 2em 0;
+ }
+
+ raqn-footer > *:last-child {
+ justify-self: end;
+ align-self: center;
+ }
+}
+
+raqn-footer ul li:last-child a {
+ border-inline-end: none;
+}
diff --git a/blocks/footer/footer.js b/blocks/footer/footer.js
index ca8c7a68..bedb5839 100644
--- a/blocks/footer/footer.js
+++ b/blocks/footer/footer.js
@@ -1,25 +1,17 @@
-import { readBlockConfig, decorateIcons } from '../../scripts/lib-franklin.js';
+import ComponentBase from '../../scripts/component-base.js';
-/**
- * loads and decorates the footer
- * @param {Element} block The footer block element
- */
-export default async function decorate(block) {
- const cfg = readBlockConfig(block);
- block.textContent = '';
-
- // fetch footer content
- const footerPath = cfg.footer || '/footer';
- const resp = await fetch(`${footerPath}.plain.html`, window.location.pathname.endsWith('/footer') ? { cache: 'reload' } : {});
-
- if (resp.ok) {
- const html = await resp.text();
-
- // decorate footer DOM
- const footer = document.createElement('div');
- footer.innerHTML = html;
+export default class Footer extends ComponentBase {
+ constructor() {
+ super();
+ this.external = '/footer.plain.html';
+ }
- decorateIcons(footer);
- block.append(footer);
+ ready() {
+ const child = this.children[0];
+ child.replaceWith(...child.children);
+ this.nav = this.querySelector('ul');
+ this.nav.setAttribute('role', 'navigation');
+ this.classList.add('full-width');
+ this.classList.add('horizontal');
}
}
diff --git a/blocks/grid/grid.css b/blocks/grid/grid.css
deleted file mode 100644
index ba453ff5..00000000
--- a/blocks/grid/grid.css
+++ /dev/null
@@ -1,30 +0,0 @@
-.section.grid {
- display: grid;
- grid-template-columns: var(--grid-template-columns, unset);
- grid-template-rows: var(--grid-template-rows, unset);
-}
-
-.section.grid .element {
- grid-column: var(--grid-column-start-position, auto) / var(--grid-column-end-position, auto);
- grid-row: var(--grid-row-start-position, auto) / var(--grid-row-end-position, auto);
- width: 100%;
- max-width: unset;
- background-color: var(--background-color, transparent);
- color: var(--text-color);
- margin: auto;
-}
-
-.section.grid .element .image-wrapper {
- margin: 0;
- padding: 0;
-}
-
-.section.grid .element img {
- width: 100%;
- height: auto;
-}
-
-.section.grid .element p {
- margin: 0;
- padding: var(--padding-vertical) var(--padding-horizontal);
-}
diff --git a/blocks/grid/grid.js b/blocks/grid/grid.js
deleted file mode 100644
index 5a9fc3f8..00000000
--- a/blocks/grid/grid.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import {
- addCssVariables,
-} from '../../scripts/lib-franklin.js';
-
-export default function decorate(block) {
- const elements = [...block.querySelectorAll(':scope > div')];
-
- const columnTemplate = elements.find((e) => e.dataset.gridColumns)?.dataset.gridColumns;
- const rowTemplate = elements.find((e) => e.dataset.gridRows)?.dataset.gridRows;
- if (columnTemplate || rowTemplate) {
- addCssVariables(block, {
- 'grid-template-columns': columnTemplate,
- 'grid-template-rows': rowTemplate,
- });
- }
-
- elements.forEach((e) => {
- e.classList.add('element');
-
- const [[startColumnPosition, startRowPosition], [endColumnPosition, endRowPosition]] = e.dataset.gridPosition.split(/\s*\/\s*/).map((p) => p.split(/\s*-\s*/));
-
- addCssVariables(e, {
- 'grid-column-start-position': startColumnPosition,
- 'grid-row-start-position': startRowPosition,
- 'grid-column-end-position': endColumnPosition,
- 'grid-row-end-position': endRowPosition,
- });
- });
-}
diff --git a/blocks/header/header.css b/blocks/header/header.css
index 09581712..689a4775 100644
--- a/blocks/header/header.css
+++ b/blocks/header/header.css
@@ -1,19 +1,12 @@
-header a:any-link {
- color: inherit;
-}
-
-header p,
-header .section.grid .element p {
- margin: 0;
- padding: 0;
-}
-
-a.button:any-link {
- background-color: transparent;
- color: inherit;
- font-weight: normal;
-}
+raqn-header {
+ --scope-background: var(--scope-header-background, #fff);
+ --scope-color: var(--scope-header-color, #000);
-a.button:any-link .icon {
- vertical-align: middle;
+ position: fixed;
+ width: 100%;
+ min-height: var(--scope-header-height, 64px);
+ display: grid;
+ background: var(--scope-header-background, #fff);
+ align-items: center;
+ z-index: 100;
}
diff --git a/blocks/header/header.js b/blocks/header/header.js
index c8512c3f..185e8f60 100644
--- a/blocks/header/header.js
+++ b/blocks/header/header.js
@@ -1,11 +1,13 @@
-import {
- loadBlocks,
-} from '../../scripts/lib-franklin.js';
-import {
- decorateMain,
-} from '../../scripts/scripts.js';
+import ComponentBase from '../../scripts/component-base.js';
+import { eagerImage } from '../../scripts/libs.js';
-export default async function decorate(block) {
- decorateMain(block);
- loadBlocks(block);
+export default class Header extends ComponentBase {
+ external = '/header.plain.html';
+
+ dependencies = ['navigation'];
+
+ async processExternal(response) {
+ await super.processExternal(response);
+ eagerImage(this, 1);
+ }
}
diff --git a/blocks/hero/hero.css b/blocks/hero/hero.css
index cc5f4fa5..822306c0 100644
--- a/blocks/hero/hero.css
+++ b/blocks/hero/hero.css
@@ -1,39 +1,25 @@
/* block specific CSS goes here */
-main .hero-container > div {
- max-width: unset;
-}
-
-main .hero-container {
- padding: 0;
-}
-
-main .hero {
- position: relative;
- padding: 32px;
- min-height: 300px;
-}
+raqn-hero {
+ --hero-background: var(--scope-background, black);
+ --hero-color: var(--scope-color, white);
+ --hero-grid-template-columns: var(--scope-hero-columns, 1fr);
+ --hero-hero-order: 0;
+ --hero-padding-block: var(--scope-hero-padding-block, 40px);
-main .hero h1 {
- max-width: 1200px;
- margin-left: auto;
- margin-right: auto;
- color: white;
+ background: var(--hero-background);
+ color: var(--hero-color);
+ align-items: center;
+ grid-template-columns: var(--hero-grid-template-columns, 1fr);
+ padding-block: var(--hero-padding-block);
}
-main .hero picture {
- position: absolute;
- z-index: -1;
- top: 0;
- left: 0;
- bottom: 0;
- right: 0;
- object-fit: cover;
- box-sizing: border-box;
+@media screen and (min-width: 767px) {
+ raqn-hero {
+ --hero-grid-template-columns: 0.6fr 0.4fr;
+ }
}
-main .hero img {
- object-fit: cover;
- width: 100%;
- height: 100%;
+raqn-hero > div:first-child {
+ order: var(--hero-hero-order);
}
diff --git a/blocks/hero/hero.js b/blocks/hero/hero.js
index e69de29b..ce998437 100644
--- a/blocks/hero/hero.js
+++ b/blocks/hero/hero.js
@@ -0,0 +1,16 @@
+import ComponentBase from '../../scripts/component-base.js';
+
+export default class Hero extends ComponentBase {
+ get observedAttributes() {
+ return ['order'];
+ }
+
+ ready() {
+ const child = this.children[0];
+ child.replaceWith(...child.children);
+ this.classList.add('full-width');
+ this.setAttribute('role', 'banner');
+ this.setAttribute('aria-label', 'hero');
+ this.style.setProperty('--hero-hero-order', this.getAttribute('order'));
+ }
+}
diff --git a/blocks/icon/icon.css b/blocks/icon/icon.css
new file mode 100644
index 00000000..b34e808b
--- /dev/null
+++ b/blocks/icon/icon.css
@@ -0,0 +1,22 @@
+raqn-icon {
+ display: inline-flex;
+ font-size: 1em;
+ line-height: 1em;
+ text-align: center;
+ min-width: var(--scope-icon-size, 1em);
+ min-height: var(--scope-icon-size, 1em);
+ justify-content: var(--scope-icon-align, start);
+ text-transform: none;
+ vertical-align: middle;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+raqn-icon svg {
+ display: inline-block;
+ width: var(--scope-icon-size, 1em);
+ height: var(--scope-icon-size, 1em);
+ fill: currentcolor;
+ overflow: hidden;
+ vertical-align: middle;
+}
diff --git a/blocks/icon/icon.js b/blocks/icon/icon.js
new file mode 100644
index 00000000..08e6e075
--- /dev/null
+++ b/blocks/icon/icon.js
@@ -0,0 +1,104 @@
+import ComponentBase from '../../scripts/component-base.js';
+
+export default class Icon extends ComponentBase {
+ constructor() {
+ super();
+ this.setupSprite();
+ }
+
+ setupSprite() {
+ this.svgSprite = document.getElementById('franklin-svg-sprite');
+ if (!this.svgSprite) {
+ this.svgSprite = document.createElement('div');
+ this.svgSprite.id = 'franklin-svg-sprite';
+ document.body.append(this.svgSprite);
+ }
+ }
+
+ get iconUrl() {
+ return `/assets/icons/${this.iconName}.svg`;
+ }
+
+ get cache() {
+ window.ICONS_CACHE = window.ICONS_CACHE || {};
+ return window.ICONS_CACHE;
+ }
+
+ async connected() {
+ this.iconName = this.getAttribute('icon');
+ if (!this.cache[this.iconName]) {
+ this.cache[this.iconName] = {
+ loading: new Promise((resolve) => {
+ resolve(this.load(this.iconUrl));
+ }),
+ };
+ } else {
+ await this.cache[this.iconName].loading;
+ this.innerHTML = this.template();
+ }
+ this.classList.add('loaded');
+ }
+
+ template() {
+ const { viewBox } = this.cache[this.iconName];
+ const attributes = Object.keys({ viewBox })
+ .map((k) => {
+ if (this.cache[this.iconName][k]) {
+ return `${k}="${this.cache[this.iconName][k]}"`;
+ }
+ return '';
+ })
+ .join(' ');
+ return ``;
+ }
+
+ iconTemplate(iconName, svg, viewBox, width, height) {
+ return `${svg.innerHTML}`;
+ }
+
+ async processExternal(response) {
+ if (response.ok) {
+ const { iconName } = this;
+ this.svg = await response.text();
+
+ if (this.svg.match(/(
diff --git a/icons/search.svg b/icons/search.svg
deleted file mode 100644
index 637c677b..00000000
--- a/icons/search.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/package-lock.json b/package-lock.json
index c23bc9b6..365f926a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,7 +20,10 @@
"chai": "4.3.7",
"eslint": "8.35.0",
"eslint-config-airbnb-base": "15.0.0",
+ "eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "2.27.5",
+ "husky": "^8.0.0",
+ "prettier-eslint": "^16.2.0",
"semantic-release": "21.0.5",
"sinon": "15.0.1",
"stylelint": "15.2.0",
@@ -539,6 +542,18 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
+ "node_modules/@jest/schemas": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
+ "dev": true,
+ "dependencies": {
+ "@sinclair/typebox": "^0.27.8"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
@@ -1623,6 +1638,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@sinclair/typebox": {
+ "version": "0.27.8",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
+ "dev": true
+ },
"node_modules/@sinonjs/commons": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
@@ -1932,6 +1953,160 @@
"@types/node": "*"
}
},
+ "node_modules/@typescript-eslint/parser": {
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.16.0.tgz",
+ "integrity": "sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "6.16.0",
+ "@typescript-eslint/types": "6.16.0",
+ "@typescript-eslint/typescript-estree": "6.16.0",
+ "@typescript-eslint/visitor-keys": "6.16.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz",
+ "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "6.16.0",
+ "@typescript-eslint/visitor-keys": "6.16.0"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz",
+ "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==",
+ "dev": true,
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz",
+ "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "6.16.0",
+ "@typescript-eslint/visitor-keys": "6.16.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "9.0.3",
+ "semver": "^7.5.4",
+ "ts-api-utils": "^1.0.1"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+ "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz",
+ "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "6.16.0",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/@web/browser-logs": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/@web/browser-logs/-/browser-logs-0.2.5.tgz",
@@ -3027,6 +3202,15 @@
"node": ">=8"
}
},
+ "node_modules/common-tags": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
+ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
"node_modules/compare-func": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
@@ -3574,6 +3758,12 @@
"node": ">=8"
}
},
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "dev": true
+ },
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -4016,6 +4206,18 @@
"eslint-plugin-import": "^2.25.2"
}
},
+ "node_modules/eslint-config-prettier": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
"node_modules/eslint-import-resolver-node": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz",
@@ -5057,6 +5259,27 @@
"node": ">= 0.4.0"
}
},
+ "node_modules/has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-ansi/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/has-bigints": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
@@ -5281,6 +5504,21 @@
"node": ">=10.17.0"
}
},
+ "node_modules/husky": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
+ "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
+ "dev": true,
+ "bin": {
+ "husky": "lib/bin.js"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -6449,6 +6687,84 @@
"node": ">=8"
}
},
+ "node_modules/loglevel": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz",
+ "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6.0"
+ },
+ "funding": {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/loglevel"
+ }
+ },
+ "node_modules/loglevel-colored-level-prefix": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz",
+ "integrity": "sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^1.1.3",
+ "loglevel": "^1.4.1"
+ }
+ },
+ "node_modules/loglevel-colored-level-prefix/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/loglevel-colored-level-prefix/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/loglevel-colored-level-prefix/node_modules/chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/loglevel-colored-level-prefix/node_modules/strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/loglevel-colored-level-prefix/node_modules/supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/loupe": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.3.tgz",
@@ -10766,6 +11082,70 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz",
+ "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-eslint": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-16.2.0.tgz",
+ "integrity": "sha512-GDTSKc62VaLceiaI/qMaKo2oco2CIWtbj4Zr6ckhbTgcBL/uR0d9jkMzh9OtBIT/Z7iBoCB4OHj/aJ5YuNgAuA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/parser": "^6.7.5",
+ "common-tags": "^1.4.0",
+ "dlv": "^1.1.0",
+ "eslint": "^8.7.0",
+ "indent-string": "^4.0.0",
+ "lodash.merge": "^4.6.0",
+ "loglevel-colored-level-prefix": "^1.0.0",
+ "prettier": "^3.0.1",
+ "pretty-format": "^29.7.0",
+ "require-relative": "^0.8.7",
+ "typescript": "^5.2.2",
+ "vue-eslint-parser": "^9.1.0"
+ },
+ "engines": {
+ "node": ">=16.10.0"
+ }
+ },
+ "node_modules/pretty-format": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
+ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^18.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -10912,6 +11292,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-is": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
+ "dev": true
+ },
"node_modules/read-pkg": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -11166,6 +11552,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/require-relative": {
+ "version": "0.8.7",
+ "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
+ "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==",
+ "dev": true
+ },
"node_modules/resolve": {
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
@@ -12658,6 +13050,18 @@
"node": ">=8"
}
},
+ "node_modules/ts-api-utils": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
+ "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
+ "dev": true,
+ "engines": {
+ "node": ">=16.13.0"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
+ }
+ },
"node_modules/tsconfig-paths": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
@@ -12751,6 +13155,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/typescript": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
+ "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
"node_modules/typical": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz",
@@ -12926,6 +13343,82 @@
"node": ">= 0.8"
}
},
+ "node_modules/vue-eslint-parser": {
+ "version": "9.3.2",
+ "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz",
+ "integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.3.4",
+ "eslint-scope": "^7.1.1",
+ "eslint-visitor-keys": "^3.3.0",
+ "espree": "^9.3.1",
+ "esquery": "^1.4.0",
+ "lodash": "^4.17.21",
+ "semver": "^7.3.6"
+ },
+ "engines": {
+ "node": "^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=6.0.0"
+ }
+ },
+ "node_modules/vue-eslint-parser/node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/vue-eslint-parser/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/vue-eslint-parser/node_modules/semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
@@ -13587,6 +14080,15 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
+ "@jest/schemas": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
+ "dev": true,
+ "requires": {
+ "@sinclair/typebox": "^0.27.8"
+ }
+ },
"@jridgewell/gen-mapping": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
@@ -14341,6 +14843,12 @@
}
}
},
+ "@sinclair/typebox": {
+ "version": "0.27.8",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
+ "dev": true
+ },
"@sinonjs/commons": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
@@ -14650,6 +15158,98 @@
"@types/node": "*"
}
},
+ "@typescript-eslint/parser": {
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.16.0.tgz",
+ "integrity": "sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/scope-manager": "6.16.0",
+ "@typescript-eslint/types": "6.16.0",
+ "@typescript-eslint/typescript-estree": "6.16.0",
+ "@typescript-eslint/visitor-keys": "6.16.0",
+ "debug": "^4.3.4"
+ }
+ },
+ "@typescript-eslint/scope-manager": {
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz",
+ "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "6.16.0",
+ "@typescript-eslint/visitor-keys": "6.16.0"
+ }
+ },
+ "@typescript-eslint/types": {
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz",
+ "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz",
+ "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "6.16.0",
+ "@typescript-eslint/visitor-keys": "6.16.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "9.0.3",
+ "semver": "^7.5.4",
+ "ts-api-utils": "^1.0.1"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+ "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ }
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz",
+ "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "6.16.0",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true
+ }
+ }
+ },
"@web/browser-logs": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/@web/browser-logs/-/browser-logs-0.2.5.tgz",
@@ -15441,6 +16041,12 @@
}
}
},
+ "common-tags": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
+ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
+ "dev": true
+ },
"compare-func": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
@@ -15845,6 +16451,12 @@
"path-type": "^4.0.0"
}
},
+ "dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "dev": true
+ },
"doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -16279,6 +16891,13 @@
"semver": "^6.3.0"
}
},
+ "eslint-config-prettier": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "dev": true,
+ "requires": {}
+ },
"eslint-import-resolver-node": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz",
@@ -16985,6 +17604,23 @@
"function-bind": "^1.1.1"
}
},
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true
+ }
+ }
+ },
"has-bigints": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
@@ -17149,6 +17785,12 @@
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
"dev": true
},
+ "husky": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
+ "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
+ "dev": true
+ },
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -18030,6 +18672,64 @@
}
}
},
+ "loglevel": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz",
+ "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==",
+ "dev": true
+ },
+ "loglevel-colored-level-prefix": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz",
+ "integrity": "sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "loglevel": "^1.4.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true
+ }
+ }
+ },
"loupe": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.3.tgz",
@@ -20970,6 +21670,51 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
+ "prettier": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz",
+ "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==",
+ "dev": true
+ },
+ "prettier-eslint": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-16.2.0.tgz",
+ "integrity": "sha512-GDTSKc62VaLceiaI/qMaKo2oco2CIWtbj4Zr6ckhbTgcBL/uR0d9jkMzh9OtBIT/Z7iBoCB4OHj/aJ5YuNgAuA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/parser": "^6.7.5",
+ "common-tags": "^1.4.0",
+ "dlv": "^1.1.0",
+ "eslint": "^8.7.0",
+ "indent-string": "^4.0.0",
+ "lodash.merge": "^4.6.0",
+ "loglevel-colored-level-prefix": "^1.0.0",
+ "prettier": "^3.0.1",
+ "pretty-format": "^29.7.0",
+ "require-relative": "^0.8.7",
+ "typescript": "^5.2.2",
+ "vue-eslint-parser": "^9.1.0"
+ }
+ },
+ "pretty-format": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
+ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
+ "dev": true,
+ "requires": {
+ "@jest/schemas": "^29.6.3",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^18.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true
+ }
+ }
+ },
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -21080,6 +21825,12 @@
}
}
},
+ "react-is": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
+ "dev": true
+ },
"read-pkg": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -21269,6 +22020,12 @@
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true
},
+ "require-relative": {
+ "version": "0.8.7",
+ "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
+ "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==",
+ "dev": true
+ },
"resolve": {
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
@@ -22370,6 +23127,13 @@
"integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==",
"dev": true
},
+ "ts-api-utils": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
+ "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
+ "dev": true,
+ "requires": {}
+ },
"tsconfig-paths": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
@@ -22441,6 +23205,12 @@
"is-typed-array": "^1.1.9"
}
},
+ "typescript": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
+ "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+ "dev": true
+ },
"typical": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz",
@@ -22567,6 +23337,54 @@
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"dev": true
},
+ "vue-eslint-parser": {
+ "version": "9.3.2",
+ "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz",
+ "integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.3.4",
+ "eslint-scope": "^7.1.1",
+ "eslint-visitor-keys": "^3.3.0",
+ "espree": "^9.3.1",
+ "esquery": "^1.4.0",
+ "lodash": "^4.17.21",
+ "semver": "^7.3.6"
+ },
+ "dependencies": {
+ "eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true
+ },
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ },
+ "semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ }
+ }
+ },
"webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
diff --git a/package.json b/package.json
index 4587406e..589812c2 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,8 @@
"lint:css": "stylelint blocks/**/*.css styles/*.css",
"lint": "npm run lint:js && npm run lint:css",
"lint:fix": "npm run lint:js -- --fix && npm run lint:css -- --fix",
- "semantic-release": "semantic-release --debug"
+ "semantic-release": "semantic-release --debug",
+ "prepare": "husky install"
},
"repository": {
"type": "git",
@@ -21,21 +22,24 @@
},
"homepage": "https://github.com/adobe/helix-project-boilerplate#readme",
"devDependencies": {
+ "@babel/core": "7.21.0",
+ "@babel/eslint-parser": "7.19.1",
+ "@esm-bundle/chai": "4.3.4-fix.0",
"@semantic-release/changelog": "6.0.3",
"@semantic-release/exec": "6.0.3",
"@semantic-release/git": "10.0.1",
- "semantic-release": "21.0.5",
- "@babel/core": "7.21.0",
- "@babel/eslint-parser": "7.19.1",
+ "@web/test-runner": "0.15.1",
+ "@web/test-runner-commands": "0.6.5",
"chai": "4.3.7",
"eslint": "8.35.0",
"eslint-config-airbnb-base": "15.0.0",
+ "eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "2.27.5",
- "@esm-bundle/chai": "4.3.4-fix.0",
- "@web/test-runner": "0.15.1",
- "@web/test-runner-commands": "0.6.5",
+ "semantic-release": "21.0.5",
+ "prettier-eslint": "^16.2.0",
"sinon": "15.0.1",
"stylelint": "15.2.0",
- "stylelint-config-standard": "30.0.1"
+ "stylelint-config-standard": "30.0.1",
+ "husky": "^8.0.0"
}
}
diff --git a/scripts/component-base.js b/scripts/component-base.js
new file mode 100644
index 00000000..b64156b5
--- /dev/null
+++ b/scripts/component-base.js
@@ -0,0 +1,51 @@
+import { init, start } from './init.js';
+
+export default class ComponentBase extends HTMLElement {
+ constructor() {
+ super();
+ this.external = false;
+ this.dependencies = [];
+ this.uuid = `gen${crypto.randomUUID().split('-')[0]}`;
+ }
+
+ async connectedCallback() {
+ const initialized = this.getAttribute('initialized');
+ if (!initialized) {
+ this.setAttribute('id', this.uuid);
+ if (this.external) {
+ await this.load(this.external);
+ }
+ if (this.dependencies.length > 0) {
+ await Promise.all(this.dependencies.map((dep) => start({ name: dep })));
+ }
+ this.connected();
+ this.ready();
+ this.setAttribute('initialized', true);
+ }
+ }
+
+ async load(block) {
+ const response = await fetch(
+ `${block}`,
+ window.location.pathname.endsWith(block) ? { cache: 'reload' } : {},
+ );
+ return this.processExternal(response);
+ }
+
+ async processExternal(response) {
+ if (response.ok) {
+ const html = await response.text();
+ this.innerHTML = html;
+ return this.refresh(this);
+ }
+ return response;
+ }
+
+ refresh(el = this) {
+ init(el);
+ }
+
+ connected() {}
+
+ ready() {}
+}
diff --git a/scripts/component-loader.js b/scripts/component-loader.js
new file mode 100644
index 00000000..512a8b9c
--- /dev/null
+++ b/scripts/component-loader.js
@@ -0,0 +1,131 @@
+import { config, getBreakPoint } from './libs.js';
+
+export default class ComponentLoader {
+ constructor(blockName, element) {
+ window.raqnComponents = window.raqnComponents || {};
+ this.blockName = blockName;
+ this.setBlockPaths();
+ this.block = element;
+ if (this.block) {
+ this.setParams();
+ this.content = this.block.children;
+ }
+ }
+
+ async loadCSS(href) {
+ return new Promise((resolve, reject) => {
+ if (!document.querySelector(`head > link[href="${href}"]`)) {
+ const link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = href;
+ link.onload = resolve;
+ link.onerror = reject;
+ document.head.append(link);
+ } else {
+ resolve();
+ }
+ });
+ }
+
+ setParams() {
+ const mediaParams = {};
+ this.params = {
+ ...Array.from(this.block.classList)
+ .filter((c) => c !== this.blockName && c !== 'block')
+ .reduce((acc, c) => {
+ const values = c.split('-');
+ let key = values.shift();
+ const breakpoint = getBreakPoint();
+ if (breakpoint === key) {
+ key = values.shift();
+ mediaParams[key] = values.join('-');
+ return acc;
+ }
+
+ if (config.breakpoints[key] !== undefined) {
+ return acc;
+ }
+
+ if (acc[key] && Array.isArray(acc[key])) {
+ acc[key].push(values.join('-'));
+ } else if (acc[key]) {
+ acc[key] = [acc[key], values.join('-')];
+ } else {
+ acc[key] = values.join('-');
+ }
+ return acc;
+ }, {}),
+ ...mediaParams,
+ };
+ }
+
+ setBlockPaths() {
+ this.cssPath = `/blocks/${this.blockName}/${this.blockName}.css`;
+ this.jsPath = `/blocks/${this.blockName}/${this.blockName}.js`;
+ }
+
+ setupElement() {
+ const elementName = `raqn-${this.blockName.toLowerCase()}`;
+ const element = document.createElement(elementName);
+ element.append(...this.block.children);
+ Object.keys(this.params).forEach((key) => {
+ const value = Array.isArray(this.params[key])
+ ? this.params[key].join(' ')
+ : this.params[key];
+ element.setAttribute(key, value);
+ });
+ this.block.replaceWith(element);
+ }
+
+ async loadWebComponent() {
+ return new Promise((resolve, reject) => {
+ (async () => {
+ try {
+ const mod = await import(this.jsPath);
+ if (
+ mod.default &&
+ mod.default.name &&
+ mod.default.name !== 'decorate'
+ ) {
+ const { name } = mod.default;
+ const elementName = `raqn-${name.toLowerCase()}`;
+ // define the custom element if it doesn't exist
+ if (!window.raqnComponents[name]) {
+ const Contructor = mod.default;
+ customElements.define(elementName, Contructor);
+ window.raqnComponents[name] = Contructor;
+ }
+ if (this.block) {
+ this.setupElement();
+ }
+ } else if (mod.default) {
+ await mod.default(this.block);
+ }
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.log(`failed to load module for ${this.blockName}`, error);
+ return reject(error);
+ }
+ return resolve();
+ })();
+ });
+ }
+
+ async decorate() {
+ if (window.raqnComponents[this.blockName]) {
+ return this.setupElement();
+ }
+ try {
+ const cssLoaded = this.loadCSS(this.cssPath).catch(() =>
+ // eslint-disable-next-line no-console
+ console.log(`${this.cssPath} does not exist`),
+ );
+ const decorationComplete = this.loadWebComponent();
+ return Promise.all([decorationComplete, cssLoaded]);
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.log(`failed to load module for ${this.blockName}`, error);
+ return Promise.resolve();
+ }
+ }
+}
diff --git a/scripts/delayed.js b/scripts/delayed.js
deleted file mode 100644
index 920b4ad8..00000000
--- a/scripts/delayed.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// eslint-disable-next-line import/no-cycle
-import { sampleRUM } from './lib-franklin.js';
-
-// Core Web Vitals RUM collection
-sampleRUM('cwv');
-
-// add more delayed functionality here
diff --git a/scripts/init.js b/scripts/init.js
new file mode 100644
index 00000000..e0a607ae
--- /dev/null
+++ b/scripts/init.js
@@ -0,0 +1,86 @@
+import ComponentLoader from './component-loader.js';
+import { config, debounce, eagerImage, getBreakPoint } from './libs.js';
+
+export function getInfos(blocks) {
+ return blocks.map((block) => {
+ let el = block;
+ const tagName = el.tagName.toLowerCase();
+ let name = tagName;
+ if (!config.elementBlocks.includes(tagName)) {
+ [name] = Array.from(el.classList);
+ } else {
+ // allow original way of defining blocks
+ el = document.createElement('div');
+ block.append(el);
+ }
+ return {
+ name,
+ el,
+ };
+ });
+}
+
+function getMeta(name) {
+ const meta = document.querySelector(`meta[name="${name}"]`);
+ if (!meta) {
+ return null;
+ }
+ return meta.content;
+}
+
+function lcpPriority() {
+ const eagerImages = getMeta('eager-images');
+ if (eagerImages) {
+ const length = parseInt(eagerImages, 10);
+ eagerImage(document.body, length);
+ }
+ const lcp = getMeta('lcp');
+ window.raqnLCP = lcp ? lcp.split(',').map((name) => ({ name })) : [];
+}
+
+export async function start({ name, el }) {
+ const loader = new ComponentLoader(name, el);
+ return loader.decorate();
+}
+
+export async function init(node = document) {
+ let blocks = Array.from(node.querySelectorAll('[class]:not([class^=style]'));
+
+ if (node === document) {
+ const header = node.querySelector('header');
+ const footer = node.querySelector('footer');
+ blocks = [header, ...blocks, footer];
+ }
+
+ const data = getInfos(blocks);
+ const lcp = window.raqnLCP;
+ const delay = window.raqnLCPDelay || [];
+ const priority = data.filter(({ name }) => lcp.includes(name));
+ const rest = data.filter(
+ ({ name }) => !lcp.includes(name) && !delay.includes(name),
+ );
+
+ // start with lcp and priority
+ Promise.all([
+ ...lcp.map(({ name, el }) => start({ name, el })),
+ ...priority.map(({ name, el }) => start({ name, el })),
+ ]);
+ // timeout for the rest to proper prioritize in case of stalled loading
+ rest.map(({ name, el }) => setTimeout(() => start({ name, el })));
+
+ // reload on breakpoint change to reset params and variables
+ window.raqnBreakpoint = getBreakPoint();
+ window.addEventListener(
+ 'resize',
+ debounce(() => {
+ // only on width / breakpoint changes
+ if (window.raqnBreakpoint !== getBreakPoint()) {
+ window.location.reload();
+ }
+ }, 100),
+ );
+}
+// mechanism of retrieving lang to be used in the app
+document.documentElement.lang = document.documentElement.lang || 'en';
+lcpPriority();
+init();
diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js
deleted file mode 100644
index 7b6cf0cf..00000000
--- a/scripts/lib-franklin.js
+++ /dev/null
@@ -1,737 +0,0 @@
-/*
- * Copyright 2022 Adobe. All rights reserved.
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License. You may obtain a copy
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
- * OF ANY KIND, either express or implied. See the License for the specific language
- * governing permissions and limitations under the License.
- */
-
-/**
- * log RUM if part of the sample.
- * @param {string} checkpoint identifies the checkpoint in funnel
- * @param {Object} data additional data for RUM sample
- */
-export function sampleRUM(checkpoint, data = {}) {
- sampleRUM.defer = sampleRUM.defer || [];
- const defer = (fnname) => {
- sampleRUM[fnname] = sampleRUM[fnname]
- || ((...args) => sampleRUM.defer.push({ fnname, args }));
- };
- sampleRUM.drain = sampleRUM.drain
- || ((dfnname, fn) => {
- sampleRUM[dfnname] = fn;
- sampleRUM.defer
- .filter(({ fnname }) => dfnname === fnname)
- .forEach(({ fnname, args }) => sampleRUM[fnname](...args));
- });
- sampleRUM.always = sampleRUM.always || [];
- sampleRUM.always.on = (chkpnt, fn) => { sampleRUM.always[chkpnt] = fn; };
- sampleRUM.on = (chkpnt, fn) => { sampleRUM.cases[chkpnt] = fn; };
- defer('observe');
- defer('cwv');
- try {
- window.hlx = window.hlx || {};
- if (!window.hlx.rum) {
- const usp = new URLSearchParams(window.location.search);
- const weight = (usp.get('rum') === 'on') ? 1 : 100; // with parameter, weight is 1. Defaults to 100.
- // eslint-disable-next-line no-bitwise
- const hashCode = (s) => s.split('').reduce((a, b) => (((a << 5) - a) + b.charCodeAt(0)) | 0, 0);
- const id = `${hashCode(window.location.href)}-${new Date().getTime()}-${Math.random().toString(16).substr(2, 14)}`;
- const random = Math.random();
- const isSelected = (random * weight < 1);
- const urlSanitizers = {
- full: () => window.location.href,
- origin: () => window.location.origin,
- path: () => window.location.href.replace(/\?.*$/, ''),
- };
- // eslint-disable-next-line object-curly-newline, max-len
- window.hlx.rum = { weight, id, random, isSelected, sampleRUM, sanitizeURL: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'] };
- }
- const { weight, id } = window.hlx.rum;
- if (window.hlx && window.hlx.rum && window.hlx.rum.isSelected) {
- const sendPing = (pdata = data) => {
- // eslint-disable-next-line object-curly-newline, max-len, no-use-before-define
- const body = JSON.stringify({ weight, id, referer: window.hlx.rum.sanitizeURL(), checkpoint, ...data });
- const url = `https://rum.hlx.page/.rum/${weight}`;
- // eslint-disable-next-line no-unused-expressions
- navigator.sendBeacon(url, body);
- // eslint-disable-next-line no-console
- console.debug(`ping:${checkpoint}`, pdata);
- };
- sampleRUM.cases = sampleRUM.cases || {
- cwv: () => sampleRUM.cwv(data) || true,
- lazy: () => {
- // use classic script to avoid CORS issues
- const script = document.createElement('script');
- script.src = 'https://rum.hlx.page/.rum/@adobe/helix-rum-enhancer@^1/src/index.js';
- document.head.appendChild(script);
- return true;
- },
- };
- sendPing(data);
- if (sampleRUM.cases[checkpoint]) { sampleRUM.cases[checkpoint](); }
- }
- if (sampleRUM.always[checkpoint]) { sampleRUM.always[checkpoint](data); }
- } catch (error) {
- // something went wrong
- }
-}
-
-/**
- * Loads a CSS file.
- * @param {string} href URL to the CSS file
- */
-export async function loadCSS(href) {
- return new Promise((resolve, reject) => {
- if (!document.querySelector(`head > link[href="${href}"]`)) {
- const link = document.createElement('link');
- link.rel = 'stylesheet';
- link.href = href;
- link.onload = resolve;
- link.onerror = reject;
- document.head.append(link);
- } else {
- resolve();
- }
- });
-}
-
-/**
- * Loads a non module JS file.
- * @param {string} src URL to the JS file
- * @param {Object} attrs additional optional attributes
- */
-
-export async function loadScript(src, attrs) {
- return new Promise((resolve, reject) => {
- if (!document.querySelector(`head > script[src="${src}"]`)) {
- const script = document.createElement('script');
- script.src = src;
- if (attrs) {
- // eslint-disable-next-line no-restricted-syntax, guard-for-in
- for (const attr in attrs) {
- script.setAttribute(attr, attrs[attr]);
- }
- }
- script.onload = resolve;
- script.onerror = reject;
- document.head.append(script);
- } else {
- resolve();
- }
- });
-}
-
-/**
- * Retrieves the content of metadata tags.
- * @param {string} name The metadata name (or property)
- * @returns {string} The metadata value(s)
- */
-export function getMetadata(name) {
- const attr = name && name.includes(':') ? 'property' : 'name';
- const meta = [...document.head.querySelectorAll(`meta[${attr}="${name}"]`)].map((m) => m.content).join(', ');
- return meta || '';
-}
-
-/**
- * Sanitizes a string for use as class name.
- * @param {string} name The unsanitized string
- * @returns {string} The class name
- */
-export function toClassName(name) {
- return typeof name === 'string'
- ? name.toLowerCase().replace(/[^0-9a-z]/gi, '-').replace(/-+/g, '-').replace(/^-|-$/g, '')
- : '';
-}
-
-/**
- * Sanitizes a string for use as a js property name.
- * @param {string} name The unsanitized string
- * @returns {string} The camelCased name
- */
-export function toCamelCase(name) {
- return toClassName(name).replace(/-([a-z])/g, (g) => g[1].toUpperCase());
-}
-
-const ICONS_CACHE = {};
-/**
- * Replace icons with inline SVG and prefix with codeBasePath.
- * @param {Element} [element] Element containing icons
- */
-export async function decorateIcons(element) {
- // Prepare the inline sprite
- let svgSprite = document.getElementById('franklin-svg-sprite');
- if (!svgSprite) {
- const div = document.createElement('div');
- div.innerHTML = '';
- svgSprite = div.firstElementChild;
- document.body.append(div.firstElementChild);
- }
-
- // Download all new icons
- const icons = [...element.querySelectorAll('span.icon')];
- await Promise.all(icons.map(async (span) => {
- const iconName = Array.from(span.classList).find((c) => c.startsWith('icon-')).substring(5);
- if (!ICONS_CACHE[iconName]) {
- ICONS_CACHE[iconName] = true;
- try {
- const response = await fetch(`${window.hlx.iconsPath}/${iconName}.svg`);
- if (!response.ok) {
- ICONS_CACHE[iconName] = false;
- return;
- }
- // Styled icons don't play nice with the sprite approach because of shadow dom isolation
- // and same for internal references
- const svg = await response.text();
- if (svg.match(/(