@docusaurus/theme-classic 0.0.0-4297 → 0.0.0-4305

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.
@@ -11,6 +11,18 @@ declare module '@docusaurus/theme-classic' {
11
11
  };
12
12
  }
13
13
 
14
+ declare module '@theme/Admonition' {
15
+ import type {ReactNode} from 'react';
16
+
17
+ export interface Props {
18
+ readonly children: ReactNode;
19
+ readonly type: 'note' | 'tip' | 'danger' | 'info' | 'caution';
20
+ readonly icon?: ReactNode;
21
+ readonly title?: string;
22
+ }
23
+ export default function Admonition(props: Props): JSX.Element;
24
+ }
25
+
14
26
  declare module '@theme/AnnouncementBar' {
15
27
  const AnnouncementBar: () => JSX.Element | null;
16
28
  export default AnnouncementBar;
@@ -68,13 +68,16 @@ function translateNavbar(navbar, navbarTranslations) {
68
68
  };
69
69
  }
70
70
 
71
+ function isMultiColumnFooterLinks(links) {
72
+ return links.length > 0 && '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 = (0, _lodash.chain)(isMultiColumnFooterLinks(footer.links) ? 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
79
  })).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 => {
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
+ })), _utilsValidation.Joi.array().items(FooterLinkItemSchema)).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
+ }).default([])
267
269
  }).optional(),
268
270
  prism: _utilsValidation.Joi.object({
269
271
  theme: _utilsValidation.Joi.object({
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import React from 'react';
8
+ import clsx from 'clsx';
9
+ const icons = {
10
+ note: (
11
+ <svg
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ width="14"
14
+ height="16"
15
+ viewBox="0 0 14 16">
16
+ <path
17
+ fillRule="evenodd"
18
+ d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"
19
+ />
20
+ </svg>
21
+ ),
22
+ tip: (
23
+ <svg
24
+ xmlns="http://www.w3.org/2000/svg"
25
+ width="12"
26
+ height="16"
27
+ viewBox="0 0 12 16">
28
+ <path
29
+ fillRule="evenodd"
30
+ d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"
31
+ />
32
+ </svg>
33
+ ),
34
+ danger: (
35
+ <svg
36
+ xmlns="http://www.w3.org/2000/svg"
37
+ width="12"
38
+ height="16"
39
+ viewBox="0 0 12 16">
40
+ <path
41
+ fillRule="evenodd"
42
+ d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"
43
+ />
44
+ </svg>
45
+ ),
46
+ info: (
47
+ <svg
48
+ xmlns="http://www.w3.org/2000/svg"
49
+ width="14"
50
+ height="16"
51
+ viewBox="0 0 14 16">
52
+ <path
53
+ fillRule="evenodd"
54
+ d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"
55
+ />
56
+ </svg>
57
+ ),
58
+ caution: (
59
+ <svg
60
+ xmlns="http://www.w3.org/2000/svg"
61
+ width="16"
62
+ height="16"
63
+ viewBox="0 0 16 16">
64
+ <path
65
+ fillRule="evenodd"
66
+ d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"
67
+ />
68
+ </svg>
69
+ ),
70
+ };
71
+ const ifmClassNames = {
72
+ note: 'secondary',
73
+ tip: 'success',
74
+ danger: 'danger',
75
+ info: 'info',
76
+ caution: 'warning',
77
+ };
78
+ export default function Admonition({
79
+ children,
80
+ type,
81
+ title = type,
82
+ icon = icons[type],
83
+ }) {
84
+ return (
85
+ <div
86
+ className={clsx(
87
+ 'admonition',
88
+ `admonition-${type}`,
89
+ 'alert',
90
+ `alert--${ifmClassNames[type]}`,
91
+ )}>
92
+ <div className="admonition-heading">
93
+ <h5>
94
+ <span className="admonition-icon">{icon}</span>
95
+ {title}
96
+ </h5>
97
+ </div>
98
+ <div className="admonition-content">{children}</div>
99
+ </div>
100
+ );
101
+ }
@@ -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-4297",
3
+ "version": "0.0.0-4305",
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-4297",
27
- "@docusaurus/plugin-content-blog": "0.0.0-4297",
28
- "@docusaurus/plugin-content-docs": "0.0.0-4297",
29
- "@docusaurus/plugin-content-pages": "0.0.0-4297",
30
- "@docusaurus/theme-common": "0.0.0-4297",
31
- "@docusaurus/theme-translations": "0.0.0-4297",
32
- "@docusaurus/utils": "0.0.0-4297",
33
- "@docusaurus/utils-validation": "0.0.0-4297",
26
+ "@docusaurus/core": "0.0.0-4305",
27
+ "@docusaurus/plugin-content-blog": "0.0.0-4305",
28
+ "@docusaurus/plugin-content-docs": "0.0.0-4305",
29
+ "@docusaurus/plugin-content-pages": "0.0.0-4305",
30
+ "@docusaurus/theme-common": "0.0.0-4305",
31
+ "@docusaurus/theme-translations": "0.0.0-4305",
32
+ "@docusaurus/utils": "0.0.0-4305",
33
+ "@docusaurus/utils-validation": "0.0.0-4305",
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-4297",
49
- "@docusaurus/types": "0.0.0-4297",
48
+ "@docusaurus/module-type-aliases": "0.0.0-4305",
49
+ "@docusaurus/types": "0.0.0-4305",
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": "84ae34c33c664bf856f3a92047919a074e087d77"
64
+ "gitHead": "4a85dc6ac221bf5771365cbb856daeb61fe24a33"
65
65
  }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import React from 'react';
9
+ import clsx from 'clsx';
10
+ import {Props} from '@theme/Admonition';
11
+
12
+ const icons = {
13
+ note: (
14
+ <svg
15
+ xmlns="http://www.w3.org/2000/svg"
16
+ width="14"
17
+ height="16"
18
+ viewBox="0 0 14 16">
19
+ <path
20
+ fillRule="evenodd"
21
+ d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"
22
+ />
23
+ </svg>
24
+ ),
25
+ tip: (
26
+ <svg
27
+ xmlns="http://www.w3.org/2000/svg"
28
+ width="12"
29
+ height="16"
30
+ viewBox="0 0 12 16">
31
+ <path
32
+ fillRule="evenodd"
33
+ d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"
34
+ />
35
+ </svg>
36
+ ),
37
+ danger: (
38
+ <svg
39
+ xmlns="http://www.w3.org/2000/svg"
40
+ width="12"
41
+ height="16"
42
+ viewBox="0 0 12 16">
43
+ <path
44
+ fillRule="evenodd"
45
+ d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"
46
+ />
47
+ </svg>
48
+ ),
49
+ info: (
50
+ <svg
51
+ xmlns="http://www.w3.org/2000/svg"
52
+ width="14"
53
+ height="16"
54
+ viewBox="0 0 14 16">
55
+ <path
56
+ fillRule="evenodd"
57
+ d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"
58
+ />
59
+ </svg>
60
+ ),
61
+ caution: (
62
+ <svg
63
+ xmlns="http://www.w3.org/2000/svg"
64
+ width="16"
65
+ height="16"
66
+ viewBox="0 0 16 16">
67
+ <path
68
+ fillRule="evenodd"
69
+ d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"
70
+ />
71
+ </svg>
72
+ ),
73
+ };
74
+
75
+ const ifmClassNames = {
76
+ note: 'secondary',
77
+ tip: 'success',
78
+ danger: 'danger',
79
+ info: 'info',
80
+ caution: 'warning',
81
+ };
82
+
83
+ export default function Admonition({
84
+ children,
85
+ type,
86
+ title = type,
87
+ icon = icons[type],
88
+ }: Props): JSX.Element {
89
+ return (
90
+ <div
91
+ className={clsx(
92
+ 'admonition',
93
+ `admonition-${type}`,
94
+ 'alert',
95
+ `alert--${ifmClassNames[type]}`,
96
+ )}>
97
+ <div className="admonition-heading">
98
+ <h5>
99
+ <span className="admonition-icon">{icon}</span>
100
+ {title}
101
+ </h5>
102
+ </div>
103
+ <div className="admonition-content">{children}</div>
104
+ </div>
105
+ );
106
+ }
@@ -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,18 @@ declare module '@docusaurus/theme-classic' {
11
11
  };
12
12
  }
13
13
 
14
+ declare module '@theme/Admonition' {
15
+ import type {ReactNode} from 'react';
16
+
17
+ export interface Props {
18
+ readonly children: ReactNode;
19
+ readonly type: 'note' | 'tip' | 'danger' | 'info' | 'caution';
20
+ readonly icon?: ReactNode;
21
+ readonly title?: string;
22
+ }
23
+ export default function Admonition(props: Props): JSX.Element;
24
+ }
25
+
14
26
  declare module '@theme/AnnouncementBar' {
15
27
  const AnnouncementBar: () => JSX.Element | null;
16
28
  export default AnnouncementBar;
@@ -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,10 +71,17 @@ function translateNavbar(
69
71
  };
70
72
  }
71
73
 
74
+ function isMultiColumnFooterLinks(
75
+ links: MultiColumnFooter['links'] | SimpleFooter['links'],
76
+ ): links is MultiColumnFooter['links'] {
77
+ return links.length > 0 && 'title' in links[0];
78
+ }
79
+
72
80
  function getFooterTranslationFile(footer: Footer): TranslationFileContent {
73
- // TODO POC code
74
81
  const footerLinkTitles: TranslationFileContent = chain(
75
- footer.links.filter((link) => !!link.title),
82
+ isMultiColumnFooterLinks(footer.links)
83
+ ? footer.links.filter((link) => !!link.title)
84
+ : [],
76
85
  )
77
86
  .keyBy((link) => `link.title.${link.title}`)
78
87
  .mapValues((link) => ({
@@ -82,7 +91,11 @@ function getFooterTranslationFile(footer: Footer): TranslationFileContent {
82
91
  .value();
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,13 +311,18 @@ const ThemeConfigSchema = Joi.object({
311
311
  href: Joi.string(),
312
312
  }),
313
313
  copyright: Joi.string(),
314
- links: Joi.array()
315
- .items(
314
+ links: Joi.alternatives(
315
+ Joi.array().items(
316
316
  Joi.object({
317
- title: Joi.string().allow(null),
317
+ title: Joi.string().allow(null).default(null),
318
318
  items: Joi.array().items(FooterLinkItemSchema).default([]),
319
319
  }),
320
- )
320
+ ),
321
+ Joi.array().items(FooterLinkItemSchema),
322
+ )
323
+ .messages({
324
+ '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`,
325
+ })
321
326
  .default([]),
322
327
  }).optional(),
323
328
  prism: Joi.object({