From 627cc87b3db51d35b2cf63bfa57af94500d01531 Mon Sep 17 00:00:00 2001 From: Andreas Haller Date: Tue, 6 Aug 2024 09:34:40 +0200 Subject: [PATCH 1/5] docs.raqn.io: adding basic table of content for developers content from markdown --- .eslintignore | 1 + blocks/developers-toc/developers-toc.css | 6 +++++ blocks/developers-toc/developers-toc.js | 32 ++++++++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 .eslintignore create mode 100644 blocks/developers-toc/developers-toc.css create mode 100644 blocks/developers-toc/developers-toc.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..c3de7ee0 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +tools/importer/helix-importer-ui/* diff --git a/blocks/developers-toc/developers-toc.css b/blocks/developers-toc/developers-toc.css new file mode 100644 index 00000000..58f7d06f --- /dev/null +++ b/blocks/developers-toc/developers-toc.css @@ -0,0 +1,6 @@ +raqn-developers-toc h2, +raqn-developers-toc h3 { + margin: 10px; + color: var(--scope-color); + font-size: var(--scope-font-size, 1.3em); +} \ No newline at end of file diff --git a/blocks/developers-toc/developers-toc.js b/blocks/developers-toc/developers-toc.js new file mode 100644 index 00000000..2226add7 --- /dev/null +++ b/blocks/developers-toc/developers-toc.js @@ -0,0 +1,32 @@ +import ComponentBase from '../../scripts/component-base.js'; + +const sitePathPrefix = 'developers'; +const henkelOrg = 'henkeldx'; + +export default class DeveloperToc extends ComponentBase { + ready() { + this.setupToc(); + } + + async setupToc() { + const response = await fetch(`/${sitePathPrefix}/query-index.json`); + if(!response.ok) return; + const json = await response.json(); + const repositories = json.data.reduce((map, page) => { + const [, , project, repository] = page.path.split('/'); + if(!map[project]) map[project] = new Set(); + map[project].add(repository); + return map; + }, {}); + const generatePages = (project, repository) => { + const pages = json.data.filter((page) => page.path.startsWith(`/${henkelOrg}/${project}/${repository}`)); + return pages.map((page) => `
  • ${page.title}
  • `); + }; + const generateRepository = (project, repository) => `
  • ${repository}

    +
  • `; + const generateProject = (project) => `
  • ${project}

    +
  • `; + const orgs = ``; + this.innerHTML = orgs; + } +} From 00a4b79d7dce7d5fca91aad833fcaaeb0ce0a64f Mon Sep 17 00:00:00 2001 From: Andreas Haller Date: Wed, 4 Sep 2024 16:19:35 +0200 Subject: [PATCH 2/5] add template mechanism for developers content --- blocks/developers-toc/developers-toc.js | 129 ++++++++++++++++++++---- scripts/init.js | 8 ++ templates/developers-content.js | 4 + 3 files changed, 122 insertions(+), 19 deletions(-) create mode 100644 templates/developers-content.js diff --git a/blocks/developers-toc/developers-toc.js b/blocks/developers-toc/developers-toc.js index 2226add7..64471c60 100644 --- a/blocks/developers-toc/developers-toc.js +++ b/blocks/developers-toc/developers-toc.js @@ -1,32 +1,123 @@ import ComponentBase from '../../scripts/component-base.js'; const sitePathPrefix = 'developers'; -const henkelOrg = 'henkeldx'; export default class DeveloperToc extends ComponentBase { ready() { - this.setupToc(); + this.generateTablesOfContent(); } - async setupToc() { + isIndex(node) { + return node.page && node.segment === 'README'; + } + + async loadPageHierarchy() { const response = await fetch(`/${sitePathPrefix}/query-index.json`); - if(!response.ok) return; + if(!response.ok) return []; const json = await response.json(); - const repositories = json.data.reduce((map, page) => { - const [, , project, repository] = page.path.split('/'); - if(!map[project]) map[project] = new Set(); - map[project].add(repository); - return map; - }, {}); - const generatePages = (project, repository) => { - const pages = json.data.filter((page) => page.path.startsWith(`/${henkelOrg}/${project}/${repository}`)); - return pages.map((page) => `
  • ${page.title}
  • `); + + const pageHierarchy = []; + const pageHierarchyObject = {children:pageHierarchy}; + let currentNode; + json.data.forEach(page => { + const segments = page.path.split('/').slice(1); + let currentParent = pageHierarchyObject; + let nodePath = ''; + segments.forEach((segment) => { + nodePath += `/${segment}`; + let node = currentParent.children.find((child) => child.segment === segment); + if (!node) { + node = { + nodePath, + segment, + active: window.location.pathname.startsWith(nodePath), + children: [], + }; + if(nodePath === page.path) { + node.page = page; + if(this.isIndex(node)) { + currentParent.link = page.path; + } + if(!currentNode && node.active) { + currentNode = node; + } + } + currentParent.children.push(node); + } + currentParent = node; + }); + }); + + const postProcessHierarchy = (node) => { + node.children.sort((a, b) => a.segment.localeCompare(b.segment)); + if(!node.page && !node.link) { + const firstChildPage = node.children.find((child) => child.page); + if(firstChildPage) { + node.link = firstChildPage.path; + } + } + node.children.forEach((child) => postProcessHierarchy(child)); }; - const generateRepository = (project, repository) => `
  • ${repository}

    -
      ${generatePages(project, repository)}
  • `; - const generateProject = (project) => `
  • ${project}

    -
      ${[...repositories[project]].map((repository) => generateRepository(project, repository))}
  • `; - const orgs = ``; - this.innerHTML = orgs; + postProcessHierarchy(pageHierarchyObject); + + return [pageHierarchy, currentNode]; + } + + generateRepository(repository) { + const a = document.createElement('a'); + a.href = repository.link; + a.innerText = repository.segment; + return `
  • ${a.outerHTML}

    `; + } + + generateProjects(org) { + return org.children.map((project) => { + const h2 = document.createElement('h2'); + h2.innerText = `${org.segment} - ${project.segment}`; + return `
  • ${h2.outerHTML} +
      ${ project.children.map((repository) => this.generateRepository(repository)).join('')}
  • `; + }).join(''); + } + + generatePages(node) { + if(this.isIndex(node)) return ''; + + const link = node.link || node.page?.path; + const li = document.createElement('li'); + if(link) { + const a = document.createElement('a'); + a.href = link; + a.innerText = node.segment; + li.innerHTML = a.outerHTML; + } else { + li.innerText = node.segment; + } + + const childrenHTML = node.children.map((child) => this.generatePages(child)).join(''); + if(childrenHTML) { + const ul = document.createElement('ul'); + ul.innerHTML = childrenHTML; + li.appendChild(ul); + } + + return li.outerHTML; + } + + async generateTablesOfContent() { + const [pageHierarchy, currentNode] = await this.loadPageHierarchy(); + const currentOrg = pageHierarchy.find((org) => org.active); + const currentProject = currentOrg?.children.find((project) => project.active); + const currentRepository = currentProject?.children.find((repository) => repository.active); + + let tocs = ``; + + if(currentRepository && currentNode) { + const h2 = document.createElement('h2'); + h2.innerText = `${currentOrg.segment} - ${currentProject.segment} - ${currentRepository.segment}`; + tocs += `
    ${h2.outerHTML} +
      ${currentRepository.children.map((child) => this.generatePages(child)).join('')}
    `; + } + + this.innerHTML = tocs; } } diff --git a/scripts/init.js b/scripts/init.js index 5d3f9b94..3850c46f 100644 --- a/scripts/init.js +++ b/scripts/init.js @@ -93,6 +93,7 @@ export const onLoadComponents = { ], async init() { + await this.setupTemplateAndAutoBlocks(); this.setLcp(); this.setStructure(); this.queryAllBlocks(); @@ -102,6 +103,13 @@ export const onLoadComponents = { this.initBlocks(); }, + async setupTemplateAndAutoBlocks() { + const template = getMeta('template'); + if(template) { + await import(`/templates/${template}.js`); + } + }, + queryAllBlocks() { this.blocks = [ document.body.querySelector(globalConfig.semanticBlocks[0]), diff --git a/templates/developers-content.js b/templates/developers-content.js new file mode 100644 index 00000000..7850e1b6 --- /dev/null +++ b/templates/developers-content.js @@ -0,0 +1,4 @@ +const main = document.body.querySelector('main'); +const section = document.createElement('div'); +section.innerHTML = '
    '; +main.prepend(section); From ddbbb2aec1fc271849a249c7fbdeb756ccb6aae5 Mon Sep 17 00:00:00 2001 From: Andreas Haller Date: Wed, 4 Sep 2024 16:29:14 +0200 Subject: [PATCH 3/5] add template mechanism for developers content --- blocks/developers-toc/developers-toc.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/blocks/developers-toc/developers-toc.js b/blocks/developers-toc/developers-toc.js index 64471c60..ba6130c0 100644 --- a/blocks/developers-toc/developers-toc.js +++ b/blocks/developers-toc/developers-toc.js @@ -11,13 +11,20 @@ export default class DeveloperToc extends ComponentBase { return node.page && node.segment === 'README'; } + toLink(path) { + if(window.location.host.startsWith('localhost') || window.location.host.search(/\.aem\.(page|live)/) > 0) { + return path; + } + return `/${sitePathPrefix}${path}`; + } + async loadPageHierarchy() { const response = await fetch(`/${sitePathPrefix}/query-index.json`); if(!response.ok) return []; const json = await response.json(); const pageHierarchy = []; - const pageHierarchyObject = {children:pageHierarchy}; + const pageHierarchyObject = { children:pageHierarchy }; let currentNode; json.data.forEach(page => { const segments = page.path.split('/').slice(1); @@ -65,7 +72,7 @@ export default class DeveloperToc extends ComponentBase { generateRepository(repository) { const a = document.createElement('a'); - a.href = repository.link; + a.href = this.toLink(repository.link); a.innerText = repository.segment; return `
  • ${a.outerHTML}

    `; } @@ -86,7 +93,7 @@ export default class DeveloperToc extends ComponentBase { const li = document.createElement('li'); if(link) { const a = document.createElement('a'); - a.href = link; + a.href = this.toLink(link); a.innerText = node.segment; li.innerHTML = a.outerHTML; } else { From 4ca272a29cb729537951aedccaa2f3ed2e8f0cda Mon Sep 17 00:00:00 2001 From: Andreas Haller Date: Fri, 6 Sep 2024 11:02:38 +0200 Subject: [PATCH 4/5] add template to the structure mechanism --- .../developers-content.css} | 0 .../developers-content.js} | 26 ++++++++++++++++--- scripts/init.js | 14 +++++----- scripts/libs.js | 4 +++ templates/developers-content.js | 4 --- 5 files changed, 33 insertions(+), 15 deletions(-) rename blocks/{developers-toc/developers-toc.css => developers-content/developers-content.css} (100%) rename blocks/{developers-toc/developers-toc.js => developers-content/developers-content.js} (88%) delete mode 100644 templates/developers-content.js diff --git a/blocks/developers-toc/developers-toc.css b/blocks/developers-content/developers-content.css similarity index 100% rename from blocks/developers-toc/developers-toc.css rename to blocks/developers-content/developers-content.css diff --git a/blocks/developers-toc/developers-toc.js b/blocks/developers-content/developers-content.js similarity index 88% rename from blocks/developers-toc/developers-toc.js rename to blocks/developers-content/developers-content.js index ba6130c0..41971f02 100644 --- a/blocks/developers-toc/developers-toc.js +++ b/blocks/developers-content/developers-content.js @@ -3,12 +3,32 @@ import ComponentBase from '../../scripts/component-base.js'; const sitePathPrefix = 'developers'; export default class DeveloperToc extends ComponentBase { + static loaderConfig = { + ...ComponentBase.loaderConfig, + targetsSelectors: 'main > div:first-child', + targetsSelectorsLimit: 1, + }; + + extendConfig() { + return [ + ...super.extendConfig(), + { + contentFromTargets: false, + addToTargetMethod: 'replaceWith', + targetsAsContainers: { + addToTargetMethod: 'prepend', + contentFromTargets: false, + }, + }, + ]; + } + ready() { this.generateTablesOfContent(); } isIndex(node) { - return node.page && node.segment === 'README'; + return node.page && (node.segment === 'README' || node.segment === 'readme'); } toLink(path) { @@ -60,13 +80,13 @@ export default class DeveloperToc extends ComponentBase { if(!node.page && !node.link) { const firstChildPage = node.children.find((child) => child.page); if(firstChildPage) { - node.link = firstChildPage.path; + node.link = firstChildPage.page.path; } } node.children.forEach((child) => postProcessHierarchy(child)); }; postProcessHierarchy(pageHierarchyObject); - + return [pageHierarchy, currentNode]; } diff --git a/scripts/init.js b/scripts/init.js index 3850c46f..5350202f 100644 --- a/scripts/init.js +++ b/scripts/init.js @@ -93,7 +93,6 @@ export const onLoadComponents = { ], async init() { - await this.setupTemplateAndAutoBlocks(); this.setLcp(); this.setStructure(); this.queryAllBlocks(); @@ -103,13 +102,6 @@ export const onLoadComponents = { this.initBlocks(); }, - async setupTemplateAndAutoBlocks() { - const template = getMeta('template'); - if(template) { - await import(`/templates/${template}.js`); - } - }, - queryAllBlocks() { this.blocks = [ document.body.querySelector(globalConfig.semanticBlocks[0]), @@ -151,6 +143,12 @@ export const onLoadComponents = { componentName: name.trim(), }; }); + const template = getMeta('template'); + if(template) { + this.structureComponents = [...this.structureComponents, { + componentName: template, + }]; + } }, setLcpBlocks() { diff --git a/scripts/libs.js b/scripts/libs.js index 11c0be2b..36f98a6f 100644 --- a/scripts/libs.js +++ b/scripts/libs.js @@ -43,6 +43,10 @@ export const metaTags = { metaNamePrefix: 'structure', // contentType: 'boolean string', }, + template: { + metaName: 'structure', + // contentType: 'boolean string', + }, lcp: { metaName: 'lcp', fallbackContent: ['theming', 'header', 'breadcrumbs'], diff --git a/templates/developers-content.js b/templates/developers-content.js deleted file mode 100644 index 7850e1b6..00000000 --- a/templates/developers-content.js +++ /dev/null @@ -1,4 +0,0 @@ -const main = document.body.querySelector('main'); -const section = document.createElement('div'); -section.innerHTML = '
    '; -main.prepend(section); From 61abdf4fdfbf52047c0846f84aaeb55dde0106d6 Mon Sep 17 00:00:00 2001 From: Andreas Haller Date: Fri, 6 Sep 2024 11:24:13 +0200 Subject: [PATCH 5/5] add template to the structure mechanism --- scripts/init.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/init.js b/scripts/init.js index 5350202f..57ce18fd 100644 --- a/scripts/init.js +++ b/scripts/init.js @@ -143,7 +143,7 @@ export const onLoadComponents = { componentName: name.trim(), }; }); - const template = getMeta('template'); + const template = getMeta(metaTags.template.metaName); if(template) { this.structureComponents = [...this.structureComponents, { componentName: template,