@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,238 @@
1
+ import { Node } from '../../node.js'
2
+ import { convertHTMLEntities, gitmoji, log } from '@gouvfr/dsfr-forge'
3
+
4
+ class ChangelogLeafDirective extends Node {
5
+ structure (data) {
6
+ if (!data.changelog) {
7
+ log.warn('ChangelogLeafDirective: missing "changelog"');
8
+ return data;
9
+ }
10
+
11
+ return super.structure({
12
+ type: 'htmlContainer',
13
+ tagName: 'div',
14
+ classes: ['dsfr-doc-changelog'],
15
+ children: data.changelog.map(version => this.structureVersion(version, data.dateFormat))
16
+ });
17
+ }
18
+
19
+ structureVersion (version, dateFormat) {
20
+ const date = new Intl.DateTimeFormat(dateFormat, {
21
+ year: "numeric",
22
+ month: "long",
23
+ day: "numeric"
24
+ }).format(new Date(version.date));
25
+
26
+ return {
27
+ type: 'htmlContainer',
28
+ tagName: 'div',
29
+ classes: ['dsfr-doc-changelog__version', 'fr-mb-12v'],
30
+ children: [
31
+ {
32
+ type: 'heading',
33
+ depth: 4,
34
+ children: [
35
+ {
36
+ type: 'link',
37
+ url: version.url,
38
+ children: [
39
+ {
40
+ type: 'text',
41
+ value: version.id
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ type: 'text',
47
+ value: ` - ${date}`
48
+ }
49
+ ]
50
+ },
51
+ {
52
+ type: 'htmlContainer',
53
+ tagName: 'div',
54
+ classes: ['fr-grid-row', 'fr-grid-row--gutters'],
55
+ children: version.commits.map(commit => this.structureCommit(commit))
56
+ }
57
+ ]
58
+ };
59
+ }
60
+
61
+ structureCommit (commit) {
62
+ const contentChildren = [
63
+ {
64
+ type: 'heading',
65
+ depth: 5,
66
+ data: { isSemantic: true },
67
+ classes: ['fr-tile__title'],
68
+ children: [
69
+ {
70
+ type: 'link',
71
+ url: commit.pull,
72
+ children: [
73
+ {
74
+ type: 'text',
75
+ value: convertHTMLEntities(commit.title)
76
+ }
77
+ ]
78
+ }
79
+ ]
80
+ }
81
+ ];
82
+
83
+ if (commit.description) {
84
+ commit.description.forEach(node => {
85
+ node.classes = (node.classes ?? []).concat(['fr-tile__desc']);
86
+ });
87
+ contentChildren.push(...commit.description);
88
+ }
89
+
90
+ if (commit.change) {
91
+ contentChildren.push({
92
+ type: 'paragraph',
93
+ classes: ['fr-tile__desc'],
94
+ children: [
95
+ {
96
+ type: 'text',
97
+ value: 'Breaking change:'
98
+ }
99
+ ]
100
+ });
101
+ commit.change.forEach(node => {
102
+ node.classes = (node.classes ?? []).concat(['fr-tile__desc']);
103
+ });
104
+ contentChildren.push(...commit.change);
105
+ }
106
+
107
+ let badgeClasses = ['fr-badge'];
108
+
109
+ switch (commit.type) {
110
+ case 'feat':
111
+ badgeClasses.push('fr-badge--success', 'fr-badge--no-icon');
112
+ break;
113
+ case 'fix':
114
+ badgeClasses.push('fr-badge--warning', 'fr-badge--no-icon');
115
+ break;
116
+ case 'refactor':
117
+ badgeClasses.push('fr-badge--info', 'fr-badge--no-icon');
118
+ break;
119
+ case 'docs':
120
+ case 'doc':
121
+ badgeClasses.push('fr-badge--info', 'fr-badge--no-icon');
122
+ break;
123
+ }
124
+
125
+ const badges = [
126
+ {
127
+ type: 'htmlContainer',
128
+ tagName: 'p',
129
+ classes: badgeClasses,
130
+ children: [
131
+ {
132
+ type: 'text',
133
+ value: (commit.gitmojis?.length ? `${commit.gitmojis.join('')} ` : '') + commit.type
134
+ }
135
+ ]
136
+ }
137
+ ]
138
+
139
+ if (commit.isBreaking) {
140
+ badges.push({
141
+ type: 'htmlContainer',
142
+ tagName: 'p',
143
+ classes: ['fr-badge', 'fr-badge--error', 'fr-badge--no-icon'],
144
+ children: [
145
+ {
146
+ type: 'text',
147
+ value: `${gitmoji(':boom:')} Breaking change`
148
+ }
149
+ ]
150
+ });
151
+ }
152
+
153
+ const badgesGroup = badges.length === 1 ? badges : [
154
+ {
155
+ type: 'htmlContainer',
156
+ tagName: 'div',
157
+ classes: ['fr-badges-group'],
158
+ children: badges
159
+ }
160
+ ]
161
+
162
+ contentChildren.push({
163
+ type: 'htmlContainer',
164
+ tagName: 'div',
165
+ classes: ['fr-tile__start'],
166
+ children: badgesGroup
167
+ });
168
+
169
+ switch (true) {
170
+ case !commit.scopes?.length:
171
+ case commit.scopes.length === 1 && commit.scopes[0] === '*':
172
+ break;
173
+
174
+ default:
175
+ contentChildren.push({
176
+ type: 'htmlContainer',
177
+ tagName: 'div',
178
+ classes: ['fr-tile__start'],
179
+ children: [
180
+ {
181
+ type: 'htmlContainer',
182
+ tagName: 'div',
183
+ classes: ['fr-tags-group'],
184
+ children: commit.scopes.map(scope => ({
185
+ type: 'htmlContainer',
186
+ tagName: 'p',
187
+ classes: ['fr-tag'],
188
+ children: [{ type: 'text', value: scope }]
189
+ }))
190
+ }
191
+ ]
192
+ });
193
+ }
194
+
195
+ contentChildren.push({
196
+ type: 'paragraph',
197
+ classes: ['fr-tile__detail'],
198
+ children: [
199
+ {
200
+ type: 'text',
201
+ value: `#${commit.id}`
202
+ }
203
+ ]
204
+ });
205
+
206
+ return {
207
+ type: 'htmlContainer',
208
+ tagName: 'div',
209
+ classes: ['fr-col-12', 'fr-col-md-8'],
210
+ children: [
211
+ {
212
+ type: 'htmlContainer',
213
+ tagName: 'div',
214
+ classes: ['fr-tile', 'fr-tile--sm', 'fr-enlarge-link', 'fr-tile--horizontal'],
215
+ children: [
216
+ {
217
+ type: 'htmlContainer',
218
+ tagName: 'div',
219
+ classes: ['fr-tile__body'],
220
+ children: [
221
+ {
222
+ type: 'htmlContainer',
223
+ tagName: 'div',
224
+ classes: ['fr-tile__content'],
225
+ children: contentChildren
226
+ }
227
+ ]
228
+ }
229
+ ]
230
+ }
231
+ ]
232
+ };
233
+ }
234
+ }
235
+
236
+ ChangelogLeafDirective.NAME = 'dsfr-doc-changelog';
237
+
238
+ export { ChangelogLeafDirective };
@@ -0,0 +1,21 @@
1
+ import { Node } from '../../node.js';
2
+
3
+ class FigmaLeafDirective extends Node {
4
+ constructor (data) {
5
+ super(data, 'iframe');
6
+ if (!this.attributes.hasAttribute('width')) this.attributes.setAttribute('width', '100%');
7
+ if (!this.attributes.hasAttribute('height')) this.attributes.setAttribute('height', '100%');
8
+ }
9
+
10
+ async render() {
11
+ return `
12
+ <div class="dsfr-doc-figma-leaf">
13
+ ${await super.render()}
14
+ </div>
15
+ `;
16
+ }
17
+ }
18
+
19
+ FigmaLeafDirective.NAME = 'dsfr-doc-figma';
20
+
21
+ export { FigmaLeafDirective };
@@ -0,0 +1,71 @@
1
+ import { Node } from '../../node.js'
2
+ import { log } from '@gouvfr/dsfr-forge';
3
+
4
+ class GuidelineContainerDirective extends Node {
5
+ structure (data) {
6
+ const image = Node.getImageChild(data);
7
+
8
+ if (!image) {
9
+ log.warn('GuidelineContainerDirective: missing image');
10
+ return data;
11
+ }
12
+
13
+ const valid = data.attributes.valid.toLowerCase() === 'true';
14
+ const col = data.attributes.col || '12';
15
+ const description = data.children.filter(child => {
16
+ switch (true) {
17
+ case child?.data?.directiveLabel === true:
18
+ return false;
19
+ case Node.getImageChild(child) !== null:
20
+ return false;
21
+ default:
22
+ return true;
23
+ }
24
+ });
25
+ const title = valid ? data.fragments.do : data.fragments.dont;
26
+
27
+ return super.structure({
28
+ type: 'htmlContainer',
29
+ tagName: 'div',
30
+ classes: ['fr-col-12', `fr-col-md-${col}`],
31
+ children: [
32
+ {
33
+ type: 'htmlContainer',
34
+ tagName: 'div',
35
+ classes: ['dsfr-doc-guideline', `dsfr-doc-guideline--${valid ? 'do' : 'dont'}`],
36
+ children: [
37
+ {
38
+ type: 'image',
39
+ classes: ['dsfr-doc-guideline__img'],
40
+ attributes: {
41
+ src: image.url,
42
+ alt: title
43
+ }
44
+ },
45
+ {
46
+ type: 'htmlContainer',
47
+ tagName: 'div',
48
+ classes: ['dsfr-doc-guideline__content'],
49
+ children: [
50
+ {
51
+ type: 'paragraph',
52
+ classes: ['dsfr-doc-guideline__title'],
53
+ children: [
54
+ {
55
+ type: 'text',
56
+ value: title
57
+ }
58
+ ]
59
+ },
60
+ ].concat(description)
61
+ }
62
+ ]
63
+ }
64
+ ]
65
+ });
66
+ }
67
+ }
68
+
69
+ GuidelineContainerDirective.NAME = 'dsfr-doc-guideline';
70
+
71
+ export { GuidelineContainerDirective };
@@ -0,0 +1,13 @@
1
+ import { Node } from '../../node.js'
2
+
3
+ class GuidelinesContainerDirective extends Node {
4
+ constructor (data) {
5
+ super(data, 'div');
6
+ this.attributes.addClass('fr-grid-row');
7
+ this.attributes.addClass('fr-grid-row--gutters');
8
+ }
9
+ }
10
+
11
+ GuidelinesContainerDirective.NAME = 'dsfr-doc-guidelines';
12
+
13
+ export { GuidelinesContainerDirective };
@@ -0,0 +1,13 @@
1
+ import { Node } from '../../node.js'
2
+
3
+ class ImageTextDirective extends Node {
4
+ constructor (data) {
5
+ super(data);
6
+ this._image = this.findDescendantsByType('image')[0];
7
+ if (this.attributes.hasAttribute('responsive') && this._image) this._image.attributes.addClass('fr-responsive-img');
8
+ }
9
+ }
10
+
11
+ ImageTextDirective.NAME = 'dsfr-doc-image';
12
+
13
+ export { ImageTextDirective };
@@ -0,0 +1,46 @@
1
+ import { Node } from '../../node.js';
2
+
3
+ class PinLeafDirective extends Node {
4
+ structure (data) {
5
+ const required = data?.attributes?.required;
6
+ const add = data?.attributes?.add;
7
+ const titleChildren = data?.children[0];
8
+ const requiredFragment = data.fragments?.anatomy?.required;
9
+ const optionalFragment = data.fragments?.anatomy?.optional;
10
+ let descriptionValue = `${required === 'true' ? requiredFragment : optionalFragment}`;
11
+ if (add) descriptionValue += ` ${add}`;
12
+
13
+ if (!titleChildren) {
14
+ log.warn(`PinLeafDirective: missing label on pin`);
15
+ }
16
+
17
+ return {
18
+ type: 'htmlContainer',
19
+ tagName: 'li',
20
+ classes: ['dsfr-doc-anatomy__pin', required === 'true' ? 'dsfr-doc-anatomy__pin--required' : ''],
21
+ children: [
22
+ {
23
+ type: 'htmlContainer',
24
+ tagName: 'span',
25
+ classes: ['dsfr-doc-anatomy__title'],
26
+ children: [titleChildren]
27
+ },
28
+ {
29
+ type: 'htmlContainer',
30
+ tagName: 'span',
31
+ classes: ['dsfr-doc-anatomy__description'],
32
+ children: [
33
+ {
34
+ type: 'text',
35
+ value: descriptionValue
36
+ }
37
+ ]
38
+ }
39
+ ]
40
+ };
41
+ }
42
+ }
43
+
44
+ PinLeafDirective.NAME = 'dsfr-doc-pin';
45
+
46
+ export { PinLeafDirective };
@@ -0,0 +1,18 @@
1
+ import { Node } from '../../node.js';
2
+
3
+ class StorybookLeafDirective extends Node {
4
+
5
+ // TODO:
6
+ // - script externe pour le resize de l'iframe
7
+ // - apossibilité de multiple storybook (baser sur id de la story ?)
8
+ // - bug resize down
9
+ // - bouton communication avec iframe pour changer le theme exemple
10
+ // - changement theme iframe en fonction theme parent
11
+ async render() {
12
+ return `<div class="storybook-leaf"><iframe ${this.renderAttributes()}></iframe></div>`;
13
+ }
14
+ }
15
+
16
+ StorybookLeafDirective.NAME = 'dsfr-doc-storybook';
17
+
18
+ export { StorybookLeafDirective };
@@ -0,0 +1,22 @@
1
+ import { Node } from '../../node.js';
2
+
3
+ class TabContainerDirective extends Node {
4
+ structure (data) {
5
+ const tabId = data?.attributes?.id;
6
+ return super.structure({
7
+ type: 'htmlContainer',
8
+ tagName: 'div',
9
+ attributes: {
10
+ id: `${tabId}-panel`,
11
+ role: 'tabpanel',
12
+ 'aria-labelledby': tabId
13
+ },
14
+ classes: ['fr-tabs__panel'],
15
+ children: data.children
16
+ });
17
+ }
18
+ }
19
+
20
+ TabContainerDirective.NAME = 'fr-tab';
21
+
22
+ export { TabContainerDirective };
@@ -0,0 +1,49 @@
1
+ import { Node } from '../../node.js';
2
+ import { HtmlNode } from '../../generic/html-node.js';
3
+
4
+ class TabNavigationContainerDirective extends Node {
5
+ constructor(data) {
6
+ super(data);
7
+ this._id = data.attributes.id || 'dsfr-doc-tab-navigation--collapse';
8
+ const list = this.findDescendantsByType('list')[0];
9
+ list.attributes.setAttribute('role', 'tablist');
10
+ const items = list.findDescendantsByType('listItem');
11
+ for (const item of items) {
12
+ item.attributes.setAttribute('role', 'presentation');
13
+ let node;
14
+ let isSelected = false;
15
+ const link = item.findDescendantsByType('link')[0];
16
+ if (link) {
17
+ node = link;
18
+ } else {
19
+ this._currentNode = item._children[0];
20
+
21
+ node = new HtmlNode({ type: 'html', value: '<div>' });
22
+ const close = new HtmlNode({ type: 'html', value: '</div>' });
23
+ item._children = [node, ...item._children, close];
24
+ isSelected = true;
25
+ }
26
+ node.attributes.setAttribute('role', 'tab');
27
+ node.attributes.setAttribute('aria-selected', isSelected);
28
+ node.attributes.addClass('dsfr-doc-tab-navigation__tab');
29
+ }
30
+ }
31
+ async render() {
32
+ this.attributes.setAttribute('role', 'navigation');
33
+ this.attributes.addClass('dsfr-doc-tab-navigation');
34
+ return `<nav ${this.renderAttributes()}>
35
+ <div class="fr-sidemenu">
36
+ <div class="fr-sidemenu__inner">
37
+ <button aria-expanded="false" aria-controls="${this._id}" type="button" class="fr-sidemenu__btn">${await this._currentNode.render()}</button>
38
+ <div class="fr-collapse" id="${this._id}">
39
+ ${await super.render()}
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </nav>`;
44
+ }
45
+ }
46
+
47
+ TabNavigationContainerDirective.NAME = 'dsfr-doc-tab-navigation';
48
+
49
+ export { TabNavigationContainerDirective };
@@ -0,0 +1,56 @@
1
+ import { Node } from '../../node.js';
2
+ import { log } from '@gouvfr/dsfr-forge';
3
+
4
+ const VALIGN = new Map([
5
+ ['top', 'fr-cell--top'],
6
+ ['bottom', 'fr-cell--bottom']
7
+ ]);
8
+
9
+ class TableContainerDirective extends Node {
10
+ constructor (data) {
11
+ super(data);
12
+ this.applyAttributes(data.attributes);
13
+ }
14
+
15
+ structure (data) {
16
+ const directiveLabel = data.children?.[0];
17
+ if (!directiveLabel.data?.directiveLabel) {
18
+ log.warn('TableContainerDirective: missing caption');
19
+ return data;
20
+ }
21
+ data.children.shift();
22
+ const table = data.children?.find(child => child.type === 'table');
23
+ if (table ) {
24
+ table.caption = directiveLabel.children?.[0]?.value;
25
+ }
26
+ return data;
27
+ }
28
+
29
+ applyAttributes (attributes) {
30
+ const table = this.findDescendantsByType('table')[0];
31
+ if (!table) return;
32
+
33
+ if (attributes.scroll === 'false') table.attributes.addClass('fr-table--no-scroll');
34
+ if (attributes.caption === 'false') table.attributes.addClass('fr-table--no-caption');
35
+
36
+ const cellCount = table.children[0].children.length;
37
+
38
+ const valign = attributes.valign ? attributes.valign.split(',') : null;
39
+ if (valign?.length === 1 && VALIGN.has(valign[0])) {
40
+ valign.length = cellCount;
41
+ valign.fill(valign[0]);
42
+ }
43
+
44
+ table.children.forEach((row, index) => {
45
+ row.children.forEach((cell, cellIndex) => {
46
+ if (valign && valign[cellIndex] && VALIGN.has(valign[cellIndex])) {
47
+ cell.attributes.addClass(VALIGN.get(valign[cellIndex]));
48
+ }
49
+ });
50
+ });
51
+ }
52
+ }
53
+
54
+ TableContainerDirective.NAME = 'dsfr-doc-table';
55
+
56
+ export { TableContainerDirective };
@@ -0,0 +1,71 @@
1
+ import { Node } from '../../node.js';
2
+ import { log } from '@gouvfr/dsfr-forge';
3
+ import { TabContainerDirective } from './tab-container-directive.js';
4
+
5
+ class TabsContainerDirective extends Node {
6
+ structure (data) {
7
+ const label = data.children.filter(child => child?.data?.directiveLabel)?.[0]?.children?.[0]?.value;
8
+ const contentChildren = data.children.filter(child => !child?.data?.directiveLabel);
9
+ const tabs = contentChildren
10
+ .filter(child => child.type === 'containerDirective' && child.name === TabContainerDirective.NAME)
11
+ .map(this.structureTab);
12
+
13
+ const listAttributes = {
14
+ role: 'tablist'
15
+ };
16
+ if (label) {
17
+ listAttributes['aria-label'] = label;
18
+ }
19
+ else log.warn('TabsContainerDirective: missing label on fr-tabs');
20
+
21
+ return super.structure({
22
+ type: 'htmlContainer',
23
+ tagName: 'div',
24
+ classes: ['fr-tabs'],
25
+ children: [
26
+ {
27
+ type: 'htmlContainer',
28
+ tagName: 'ul',
29
+ classes: ['fr-tabs__list'],
30
+ attributes: listAttributes,
31
+ children: tabs
32
+ },
33
+ ...contentChildren
34
+ ]
35
+ });
36
+ }
37
+
38
+ structureTab (data, index) {
39
+ const tabId = data?.attributes?.id;
40
+ const tabTitleChildren = data.children.filter(child => child?.data?.directiveLabel)?.[0]?.children;
41
+
42
+ if (!tabTitleChildren) {
43
+ log.warn(`TabsContainerDirective: missing label on fr-tab ${index + 1}`);
44
+ }
45
+
46
+ return {
47
+ type: 'htmlContainer',
48
+ tagName: 'li',
49
+ attributes: {
50
+ role: 'presentation'
51
+ },
52
+ children: [
53
+ {
54
+ type: 'htmlContainer',
55
+ tagName: 'button',
56
+ classes: ['fr-tabs__tab'],
57
+ attributes: {
58
+ role: 'tab',
59
+ 'aria-controls': `${tabId}-panel`,
60
+ 'aria-selected': index === 0
61
+ },
62
+ children: tabTitleChildren
63
+ }
64
+ ]
65
+ };
66
+ }
67
+ }
68
+
69
+ TabsContainerDirective.NAME = 'fr-tabs';
70
+
71
+ export { TabsContainerDirective };
@@ -0,0 +1,26 @@
1
+ import { Node } from '../node.js';
2
+
3
+ const LEVELS = new Map([
4
+ ['note', { icon: 'information-line', color: 'blue-cumulus' }],
5
+ ['tip', { icon: 'lightbulb-line', color: 'green-emeraude' }],
6
+ ['important', { icon: 'feedback-line', color: 'purple-glycine' }],
7
+ ['warning', { icon: 'alert-line', color: 'yellow-moutarde' }],
8
+ ['caution', { icon: 'spam-2-line', color: 'pink-tuile' }]
9
+ ]);
10
+
11
+ class BlockquoteNode extends Node {
12
+ constructor (data) {
13
+ if (data?.children?.[0]?.type === 'heading') data.children[0].attributes = { class: 'fr-callout__title' };
14
+ super(data, 'div');
15
+ this.attributes.addClass('fr-callout fr-mb-6v');
16
+
17
+ if (data?.level && LEVELS.has(data.level)) {
18
+ const level = LEVELS.get(data.level);
19
+ this.attributes.addClasses(`fr-icon-${level.icon}`, `fr-callout--${level.color}`);
20
+ }
21
+ }
22
+ }
23
+
24
+ BlockquoteNode.TYPE = 'blockquote';
25
+
26
+ export { BlockquoteNode };
@@ -0,0 +1,11 @@
1
+ import { Node } from '../node.js';
2
+
3
+ class BreakNode extends Node {
4
+ async render() {
5
+ return '<br>';
6
+ }
7
+ }
8
+
9
+ BreakNode.TYPE = 'break';
10
+
11
+ export { BreakNode };