@gouvfr/dsfr-roller 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +11 -0
  3. package/index.js +5 -0
  4. package/package.json +65 -0
  5. package/src/component/component.js +50 -0
  6. package/src/component/components/action.js +28 -0
  7. package/src/component/components/breadcrumb.js +33 -0
  8. package/src/component/components/button.js +37 -0
  9. package/src/component/components/display-body.js +41 -0
  10. package/src/component/components/display-modal.js +29 -0
  11. package/src/component/components/footer.js +85 -0
  12. package/src/component/components/header.js +96 -0
  13. package/src/component/components/navigation.js +72 -0
  14. package/src/component/components/sidemenu.js +58 -0
  15. package/src/component/components/skiplink.js +49 -0
  16. package/src/component/components/translate.js +32 -0
  17. package/src/component/components/version.js +21 -0
  18. package/src/component/ejs/version/version.ejs +46 -0
  19. package/src/component/ejs/version/versions.ejs +31 -0
  20. package/src/component/ejs-pkg.js +6 -0
  21. package/src/component/render.js +19 -0
  22. package/src/constants.js +7 -0
  23. package/src/core/format-html.js +16 -0
  24. package/src/core/format-link.js +8 -0
  25. package/src/core/html-renderable.js +10 -0
  26. package/src/core/renderable.js +20 -0
  27. package/src/integration/bundler.js +47 -0
  28. package/src/integration/indexing/indexer.js +23 -0
  29. package/src/integration/indexing/sitemap.js +44 -0
  30. package/src/integration/redirect/redirection-type.js +4 -0
  31. package/src/integration/redirect/redirection.js +77 -0
  32. package/src/node/custom/html-container-node.js +7 -0
  33. package/src/node/directive/doc/accordion-container-directive.js +59 -0
  34. package/src/node/directive/doc/accordions-group-container-directive.js +12 -0
  35. package/src/node/directive/doc/anatomy-container-directive.js +61 -0
  36. package/src/node/directive/doc/button-leaf-directive.js +37 -0
  37. package/src/node/directive/doc/changelog-leaf-directive.js +238 -0
  38. package/src/node/directive/doc/figma-leaf-directive.js +21 -0
  39. package/src/node/directive/doc/guideline-container-directive.js +71 -0
  40. package/src/node/directive/doc/guidelines-container-directive.js +13 -0
  41. package/src/node/directive/doc/image-text-directive.js +13 -0
  42. package/src/node/directive/doc/pin-leaf-directive.js +46 -0
  43. package/src/node/directive/doc/storybook-leaf-directive.js +18 -0
  44. package/src/node/directive/doc/tab-container-directive.js +22 -0
  45. package/src/node/directive/doc/tab-navigation-container-directive.js +49 -0
  46. package/src/node/directive/doc/table-container-directive.js +56 -0
  47. package/src/node/directive/doc/tabs-container-directive.js +71 -0
  48. package/src/node/generic/blockquote-node.js +26 -0
  49. package/src/node/generic/break-node.js +11 -0
  50. package/src/node/generic/code-node.js +37 -0
  51. package/src/node/generic/definition-node.js +13 -0
  52. package/src/node/generic/emphasis-node.js +11 -0
  53. package/src/node/generic/heading-node.js +56 -0
  54. package/src/node/generic/html-node.js +11 -0
  55. package/src/node/generic/image-node.js +14 -0
  56. package/src/node/generic/image-reference-node.js +14 -0
  57. package/src/node/generic/inline-code-node.js +21 -0
  58. package/src/node/generic/link-node.js +26 -0
  59. package/src/node/generic/link-reference-node.js +14 -0
  60. package/src/node/generic/list-item-node.js +11 -0
  61. package/src/node/generic/list-node.js +15 -0
  62. package/src/node/generic/paragraph-node.js +11 -0
  63. package/src/node/generic/strong-node.js +11 -0
  64. package/src/node/generic/text-node.js +15 -0
  65. package/src/node/generic/thematic-break-node.js +12 -0
  66. package/src/node/gfm/table-body-node.js +11 -0
  67. package/src/node/gfm/table-cell-node.js +17 -0
  68. package/src/node/gfm/table-head-node.js +11 -0
  69. package/src/node/gfm/table-header-node.js +16 -0
  70. package/src/node/gfm/table-node.js +91 -0
  71. package/src/node/gfm/table-row-node.js +11 -0
  72. package/src/node/node-factory.js +128 -0
  73. package/src/node/node-root.js +25 -0
  74. package/src/node/node.js +106 -0
  75. package/src/page/body/custom-header.js +40 -0
  76. package/src/page/body/main.js +23 -0
  77. package/src/page/body/scheme.js +9 -0
  78. package/src/page/head/canonical.js +16 -0
  79. package/src/page/head/favicon.js +14 -0
  80. package/src/page/head/head.js +35 -0
  81. package/src/page/head/share.js +14 -0
  82. package/src/page/head/stylesheets.js +48 -0
  83. package/src/page/head/title.js +9 -0
  84. package/src/page/html.js +46 -0
  85. package/src/page/page.js +39 -0
  86. package/src/page/scripts/highlight.js +53 -0
  87. package/src/page/scripts/scripts.js +57 -0
  88. package/src/script/main/core/element.js +29 -0
  89. package/src/script/main/elements/copy-snippet.js +35 -0
  90. package/src/script/main/elements/searchbar.js +18 -0
  91. package/src/script/main/elements/storybook.js +20 -0
  92. package/src/script/main/index.js +10 -0
  93. package/src/script/search/index.js +0 -0
  94. package/src/style/main/components/_dsfr-doc-anatomy.scss +54 -0
  95. package/src/style/main/components/_dsfr-doc-code-snippet.scss +9 -0
  96. package/src/style/main/components/_dsfr-doc-figma-leaf.scss +6 -0
  97. package/src/style/main/components/_dsfr-doc-guideline.scss +58 -0
  98. package/src/style/main/components/_dsfr-doc-tab-navigation.scss +99 -0
  99. package/src/style/main/components/_dsfr-doc-version.scss +154 -0
  100. package/src/style/main/components/_index.scss +6 -0
  101. package/src/style/main/index.scss +3 -0
  102. package/src/style/main/third-party/_highlight.scss +227 -0
  103. package/src/style/main/third-party/_index.scss +1 -0
  104. package/src/style/main/utility/_dsfr-doc-code.scss +9 -0
  105. package/src/style/main/utility/_font.scss +31 -0
  106. package/src/style/main/utility/_icon.scss +4 -0
  107. package/src/style/main/utility/_index.scss +3 -0
  108. package/src/style/search/index.scss +0 -0
  109. package/src/template/template-factory.js +16 -0
  110. package/src/template/template.js +27 -0
  111. package/src/template/templates/editorial-template.js +45 -0
  112. package/src/template/templates/home-template.js +18 -0
  113. package/src/template/templates/search-template.js +20 -0
@@ -0,0 +1,106 @@
1
+ import { Renderable } from '../core/renderable.js';
2
+ import { nodeFactory } from './node-factory.js';
3
+ import { log, normalize, TagAttributes } from '@gouvfr/dsfr-forge';
4
+ class Node extends Renderable {
5
+ constructor (data, tagName = null, isSelfClosing = false, hasNormalizedId = false) {
6
+ super(data);
7
+ this._tagName = tagName || this.data.tagName;
8
+ this._isSelfClosing = isSelfClosing;
9
+ this._hasNormalizedId = hasNormalizedId;
10
+ this._type = this.data.type;
11
+ this._children = Array.isArray(this.data.children) ? this.data.children.map(childData => nodeFactory.create(childData)) : [];
12
+ if (typeof this.data?.attributes === 'object' && Object.keys(this.data.attributes).length > 0) {
13
+ Object.entries(this.data.attributes).forEach(([key, value]) => {
14
+ this.attributes.setAttribute(key, value);
15
+ });
16
+ }
17
+ if (typeof this.data?.classes === 'object' && Array.isArray(this.data.classes)) {
18
+ this.attributes.setClasses(this.data.classes);
19
+ }
20
+ }
21
+
22
+ get children () {
23
+ return this._children;
24
+ }
25
+
26
+ get type () {
27
+ return this._type;
28
+ }
29
+
30
+ set tagName (value) {
31
+ this._tagName = value;
32
+ }
33
+
34
+ get tagName () {
35
+ return this._tagName;
36
+ }
37
+
38
+ get isSelfClosing () {
39
+ return this._isSelfClosing;
40
+ }
41
+
42
+ get hasNormalizedId () {
43
+ return this._hasNormalizedId;
44
+ }
45
+
46
+ get attributes () {
47
+ if (!this._attributes) {
48
+ this._attributes = new TagAttributes();
49
+ }
50
+ return this._attributes;
51
+ }
52
+
53
+ renderAttributes () {
54
+ return this._attributes ? this._attributes.render() : '';
55
+ }
56
+
57
+ getAttributes () {
58
+ return this._attributes ? this._attributes.getAttributes() : {};
59
+ }
60
+
61
+ findDescendantsByType (type) {
62
+ const descendants = [];
63
+ for (const child of this._children) {
64
+ if (child.type === type) descendants.push(child);
65
+ descendants.push(...child.findDescendantsByType(type));
66
+ }
67
+ return descendants;
68
+ }
69
+
70
+ async renderChildren () {
71
+ let html = '';
72
+ for (const child of this._children) {
73
+ html += await child.render();
74
+ }
75
+ return html;
76
+ }
77
+
78
+ async render () {
79
+ const html = await this.renderChildren();
80
+
81
+ if (!this.tagName) return html;
82
+
83
+ if (this.hasNormalizedId) {
84
+ this.attributes.setAttribute('id', normalize(this.text));
85
+ }
86
+
87
+ return this.isSelfClosing ? `<${this.tagName}${this.renderAttributes()}/>` : `<${this.tagName}${this.renderAttributes()}>${html}</${this.tagName}>`;
88
+ }
89
+
90
+ get text () {
91
+ return this._children.map(child => child.text).join(' ');
92
+ }
93
+
94
+ static getImageChild (data) {
95
+ if (data.type === 'image') return data;
96
+ if (data.children) {
97
+ for (const child of data.children) {
98
+ const image = Node.getImageChild(child);
99
+ if (image) return image;
100
+ }
101
+ }
102
+ return null;
103
+ }
104
+ }
105
+
106
+ export { Node };
@@ -0,0 +1,40 @@
1
+ import { Renderable } from '../../core/renderable.js';
2
+ import { Translate } from '../../component/components/translate.js';
3
+ import { Version } from '../../component/components/version.js';
4
+ import { Header } from '../../component/components/header.js';
5
+ import { Navigation } from '../../component/components/navigation.js';
6
+
7
+ class CustomHeader extends Renderable {
8
+ constructor (data) {
9
+ super(data);
10
+ if (data.translate) this._translate = new Translate(data.translate);
11
+ this._version = new Version(data.version);
12
+ this._header = new Header(data.resource.header);
13
+ this._navigation = new Navigation(data.resource.navigation.main);
14
+ }
15
+ async render () {
16
+ let toolsContent = await this._version.render({ id: 'version', collapseId: 'version-collapse' });
17
+ if (this._translate) toolsContent += await this._translate.render({ id: 'translate', collapseId: 'translate-collapse' });
18
+
19
+ let menuContent = await this._version.render({ id: 'version-menu', collapseId: 'version-collapse-menu' });
20
+ if (this._translate) menuContent += await this._translate.render({ id: 'translate-menu', collapseId: 'translate-collapse-menu' });
21
+
22
+ const navigation = await this._navigation.format();
23
+
24
+ return await this._header.render({
25
+ body: {
26
+ tools: {
27
+ toolsContent: toolsContent
28
+ }
29
+ },
30
+ menu: {
31
+ tools: {
32
+ toolsContent: menuContent
33
+ },
34
+ navigation: navigation
35
+ }
36
+ });
37
+ }
38
+ }
39
+
40
+ export { CustomHeader };
@@ -0,0 +1,23 @@
1
+ import { templateFactory } from '../../template/template-factory.js';
2
+ import { Renderable } from '../../core/renderable.js';
3
+
4
+ class Main extends Renderable {
5
+ constructor (data) {
6
+ super(data);
7
+ this._template = templateFactory(this.data);
8
+ }
9
+
10
+ async render () {
11
+ return `
12
+ <main id="content" role="main">
13
+ ${await this._template.render()}
14
+ </main>
15
+ `;
16
+ }
17
+
18
+ get text () {
19
+ return this._template.text;
20
+ }
21
+ }
22
+
23
+ export { Main };
@@ -0,0 +1,9 @@
1
+ import { Renderable } from '../../core/renderable.js';
2
+
3
+ class Scheme extends Renderable {
4
+ async render () {
5
+ return '<script type="module">const e="system",t="dark",c="dark",o="data-fr-theme",a="data-fr-scheme",r=`:root[${o}], :root[${a}]`,m=()=>{document.documentElement.setAttribute(o,c),document.documentElement.style.colorScheme="dark"},n=()=>{window.matchMedia("(prefers-color-scheme: dark)").matches&&m()};(()=>{if(document.documentElement.matches(r)){const c=(()=>{try{return"localStorage"in window&&null!==window.localStorage}catch(e){return!1}})()?localStorage.getItem("scheme"):"",o=document.documentElement.getAttribute(a);switch(!0){case c===t:m();break;case c===e:n();break;case o===t:m();break;case o===e:n()}}})();</script>';
6
+ }
7
+ }
8
+
9
+ export { Scheme };
@@ -0,0 +1,16 @@
1
+ import { DOMAIN } from '../../constants.js'
2
+ import { Renderable } from '../../core/renderable.js';
3
+
4
+ class Canonical extends Renderable {
5
+ async render () {
6
+ const links = [`<link rel="canonical" href="${DOMAIN}${this.data.url}">`];
7
+
8
+ for (const alt of this.data.alts) {
9
+ links.push(`<link rel="alternate" href="${DOMAIN}${alt.url}" hreflang="${alt.lang}">`);
10
+ }
11
+
12
+ return links.join('\n');
13
+ }
14
+ }
15
+
16
+ export { Canonical };
@@ -0,0 +1,14 @@
1
+ import { Renderable } from '../../core/renderable.js';
2
+
3
+ class Favicon extends Renderable {
4
+ async render () {
5
+ return `
6
+ <meta name="theme-color" content="#000091">
7
+ <link rel="apple-touch-icon" href="/dist/favicon/apple-touch-icon.png">
8
+ <link rel="icon" href="/dist/favicon/favicon.svg" type="image/svg+xml">
9
+ <link rel="shortcut icon" href="/dist/favicon/favicon.ico" type="image/x-icon">
10
+ <link rel="manifest" href="/dist/favicon/manifest.webmanifest" crossorigin="use-credentials">`
11
+ }
12
+ }
13
+
14
+ export { Favicon };
@@ -0,0 +1,35 @@
1
+ import { Title } from './title.js';
2
+ import { Canonical } from './canonical.js';
3
+ import { Stylesheets } from './stylesheets.js';
4
+ import { Renderable } from '../../core/renderable.js';
5
+ import { Favicon } from './favicon.js';
6
+ import { Share } from './share.js';
7
+
8
+ class Head extends Renderable {
9
+
10
+ constructor (data) {
11
+ super(data);
12
+ this._title = new Title(data);
13
+ this._canonical = new Canonical(data);
14
+ this._favicon = new Favicon(data);
15
+ this._share = new Share(data);
16
+ this._stylesheets = new Stylesheets(data);
17
+ }
18
+
19
+ async render () {
20
+ return `
21
+ <head>
22
+ ${await this._title.render()}
23
+ <meta charset="utf-8">
24
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
25
+ <meta name="format-detection" content="telephone=no,date=no,address=no,email=no,url=no">
26
+ ${await this._canonical.render()}
27
+ ${await this._favicon.render()}
28
+ ${await this._share.render()}
29
+ ${await this._stylesheets.render()}
30
+ </head>
31
+ `;
32
+ }
33
+ }
34
+
35
+ export { Head };
@@ -0,0 +1,14 @@
1
+ import { Renderable } from '../../core/renderable.js';
2
+
3
+ class Share extends Renderable {
4
+ constructor (data) {
5
+ super(data);
6
+ }
7
+
8
+ async render () {
9
+ // facebook, twitter, linkedin, bluesky
10
+ return '<!-- Share -->';
11
+ }
12
+ }
13
+
14
+ export { Share };
@@ -0,0 +1,48 @@
1
+ import { Renderable } from '../../core/renderable.js';
2
+ import { TagAttributes } from '@gouvfr/dsfr-forge'
3
+
4
+ const DIST_FILES = [
5
+ 'dsfr',
6
+ 'utility/utility',
7
+ ];
8
+
9
+ const DEFAULT_LIB_FILES = [
10
+ 'dsfr-doc-main',
11
+ ];
12
+
13
+ const STYLES_MAP = new Map([
14
+ ['search', 'dsfr-doc-search']
15
+ ]);
16
+
17
+ class Stylesheets extends Renderable {
18
+ constructor (data) {
19
+ super(data);
20
+ this._stylesheets = [
21
+ ...DIST_FILES.map(filename => new Stylesheet({ href: `/dist/${filename}.min.css` })),
22
+ ...DEFAULT_LIB_FILES.map(filename => new Stylesheet({ href: `/lib/${filename}.min.css` }))
23
+ ];
24
+ if (Array.isArray(this.data.styles) && this.data.styles.length > 0) {
25
+ this._stylesheets.push(...this.data.styles.map(style => new Stylesheet({ href: `/lib/${STYLES_MAP.get(style)}.min.css` })));
26
+ }
27
+ }
28
+ async render () {
29
+ const stylesheets = await Promise.all(this._stylesheets.map(async stylesheet => await stylesheet.render()));
30
+ return stylesheets.join('\n');
31
+ }
32
+ }
33
+
34
+ class Stylesheet extends Renderable {
35
+ constructor (data) {
36
+ super(data);
37
+ this._attributes = new TagAttributes();
38
+ this._attributes.setAttribute('rel', 'stylesheet');
39
+ this._attributes.setAttribute('href', this.data.href);
40
+ if (this.data.integrity) this._attributes.setAttribute('integrity', this.data.integrity);
41
+ if (this.data.crossorigin) this._attributes.setAttribute('crossorigin', this.data.crossorigin);
42
+ }
43
+ async render () {
44
+ return `<link${this._attributes.render()}>`;
45
+ }
46
+ }
47
+
48
+ export { Stylesheets };
@@ -0,0 +1,9 @@
1
+ import { Renderable } from '../../core/renderable.js';
2
+
3
+ class Title extends Renderable {
4
+ async render () {
5
+ return `<title>${this.data.title}</title>`;
6
+ }
7
+ }
8
+
9
+ export { Title };
@@ -0,0 +1,46 @@
1
+ import { Head } from './head/head.js';
2
+ import { Footer } from '../component/components/footer.js';
3
+ import { DisplayModal } from '../component/components/display-modal.js';
4
+ import { Skiplink } from '../component/components/skiplink.js';
5
+ import { CustomHeader } from './body/custom-header.js';
6
+ import { Scheme } from './body/scheme.js';
7
+ import { Main } from './body/main.js';
8
+ import { Scripts } from './scripts/scripts.js';
9
+ import { HtmlRenderable } from '../core/html-renderable.js';
10
+
11
+ class Html extends HtmlRenderable {
12
+ constructor(data) {
13
+ super(data);
14
+
15
+ this._head = new Head(data);
16
+ this._scheme = new Scheme(data);
17
+ this._skiplink = new Skiplink(data);
18
+ this._header = new CustomHeader(data);
19
+ this._main = new Main(data);
20
+ this._footer = new Footer(data.resource.footer);
21
+ this._displayModal = new DisplayModal(data);
22
+ this._scripts = new Scripts(data);
23
+ }
24
+
25
+ async render () {
26
+ return this.format(`<!doctype html>
27
+ <html lang="${this.data.lang}" data-fr-theme>
28
+ ${await this._head.render()}
29
+ <body>
30
+ ${await this._scheme.render()}
31
+ ${await this._skiplink.render()}
32
+ ${await this._header.render()}
33
+ ${await this._main.render()}
34
+ ${await this._footer.render()}
35
+ ${await this._displayModal.render()}
36
+ ${await this._scripts.render()}
37
+ </body>
38
+ </html>`);
39
+ }
40
+
41
+ get text () {
42
+ return this._main.text;
43
+ }
44
+ }
45
+
46
+ export { Html };
@@ -0,0 +1,39 @@
1
+ import fs from 'fs';
2
+ import yaml from 'yaml';
3
+ import { Html } from './html.js';
4
+ import { log } from '@gouvfr/dsfr-forge';
5
+ import { Renderable } from '../core/renderable.js'
6
+
7
+ class Page extends Renderable {
8
+ constructor (src) {
9
+ const dataFile = fs.readFileSync(src, 'utf8');
10
+ const data = yaml.parse(dataFile);
11
+ super(data);
12
+ this._src = src;
13
+ this._dest = `${this.data.url}/index.html`;
14
+ this._html = new Html(this.data);
15
+ }
16
+
17
+ async render () {
18
+ log.info(`Rendering page ${this._src}`);
19
+ return this._html.render();
20
+ }
21
+
22
+ get document () {
23
+ return {
24
+ url: this.data.url,
25
+ title: this.data.title,
26
+ keywords: this.data.keywords,
27
+ summary: this.data.summary,
28
+ excerpt: this.data.excerpt,
29
+ boost: this.data.boost,
30
+ text: this._html.text
31
+ }
32
+ }
33
+
34
+ get dest () {
35
+ return this._dest;
36
+ }
37
+ }
38
+
39
+ export { Page };
@@ -0,0 +1,53 @@
1
+ export const HIGHLIGHT_SCRIPTS = [
2
+ {
3
+ src: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js',
4
+ integrity: 'sha512-6yoqbrcLAHDWAdQmiRlHG4+m0g/CT/V9AGyxabG8j7Jk8j3r3K6due7oqpiRMZqcYe9WM2gPcaNNxnl2ux+3tA==',
5
+ crossorigin: 'anonymous',
6
+ referrerpolicy: 'no-referrer'
7
+ },
8
+ {
9
+ src: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/javascript.min.js',
10
+ integrity: 'sha512-XrpvbK+zc0wErJG1VptH0H4w4zyiniHOBR35DJ1VISA+cqYxhksvqFwZk0M8lX9ylaIjTXoMYolOPb93zdrGpg==',
11
+ crossorigin: 'anonymous',
12
+ referrerpolicy: 'no-referrer'
13
+ },
14
+ {
15
+ src: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/xml.min.js',
16
+ integrity: 'sha512-dG+W2e5Wf51XUF9HqsX31z5+nTTuxe8wpOEC3/1gCJImJusP1FZS1PHxiH3NjBUQJ6oDpVRKKXH7+aCVd+wkDA==',
17
+ crossorigin: 'anonymous',
18
+ referrerpolicy: 'no-referrer'
19
+ },
20
+ {
21
+ src: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/scss.min.js',
22
+ integrity: 'sha512-fhGqLFbE7MvF94wQ72OFn5VsWhqnvxciu8Zq2omUg641h1LxrluzrmGBXEF0Bhlev3p5Am9kpLfunqGw5JaRtA==',
23
+ crossorigin: 'anonymous',
24
+ referrerpolicy: 'no-referrer'
25
+ },
26
+ {
27
+ src: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/css.min.js',
28
+ integrity: 'sha512-fpDUuCO8gpUPZ7TzS3mjJsopogeCbFf94kXHQNzOdEQXksHWOiOHaynatkhBRQraX1GMVtLlU5Z/8NWuK8TLLw==',
29
+ crossorigin: 'anonymous',
30
+ referrerpolicy: 'no-referrer'
31
+ },
32
+ {
33
+ src: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/bash.min.js',
34
+ integrity: 'sha512-4ea7XTSjSgnYA2UCmeriRmmIfJ5iXR562Y8faBB+y3BKcwftFMofy6ff0wX4A4UmR155PlFghb5np+WrAVIoaw==',
35
+ crossorigin: 'anonymous',
36
+ referrerpolicy: 'no-referrer'
37
+ },
38
+ {
39
+ src: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/json.min.js',
40
+ integrity: 'sha512-hWf27MxSv3ZoSOnIh0STq7QrN5YWaGxD53WCPl8GS7WboKLvz+x/FK6431QNwZ6vz6tigXSw1D1z9gz1WwoObg==',
41
+ crossorigin: 'anonymous',
42
+ referrerpolicy: 'no-referrer'
43
+ },
44
+ {
45
+ src: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/yaml.min.js',
46
+ integrity: 'sha512-10TmDDlpvsCOKvlpApa381uvcDbBL4WeJw0txuN8c786A6kMyS/Ts93o4XbtDtBnzXzdqtTbBb1xiDW5YP3x9w==',
47
+ crossorigin: 'anonymous',
48
+ referrerpolicy: 'no-referrer'
49
+ },
50
+ {
51
+ innerScript: 'hljs.highlightAll();'
52
+ }
53
+ ];
@@ -0,0 +1,57 @@
1
+ import { Renderable } from '../../core/renderable.js';
2
+ import { TagAttributes } from '@gouvfr/dsfr-forge';
3
+ import { HIGHLIGHT_SCRIPTS } from './highlight.js'
4
+
5
+ const DIST_FILES = [
6
+ 'dsfr',
7
+ ];
8
+
9
+ const DEFAULT_LIB_FILES = [
10
+ 'dsfr-doc-main',
11
+ ];
12
+
13
+ const SCRIPTS_MAP = new Map([
14
+ ['search', 'dsfr-doc-search']
15
+ ]);
16
+
17
+ class Scripts extends Renderable {
18
+ constructor (data) {
19
+ super(data);
20
+ this._innerScript = this.data.innerScript ?? '';
21
+ this._scripts = [
22
+ ...DIST_FILES.map(filename => new Script({ src: `/dist/${filename}.module.min.js`, type: 'module' })),
23
+ ...DEFAULT_LIB_FILES.map(filename => new Script({ src: `/lib/${filename}.min.js`, type: 'module' })),
24
+ ];
25
+
26
+ if (Array.isArray(this.data.scripts) && this.data.scripts.length > 0) {
27
+ this._scripts.push(...this.data.scripts.map(script => new Script({ src: `/lib/${SCRIPTS_MAP.get(script)}.min.js`, type: 'module' })));
28
+ }
29
+
30
+ this._scripts.push(...HIGHLIGHT_SCRIPTS.map(data => new Script(data)));
31
+ }
32
+
33
+ async render () {
34
+ const scripts = await Promise.all(this._scripts.map(async script => await script.render()));
35
+ return scripts.join('\n');
36
+ }
37
+ }
38
+
39
+ class Script extends Renderable {
40
+ constructor (data) {
41
+ super(data)
42
+ this._innerScript = this.data.innerScript ?? '';
43
+ this._attributes = new TagAttributes();
44
+ if (this.data.type) this._attributes.setAttribute('type', this.data.type);
45
+ if (this.data.src) this._attributes.setAttribute('src', this.data.src);
46
+ if (this.data.integrity) this._attributes.setAttribute('integrity', this.data.integrity);
47
+ if (this.data.crossorigin) this._attributes.setAttribute('crossorigin', this.data.crossorigin);
48
+ if (this.data.referrerPolicy) this._attributes.setAttribute('referrerpolicy', this.data.referrerPolicy);
49
+ if (this.data.defer) this._attributes.setAttribute('defer', '');
50
+ if (this.data.async) this._attributes.setAttribute('async', '');
51
+ }
52
+ async render () {
53
+ return `<script${this._attributes.render()}>${this._innerScript}</script>`;
54
+ }
55
+ }
56
+
57
+ export { Scripts };
@@ -0,0 +1,29 @@
1
+ class Element {
2
+ constructor (element) {
3
+ this._element = element;
4
+ this.init();
5
+ }
6
+
7
+ get element () {
8
+ return this._element;
9
+ }
10
+
11
+ init () {}
12
+
13
+ observeResize (target) {
14
+ this._observer = new ResizeObserver(this.resize.bind(this));
15
+ this._observer.observe(target);
16
+ }
17
+
18
+ resize () {}
19
+
20
+ listenClick (target) {
21
+ target.addEventListener('click', this.handleClick.bind(this));
22
+ }
23
+
24
+ handleClick () {}
25
+ }
26
+
27
+ const instantiateElements = (selector, ElementClass) => [...document.querySelectorAll(selector)].map(element => new ElementClass(element));
28
+
29
+ export { Element, instantiateElements };
@@ -0,0 +1,35 @@
1
+ import { Element } from '../core/element.js'
2
+
3
+ class CopySnippet extends Element {
4
+ get button () {
5
+ return this.element;
6
+ }
7
+ init() {
8
+ this._copyLabel = this.button.innerText;
9
+ this._copiedLabel = this.button.getAttribute('data-label-copied');
10
+ this._code = this.button.previousElementSibling.innerText;
11
+ this.listenClick(this.button);
12
+ }
13
+
14
+ get isCopied () {
15
+ return this._isCopied;
16
+ }
17
+
18
+ set isCopied (value) {
19
+ this._isCopied = value;
20
+ this.button.innerText = value ? this._copiedLabel : this._copyLabel;
21
+ this.button.disabled = value;
22
+ }
23
+
24
+ handleClick () {
25
+ navigator.clipboard.writeText(this._code);
26
+ this.isCopied = true;
27
+ setTimeout(this._handleTimeout.bind(this), 1500);
28
+ }
29
+
30
+ _handleTimeout () {
31
+ this.isCopied = false;
32
+ }
33
+ }
34
+
35
+ export default CopySnippet;
@@ -0,0 +1,18 @@
1
+ import { Element } from '../core/element.js'
2
+
3
+ class Searchbar extends Element {
4
+ init () {
5
+ this._input = this.element.querySelector('#search-input');
6
+ this._button = this.element.querySelector('#search-button');
7
+ this.listenClick(this._button);
8
+ }
9
+
10
+ handleClick () {
11
+ const query = this._input.value;
12
+ const href = this._button.getAttribute('data-href');
13
+ document.location.href = `${href}?query=${query}`;
14
+ }
15
+
16
+ }
17
+
18
+ export { Searchbar };
@@ -0,0 +1,20 @@
1
+ import { Element } from '../core/element.js'
2
+
3
+ class Storybook extends Element {
4
+ get iframe () {
5
+ return this.element;
6
+ }
7
+
8
+ init() {
9
+ this._iframeDoc = this.iframe.contentDocument;
10
+ if (!this._iframeDoc) return;
11
+ this.observeResize(this._iframeDoc.body);
12
+ }
13
+
14
+ resize () {
15
+ const height = this._iframeDoc.body?.scrollHeight ?? this._iframeDoc.documentElement?.scrollHeight;
16
+ this.iframe.style.height = isNaN(height) || !height ? '100%' : height + 'px';
17
+ }
18
+ }
19
+
20
+ export default Storybook;
@@ -0,0 +1,10 @@
1
+ import { instantiateElements } from './core/element.js'
2
+ import CopySnippet from './elements/copy-snippet.js';
3
+ import Storybook from './elements/storybook.js';
4
+ import { Searchbar } from './elements/searchbar.js'
5
+
6
+ window.onload = () => {
7
+ instantiateElements('.code-snippet--copy', CopySnippet);
8
+ instantiateElements('.storybook-leaf iframe', Storybook);
9
+ instantiateElements('#search', Searchbar);
10
+ };
File without changes