@docusaurus/theme-classic 0.0.0-4298 → 0.0.0-4299

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.
@@ -68,13 +68,16 @@ function translateNavbar(navbar, navbarTranslations) {
68
68
  };
69
69
  }
70
70
 
71
+ function isMultiColumnFooterLinks(links) {
72
+ return 'title' in links[0];
73
+ }
74
+
71
75
  function getFooterTranslationFile(footer) {
72
- // TODO POC code
73
- const footerLinkTitles = (0, _lodash.chain)(footer.links.filter(link => !!link.title)).keyBy(link => `link.title.${link.title}`).mapValues(link => ({
76
+ const footerLinkTitles = isMultiColumnFooterLinks(footer.links) ? (0, _lodash.chain)(footer.links.filter(link => !!link.title)).keyBy(link => `link.title.${link.title}`).mapValues(link => ({
74
77
  message: link.title,
75
78
  description: `The title of the footer links column with title=${link.title} in the footer`
76
- })).value();
77
- const footerLinkLabels = (0, _lodash.chain)(footer.links.flatMap(link => link.items).filter(link => !!link.label)).keyBy(linkItem => `link.item.label.${linkItem.label}`).mapValues(linkItem => {
79
+ })).value() : {};
80
+ const footerLinkLabels = (0, _lodash.chain)(isMultiColumnFooterLinks(footer.links) ? footer.links.flatMap(link => link.items).filter(link => !!link.label) : footer.links.filter(link => !!link.label)).keyBy(linkItem => `link.item.label.${linkItem.label}`).mapValues(linkItem => {
78
81
  var _linkItem$to;
79
82
 
80
83
  return {
@@ -94,7 +97,7 @@ function getFooterTranslationFile(footer) {
94
97
  function translateFooter(footer, footerTranslations) {
95
98
  var _footerTranslations$c, _footerTranslations$c2;
96
99
 
97
- const links = footer.links.map(link => {
100
+ const links = isMultiColumnFooterLinks(footer.links) ? footer.links.map(link => {
98
101
  var _footerTranslations$$, _footerTranslations$;
99
102
 
100
103
  return { ...link,
@@ -107,6 +110,12 @@ function translateFooter(footer, footerTranslations) {
107
110
  };
108
111
  })
109
112
  };
113
+ }) : footer.links.map(link => {
114
+ var _footerTranslations$$3, _footerTranslations$3;
115
+
116
+ return { ...link,
117
+ label: (_footerTranslations$$3 = (_footerTranslations$3 = footerTranslations[`link.item.label.${link.label}`]) === null || _footerTranslations$3 === void 0 ? void 0 : _footerTranslations$3.message) !== null && _footerTranslations$$3 !== void 0 ? _footerTranslations$$3 : link.label
118
+ };
110
119
  });
111
120
  const copyright = (_footerTranslations$c = (_footerTranslations$c2 = footerTranslations.copyright) === null || _footerTranslations$c2 === void 0 ? void 0 : _footerTranslations$c2.message) !== null && _footerTranslations$c !== void 0 ? _footerTranslations$c : footer.copyright;
112
121
  return { ...footer,
@@ -260,10 +260,12 @@ const ThemeConfigSchema = _utilsValidation.Joi.object({
260
260
  href: _utilsValidation.Joi.string()
261
261
  }),
262
262
  copyright: _utilsValidation.Joi.string(),
263
- links: _utilsValidation.Joi.array().items(_utilsValidation.Joi.object({
264
- title: _utilsValidation.Joi.string().allow(null),
263
+ links: _utilsValidation.Joi.alternatives(_utilsValidation.Joi.array().items(_utilsValidation.Joi.object({
264
+ title: _utilsValidation.Joi.string().allow(null).default(null),
265
265
  items: _utilsValidation.Joi.array().items(FooterLinkItemSchema).default([])
266
- })).default([])
266
+ })).default([]), _utilsValidation.Joi.array().items(FooterLinkItemSchema).default([])).messages({
267
+ 'alternatives.match': `The footer must be either simple or multi-column, and not a mix of the two. See: https://docusaurus.io/docs/api/themes/configuration#footer-links`
268
+ })
267
269
  }).optional(),
268
270
  prism: _utilsValidation.Joi.object({
269
271
  theme: _utilsValidation.Joi.object({
@@ -54,6 +54,66 @@ function FooterLogo({sources, alt, width, height}) {
54
54
  );
55
55
  }
56
56
 
57
+ function MultiColumnLinks({links}) {
58
+ return (
59
+ <>
60
+ {links.map((linkItem, i) => (
61
+ <div key={i} className="col footer__col">
62
+ <div className="footer__title">{linkItem.title}</div>
63
+ <ul className="footer__items">
64
+ {linkItem.items.map((item, key) =>
65
+ item.html ? (
66
+ <li
67
+ key={key}
68
+ className="footer__item" // Developer provided the HTML, so assume it's safe.
69
+ // eslint-disable-next-line react/no-danger
70
+ dangerouslySetInnerHTML={{
71
+ __html: item.html,
72
+ }}
73
+ />
74
+ ) : (
75
+ <li key={item.href || item.to} className="footer__item">
76
+ <FooterLink {...item} />
77
+ </li>
78
+ ),
79
+ )}
80
+ </ul>
81
+ </div>
82
+ ))}
83
+ </>
84
+ );
85
+ }
86
+
87
+ function SimpleLinks({links}) {
88
+ return (
89
+ <div className="footer__links">
90
+ {links.map((item, key) => (
91
+ <>
92
+ {item.html ? (
93
+ <span
94
+ key={key}
95
+ className="footer__link-item" // Developer provided the HTML, so assume it's safe.
96
+ // eslint-disable-next-line react/no-danger
97
+ dangerouslySetInnerHTML={{
98
+ __html: item.html,
99
+ }}
100
+ />
101
+ ) : (
102
+ <FooterLink {...item} />
103
+ )}
104
+ {links.length !== key + 1 && (
105
+ <span className="footer__link-separator">·</span>
106
+ )}
107
+ </>
108
+ ))}
109
+ </div>
110
+ );
111
+ }
112
+
113
+ function isMultiColumnFooterLinks(links) {
114
+ return 'title' in links[0];
115
+ }
116
+
57
117
  function Footer() {
58
118
  const {footer} = useThemeConfig();
59
119
  const {copyright, links = [], logo = {}} = footer || {};
@@ -71,40 +131,18 @@ function Footer() {
71
131
  className={clsx('footer', {
72
132
  'footer--dark': footer.style === 'dark',
73
133
  })}>
74
- <div className="container">
75
- {links && links.length > 0 && (
76
- <div className="row footer__links">
77
- {links.map((linkItem, i) => (
78
- <div key={i} className="col footer__col">
79
- {linkItem.title != null ? (
80
- <div className="footer__title">{linkItem.title}</div>
81
- ) : null}
82
- {linkItem.items != null &&
83
- Array.isArray(linkItem.items) &&
84
- linkItem.items.length > 0 ? (
85
- <ul className="footer__items">
86
- {linkItem.items.map((item, key) =>
87
- item.html ? (
88
- <li
89
- key={key}
90
- className="footer__item" // Developer provided the HTML, so assume it's safe.
91
- // eslint-disable-next-line react/no-danger
92
- dangerouslySetInnerHTML={{
93
- __html: item.html,
94
- }}
95
- />
96
- ) : (
97
- <li key={item.href || item.to} className="footer__item">
98
- <FooterLink {...item} />
99
- </li>
100
- ),
101
- )}
102
- </ul>
103
- ) : null}
104
- </div>
105
- ))}
106
- </div>
107
- )}
134
+ <div className="container container-fluid">
135
+ {links &&
136
+ links.length > 0 &&
137
+ (isMultiColumnFooterLinks(links) ? (
138
+ <div className="row footer__links">
139
+ <MultiColumnLinks links={links} />
140
+ </div>
141
+ ) : (
142
+ <div className="footer__links text--center">
143
+ <SimpleLinks links={links} />
144
+ </div>
145
+ ))}
108
146
  {(logo || copyright) && (
109
147
  <div className="footer__bottom text--center">
110
148
  {logo && (logo.src || logo.srcDark) && (
@@ -139,4 +177,4 @@ function Footer() {
139
177
  );
140
178
  }
141
179
 
142
- export default Footer;
180
+ export default React.memo(Footer);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/theme-classic",
3
- "version": "0.0.0-4298",
3
+ "version": "0.0.0-4299",
4
4
  "description": "Classic theme for Docusaurus",
5
5
  "main": "lib/index.js",
6
6
  "types": "src/theme-classic.d.ts",
@@ -23,14 +23,14 @@
23
23
  "update-code-translations": "node -e 'require(\"./update-code-translations.js\").run()'"
24
24
  },
25
25
  "dependencies": {
26
- "@docusaurus/core": "0.0.0-4298",
27
- "@docusaurus/plugin-content-blog": "0.0.0-4298",
28
- "@docusaurus/plugin-content-docs": "0.0.0-4298",
29
- "@docusaurus/plugin-content-pages": "0.0.0-4298",
30
- "@docusaurus/theme-common": "0.0.0-4298",
31
- "@docusaurus/theme-translations": "0.0.0-4298",
32
- "@docusaurus/utils": "0.0.0-4298",
33
- "@docusaurus/utils-validation": "0.0.0-4298",
26
+ "@docusaurus/core": "0.0.0-4299",
27
+ "@docusaurus/plugin-content-blog": "0.0.0-4299",
28
+ "@docusaurus/plugin-content-docs": "0.0.0-4299",
29
+ "@docusaurus/plugin-content-pages": "0.0.0-4299",
30
+ "@docusaurus/theme-common": "0.0.0-4299",
31
+ "@docusaurus/theme-translations": "0.0.0-4299",
32
+ "@docusaurus/utils": "0.0.0-4299",
33
+ "@docusaurus/utils-validation": "0.0.0-4299",
34
34
  "@mdx-js/mdx": "^1.6.21",
35
35
  "@mdx-js/react": "^1.6.21",
36
36
  "clsx": "^1.1.1",
@@ -45,8 +45,8 @@
45
45
  "rtlcss": "^3.3.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@docusaurus/module-type-aliases": "0.0.0-4298",
49
- "@docusaurus/types": "0.0.0-4298",
48
+ "@docusaurus/module-type-aliases": "0.0.0-4299",
49
+ "@docusaurus/types": "0.0.0-4299",
50
50
  "@types/mdx-js__react": "^1.5.4",
51
51
  "@types/parse-numeric-range": "^0.0.1",
52
52
  "@types/rtlcss": "^3.1.1",
@@ -61,5 +61,5 @@
61
61
  "engines": {
62
62
  "node": ">=14"
63
63
  },
64
- "gitHead": "ca4920916dff261f4dbffd3b2465a5aac3a66c3f"
64
+ "gitHead": "57801f0f237dda31b5aae8c8fdddea45528f5bf1"
65
65
  }
@@ -9,7 +9,12 @@ import React from 'react';
9
9
  import clsx from 'clsx';
10
10
 
11
11
  import Link from '@docusaurus/Link';
12
- import {FooterLinkItem, useThemeConfig} from '@docusaurus/theme-common';
12
+ import {
13
+ FooterLinkItem,
14
+ useThemeConfig,
15
+ MultiColumnFooter,
16
+ SimpleFooter,
17
+ } from '@docusaurus/theme-common';
13
18
  import useBaseUrl from '@docusaurus/useBaseUrl';
14
19
  import isInternalUrl from '@docusaurus/isInternalUrl';
15
20
  import styles from './styles.module.css';
@@ -66,6 +71,70 @@ function FooterLogo({
66
71
  );
67
72
  }
68
73
 
74
+ function MultiColumnLinks({links}: {links: MultiColumnFooter['links']}) {
75
+ return (
76
+ <>
77
+ {links.map((linkItem, i) => (
78
+ <div key={i} className="col footer__col">
79
+ <div className="footer__title">{linkItem.title}</div>
80
+ <ul className="footer__items">
81
+ {linkItem.items.map((item, key) =>
82
+ item.html ? (
83
+ <li
84
+ key={key}
85
+ className="footer__item"
86
+ // Developer provided the HTML, so assume it's safe.
87
+ // eslint-disable-next-line react/no-danger
88
+ dangerouslySetInnerHTML={{
89
+ __html: item.html,
90
+ }}
91
+ />
92
+ ) : (
93
+ <li key={item.href || item.to} className="footer__item">
94
+ <FooterLink {...item} />
95
+ </li>
96
+ ),
97
+ )}
98
+ </ul>
99
+ </div>
100
+ ))}
101
+ </>
102
+ );
103
+ }
104
+
105
+ function SimpleLinks({links}: {links: SimpleFooter['links']}) {
106
+ return (
107
+ <div className="footer__links">
108
+ {links.map((item, key) => (
109
+ <>
110
+ {item.html ? (
111
+ <span
112
+ key={key}
113
+ className="footer__link-item"
114
+ // Developer provided the HTML, so assume it's safe.
115
+ // eslint-disable-next-line react/no-danger
116
+ dangerouslySetInnerHTML={{
117
+ __html: item.html,
118
+ }}
119
+ />
120
+ ) : (
121
+ <FooterLink {...item} />
122
+ )}
123
+ {links.length !== key + 1 && (
124
+ <span className="footer__link-separator">·</span>
125
+ )}
126
+ </>
127
+ ))}
128
+ </div>
129
+ );
130
+ }
131
+
132
+ function isMultiColumnFooterLinks(
133
+ links: MultiColumnFooter['links'] | SimpleFooter['links'],
134
+ ): links is MultiColumnFooter['links'] {
135
+ return 'title' in links[0];
136
+ }
137
+
69
138
  function Footer(): JSX.Element | null {
70
139
  const {footer} = useThemeConfig();
71
140
 
@@ -84,41 +153,18 @@ function Footer(): JSX.Element | null {
84
153
  className={clsx('footer', {
85
154
  'footer--dark': footer.style === 'dark',
86
155
  })}>
87
- <div className="container">
88
- {links && links.length > 0 && (
89
- <div className="row footer__links">
90
- {links.map((linkItem, i) => (
91
- <div key={i} className="col footer__col">
92
- {linkItem.title != null ? (
93
- <div className="footer__title">{linkItem.title}</div>
94
- ) : null}
95
- {linkItem.items != null &&
96
- Array.isArray(linkItem.items) &&
97
- linkItem.items.length > 0 ? (
98
- <ul className="footer__items">
99
- {linkItem.items.map((item, key) =>
100
- item.html ? (
101
- <li
102
- key={key}
103
- className="footer__item"
104
- // Developer provided the HTML, so assume it's safe.
105
- // eslint-disable-next-line react/no-danger
106
- dangerouslySetInnerHTML={{
107
- __html: item.html,
108
- }}
109
- />
110
- ) : (
111
- <li key={item.href || item.to} className="footer__item">
112
- <FooterLink {...item} />
113
- </li>
114
- ),
115
- )}
116
- </ul>
117
- ) : null}
118
- </div>
119
- ))}
120
- </div>
121
- )}
156
+ <div className="container container-fluid">
157
+ {links &&
158
+ links.length > 0 &&
159
+ (isMultiColumnFooterLinks(links) ? (
160
+ <div className="row footer__links">
161
+ <MultiColumnLinks links={links} />
162
+ </div>
163
+ ) : (
164
+ <div className="footer__links text--center">
165
+ <SimpleLinks links={links} />
166
+ </div>
167
+ ))}
122
168
  {(logo || copyright) && (
123
169
  <div className="footer__bottom text--center">
124
170
  {logo && (logo.src || logo.srcDark) && (
@@ -154,4 +200,4 @@ function Footer(): JSX.Element | null {
154
200
  );
155
201
  }
156
202
 
157
- export default Footer;
203
+ export default React.memo(Footer);
@@ -11,6 +11,8 @@ import {
11
11
  Navbar,
12
12
  NavbarItem,
13
13
  Footer,
14
+ MultiColumnFooter,
15
+ SimpleFooter,
14
16
  } from '@docusaurus/theme-common';
15
17
 
16
18
  import {keyBy, chain} from 'lodash';
@@ -69,20 +71,31 @@ function translateNavbar(
69
71
  };
70
72
  }
71
73
 
74
+ function isMultiColumnFooterLinks(
75
+ links: MultiColumnFooter['links'] | SimpleFooter['links'],
76
+ ): links is MultiColumnFooter['links'] {
77
+ return 'title' in links[0];
78
+ }
79
+
72
80
  function getFooterTranslationFile(footer: Footer): TranslationFileContent {
73
- // TODO POC code
74
- const footerLinkTitles: TranslationFileContent = chain(
75
- footer.links.filter((link) => !!link.title),
81
+ const footerLinkTitles: TranslationFileContent = isMultiColumnFooterLinks(
82
+ footer.links,
76
83
  )
77
- .keyBy((link) => `link.title.${link.title}`)
78
- .mapValues((link) => ({
79
- message: link.title!,
80
- description: `The title of the footer links column with title=${link.title} in the footer`,
81
- }))
82
- .value();
84
+ ? chain(footer.links.filter((link) => !!link.title))
85
+ .keyBy((link) => `link.title.${link.title}`)
86
+ .mapValues((link) => ({
87
+ message: link.title!,
88
+ description: `The title of the footer links column with title=${link.title} in the footer`,
89
+ }))
90
+ .value()
91
+ : {};
83
92
 
84
93
  const footerLinkLabels: TranslationFileContent = chain(
85
- footer.links.flatMap((link) => link.items).filter((link) => !!link.label),
94
+ isMultiColumnFooterLinks(footer.links)
95
+ ? footer.links
96
+ .flatMap((link) => link.items)
97
+ .filter((link) => !!link.label)
98
+ : footer.links.filter((link) => !!link.label),
86
99
  )
87
100
  .keyBy((linkItem) => `link.item.label.${linkItem.label}`)
88
101
  .mapValues((linkItem) => ({
@@ -108,17 +121,24 @@ function translateFooter(
108
121
  footer: Footer,
109
122
  footerTranslations: TranslationFileContent,
110
123
  ): Footer {
111
- const links = footer.links.map((link) => ({
112
- ...link,
113
- title:
114
- footerTranslations[`link.title.${link.title}`]?.message ?? link.title,
115
- items: link.items.map((linkItem) => ({
116
- ...linkItem,
117
- label:
118
- footerTranslations[`link.item.label.${linkItem.label}`]?.message ??
119
- linkItem.label,
120
- })),
121
- }));
124
+ const links = isMultiColumnFooterLinks(footer.links)
125
+ ? footer.links.map((link) => ({
126
+ ...link,
127
+ title:
128
+ footerTranslations[`link.title.${link.title}`]?.message ?? link.title,
129
+ items: link.items.map((linkItem) => ({
130
+ ...linkItem,
131
+ label:
132
+ footerTranslations[`link.item.label.${linkItem.label}`]?.message ??
133
+ linkItem.label,
134
+ })),
135
+ }))
136
+ : footer.links.map((link) => ({
137
+ ...link,
138
+ label:
139
+ footerTranslations[`link.item.label.${link.label}`]?.message ??
140
+ link.label,
141
+ }));
122
142
 
123
143
  const copyright = footerTranslations.copyright?.message ?? footer.copyright;
124
144
 
@@ -311,14 +311,19 @@ const ThemeConfigSchema = Joi.object({
311
311
  href: Joi.string(),
312
312
  }),
313
313
  copyright: Joi.string(),
314
- links: Joi.array()
315
- .items(
316
- Joi.object({
317
- title: Joi.string().allow(null),
318
- items: Joi.array().items(FooterLinkItemSchema).default([]),
319
- }),
320
- )
321
- .default([]),
314
+ links: Joi.alternatives(
315
+ Joi.array()
316
+ .items(
317
+ Joi.object({
318
+ title: Joi.string().allow(null).default(null),
319
+ items: Joi.array().items(FooterLinkItemSchema).default([]),
320
+ }),
321
+ )
322
+ .default([]),
323
+ Joi.array().items(FooterLinkItemSchema).default([]),
324
+ ).messages({
325
+ 'alternatives.match': `The footer must be either simple or multi-column, and not a mix of the two. See: https://docusaurus.io/docs/api/themes/configuration#footer-links`,
326
+ }),
322
327
  }).optional(),
323
328
  prism: Joi.object({
324
329
  theme: Joi.object({