mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-14 16:52:39 +02:00
chore: backport retro compatible commits for the Docusaurus v2.3 release (#8585)
Co-authored-by: stnor <stefan@selessia.com> Co-authored-by: Joshua Chen <sidachen2003@gmail.com> Co-authored-by: sebastienlorber <lorber.sebastien@gmail.com> Co-authored-by: Sébastien Lorber <slorber@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Matija Sirk <matija.sirk@kopit.si> Co-authored-by: AHMET BAYHAN BAYRAMOGLU <49499275+ABB65@users.noreply.github.com> Co-authored-by: Stefan Norberg <stefan@norberg.org> Co-authored-by: Josh Goldberg <git@joshuakgoldberg.com> Co-authored-by: Muhammad Hammad <33136628+mhnaeem@users.noreply.github.com> Co-authored-by: Denis Al-Khelali <denis.al-khelali@itechart-group.com> Co-authored-by: Balthasar Hofer <lebalz@outlook.com> Co-authored-by: Danny Kim <0916dhkim@gmail.com> Co-authored-by: Frieder Bluemle <frieder.bluemle@gmail.com> Co-authored-by: John Reilly <johnny_reilly@hotmail.com> Co-authored-by: Robert Lawrence <62929526+r-lawrence@users.noreply.github.com> Co-authored-by: Sadegh Karimi <sadegh.krmi@gmail.com> Co-authored-by: Lachlan Heywood <lachieh@users.noreply.github.com> Co-authored-by: mturoci <64769322+mturoci@users.noreply.github.com> Co-authored-by: 宋锦丰 <36468758+SJFCS@users.noreply.github.com> Co-authored-by: Nguyễn Thành Nam <namnguyenthanh.work@gmail.com> Co-authored-by: Dongjoon Lee <djunnni@gmail.com> Co-authored-by: Thomas.CA <44041651+Thomascogez@users.noreply.github.com> Co-authored-by: Riccardo <riccardo.odone@gmail.com> Co-authored-by: Lane Goolsby <lanegoolsby@yahoo.com> Co-authored-by: Mariusz Krzaczkowski <m.krzaczkowski@yetiforce.com> Co-authored-by: Matija Sirk <sirkmatija@gmail.com> Co-authored-by: Jiří <zmrhal.j@gmail.com>
This commit is contained in:
parent
de972142a8
commit
c84d779627
173 changed files with 2640 additions and 1199 deletions
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "new.docusaurus.io",
|
"name": "new.docusaurus.io",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npx --package netlify-cli netlify dev"
|
"start": "npx --package netlify-cli netlify dev"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"useWorkspaces": true,
|
"useWorkspaces": true,
|
||||||
"changelog": {
|
"changelog": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "create-docusaurus",
|
"name": "create-docusaurus",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Create Docusaurus apps easily.",
|
"description": "Create Docusaurus apps easily.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -22,8 +22,8 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/logger": "2.2.0",
|
"@docusaurus/logger": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"commander": "^5.1.0",
|
"commander": "^5.1.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "docusaurus-2-classic-typescript-template",
|
"name": "docusaurus-2-classic-typescript-template",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docusaurus": "docusaurus",
|
"docusaurus": "docusaurus",
|
||||||
|
@ -15,8 +15,8 @@
|
||||||
"typecheck": "tsc"
|
"typecheck": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/preset-classic": "2.2.0",
|
"@docusaurus/preset-classic": "2.3.0",
|
||||||
"@mdx-js/react": "^1.6.22",
|
"@mdx-js/react": "^1.6.22",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"prism-react-renderer": "^1.3.5",
|
"prism-react-renderer": "^1.3.5",
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
"react-dom": "^17.0.2"
|
"react-dom": "^17.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/module-type-aliases": "2.2.0",
|
"@docusaurus/module-type-aliases": "2.3.0",
|
||||||
"@tsconfig/docusaurus": "^1.0.5",
|
"@tsconfig/docusaurus": "^1.0.5",
|
||||||
"typescript": "^4.7.4"
|
"typescript": "^4.7.4"
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,17 +8,22 @@ const darkCodeTheme = require('prism-react-renderer/themes/dracula');
|
||||||
const config = {
|
const config = {
|
||||||
title: 'My Site',
|
title: 'My Site',
|
||||||
tagline: 'Dinosaurs are cool',
|
tagline: 'Dinosaurs are cool',
|
||||||
url: 'https://your-docusaurus-test-site.com',
|
|
||||||
baseUrl: '/',
|
|
||||||
onBrokenLinks: 'throw',
|
|
||||||
onBrokenMarkdownLinks: 'warn',
|
|
||||||
favicon: 'img/favicon.ico',
|
favicon: 'img/favicon.ico',
|
||||||
|
|
||||||
|
// Set the production url of your site here
|
||||||
|
url: 'https://your-docusaurus-test-site.com',
|
||||||
|
// Set the /<baseUrl>/ pathname under which your site is served
|
||||||
|
// For GitHub pages deployment, it is often '/<projectName>/'
|
||||||
|
baseUrl: '/',
|
||||||
|
|
||||||
// GitHub pages deployment config.
|
// GitHub pages deployment config.
|
||||||
// If you aren't using GitHub pages, you don't need these.
|
// If you aren't using GitHub pages, you don't need these.
|
||||||
organizationName: 'facebook', // Usually your GitHub org/user name.
|
organizationName: 'facebook', // Usually your GitHub org/user name.
|
||||||
projectName: 'docusaurus', // Usually your repo name.
|
projectName: 'docusaurus', // Usually your repo name.
|
||||||
|
|
||||||
|
onBrokenLinks: 'throw',
|
||||||
|
onBrokenMarkdownLinks: 'warn',
|
||||||
|
|
||||||
// Even if you don't use internalization, you can use this field to set useful
|
// Even if you don't use internalization, you can use this field to set useful
|
||||||
// metadata like html lang. For example, if your site is Chinese, you may want
|
// metadata like html lang. For example, if your site is Chinese, you may want
|
||||||
// to replace "en" with "zh-Hans".
|
// to replace "en" with "zh-Hans".
|
||||||
|
@ -56,6 +61,8 @@ const config = {
|
||||||
themeConfig:
|
themeConfig:
|
||||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||||
({
|
({
|
||||||
|
// Replace with your project's social card
|
||||||
|
image: 'img/docusaurus-social-card.jpg',
|
||||||
navbar: {
|
navbar: {
|
||||||
title: 'My Site',
|
title: 'My Site',
|
||||||
logo: {
|
logo: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "docusaurus-2-classic-template",
|
"name": "docusaurus-2-classic-template",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docusaurus": "docusaurus",
|
"docusaurus": "docusaurus",
|
||||||
|
@ -14,8 +14,8 @@
|
||||||
"write-heading-ids": "docusaurus write-heading-ids"
|
"write-heading-ids": "docusaurus write-heading-ids"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/preset-classic": "2.2.0",
|
"@docusaurus/preset-classic": "2.3.0",
|
||||||
"@mdx-js/react": "^1.6.22",
|
"@mdx-js/react": "^1.6.22",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"prism-react-renderer": "^1.3.5",
|
"prism-react-renderer": "^1.3.5",
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
"react-dom": "^17.0.2"
|
"react-dom": "^17.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/module-type-aliases": "2.2.0"
|
"@docusaurus/module-type-aliases": "2.3.0"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
|
|
|
@ -13,17 +13,22 @@
|
||||||
const config = {
|
const config = {
|
||||||
title: 'My Site',
|
title: 'My Site',
|
||||||
tagline: 'The tagline of my site',
|
tagline: 'The tagline of my site',
|
||||||
url: 'https://your-docusaurus-test-site.com',
|
|
||||||
baseUrl: '/',
|
|
||||||
onBrokenLinks: 'throw',
|
|
||||||
onBrokenMarkdownLinks: 'warn',
|
|
||||||
favicon: 'img/favicon.ico',
|
favicon: 'img/favicon.ico',
|
||||||
|
|
||||||
|
// Set the production url of your site here
|
||||||
|
url: 'https://your-docusaurus-test-site.com',
|
||||||
|
// Set the /<baseUrl>/ pathname under which your site is served
|
||||||
|
// For GitHub pages deployment, it is often '/<projectName>/'
|
||||||
|
baseUrl: '/',
|
||||||
|
|
||||||
// GitHub pages deployment config.
|
// GitHub pages deployment config.
|
||||||
// If you aren't using GitHub pages, you don't need these.
|
// If you aren't using GitHub pages, you don't need these.
|
||||||
organizationName: 'facebook', // Usually your GitHub org/user name.
|
organizationName: 'facebook', // Usually your GitHub org/user name.
|
||||||
projectName: 'docusaurus', // Usually your repo name.
|
projectName: 'docusaurus', // Usually your repo name.
|
||||||
|
|
||||||
|
onBrokenLinks: 'throw',
|
||||||
|
onBrokenMarkdownLinks: 'warn',
|
||||||
|
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[
|
||||||
'classic',
|
'classic',
|
||||||
|
@ -53,6 +58,7 @@ const config = {
|
||||||
themeConfig:
|
themeConfig:
|
||||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||||
({
|
({
|
||||||
|
image: 'img/docusaurus-social-card.jpg',
|
||||||
navbar: {
|
navbar: {
|
||||||
title: 'My Meta Project',
|
title: 'My Meta Project',
|
||||||
logo: {
|
logo: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "docusaurus-2-facebook-template",
|
"name": "docusaurus-2-facebook-template",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docusaurus": "docusaurus",
|
"docusaurus": "docusaurus",
|
||||||
|
@ -18,8 +18,8 @@
|
||||||
"format:diff": "prettier --config .prettierrc --list-different \"**/*.{js,jsx,ts,tsx,md,mdx}\""
|
"format:diff": "prettier --config .prettierrc --list-different \"**/*.{js,jsx,ts,tsx,md,mdx}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/preset-classic": "2.2.0",
|
"@docusaurus/preset-classic": "2.3.0",
|
||||||
"@mdx-js/react": "^1.6.22",
|
"@mdx-js/react": "^1.6.22",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
|
|
|
@ -51,7 +51,11 @@ You can use absolute paths to reference images in the static directory (`static/
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
You can reference images relative to the current file as well, as shown in [the extra guides](../tutorial-extras/manage-docs-versions.md).
|
You can reference images relative to the current file as well. This is particularly useful to colocate images close to the Markdown files using them:
|
||||||
|
|
||||||
|
```md
|
||||||
|

|
||||||
|
```
|
||||||
|
|
||||||
## Code Blocks
|
## Code Blocks
|
||||||
|
|
||||||
|
|
BIN
packages/create-docusaurus/templates/shared/static/img/docusaurus-social-card.jpg
vendored
Normal file
BIN
packages/create-docusaurus/templates/shared/static/img/docusaurus-social-card.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/cssnano-preset",
|
"name": "@docusaurus/cssnano-preset",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Advanced cssnano preset for maximum optimization.",
|
"description": "Advanced cssnano preset for maximum optimization.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/logger",
|
"name": "@docusaurus/logger",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "An encapsulated logger for semantically formatting console messages.",
|
"description": "An encapsulated logger for semantically formatting console messages.",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/mdx-loader",
|
"name": "@docusaurus/mdx-loader",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Docusaurus Loader for MDX",
|
"description": "Docusaurus Loader for MDX",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
|
@ -20,8 +20,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.18.8",
|
"@babel/parser": "^7.18.8",
|
||||||
"@babel/traverse": "^7.18.8",
|
"@babel/traverse": "^7.18.8",
|
||||||
"@docusaurus/logger": "2.2.0",
|
"@docusaurus/logger": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"@mdx-js/mdx": "^1.6.22",
|
"@mdx-js/mdx": "^1.6.22",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
|
@ -37,10 +37,9 @@
|
||||||
"webpack": "^5.73.0"
|
"webpack": "^5.73.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@types/escape-html": "^1.0.2",
|
"@types/escape-html": "^1.0.2",
|
||||||
"@types/mdast": "^3.0.10",
|
"@types/mdast": "^3.0.10",
|
||||||
"@types/mermaid": "^8.2.9",
|
|
||||||
"@types/stringify-object": "^3.3.1",
|
"@types/stringify-object": "^3.3.1",
|
||||||
"@types/unist": "^2.0.6",
|
"@types/unist": "^2.0.6",
|
||||||
"rehype-stringify": "^8.0.0",
|
"rehype-stringify": "^8.0.0",
|
||||||
|
|
10
packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/__fixtures__/nesting.md
generated
Normal file
10
packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/__fixtures__/nesting.md
generated
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Test nested Admonitions
|
||||||
|
|
||||||
|
::::info **Weather**
|
||||||
|
On nice days, you can enjoy skiing in the mountains.
|
||||||
|
|
||||||
|
:::danger *Storms*
|
||||||
|
Take care of snowstorms...
|
||||||
|
:::
|
||||||
|
|
||||||
|
::::
|
|
@ -42,3 +42,8 @@ exports[`admonitions remark plugin interpolation 1`] = `
|
||||||
"<p>Test admonition with interpolated title/body</p>
|
"<p>Test admonition with interpolated title/body</p>
|
||||||
<admonition type="tip"><mdxAdmonitionTitle>My <code>interpolated</code> <strong>title</strong> <button style={{color: "red"}} onClick={() => alert("click")}>test</mdxAdmonitionTitle><p><code>body</code> <strong>interpolated</strong> content</p></admonition>"
|
<admonition type="tip"><mdxAdmonitionTitle>My <code>interpolated</code> <strong>title</strong> <button style={{color: "red"}} onClick={() => alert("click")}>test</mdxAdmonitionTitle><p><code>body</code> <strong>interpolated</strong> content</p></admonition>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`admonitions remark plugin nesting 1`] = `
|
||||||
|
"<p>Test nested Admonitions</p>
|
||||||
|
<admonition type="info"><mdxAdmonitionTitle><strong>Weather</strong></mdxAdmonitionTitle><p>On nice days, you can enjoy skiing in the mountains.</p><admonition type="danger"><mdxAdmonitionTitle><em>Storms</em></mdxAdmonitionTitle><p>Take care of snowstorms...</p></admonition></admonition>"
|
||||||
|
`;
|
||||||
|
|
|
@ -50,4 +50,9 @@ describe('admonitions remark plugin', () => {
|
||||||
const result = await processFixture('interpolation');
|
const result = await processFixture('interpolation');
|
||||||
expect(result).toMatchSnapshot();
|
expect(result).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('nesting', async () => {
|
||||||
|
const result = await processFixture('nesting');
|
||||||
|
expect(result).toMatchSnapshot();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -52,9 +52,20 @@ const plugin: Plugin = function plugin(
|
||||||
const options = normalizeOptions(optionsInput);
|
const options = normalizeOptions(optionsInput);
|
||||||
|
|
||||||
const keywords = Object.values(options.keywords).map(escapeRegExp).join('|');
|
const keywords = Object.values(options.keywords).map(escapeRegExp).join('|');
|
||||||
|
const nestingChar = escapeRegExp(options.tag.slice(0, 1));
|
||||||
const tag = escapeRegExp(options.tag);
|
const tag = escapeRegExp(options.tag);
|
||||||
const regex = new RegExp(`${tag}(${keywords})(?: *(.*))?\n`);
|
|
||||||
const escapeTag = new RegExp(escapeRegExp(`\\${options.tag}`), 'g');
|
// resolve th nesting level of an opening tag
|
||||||
|
// ::: -> 0, :::: -> 1, ::::: -> 2 ...
|
||||||
|
const nestingLevelRegex = new RegExp(
|
||||||
|
`^${tag}(?<nestingLevel>${nestingChar}*)`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const regex = new RegExp(`${tag}${nestingChar}*(${keywords})(?: *(.*))?\n`);
|
||||||
|
const escapeTag = new RegExp(
|
||||||
|
escapeRegExp(`\\${options.tag}${options.tag.slice(0, 1)}*`),
|
||||||
|
'g',
|
||||||
|
);
|
||||||
|
|
||||||
// The tokenizer is called on blocks to determine if there is an admonition
|
// The tokenizer is called on blocks to determine if there is an admonition
|
||||||
// present and create tags for it
|
// present and create tags for it
|
||||||
|
@ -77,6 +88,11 @@ const plugin: Plugin = function plugin(
|
||||||
];
|
];
|
||||||
const food = [];
|
const food = [];
|
||||||
const content = [];
|
const content = [];
|
||||||
|
// get the nesting level of the opening tag
|
||||||
|
const openingLevel =
|
||||||
|
nestingLevelRegex.exec(opening)!.groups!.nestingLevel!.length;
|
||||||
|
// used as a stack to keep track of nested admonitions
|
||||||
|
const nestingLevels: number[] = [openingLevel];
|
||||||
|
|
||||||
let newValue = value;
|
let newValue = value;
|
||||||
// consume lines until a closing tag
|
// consume lines until a closing tag
|
||||||
|
@ -88,12 +104,32 @@ const plugin: Plugin = function plugin(
|
||||||
next !== -1 ? newValue.slice(idx + 1, next) : newValue.slice(idx + 1);
|
next !== -1 ? newValue.slice(idx + 1, next) : newValue.slice(idx + 1);
|
||||||
food.push(line);
|
food.push(line);
|
||||||
newValue = newValue.slice(idx + 1);
|
newValue = newValue.slice(idx + 1);
|
||||||
// the closing tag is NOT part of the content
|
const nesting = nestingLevelRegex.exec(line);
|
||||||
if (line.startsWith(options.tag)) {
|
idx = newValue.indexOf(NEWLINE);
|
||||||
break;
|
if (!nesting) {
|
||||||
|
content.push(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const tagLevel = nesting.groups!.nestingLevel!.length;
|
||||||
|
// first level
|
||||||
|
if (nestingLevels.length === 0) {
|
||||||
|
nestingLevels.push(tagLevel);
|
||||||
|
content.push(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const currentLevel = nestingLevels[nestingLevels.length - 1]!;
|
||||||
|
if (tagLevel < currentLevel) {
|
||||||
|
// entering a nested admonition block
|
||||||
|
nestingLevels.push(tagLevel);
|
||||||
|
} else if (tagLevel === currentLevel) {
|
||||||
|
// closing a nested admonition block
|
||||||
|
nestingLevels.pop();
|
||||||
|
// the closing tag is NOT part of the content
|
||||||
|
if (nestingLevels.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
content.push(line);
|
content.push(line);
|
||||||
idx = newValue.indexOf(NEWLINE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// consume the processed tag and replace escape sequences
|
// consume the processed tag and replace escape sequences
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/migrate",
|
"name": "@docusaurus/migrate",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "A CLI tool to migrate from older versions of Docusaurus.",
|
"description": "A CLI tool to migrate from older versions of Docusaurus.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -24,8 +24,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.18.6",
|
"@babel/core": "^7.18.6",
|
||||||
"@babel/preset-env": "^7.18.6",
|
"@babel/preset-env": "^7.18.6",
|
||||||
"@docusaurus/logger": "2.2.0",
|
"@docusaurus/logger": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"@mapbox/hast-util-to-jsx": "^2.0.0",
|
"@mapbox/hast-util-to-jsx": "^2.0.0",
|
||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
"commander": "^5.1.0",
|
"commander": "^5.1.0",
|
||||||
|
|
|
@ -120,7 +120,7 @@ exports[`migration CLI migrates complex website: write 1`] = `
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"copyright": "Copyright © 2022 Facebook Inc.",
|
"copyright": "Copyright © 2023 Facebook Inc.",
|
||||||
"logo": {
|
"logo": {
|
||||||
"src": "img/docusaurus_monochrome.svg"
|
"src": "img/docusaurus_monochrome.svg"
|
||||||
}
|
}
|
||||||
|
@ -303,7 +303,7 @@ exports[`migration CLI migrates missing versions: write 1`] = `
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"copyright": "Copyright © 2022 Facebook Inc.",
|
"copyright": "Copyright © 2023 Facebook Inc.",
|
||||||
"logo": {
|
"logo": {
|
||||||
"src": "img/docusaurus_monochrome.svg"
|
"src": "img/docusaurus_monochrome.svg"
|
||||||
}
|
}
|
||||||
|
@ -483,7 +483,7 @@ exports[`migration CLI migrates simple website: write 1`] = `
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"copyright": "Copyright © 2022 Facebook Inc.",
|
"copyright": "Copyright © 2023 Facebook Inc.",
|
||||||
"logo": {
|
"logo": {
|
||||||
"src": "img/docusaurus_monochrome.svg"
|
"src": "img/docusaurus_monochrome.svg"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/module-type-aliases",
|
"name": "@docusaurus/module-type-aliases",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Docusaurus module type aliases.",
|
"description": "Docusaurus module type aliases.",
|
||||||
"types": "./src/index.d.ts",
|
"types": "./src/index.d.ts",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/react-loadable": "5.5.2",
|
"@docusaurus/react-loadable": "5.5.2",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@types/history": "^4.7.11",
|
"@types/history": "^4.7.11",
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"@types/react-router-config": "*",
|
"@types/react-router-config": "*",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/plugin-client-redirects",
|
"name": "@docusaurus/plugin-client-redirects",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Client redirects plugin for Docusaurus.",
|
"description": "Client redirects plugin for Docusaurus.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
|
@ -18,18 +18,18 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/logger": "2.2.0",
|
"@docusaurus/logger": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"@docusaurus/utils-common": "2.2.0",
|
"@docusaurus/utils-common": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"eta": "^1.12.3",
|
"eta": "^1.12.3",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/types": "2.2.0"
|
"@docusaurus/types": "2.3.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.4 || ^17.0.0",
|
"react": "^16.8.4 || ^17.0.0",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/plugin-content-blog",
|
"name": "@docusaurus/plugin-content-blog",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Blog plugin for Docusaurus.",
|
"description": "Blog plugin for Docusaurus.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "src/plugin-content-blog.d.ts",
|
"types": "src/plugin-content-blog.d.ts",
|
||||||
|
@ -18,13 +18,13 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/logger": "2.2.0",
|
"@docusaurus/logger": "2.3.0",
|
||||||
"@docusaurus/mdx-loader": "2.2.0",
|
"@docusaurus/mdx-loader": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"@docusaurus/utils-common": "2.2.0",
|
"@docusaurus/utils-common": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"cheerio": "^1.0.0-rc.12",
|
"cheerio": "^1.0.0-rc.12",
|
||||||
"feed": "^4.2.2",
|
"feed": "^4.2.2",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
|
@ -35,9 +35,6 @@
|
||||||
"utility-types": "^3.10.0",
|
"utility-types": "^3.10.0",
|
||||||
"webpack": "^5.73.0"
|
"webpack": "^5.73.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
|
||||||
"escape-string-regexp": "^4.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.4 || ^17.0.0",
|
"react": "^16.8.4 || ^17.0.0",
|
||||||
"react-dom": "^16.8.4 || ^17.0.0"
|
"react-dom": "^16.8.4 || ^17.0.0"
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -143,4 +143,56 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
fsMock.mockClear();
|
fsMock.mockClear();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('filters to the first two entries', async () => {
|
||||||
|
const siteDir = path.join(__dirname, '__fixtures__', 'website');
|
||||||
|
const outDir = path.join(siteDir, 'build-snap');
|
||||||
|
const siteConfig = {
|
||||||
|
title: 'Hello',
|
||||||
|
baseUrl: '/myBaseUrl/',
|
||||||
|
url: 'https://docusaurus.io',
|
||||||
|
favicon: 'image/favicon.ico',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build is quite difficult to mock, so we built the blog beforehand and
|
||||||
|
// copied the output to the fixture...
|
||||||
|
await testGenerateFeeds(
|
||||||
|
{
|
||||||
|
siteDir,
|
||||||
|
siteConfig,
|
||||||
|
i18n: DefaultI18N,
|
||||||
|
outDir,
|
||||||
|
} as LoadContext,
|
||||||
|
{
|
||||||
|
path: 'blog',
|
||||||
|
routeBasePath: 'blog',
|
||||||
|
tagsBasePath: 'tags',
|
||||||
|
authorsMapPath: 'authors.yml',
|
||||||
|
include: DEFAULT_OPTIONS.include,
|
||||||
|
exclude: DEFAULT_OPTIONS.exclude,
|
||||||
|
feedOptions: {
|
||||||
|
type: [feedType],
|
||||||
|
copyright: 'Copyright',
|
||||||
|
createFeedItems: async (params) => {
|
||||||
|
const {blogPosts, defaultCreateFeedItems, ...rest} = params;
|
||||||
|
const blogPostsFiltered = blogPosts.filter(
|
||||||
|
(item, index) => index < 2,
|
||||||
|
);
|
||||||
|
return defaultCreateFeedItems({
|
||||||
|
blogPosts: blogPostsFiltered,
|
||||||
|
...rest,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
readingTime: ({content, defaultReadingTime}) =>
|
||||||
|
defaultReadingTime({content}),
|
||||||
|
truncateMarker: /<!--\s*truncate\s*-->/,
|
||||||
|
} as PluginOptions,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
fsMock.mock.calls.map((call) => call[1] as string),
|
||||||
|
).toMatchSnapshot();
|
||||||
|
fsMock.mockClear();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import escapeStringRegexp from 'escape-string-regexp';
|
import {escapeRegexp} from '@docusaurus/utils';
|
||||||
import {validateBlogPostFrontMatter} from '../frontMatter';
|
import {validateBlogPostFrontMatter} from '../frontMatter';
|
||||||
import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog';
|
import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog';
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ function testField(params: {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line jest/no-conditional-expect
|
// eslint-disable-next-line jest/no-conditional-expect
|
||||||
expect((err as Error).message).toMatch(
|
expect((err as Error).message).toMatch(
|
||||||
new RegExp(escapeStringRegexp(message)),
|
new RegExp(escapeRegexp(message)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import logger from '@docusaurus/logger';
|
import logger from '@docusaurus/logger';
|
||||||
import {Feed, type Author as FeedAuthor, type Item as FeedItem} from 'feed';
|
import {Feed, type Author as FeedAuthor} from 'feed';
|
||||||
import {normalizeUrl, readOutputHTMLFile} from '@docusaurus/utils';
|
import {normalizeUrl, readOutputHTMLFile} from '@docusaurus/utils';
|
||||||
import {blogPostContainerID} from '@docusaurus/utils-common';
|
import {blogPostContainerID} from '@docusaurus/utils-common';
|
||||||
import {load as cheerioLoad} from 'cheerio';
|
import {load as cheerioLoad} from 'cheerio';
|
||||||
|
@ -18,6 +18,7 @@ import type {
|
||||||
PluginOptions,
|
PluginOptions,
|
||||||
Author,
|
Author,
|
||||||
BlogPost,
|
BlogPost,
|
||||||
|
BlogFeedItem,
|
||||||
} from '@docusaurus/plugin-content-blog';
|
} from '@docusaurus/plugin-content-blog';
|
||||||
|
|
||||||
async function generateBlogFeed({
|
async function generateBlogFeed({
|
||||||
|
@ -54,14 +55,39 @@ async function generateBlogFeed({
|
||||||
copyright: feedOptions.copyright,
|
copyright: feedOptions.copyright,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const createFeedItems =
|
||||||
|
options.feedOptions.createFeedItems ?? defaultCreateFeedItems;
|
||||||
|
|
||||||
|
const feedItems = await createFeedItems({
|
||||||
|
blogPosts,
|
||||||
|
siteConfig,
|
||||||
|
outDir,
|
||||||
|
defaultCreateFeedItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
feedItems.forEach(feed.addItem);
|
||||||
|
|
||||||
|
return feed;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function defaultCreateFeedItems({
|
||||||
|
blogPosts,
|
||||||
|
siteConfig,
|
||||||
|
outDir,
|
||||||
|
}: {
|
||||||
|
blogPosts: BlogPost[];
|
||||||
|
siteConfig: DocusaurusConfig;
|
||||||
|
outDir: string;
|
||||||
|
}): Promise<BlogFeedItem[]> {
|
||||||
|
const {url: siteUrl} = siteConfig;
|
||||||
|
|
||||||
function toFeedAuthor(author: Author): FeedAuthor {
|
function toFeedAuthor(author: Author): FeedAuthor {
|
||||||
return {name: author.name, link: author.url, email: author.email};
|
return {name: author.name, link: author.url, email: author.email};
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(
|
return Promise.all(
|
||||||
blogPosts.map(async (post) => {
|
blogPosts.map(async (post) => {
|
||||||
const {
|
const {
|
||||||
id,
|
|
||||||
metadata: {
|
metadata: {
|
||||||
title: metadataTitle,
|
title: metadataTitle,
|
||||||
permalink,
|
permalink,
|
||||||
|
@ -79,10 +105,11 @@ async function generateBlogFeed({
|
||||||
);
|
);
|
||||||
const $ = cheerioLoad(content);
|
const $ = cheerioLoad(content);
|
||||||
|
|
||||||
const feedItem: FeedItem = {
|
const link = normalizeUrl([siteUrl, permalink]);
|
||||||
|
const feedItem: BlogFeedItem = {
|
||||||
title: metadataTitle,
|
title: metadataTitle,
|
||||||
id,
|
id: link,
|
||||||
link: normalizeUrl([siteUrl, permalink]),
|
link,
|
||||||
date,
|
date,
|
||||||
description,
|
description,
|
||||||
// Atom feed demands the "term", while other feeds use "name"
|
// Atom feed demands the "term", while other feeds use "name"
|
||||||
|
@ -99,9 +126,7 @@ async function generateBlogFeed({
|
||||||
|
|
||||||
return feedItem;
|
return feedItem;
|
||||||
}),
|
}),
|
||||||
).then((items) => items.forEach(feed.addItem));
|
);
|
||||||
|
|
||||||
return feed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createBlogFeedFile({
|
async function createBlogFeedFile({
|
||||||
|
|
|
@ -124,6 +124,7 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
|
||||||
.default(DEFAULT_OPTIONS.feedOptions.copyright),
|
.default(DEFAULT_OPTIONS.feedOptions.copyright),
|
||||||
}),
|
}),
|
||||||
language: Joi.string(),
|
language: Joi.string(),
|
||||||
|
createFeedItems: Joi.function(),
|
||||||
}).default(DEFAULT_OPTIONS.feedOptions),
|
}).default(DEFAULT_OPTIONS.feedOptions),
|
||||||
authorsMapPath: Joi.string().default(DEFAULT_OPTIONS.authorsMapPath),
|
authorsMapPath: Joi.string().default(DEFAULT_OPTIONS.authorsMapPath),
|
||||||
readingTime: Joi.function().default(() => DEFAULT_OPTIONS.readingTime),
|
readingTime: Joi.function().default(() => DEFAULT_OPTIONS.readingTime),
|
||||||
|
|
|
@ -9,12 +9,19 @@ declare module '@docusaurus/plugin-content-blog' {
|
||||||
import type {LoadedMDXContent} from '@docusaurus/mdx-loader';
|
import type {LoadedMDXContent} from '@docusaurus/mdx-loader';
|
||||||
import type {MDXOptions} from '@docusaurus/mdx-loader';
|
import type {MDXOptions} from '@docusaurus/mdx-loader';
|
||||||
import type {FrontMatterTag, Tag} from '@docusaurus/utils';
|
import type {FrontMatterTag, Tag} from '@docusaurus/utils';
|
||||||
import type {Plugin, LoadContext} from '@docusaurus/types';
|
import type {DocusaurusConfig, Plugin, LoadContext} from '@docusaurus/types';
|
||||||
|
import type {Item as FeedItem} from 'feed';
|
||||||
import type {Overwrite} from 'utility-types';
|
import type {Overwrite} from 'utility-types';
|
||||||
|
|
||||||
export type Assets = {
|
export type Assets = {
|
||||||
/**
|
/**
|
||||||
* If `metadata.image` is a collocated image path, this entry will be the
|
* If `metadata.yarn workspace website typecheck
|
||||||
|
4
|
||||||
|
yarn workspace v1.22.19yarn workspace website typecheck
|
||||||
|
4
|
||||||
|
yarn workspace v1.22.19yarn workspace website typecheck
|
||||||
|
4
|
||||||
|
yarn workspace v1.22.19image` is a collocated image path, this entry will be the
|
||||||
* bundler-generated image path. Otherwise, it's empty, and the image URL
|
* bundler-generated image path. Otherwise, it's empty, and the image URL
|
||||||
* should be accessed through `frontMatter.image`.
|
* should be accessed through `frontMatter.image`.
|
||||||
*/
|
*/
|
||||||
|
@ -255,6 +262,24 @@ declare module '@docusaurus/plugin-content-blog' {
|
||||||
copyright: string;
|
copyright: string;
|
||||||
/** Language of the feed. */
|
/** Language of the feed. */
|
||||||
language?: string;
|
language?: string;
|
||||||
|
/** Allow control over the construction of BlogFeedItems */
|
||||||
|
createFeedItems?: CreateFeedItemsFn;
|
||||||
|
};
|
||||||
|
|
||||||
|
type DefaultCreateFeedItemsParams = {
|
||||||
|
blogPosts: BlogPost[];
|
||||||
|
siteConfig: DocusaurusConfig;
|
||||||
|
outDir: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CreateFeedItemsFn = (
|
||||||
|
params: CreateFeedItemsParams,
|
||||||
|
) => Promise<BlogFeedItem[]>;
|
||||||
|
|
||||||
|
type CreateFeedItemsParams = DefaultCreateFeedItemsParams & {
|
||||||
|
defaultCreateFeedItems: (
|
||||||
|
params: DefaultCreateFeedItemsParams,
|
||||||
|
) => Promise<BlogFeedItem[]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -436,6 +461,8 @@ declare module '@docusaurus/plugin-content-blog' {
|
||||||
content: string;
|
content: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type BlogFeedItem = FeedItem;
|
||||||
|
|
||||||
export type BlogPaginatedMetadata = {
|
export type BlogPaginatedMetadata = {
|
||||||
/** Title of the entire blog. */
|
/** Title of the entire blog. */
|
||||||
readonly blogTitle: string;
|
readonly blogTitle: string;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/plugin-content-docs",
|
"name": "@docusaurus/plugin-content-docs",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Docs plugin for Docusaurus.",
|
"description": "Docs plugin for Docusaurus.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
@ -35,13 +35,13 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/logger": "2.2.0",
|
"@docusaurus/logger": "2.3.0",
|
||||||
"@docusaurus/mdx-loader": "2.2.0",
|
"@docusaurus/mdx-loader": "2.3.0",
|
||||||
"@docusaurus/module-type-aliases": "2.2.0",
|
"@docusaurus/module-type-aliases": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"@types/react-router-config": "^5.0.6",
|
"@types/react-router-config": "^5.0.6",
|
||||||
"combine-promises": "^1.1.0",
|
"combine-promises": "^1.1.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
|
@ -56,7 +56,6 @@
|
||||||
"@types/js-yaml": "^4.0.5",
|
"@types/js-yaml": "^4.0.5",
|
||||||
"@types/picomatch": "^2.3.0",
|
"@types/picomatch": "^2.3.0",
|
||||||
"commander": "^5.1.0",
|
"commander": "^5.1.0",
|
||||||
"escape-string-regexp": "^4.0.0",
|
|
||||||
"picomatch": "^2.3.1",
|
"picomatch": "^2.3.1",
|
||||||
"shelljs": "^0.8.5"
|
"shelljs": "^0.8.5"
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import escapeStringRegexp from 'escape-string-regexp';
|
import {escapeRegexp} from '@docusaurus/utils';
|
||||||
import {validateDocFrontMatter} from '../frontMatter';
|
import {validateDocFrontMatter} from '../frontMatter';
|
||||||
import type {DocFrontMatter} from '@docusaurus/plugin-content-docs';
|
import type {DocFrontMatter} from '@docusaurus/plugin-content-docs';
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ function testField(params: {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line jest/no-conditional-expect
|
// eslint-disable-next-line jest/no-conditional-expect
|
||||||
expect((err as Error).message).toMatch(
|
expect((err as Error).message).toMatch(
|
||||||
new RegExp(escapeStringRegexp(message)),
|
new RegExp(escapeRegexp(message)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -336,12 +336,13 @@ export default async function pluginContentDocs(
|
||||||
};
|
};
|
||||||
|
|
||||||
function createMDXLoaderRule(): RuleSetRule {
|
function createMDXLoaderRule(): RuleSetRule {
|
||||||
const contentDirs = versionsMetadata.flatMap(getContentPathList);
|
const contentDirs = versionsMetadata
|
||||||
|
.flatMap(getContentPathList)
|
||||||
|
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
|
||||||
|
.map(addTrailingPathSeparator);
|
||||||
return {
|
return {
|
||||||
test: /\.mdx?$/i,
|
test: /\.mdx?$/i,
|
||||||
include: contentDirs
|
include: contentDirs,
|
||||||
// Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
|
|
||||||
.map(addTrailingPathSeparator),
|
|
||||||
use: [
|
use: [
|
||||||
getJSLoader({isServer}),
|
getJSLoader({isServer}),
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/plugin-content-pages",
|
"name": "@docusaurus/plugin-content-pages",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Pages plugin for Docusaurus.",
|
"description": "Pages plugin for Docusaurus.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "src/plugin-content-pages.d.ts",
|
"types": "src/plugin-content-pages.d.ts",
|
||||||
|
@ -18,11 +18,11 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/mdx-loader": "2.2.0",
|
"@docusaurus/mdx-loader": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"tslib": "^2.4.0",
|
"tslib": "^2.4.0",
|
||||||
"webpack": "^5.73.0"
|
"webpack": "^5.73.0"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/plugin-debug",
|
"name": "@docusaurus/plugin-debug",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Debug plugin for Docusaurus.",
|
"description": "Debug plugin for Docusaurus.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "src/plugin-debug.d.ts",
|
"types": "src/plugin-debug.d.ts",
|
||||||
|
@ -20,9 +20,9 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"react-json-view": "^1.21.3",
|
"react-json-view": "^1.21.3",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/plugin-google-analytics",
|
"name": "@docusaurus/plugin-google-analytics",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Global analytics (analytics.js) plugin for Docusaurus.",
|
"description": "Global analytics (analytics.js) plugin for Docusaurus.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
|
@ -18,9 +18,9 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/plugin-google-gtag",
|
"name": "@docusaurus/plugin-google-gtag",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Global Site Tag (gtag.js) plugin for Docusaurus.",
|
"description": "Global Site Tag (gtag.js) plugin for Docusaurus.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
|
@ -18,9 +18,9 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|
3
packages/docusaurus-plugin-google-tag-manager/.npmignore
Normal file
3
packages/docusaurus-plugin-google-tag-manager/.npmignore
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.tsbuildinfo*
|
||||||
|
tsconfig*
|
||||||
|
__tests__
|
7
packages/docusaurus-plugin-google-tag-manager/README.md
Normal file
7
packages/docusaurus-plugin-google-tag-manager/README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# `@docusaurus/plugin-google-tag-manager`
|
||||||
|
|
||||||
|
Google Tag Manager plugin for Docusaurus.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
See [plugin-google-tag-manager documentation](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-google-tag-manager).
|
33
packages/docusaurus-plugin-google-tag-manager/package.json
Normal file
33
packages/docusaurus-plugin-google-tag-manager/package.json
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"name": "@docusaurus/plugin-google-tag-manager",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "Google Tag Manager (gtm.js) plugin for Docusaurus.",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"types": "lib/index.d.ts",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --build",
|
||||||
|
"watch": "tsc --build --watch"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/facebook/docusaurus.git",
|
||||||
|
"directory": "packages/docusaurus-plugin-google-tag-manager"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@docusaurus/core": "2.3.0",
|
||||||
|
"@docusaurus/types": "2.3.0",
|
||||||
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.4 || ^17.0.0",
|
||||||
|
"react-dom": "^16.8.4 || ^17.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.14"
|
||||||
|
}
|
||||||
|
}
|
78
packages/docusaurus-plugin-google-tag-manager/src/index.ts
Normal file
78
packages/docusaurus-plugin-google-tag-manager/src/index.ts
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Joi} from '@docusaurus/utils-validation';
|
||||||
|
import type {
|
||||||
|
LoadContext,
|
||||||
|
Plugin,
|
||||||
|
OptionValidationContext,
|
||||||
|
} from '@docusaurus/types';
|
||||||
|
import type {PluginOptions, Options} from './options';
|
||||||
|
|
||||||
|
export default function pluginGoogleAnalytics(
|
||||||
|
context: LoadContext,
|
||||||
|
options: PluginOptions,
|
||||||
|
): Plugin {
|
||||||
|
const {containerId} = options;
|
||||||
|
const isProd = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'docusaurus-plugin-google-tag-manager',
|
||||||
|
|
||||||
|
contentLoaded({actions}) {
|
||||||
|
actions.setGlobalData(options);
|
||||||
|
},
|
||||||
|
|
||||||
|
injectHtmlTags() {
|
||||||
|
if (!isProd) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
preBodyTags: [
|
||||||
|
{
|
||||||
|
tagName: 'noscript',
|
||||||
|
innerHTML: `<iframe src="https://www.googletagmanager.com/ns.html?id=${containerId}" height="0" width="0" style="display:none;visibility:hidden"></iframe>`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
headTags: [
|
||||||
|
{
|
||||||
|
tagName: 'link',
|
||||||
|
attributes: {
|
||||||
|
rel: 'preconnect',
|
||||||
|
href: 'https://www.googletagmanager.com',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tagName: 'script',
|
||||||
|
innerHTML: `window.dataLayer = window.dataLayer || [];`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tagName: 'script',
|
||||||
|
innerHTML: `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||||
|
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||||
|
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||||
|
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||||
|
})(window,document,'script','dataLayer','${containerId}');`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const pluginOptionsSchema = Joi.object<PluginOptions>({
|
||||||
|
containerId: Joi.string().required(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export function validateOptions({
|
||||||
|
validate,
|
||||||
|
options,
|
||||||
|
}: OptionValidationContext<Options, PluginOptions>): PluginOptions {
|
||||||
|
return validate(pluginOptionsSchema, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type {PluginOptions, Options};
|
12
packages/docusaurus-plugin-google-tag-manager/src/options.ts
Normal file
12
packages/docusaurus-plugin-google-tag-manager/src/options.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type PluginOptions = {
|
||||||
|
containerId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Options = Partial<PluginOptions>;
|
8
packages/docusaurus-plugin-google-tag-manager/src/types.d.ts
vendored
Normal file
8
packages/docusaurus-plugin-google-tag-manager/src/types.d.ts
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference types="@docusaurus/module-type-aliases" />
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": false,
|
||||||
|
"composite": true,
|
||||||
|
"incremental": true,
|
||||||
|
"tsBuildInfoFile": "./lib/.tsbuildinfo-client",
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "esnext",
|
||||||
|
"rootDir": "src",
|
||||||
|
"outDir": "lib"
|
||||||
|
},
|
||||||
|
"include": ["src/*.d.ts"],
|
||||||
|
"exclude": ["**/__tests__/**"]
|
||||||
|
}
|
13
packages/docusaurus-plugin-google-tag-manager/tsconfig.json
Normal file
13
packages/docusaurus-plugin-google-tag-manager/tsconfig.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"references": [{"path": "./tsconfig.client.json"}],
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": false,
|
||||||
|
"incremental": true,
|
||||||
|
"tsBuildInfoFile": "./lib/.tsbuildinfo",
|
||||||
|
"rootDir": "src",
|
||||||
|
"outDir": "lib"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["**/__tests__/**"]
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/plugin-ideal-image",
|
"name": "@docusaurus/plugin-ideal-image",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Docusaurus Plugin to generate an almost ideal image (responsive, lazy-loading, and low quality placeholder).",
|
"description": "Docusaurus Plugin to generate an almost ideal image (responsive, lazy-loading, and low quality placeholder).",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "src/plugin-ideal-image.d.ts",
|
"types": "src/plugin-ideal-image.d.ts",
|
||||||
|
@ -20,12 +20,12 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/lqip-loader": "2.2.0",
|
"@docusaurus/lqip-loader": "2.3.0",
|
||||||
"@docusaurus/responsive-loader": "^1.7.0",
|
"@docusaurus/responsive-loader": "^1.7.0",
|
||||||
"@docusaurus/theme-translations": "2.2.0",
|
"@docusaurus/theme-translations": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"@endiliey/react-ideal-image": "^0.0.11",
|
"@endiliey/react-ideal-image": "^0.0.11",
|
||||||
"react-waypoint": "^10.3.0",
|
"react-waypoint": "^10.3.0",
|
||||||
"sharp": "^0.30.7",
|
"sharp": "^0.30.7",
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
"webpack": "^5.73.0"
|
"webpack": "^5.73.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/module-type-aliases": "2.2.0",
|
"@docusaurus/module-type-aliases": "2.3.0",
|
||||||
"fs-extra": "^10.1.0"
|
"fs-extra": "^10.1.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/plugin-pwa",
|
"name": "@docusaurus/plugin-pwa",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Docusaurus Plugin to add PWA support.",
|
"description": "Docusaurus Plugin to add PWA support.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "src/plugin-pwa.d.ts",
|
"types": "src/plugin-pwa.d.ts",
|
||||||
|
@ -22,12 +22,12 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.18.6",
|
"@babel/core": "^7.18.6",
|
||||||
"@babel/preset-env": "^7.18.6",
|
"@babel/preset-env": "^7.18.6",
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/theme-common": "2.2.0",
|
"@docusaurus/theme-common": "2.3.0",
|
||||||
"@docusaurus/theme-translations": "2.2.0",
|
"@docusaurus/theme-translations": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"babel-loader": "^8.2.5",
|
"babel-loader": "^8.2.5",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"core-js": "^3.23.3",
|
"core-js": "^3.23.3",
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
"workbox-window": "^6.5.3"
|
"workbox-window": "^6.5.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/module-type-aliases": "2.2.0",
|
"@docusaurus/module-type-aliases": "2.3.0",
|
||||||
"fs-extra": "^10.1.0"
|
"fs-extra": "^10.1.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/plugin-sitemap",
|
"name": "@docusaurus/plugin-sitemap",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Simple sitemap generation plugin for Docusaurus.",
|
"description": "Simple sitemap generation plugin for Docusaurus.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
|
@ -18,12 +18,12 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/logger": "2.2.0",
|
"@docusaurus/logger": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"@docusaurus/utils-common": "2.2.0",
|
"@docusaurus/utils-common": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"sitemap": "^7.1.1",
|
"sitemap": "^7.1.1",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/preset-classic",
|
"name": "@docusaurus/preset-classic",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Classic preset for Docusaurus.",
|
"description": "Classic preset for Docusaurus.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
|
@ -18,18 +18,19 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/plugin-content-blog": "2.2.0",
|
"@docusaurus/plugin-content-blog": "2.3.0",
|
||||||
"@docusaurus/plugin-content-docs": "2.2.0",
|
"@docusaurus/plugin-content-docs": "2.3.0",
|
||||||
"@docusaurus/plugin-content-pages": "2.2.0",
|
"@docusaurus/plugin-content-pages": "2.3.0",
|
||||||
"@docusaurus/plugin-debug": "2.2.0",
|
"@docusaurus/plugin-debug": "2.3.0",
|
||||||
"@docusaurus/plugin-google-analytics": "2.2.0",
|
"@docusaurus/plugin-google-analytics": "2.3.0",
|
||||||
"@docusaurus/plugin-google-gtag": "2.2.0",
|
"@docusaurus/plugin-google-gtag": "2.3.0",
|
||||||
"@docusaurus/plugin-sitemap": "2.2.0",
|
"@docusaurus/plugin-google-tag-manager": "2.3.0",
|
||||||
"@docusaurus/theme-classic": "2.2.0",
|
"@docusaurus/plugin-sitemap": "2.3.0",
|
||||||
"@docusaurus/theme-common": "2.2.0",
|
"@docusaurus/theme-classic": "2.3.0",
|
||||||
"@docusaurus/theme-search-algolia": "2.2.0",
|
"@docusaurus/theme-common": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0"
|
"@docusaurus/theme-search-algolia": "2.3.0",
|
||||||
|
"@docusaurus/types": "2.3.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.4 || ^17.0.0",
|
"react": "^16.8.4 || ^17.0.0",
|
||||||
|
|
|
@ -40,6 +40,7 @@ export default function preset(
|
||||||
theme,
|
theme,
|
||||||
googleAnalytics,
|
googleAnalytics,
|
||||||
gtag,
|
gtag,
|
||||||
|
googleTagManager,
|
||||||
...rest
|
...rest
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
|
@ -80,6 +81,11 @@ export default function preset(
|
||||||
if (gtag) {
|
if (gtag) {
|
||||||
plugins.push(makePluginConfig('@docusaurus/plugin-google-gtag', gtag));
|
plugins.push(makePluginConfig('@docusaurus/plugin-google-gtag', gtag));
|
||||||
}
|
}
|
||||||
|
if (googleTagManager) {
|
||||||
|
plugins.push(
|
||||||
|
makePluginConfig('@docusaurus/plugin-google-gtag', googleTagManager),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (isProd && sitemap !== false) {
|
if (isProd && sitemap !== false) {
|
||||||
plugins.push(makePluginConfig('@docusaurus/plugin-sitemap', sitemap));
|
plugins.push(makePluginConfig('@docusaurus/plugin-sitemap', sitemap));
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import type {Options as PagesPluginOptions} from '@docusaurus/plugin-content-pag
|
||||||
import type {Options as SitemapPluginOptions} from '@docusaurus/plugin-sitemap';
|
import type {Options as SitemapPluginOptions} from '@docusaurus/plugin-sitemap';
|
||||||
import type {Options as GAPluginOptions} from '@docusaurus/plugin-google-analytics';
|
import type {Options as GAPluginOptions} from '@docusaurus/plugin-google-analytics';
|
||||||
import type {Options as GtagPluginOptions} from '@docusaurus/plugin-google-gtag';
|
import type {Options as GtagPluginOptions} from '@docusaurus/plugin-google-gtag';
|
||||||
|
import type {Options as GTMPluginOptions} from '@docusaurus/plugin-google-tag-manager';
|
||||||
import type {Options as ThemeOptions} from '@docusaurus/theme-classic';
|
import type {Options as ThemeOptions} from '@docusaurus/theme-classic';
|
||||||
import type {ThemeConfig as BaseThemeConfig} from '@docusaurus/types';
|
import type {ThemeConfig as BaseThemeConfig} from '@docusaurus/types';
|
||||||
import type {UserThemeConfig as ClassicThemeConfig} from '@docusaurus/theme-common';
|
import type {UserThemeConfig as ClassicThemeConfig} from '@docusaurus/theme-common';
|
||||||
|
@ -42,6 +43,7 @@ export type Options = {
|
||||||
* is present.
|
* is present.
|
||||||
*/
|
*/
|
||||||
gtag?: GtagPluginOptions;
|
gtag?: GtagPluginOptions;
|
||||||
|
googleTagManager?: GTMPluginOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ThemeConfig = BaseThemeConfig &
|
export type ThemeConfig = BaseThemeConfig &
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/remark-plugin-npm2yarn",
|
"name": "@docusaurus/remark-plugin-npm2yarn",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Remark plugin for converting npm commands to Yarn commands as tabs.",
|
"description": "Remark plugin for converting npm commands to Yarn commands as tabs.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/theme-classic",
|
"name": "@docusaurus/theme-classic",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Classic theme for Docusaurus",
|
"description": "Classic theme for Docusaurus",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "src/theme-classic.d.ts",
|
"types": "src/theme-classic.d.ts",
|
||||||
|
@ -20,18 +20,18 @@
|
||||||
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/mdx-loader": "2.2.0",
|
"@docusaurus/mdx-loader": "2.3.0",
|
||||||
"@docusaurus/module-type-aliases": "2.2.0",
|
"@docusaurus/module-type-aliases": "2.3.0",
|
||||||
"@docusaurus/plugin-content-blog": "2.2.0",
|
"@docusaurus/plugin-content-blog": "2.3.0",
|
||||||
"@docusaurus/plugin-content-docs": "2.2.0",
|
"@docusaurus/plugin-content-docs": "2.3.0",
|
||||||
"@docusaurus/plugin-content-pages": "2.2.0",
|
"@docusaurus/plugin-content-pages": "2.3.0",
|
||||||
"@docusaurus/theme-common": "2.2.0",
|
"@docusaurus/theme-common": "2.3.0",
|
||||||
"@docusaurus/theme-translations": "2.2.0",
|
"@docusaurus/theme-translations": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"@docusaurus/utils-common": "2.2.0",
|
"@docusaurus/utils-common": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"@mdx-js/react": "^1.6.22",
|
"@mdx-js/react": "^1.6.22",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"copy-text-to-clipboard": "^3.0.1",
|
"copy-text-to-clipboard": "^3.0.1",
|
||||||
|
|
|
@ -62,7 +62,7 @@ describe('themeConfig', () => {
|
||||||
textColor: '#000',
|
textColor: '#000',
|
||||||
isCloseable: true,
|
isCloseable: true,
|
||||||
},
|
},
|
||||||
image: 'img/docusaurus-soc.png',
|
image: 'img/docusaurus-social-card.jpg',
|
||||||
navbar: {
|
navbar: {
|
||||||
style: 'primary',
|
style: 'primary',
|
||||||
hideOnScroll: true,
|
hideOnScroll: true,
|
||||||
|
|
|
@ -20,6 +20,14 @@ export default function getSwizzleConfig(): SwizzleConfig {
|
||||||
description:
|
description:
|
||||||
'The component used to render multi-line code blocks, generally used in Markdown files.',
|
'The component used to render multi-line code blocks, generally used in Markdown files.',
|
||||||
},
|
},
|
||||||
|
'CodeBlock/Content': {
|
||||||
|
actions: {
|
||||||
|
eject: 'unsafe',
|
||||||
|
wrap: 'forbidden',
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
'The folder containing components responsible for rendering different types of CodeBlock content.',
|
||||||
|
},
|
||||||
ColorModeToggle: {
|
ColorModeToggle: {
|
||||||
actions: {
|
actions: {
|
||||||
eject: 'safe',
|
eject: 'safe',
|
||||||
|
@ -28,6 +36,14 @@ export default function getSwizzleConfig(): SwizzleConfig {
|
||||||
description:
|
description:
|
||||||
'The color mode toggle to switch between light and dark mode.',
|
'The color mode toggle to switch between light and dark mode.',
|
||||||
},
|
},
|
||||||
|
'DocBreadcrumbs/Items': {
|
||||||
|
actions: {
|
||||||
|
eject: 'unsafe',
|
||||||
|
wrap: 'forbidden', // Can't wrap a folder
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
'The components responsible for rendering the breadcrumb items',
|
||||||
|
},
|
||||||
DocCardList: {
|
DocCardList: {
|
||||||
actions: {
|
actions: {
|
||||||
eject: 'safe',
|
eject: 'safe',
|
||||||
|
@ -36,6 +52,17 @@ export default function getSwizzleConfig(): SwizzleConfig {
|
||||||
description:
|
description:
|
||||||
'The component responsible for rendering a list of sidebar items cards.\nNotable used on the category generated-index pages.',
|
'The component responsible for rendering a list of sidebar items cards.\nNotable used on the category generated-index pages.',
|
||||||
},
|
},
|
||||||
|
'DocItem/TOC': {
|
||||||
|
actions: {
|
||||||
|
// Forbidden because it's a parent folder, makes the CLI crash atm
|
||||||
|
// TODO the CLI should rather support --eject
|
||||||
|
// Subfolders can be swizzled
|
||||||
|
eject: 'forbidden',
|
||||||
|
wrap: 'forbidden',
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
'The DocItem TOC is not directly swizzle-able, but you can swizzle its sub-components.',
|
||||||
|
},
|
||||||
DocSidebar: {
|
DocSidebar: {
|
||||||
actions: {
|
actions: {
|
||||||
eject: 'unsafe', // Too much technical code in sidebar, not very safe atm
|
eject: 'unsafe', // Too much technical code in sidebar, not very safe atm
|
||||||
|
@ -101,6 +128,17 @@ export default function getSwizzleConfig(): SwizzleConfig {
|
||||||
},
|
},
|
||||||
description: 'The footer logo',
|
description: 'The footer logo',
|
||||||
},
|
},
|
||||||
|
Icon: {
|
||||||
|
actions: {
|
||||||
|
// Forbidden because it's a parent folder, makes the CLI crash atm
|
||||||
|
// TODO the CLI should rather support --eject
|
||||||
|
// Subfolders can be swizzled
|
||||||
|
eject: 'forbidden',
|
||||||
|
wrap: 'forbidden',
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
'The Icon folder is not directly swizzle-able, but you can swizzle its sub-components.',
|
||||||
|
},
|
||||||
'Icon/Arrow': {
|
'Icon/Arrow': {
|
||||||
actions: {
|
actions: {
|
||||||
eject: 'safe',
|
eject: 'safe',
|
||||||
|
@ -220,7 +258,7 @@ export default function getSwizzleConfig(): SwizzleConfig {
|
||||||
wrap: 'forbidden',
|
wrap: 'forbidden',
|
||||||
},
|
},
|
||||||
description:
|
description:
|
||||||
'The Navbar item components mapping. Can be ejected to add custom navbar item types. See https://github.com/facebook/docusaurus/issues/7227.',
|
'The Navbar item components mapping. Can be ejected to add custom navbar item types.\nSee https://github.com/facebook/docusaurus/issues/7227.',
|
||||||
},
|
},
|
||||||
NotFound: {
|
NotFound: {
|
||||||
actions: {
|
actions: {
|
||||||
|
|
|
@ -1123,38 +1123,17 @@ declare module '@theme/Mermaid' {
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/TabItem' {
|
declare module '@theme/TabItem' {
|
||||||
import type {ReactNode} from 'react';
|
import type {TabItemProps} from '@docusaurus/theme-common/internal';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props extends TabItemProps {}
|
||||||
readonly children: ReactNode;
|
|
||||||
readonly value: string;
|
|
||||||
readonly default?: boolean;
|
|
||||||
readonly label?: string;
|
|
||||||
readonly hidden?: boolean;
|
|
||||||
readonly className?: string;
|
|
||||||
readonly attributes?: {[key: string]: unknown};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function TabItem(props: Props): JSX.Element;
|
export default function TabItem(props: Props): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/Tabs' {
|
declare module '@theme/Tabs' {
|
||||||
import type {ReactElement} from 'react';
|
import type {TabsProps} from '@docusaurus/theme-common/internal';
|
||||||
import type {Props as TabItemProps} from '@theme/TabItem';
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props extends TabsProps {}
|
||||||
readonly lazy?: boolean;
|
|
||||||
readonly block?: boolean;
|
|
||||||
readonly children: readonly ReactElement<TabItemProps>[];
|
|
||||||
readonly defaultValue?: string | null;
|
|
||||||
readonly values?: readonly {
|
|
||||||
value: string;
|
|
||||||
label?: string;
|
|
||||||
attributes?: {[key: string]: unknown};
|
|
||||||
}[];
|
|
||||||
readonly groupId?: string;
|
|
||||||
readonly className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Tabs(props: Props): JSX.Element;
|
export default function Tabs(props: Props): JSX.Element;
|
||||||
}
|
}
|
||||||
|
@ -1392,3 +1371,7 @@ declare module '@theme/prism-include-languages' {
|
||||||
PrismObject: typeof PrismNamespace,
|
PrismObject: typeof PrismNamespace,
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '@theme/DocBreadcrumbs/Items/Home' {
|
||||||
|
export default function HomeBreadcrumbItem(): JSX.Element;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Link from '@docusaurus/Link';
|
||||||
|
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||||
|
import {translate} from '@docusaurus/Translate';
|
||||||
|
import IconHome from '@theme/Icon/Home';
|
||||||
|
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
export default function HomeBreadcrumbItem(): JSX.Element {
|
||||||
|
const homeHref = useBaseUrl('/');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li className="breadcrumbs__item">
|
||||||
|
<Link
|
||||||
|
aria-label={translate({
|
||||||
|
id: 'theme.docs.breadcrumbs.home',
|
||||||
|
message: 'Home page',
|
||||||
|
description: 'The ARIA label for the home page in the breadcrumbs',
|
||||||
|
})}
|
||||||
|
className="breadcrumbs__link"
|
||||||
|
href={homeHref}>
|
||||||
|
<IconHome className={styles.breadcrumbHomeIcon} />
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.breadcrumbHomeIcon {
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
vertical-align: top;
|
||||||
|
height: 1.1rem;
|
||||||
|
width: 1.1rem;
|
||||||
|
}
|
|
@ -13,9 +13,8 @@ import {
|
||||||
useHomePageRoute,
|
useHomePageRoute,
|
||||||
} from '@docusaurus/theme-common/internal';
|
} from '@docusaurus/theme-common/internal';
|
||||||
import Link from '@docusaurus/Link';
|
import Link from '@docusaurus/Link';
|
||||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
|
||||||
import {translate} from '@docusaurus/Translate';
|
import {translate} from '@docusaurus/Translate';
|
||||||
import IconHome from '@theme/Icon/Home';
|
import HomeBreadcrumbItem from '@theme/DocBreadcrumbs/Items/Home';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
@ -79,24 +78,6 @@ function BreadcrumbsItem({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function HomeBreadcrumbItem() {
|
|
||||||
const homeHref = useBaseUrl('/');
|
|
||||||
return (
|
|
||||||
<li className="breadcrumbs__item">
|
|
||||||
<Link
|
|
||||||
aria-label={translate({
|
|
||||||
id: 'theme.docs.breadcrumbs.home',
|
|
||||||
message: 'Home page',
|
|
||||||
description: 'The ARIA label for the home page in the breadcrumbs',
|
|
||||||
})}
|
|
||||||
className={clsx('breadcrumbs__link', styles.breadcrumbsItemLink)}
|
|
||||||
href={homeHref}>
|
|
||||||
<IconHome className={styles.breadcrumbHomeIcon} />
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function DocBreadcrumbs(): JSX.Element | null {
|
export default function DocBreadcrumbs(): JSX.Element | null {
|
||||||
const breadcrumbs = useSidebarBreadcrumbs();
|
const breadcrumbs = useSidebarBreadcrumbs();
|
||||||
const homePageRoute = useHomePageRoute();
|
const homePageRoute = useHomePageRoute();
|
||||||
|
|
|
@ -9,11 +9,3 @@
|
||||||
--ifm-breadcrumb-size-multiplier: 0.8;
|
--ifm-breadcrumb-size-multiplier: 0.8;
|
||||||
margin-bottom: 0.8rem;
|
margin-bottom: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumbHomeIcon {
|
|
||||||
position: relative;
|
|
||||||
top: 1px;
|
|
||||||
vertical-align: top;
|
|
||||||
height: 1.1rem;
|
|
||||||
width: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,10 +7,11 @@
|
||||||
|
|
||||||
@media (min-width: 997px) {
|
@media (min-width: 997px) {
|
||||||
.expandButton {
|
.expandButton {
|
||||||
position: sticky;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-height: 100vh;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
@ -60,15 +60,20 @@ export default function DocPageLayoutSidebar({
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<ResetOnSidebarChange>
|
<ResetOnSidebarChange>
|
||||||
<DocSidebar
|
<div
|
||||||
sidebar={sidebar}
|
className={clsx(
|
||||||
path={pathname}
|
styles.sidebarViewport,
|
||||||
onCollapse={toggleSidebar}
|
hiddenSidebar && styles.sidebarViewportHidden,
|
||||||
isHidden={hiddenSidebar}
|
)}>
|
||||||
/>
|
<DocSidebar
|
||||||
|
sidebar={sidebar}
|
||||||
|
path={pathname}
|
||||||
|
onCollapse={toggleSidebar}
|
||||||
|
isHidden={hiddenSidebar}
|
||||||
|
/>
|
||||||
|
{hiddenSidebar && <ExpandButton toggleSidebar={toggleSidebar} />}
|
||||||
|
</div>
|
||||||
</ResetOnSidebarChange>
|
</ResetOnSidebarChange>
|
||||||
|
|
||||||
{hiddenSidebar && <ExpandButton toggleSidebar={toggleSidebar} />}
|
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,4 +29,11 @@
|
||||||
width: var(--doc-sidebar-hidden-width);
|
width: var(--doc-sidebar-hidden-width);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebarViewport {
|
||||||
|
top: 0;
|
||||||
|
position: sticky;
|
||||||
|
height: 100%;
|
||||||
|
max-height: 100vh;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,4 +12,5 @@
|
||||||
|
|
||||||
.docsWrapper {
|
.docsWrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 1 0 auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,4 +43,5 @@
|
||||||
|
|
||||||
.collapseSidebarButton {
|
.collapseSidebarButton {
|
||||||
display: none;
|
display: none;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
useAnnouncementBar,
|
useAnnouncementBar,
|
||||||
useScrollPosition,
|
useScrollPosition,
|
||||||
} from '@docusaurus/theme-common/internal';
|
} from '@docusaurus/theme-common/internal';
|
||||||
|
import {translate} from '@docusaurus/Translate';
|
||||||
import DocSidebarItems from '@theme/DocSidebarItems';
|
import DocSidebarItems from '@theme/DocSidebarItems';
|
||||||
import type {Props} from '@theme/DocSidebar/Desktop/Content';
|
import type {Props} from '@theme/DocSidebar/Desktop/Content';
|
||||||
|
|
||||||
|
@ -41,6 +42,11 @@ export default function DocSidebarDesktopContent({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
|
aria-label={translate({
|
||||||
|
id: 'theme.docs.sidebar.navAriaLabel',
|
||||||
|
message: 'Docs sidebar',
|
||||||
|
description: 'The ARIA label for the sidebar navigation',
|
||||||
|
})}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'menu thin-scrollbar',
|
'menu thin-scrollbar',
|
||||||
styles.menu,
|
styles.menu,
|
||||||
|
|
|
@ -9,13 +9,9 @@
|
||||||
.sidebar {
|
.sidebar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-height: 100vh;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
padding-top: var(--ifm-navbar-height);
|
padding-top: var(--ifm-navbar-height);
|
||||||
width: var(--doc-sidebar-width);
|
width: var(--doc-sidebar-width);
|
||||||
transition: opacity 50ms ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebarWithHideableNavbar {
|
.sidebarWithHideableNavbar {
|
||||||
|
@ -24,8 +20,6 @@
|
||||||
|
|
||||||
.sidebarHidden {
|
.sidebarHidden {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import {translate} from '@docusaurus/Translate';
|
import {translate} from '@docusaurus/Translate';
|
||||||
import {useThemeConfig} from '@docusaurus/theme-common';
|
import {useThemeConfig} from '@docusaurus/theme-common';
|
||||||
|
import Link from '@docusaurus/Link';
|
||||||
import type {Props} from '@theme/Heading';
|
import type {Props} from '@theme/Heading';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
@ -22,6 +23,17 @@ export default function Heading({as: As, id, ...props}: Props): JSX.Element {
|
||||||
return <As {...props} id={undefined} />;
|
return <As {...props} id={undefined} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const anchorTitle = translate(
|
||||||
|
{
|
||||||
|
id: 'theme.common.headingLinkTitle',
|
||||||
|
message: 'Direct link to {heading}',
|
||||||
|
description: 'Title for link to heading',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: typeof props.children === 'string' ? props.children : id,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<As
|
<As
|
||||||
{...props}
|
{...props}
|
||||||
|
@ -30,19 +42,17 @@ export default function Heading({as: As, id, ...props}: Props): JSX.Element {
|
||||||
hideOnScroll
|
hideOnScroll
|
||||||
? styles.anchorWithHideOnScrollNavbar
|
? styles.anchorWithHideOnScrollNavbar
|
||||||
: styles.anchorWithStickyNavbar,
|
: styles.anchorWithStickyNavbar,
|
||||||
|
props.className,
|
||||||
)}
|
)}
|
||||||
id={id}>
|
id={id}>
|
||||||
{props.children}
|
{props.children}
|
||||||
<a
|
<Link
|
||||||
className="hash-link"
|
className="hash-link"
|
||||||
href={`#${id}`}
|
to={`#${id}`}
|
||||||
title={translate({
|
aria-label={anchorTitle}
|
||||||
id: 'theme.common.headingLinkTitle',
|
title={anchorTitle}>
|
||||||
message: 'Direct link to heading',
|
|
||||||
description: 'Title for link to heading',
|
|
||||||
})}>
|
|
||||||
​
|
​
|
||||||
</a>
|
</Link>
|
||||||
</As>
|
</As>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import React from 'react';
|
||||||
import {composeProviders} from '@docusaurus/theme-common';
|
import {composeProviders} from '@docusaurus/theme-common';
|
||||||
import {
|
import {
|
||||||
ColorModeProvider,
|
ColorModeProvider,
|
||||||
TabGroupChoiceProvider,
|
|
||||||
AnnouncementBarProvider,
|
AnnouncementBarProvider,
|
||||||
DocsPreferredVersionContextProvider,
|
DocsPreferredVersionContextProvider,
|
||||||
ScrollControllerProvider,
|
ScrollControllerProvider,
|
||||||
|
@ -21,7 +20,6 @@ import type {Props} from '@theme/Layout/Provider';
|
||||||
const Provider = composeProviders([
|
const Provider = composeProviders([
|
||||||
ColorModeProvider,
|
ColorModeProvider,
|
||||||
AnnouncementBarProvider,
|
AnnouncementBarProvider,
|
||||||
TabGroupChoiceProvider,
|
|
||||||
ScrollControllerProvider,
|
ScrollControllerProvider,
|
||||||
DocsPreferredVersionContextProvider,
|
DocsPreferredVersionContextProvider,
|
||||||
PluginHtmlClassNameProvider,
|
PluginHtmlClassNameProvider,
|
||||||
|
|
|
@ -12,6 +12,8 @@ body {
|
||||||
|
|
||||||
.mainWrapper {
|
.mainWrapper {
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Docusaurus-specific utility class */
|
/* Docusaurus-specific utility class */
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
useHideableNavbar,
|
useHideableNavbar,
|
||||||
useNavbarMobileSidebar,
|
useNavbarMobileSidebar,
|
||||||
} from '@docusaurus/theme-common/internal';
|
} from '@docusaurus/theme-common/internal';
|
||||||
|
import {translate} from '@docusaurus/Translate';
|
||||||
import NavbarMobileSidebar from '@theme/Navbar/MobileSidebar';
|
import NavbarMobileSidebar from '@theme/Navbar/MobileSidebar';
|
||||||
import type {Props} from '@theme/Navbar/Layout';
|
import type {Props} from '@theme/Navbar/Layout';
|
||||||
|
|
||||||
|
@ -36,6 +37,11 @@ export default function NavbarLayout({children}: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
ref={navbarRef}
|
ref={navbarRef}
|
||||||
|
aria-label={translate({
|
||||||
|
id: 'theme.NavBar.navAriaLabel',
|
||||||
|
message: 'Main',
|
||||||
|
description: 'The ARIA label for the main navigation',
|
||||||
|
})}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'navbar',
|
'navbar',
|
||||||
'navbar--fixed-top',
|
'navbar--fixed-top',
|
||||||
|
|
|
@ -5,23 +5,37 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, {type ReactNode} from 'react';
|
||||||
import renderer from 'react-test-renderer';
|
import renderer from 'react-test-renderer';
|
||||||
import {
|
import {ScrollControllerProvider} from '@docusaurus/theme-common/internal';
|
||||||
TabGroupChoiceProvider,
|
import {StaticRouter} from 'react-router-dom';
|
||||||
ScrollControllerProvider,
|
|
||||||
} from '@docusaurus/theme-common/internal';
|
|
||||||
import Tabs from '../index';
|
import Tabs from '../index';
|
||||||
import TabItem from '../../TabItem';
|
import TabItem from '../../TabItem';
|
||||||
|
|
||||||
|
function TestProviders({
|
||||||
|
children,
|
||||||
|
pathname = '/',
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
pathname?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<StaticRouter location={{pathname}}>
|
||||||
|
<ScrollControllerProvider>{children}</ScrollControllerProvider>
|
||||||
|
</StaticRouter>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
describe('Tabs', () => {
|
describe('Tabs', () => {
|
||||||
it('rejects bad Tabs child', () => {
|
it('rejects bad Tabs child', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
renderer.create(
|
renderer.create(
|
||||||
<Tabs>
|
<TestProviders>
|
||||||
<div>Naughty</div>
|
<Tabs>
|
||||||
<TabItem value="good">Good</TabItem>
|
<div>Naughty</div>
|
||||||
</Tabs>,
|
<TabItem value="good">Good</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
</TestProviders>,
|
||||||
);
|
);
|
||||||
}).toThrowErrorMatchingInlineSnapshot(
|
}).toThrowErrorMatchingInlineSnapshot(
|
||||||
`"Docusaurus error: Bad <Tabs> child <div>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop."`,
|
`"Docusaurus error: Bad <Tabs> child <div>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop."`,
|
||||||
|
@ -30,10 +44,12 @@ describe('Tabs', () => {
|
||||||
it('rejects bad Tabs defaultValue', () => {
|
it('rejects bad Tabs defaultValue', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
renderer.create(
|
renderer.create(
|
||||||
<Tabs defaultValue="bad">
|
<TestProviders>
|
||||||
<TabItem value="v1">Tab 1</TabItem>
|
<Tabs defaultValue="bad">
|
||||||
<TabItem value="v2">Tab 2</TabItem>
|
<TabItem value="v1">Tab 1</TabItem>
|
||||||
</Tabs>,
|
<TabItem value="v2">Tab 2</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
</TestProviders>,
|
||||||
);
|
);
|
||||||
}).toThrowErrorMatchingInlineSnapshot(
|
}).toThrowErrorMatchingInlineSnapshot(
|
||||||
`"Docusaurus error: The <Tabs> has a defaultValue "bad" but none of its children has the corresponding value. Available values are: v1, v2. If you intend to show no default tab, use defaultValue={null} instead."`,
|
`"Docusaurus error: The <Tabs> has a defaultValue "bad" but none of its children has the corresponding value. Available values are: v1, v2. If you intend to show no default tab, use defaultValue={null} instead."`,
|
||||||
|
@ -42,14 +58,16 @@ describe('Tabs', () => {
|
||||||
it('rejects duplicate values', () => {
|
it('rejects duplicate values', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
renderer.create(
|
renderer.create(
|
||||||
<Tabs>
|
<TestProviders>
|
||||||
<TabItem value="v1">Tab 1</TabItem>
|
<Tabs>
|
||||||
<TabItem value="v2">Tab 2</TabItem>
|
<TabItem value="v1">Tab 1</TabItem>
|
||||||
<TabItem value="v3">Tab 3</TabItem>
|
<TabItem value="v2">Tab 2</TabItem>
|
||||||
<TabItem value="v4">Tab 4</TabItem>
|
<TabItem value="v3">Tab 3</TabItem>
|
||||||
<TabItem value="v1">Tab 5</TabItem>
|
<TabItem value="v4">Tab 4</TabItem>
|
||||||
<TabItem value="v2">Tab 6</TabItem>
|
<TabItem value="v1">Tab 5</TabItem>
|
||||||
</Tabs>,
|
<TabItem value="v2">Tab 6</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
</TestProviders>,
|
||||||
);
|
);
|
||||||
}).toThrowErrorMatchingInlineSnapshot(
|
}).toThrowErrorMatchingInlineSnapshot(
|
||||||
`"Docusaurus error: Duplicate values "v1, v2" found in <Tabs>. Every value needs to be unique."`,
|
`"Docusaurus error: Duplicate values "v1, v2" found in <Tabs>. Every value needs to be unique."`,
|
||||||
|
@ -58,54 +76,52 @@ describe('Tabs', () => {
|
||||||
it('accepts valid Tabs config', () => {
|
it('accepts valid Tabs config', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
renderer.create(
|
renderer.create(
|
||||||
<ScrollControllerProvider>
|
<TestProviders>
|
||||||
<TabGroupChoiceProvider>
|
<Tabs>
|
||||||
<Tabs>
|
<TabItem value="v1">Tab 1</TabItem>
|
||||||
<TabItem value="v1">Tab 1</TabItem>
|
<TabItem value="v2">Tab 2</TabItem>
|
||||||
<TabItem value="v2">Tab 2</TabItem>
|
</Tabs>
|
||||||
</Tabs>
|
<Tabs>
|
||||||
<Tabs>
|
<TabItem value="v1">Tab 1</TabItem>
|
||||||
<TabItem value="v1">Tab 1</TabItem>
|
<TabItem value="v2" default>
|
||||||
<TabItem value="v2" default>
|
Tab 2
|
||||||
Tab 2
|
</TabItem>
|
||||||
</TabItem>
|
</Tabs>
|
||||||
</Tabs>
|
<Tabs defaultValue="v1">
|
||||||
<Tabs defaultValue="v1">
|
<TabItem value="v1" label="V1">
|
||||||
<TabItem value="v1" label="V1">
|
Tab 1
|
||||||
Tab 1
|
</TabItem>
|
||||||
</TabItem>
|
<TabItem value="v2" label="V2">
|
||||||
<TabItem value="v2" label="V2">
|
Tab 2
|
||||||
Tab 2
|
</TabItem>
|
||||||
</TabItem>
|
</Tabs>
|
||||||
</Tabs>
|
<Tabs
|
||||||
<Tabs
|
defaultValue="v1"
|
||||||
defaultValue="v1"
|
values={[
|
||||||
values={[
|
{value: 'v1', label: 'V1'},
|
||||||
{value: 'v1', label: 'V1'},
|
{value: 'v2', label: 'V2'},
|
||||||
{value: 'v2', label: 'V2'},
|
]}>
|
||||||
]}>
|
<TabItem value="v1">Tab 1</TabItem>
|
||||||
<TabItem value="v1">Tab 1</TabItem>
|
<TabItem value="v2">Tab 2</TabItem>
|
||||||
<TabItem value="v2">Tab 2</TabItem>
|
</Tabs>
|
||||||
</Tabs>
|
<Tabs
|
||||||
<Tabs
|
defaultValue={null}
|
||||||
defaultValue={null}
|
values={[
|
||||||
values={[
|
{value: 'v1', label: 'V1'},
|
||||||
{value: 'v1', label: 'V1'},
|
{value: 'v2', label: 'V2'},
|
||||||
{value: 'v2', label: 'V2'},
|
]}>
|
||||||
]}>
|
<TabItem value="v1">Tab 1</TabItem>
|
||||||
<TabItem value="v1">Tab 1</TabItem>
|
<TabItem value="v2">Tab 2</TabItem>
|
||||||
<TabItem value="v2">Tab 2</TabItem>
|
</Tabs>
|
||||||
</Tabs>
|
<Tabs defaultValue={null}>
|
||||||
<Tabs defaultValue={null}>
|
<TabItem value="v1" label="V1">
|
||||||
<TabItem value="v1" label="V1">
|
Tab 1
|
||||||
Tab 1
|
</TabItem>
|
||||||
</TabItem>
|
<TabItem value="v2" label="V2">
|
||||||
<TabItem value="v2" label="V2">
|
Tab 2
|
||||||
Tab 2
|
</TabItem>
|
||||||
</TabItem>
|
</Tabs>
|
||||||
</Tabs>
|
</TestProviders>,
|
||||||
</TabGroupChoiceProvider>
|
|
||||||
</ScrollControllerProvider>,
|
|
||||||
);
|
);
|
||||||
}).not.toThrow(); // TODO Better Jest infrastructure to mock the Layout
|
}).not.toThrow(); // TODO Better Jest infrastructure to mock the Layout
|
||||||
});
|
});
|
||||||
|
@ -114,22 +130,60 @@ describe('Tabs', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
const tabs = ['Apple', 'Banana', 'Carrot'];
|
const tabs = ['Apple', 'Banana', 'Carrot'];
|
||||||
renderer.create(
|
renderer.create(
|
||||||
<ScrollControllerProvider>
|
<TestProviders>
|
||||||
<TabGroupChoiceProvider>
|
<Tabs
|
||||||
<Tabs
|
// @ts-expect-error: for an edge-case that we didn't write types for
|
||||||
|
values={tabs.map((t, idx) => ({label: t, value: idx}))}
|
||||||
|
// @ts-expect-error: for an edge-case that we didn't write types for
|
||||||
|
defaultValue={0}>
|
||||||
|
{tabs.map((t, idx) => (
|
||||||
// @ts-expect-error: for an edge-case that we didn't write types for
|
// @ts-expect-error: for an edge-case that we didn't write types for
|
||||||
values={tabs.map((t, idx) => ({label: t, value: idx}))}
|
<TabItem key={idx} value={idx}>
|
||||||
// @ts-expect-error: for an edge-case that we didn't write types for
|
{t}
|
||||||
defaultValue={0}>
|
</TabItem>
|
||||||
{tabs.map((t, idx) => (
|
))}
|
||||||
// @ts-expect-error: for an edge-case that we didn't write types for
|
</Tabs>
|
||||||
<TabItem key={idx} value={idx}>
|
</TestProviders>,
|
||||||
{t}
|
);
|
||||||
</TabItem>
|
}).not.toThrow();
|
||||||
))}
|
});
|
||||||
</Tabs>
|
it('rejects if querystring is true, but groupId falsy', () => {
|
||||||
</TabGroupChoiceProvider>
|
expect(() => {
|
||||||
</ScrollControllerProvider>,
|
renderer.create(
|
||||||
|
<TestProviders>
|
||||||
|
<Tabs queryString>
|
||||||
|
<TabItem value="val1">Val1</TabItem>
|
||||||
|
<TabItem value="val2">Val2</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
</TestProviders>,
|
||||||
|
);
|
||||||
|
}).toThrow(
|
||||||
|
'Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accept querystring=true when groupId is defined', () => {
|
||||||
|
expect(() => {
|
||||||
|
renderer.create(
|
||||||
|
<TestProviders>
|
||||||
|
<Tabs queryString groupId="my-group-id">
|
||||||
|
<TabItem value="val1">Val1</TabItem>
|
||||||
|
<TabItem value="val2">Val2</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
</TestProviders>,
|
||||||
|
);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accept querystring as string, but groupId falsy', () => {
|
||||||
|
expect(() => {
|
||||||
|
renderer.create(
|
||||||
|
<TestProviders>
|
||||||
|
<Tabs queryString="qsKey">
|
||||||
|
<TabItem value="val1">Val1</TabItem>
|
||||||
|
<TabItem value="val2">Val2</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
</TestProviders>,
|
||||||
);
|
);
|
||||||
}).not.toThrow();
|
}).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,104 +5,27 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {
|
import React, {cloneElement} from 'react';
|
||||||
useState,
|
|
||||||
cloneElement,
|
|
||||||
isValidElement,
|
|
||||||
type ReactElement,
|
|
||||||
} from 'react';
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import useIsBrowser from '@docusaurus/useIsBrowser';
|
|
||||||
import {duplicates} from '@docusaurus/theme-common';
|
|
||||||
import {
|
import {
|
||||||
useScrollPositionBlocker,
|
useScrollPositionBlocker,
|
||||||
useTabGroupChoice,
|
useTabs,
|
||||||
} from '@docusaurus/theme-common/internal';
|
} from '@docusaurus/theme-common/internal';
|
||||||
|
import useIsBrowser from '@docusaurus/useIsBrowser';
|
||||||
import type {Props} from '@theme/Tabs';
|
import type {Props} from '@theme/Tabs';
|
||||||
import type {Props as TabItemProps} from '@theme/TabItem';
|
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
// A very rough duck type, but good enough to guard against mistakes while
|
function TabList({
|
||||||
// allowing customization
|
className,
|
||||||
function isTabItem(
|
block,
|
||||||
comp: ReactElement<object>,
|
selectedValue,
|
||||||
): comp is ReactElement<TabItemProps> {
|
selectValue,
|
||||||
return 'value' in comp.props;
|
tabValues,
|
||||||
}
|
}: Props & ReturnType<typeof useTabs>) {
|
||||||
|
|
||||||
function TabsComponent(props: Props): JSX.Element {
|
|
||||||
const {
|
|
||||||
lazy,
|
|
||||||
block,
|
|
||||||
defaultValue: defaultValueProp,
|
|
||||||
values: valuesProp,
|
|
||||||
groupId,
|
|
||||||
className,
|
|
||||||
} = props;
|
|
||||||
const children = React.Children.map(props.children, (child) => {
|
|
||||||
if (isValidElement(child) && isTabItem(child)) {
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
// child.type.name will give non-sensical values in prod because of
|
|
||||||
// minification, but we assume it won't throw in prod.
|
|
||||||
throw new Error(
|
|
||||||
`Docusaurus error: Bad <Tabs> child <${
|
|
||||||
// @ts-expect-error: guarding against unexpected cases
|
|
||||||
typeof child.type === 'string' ? child.type : child.type.name
|
|
||||||
}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const values =
|
|
||||||
valuesProp ??
|
|
||||||
// Only pick keys that we recognize. MDX would inject some keys by default
|
|
||||||
children.map(({props: {value, label, attributes}}) => ({
|
|
||||||
value,
|
|
||||||
label,
|
|
||||||
attributes,
|
|
||||||
}));
|
|
||||||
const dup = duplicates(values, (a, b) => a.value === b.value);
|
|
||||||
if (dup.length > 0) {
|
|
||||||
throw new Error(
|
|
||||||
`Docusaurus error: Duplicate values "${dup
|
|
||||||
.map((a) => a.value)
|
|
||||||
.join(', ')}" found in <Tabs>. Every value needs to be unique.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// When defaultValueProp is null, don't show a default tab
|
|
||||||
const defaultValue =
|
|
||||||
defaultValueProp === null
|
|
||||||
? defaultValueProp
|
|
||||||
: defaultValueProp ??
|
|
||||||
children.find((child) => child.props.default)?.props.value ??
|
|
||||||
children[0]!.props.value;
|
|
||||||
if (defaultValue !== null && !values.some((a) => a.value === defaultValue)) {
|
|
||||||
throw new Error(
|
|
||||||
`Docusaurus error: The <Tabs> has a defaultValue "${defaultValue}" but none of its children has the corresponding value. Available values are: ${values
|
|
||||||
.map((a) => a.value)
|
|
||||||
.join(
|
|
||||||
', ',
|
|
||||||
)}. If you intend to show no default tab, use defaultValue={null} instead.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const {tabGroupChoices, setTabGroupChoices} = useTabGroupChoice();
|
|
||||||
const [selectedValue, setSelectedValue] = useState(defaultValue);
|
|
||||||
const tabRefs: (HTMLLIElement | null)[] = [];
|
const tabRefs: (HTMLLIElement | null)[] = [];
|
||||||
const {blockElementScrollPositionUntilNextRender} =
|
const {blockElementScrollPositionUntilNextRender} =
|
||||||
useScrollPositionBlocker();
|
useScrollPositionBlocker();
|
||||||
|
|
||||||
if (groupId != null) {
|
|
||||||
const relevantTabGroupChoice = tabGroupChoices[groupId];
|
|
||||||
if (
|
|
||||||
relevantTabGroupChoice != null &&
|
|
||||||
relevantTabGroupChoice !== selectedValue &&
|
|
||||||
values.some((value) => value.value === relevantTabGroupChoice)
|
|
||||||
) {
|
|
||||||
setSelectedValue(relevantTabGroupChoice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleTabChange = (
|
const handleTabChange = (
|
||||||
event:
|
event:
|
||||||
| React.FocusEvent<HTMLLIElement>
|
| React.FocusEvent<HTMLLIElement>
|
||||||
|
@ -111,15 +34,11 @@ function TabsComponent(props: Props): JSX.Element {
|
||||||
) => {
|
) => {
|
||||||
const newTab = event.currentTarget;
|
const newTab = event.currentTarget;
|
||||||
const newTabIndex = tabRefs.indexOf(newTab);
|
const newTabIndex = tabRefs.indexOf(newTab);
|
||||||
const newTabValue = values[newTabIndex]!.value;
|
const newTabValue = tabValues[newTabIndex]!.value;
|
||||||
|
|
||||||
if (newTabValue !== selectedValue) {
|
if (newTabValue !== selectedValue) {
|
||||||
blockElementScrollPositionUntilNextRender(newTab);
|
blockElementScrollPositionUntilNextRender(newTab);
|
||||||
setSelectedValue(newTabValue);
|
selectValue(newTabValue);
|
||||||
|
|
||||||
if (groupId != null) {
|
|
||||||
setTabGroupChoices(groupId, String(newTabValue));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -149,61 +68,79 @@ function TabsComponent(props: Props): JSX.Element {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx('tabs-container', styles.tabList)}>
|
<ul
|
||||||
<ul
|
role="tablist"
|
||||||
role="tablist"
|
aria-orientation="horizontal"
|
||||||
aria-orientation="horizontal"
|
className={clsx(
|
||||||
className={clsx(
|
'tabs',
|
||||||
'tabs',
|
{
|
||||||
{
|
'tabs--block': block,
|
||||||
'tabs--block': block,
|
},
|
||||||
},
|
className,
|
||||||
className,
|
)}>
|
||||||
)}>
|
{tabValues.map(({value, label, attributes}) => (
|
||||||
{values.map(({value, label, attributes}) => (
|
<li
|
||||||
<li
|
// TODO extract TabListItem
|
||||||
role="tab"
|
role="tab"
|
||||||
tabIndex={selectedValue === value ? 0 : -1}
|
tabIndex={selectedValue === value ? 0 : -1}
|
||||||
aria-selected={selectedValue === value}
|
aria-selected={selectedValue === value}
|
||||||
key={value}
|
key={value}
|
||||||
ref={(tabControl) => tabRefs.push(tabControl)}
|
ref={(tabControl) => tabRefs.push(tabControl)}
|
||||||
onKeyDown={handleKeydown}
|
onKeyDown={handleKeydown}
|
||||||
onClick={handleTabChange}
|
onClick={handleTabChange}
|
||||||
{...attributes}
|
{...attributes}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'tabs__item',
|
'tabs__item',
|
||||||
styles.tabItem,
|
styles.tabItem,
|
||||||
attributes?.className as string,
|
attributes?.className as string,
|
||||||
{
|
{
|
||||||
'tabs__item--active': selectedValue === value,
|
'tabs__item--active': selectedValue === value,
|
||||||
},
|
},
|
||||||
)}>
|
)}>
|
||||||
{label ?? value}
|
{label ?? value}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{lazy ? (
|
function TabContent({
|
||||||
cloneElement(
|
lazy,
|
||||||
children.filter(
|
children,
|
||||||
(tabItem) => tabItem.props.value === selectedValue,
|
selectedValue,
|
||||||
)[0]!,
|
}: Props & ReturnType<typeof useTabs>) {
|
||||||
{className: 'margin-top--md'},
|
if (lazy) {
|
||||||
)
|
const selectedTabItem = children.find(
|
||||||
) : (
|
(tabItem) => tabItem.props.value === selectedValue,
|
||||||
<div className="margin-top--md">
|
);
|
||||||
{children.map((tabItem, i) =>
|
if (!selectedTabItem) {
|
||||||
cloneElement(tabItem, {
|
// fail-safe or fail-fast? not sure what's best here
|
||||||
key: i,
|
return null;
|
||||||
hidden: tabItem.props.value !== selectedValue,
|
}
|
||||||
}),
|
return cloneElement(selectedTabItem, {className: 'margin-top--md'});
|
||||||
)}
|
}
|
||||||
</div>
|
return (
|
||||||
|
<div className="margin-top--md">
|
||||||
|
{children.map((tabItem, i) =>
|
||||||
|
cloneElement(tabItem, {
|
||||||
|
key: i,
|
||||||
|
hidden: tabItem.props.value !== selectedValue,
|
||||||
|
}),
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TabsComponent(props: Props): JSX.Element {
|
||||||
|
const tabs = useTabs(props);
|
||||||
|
return (
|
||||||
|
<div className={clsx('tabs-container', styles.tabList)}>
|
||||||
|
<TabList {...props} {...tabs} />
|
||||||
|
<TabContent {...props} {...tabs} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function Tabs(props: Props): JSX.Element {
|
export default function Tabs(props: Props): JSX.Element {
|
||||||
const isBrowser = useIsBrowser();
|
const isBrowser = useIsBrowser();
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/theme-common",
|
"name": "@docusaurus/theme-common",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Common code for Docusaurus themes.",
|
"description": "Common code for Docusaurus themes.",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
|
@ -30,12 +30,12 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/mdx-loader": "2.2.0",
|
"@docusaurus/mdx-loader": "2.3.0",
|
||||||
"@docusaurus/module-type-aliases": "2.2.0",
|
"@docusaurus/module-type-aliases": "2.3.0",
|
||||||
"@docusaurus/plugin-content-blog": "2.2.0",
|
"@docusaurus/plugin-content-blog": "2.3.0",
|
||||||
"@docusaurus/plugin-content-docs": "2.2.0",
|
"@docusaurus/plugin-content-docs": "2.3.0",
|
||||||
"@docusaurus/plugin-content-pages": "2.2.0",
|
"@docusaurus/plugin-content-pages": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"@types/history": "^4.7.11",
|
"@types/history": "^4.7.11",
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"@types/react-router-config": "*",
|
"@types/react-router-config": "*",
|
||||||
|
@ -43,11 +43,12 @@
|
||||||
"parse-numeric-range": "^1.3.0",
|
"parse-numeric-range": "^1.3.0",
|
||||||
"prism-react-renderer": "^1.3.5",
|
"prism-react-renderer": "^1.3.5",
|
||||||
"tslib": "^2.4.0",
|
"tslib": "^2.4.0",
|
||||||
|
"use-sync-external-store": "^1.2.0",
|
||||||
"utility-types": "^3.10.0"
|
"utility-types": "^3.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"lodash": "^4.17.21"
|
"lodash": "^4.17.21"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React, {
|
|
||||||
useState,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useContext,
|
|
||||||
type ReactNode,
|
|
||||||
} from 'react';
|
|
||||||
import {createStorageSlot, listStorageKeys} from '../utils/storageUtils';
|
|
||||||
import {ReactContextError} from '../utils/reactUtils';
|
|
||||||
|
|
||||||
const TAB_CHOICE_PREFIX = 'docusaurus.tab.';
|
|
||||||
|
|
||||||
type ContextValue = {
|
|
||||||
/** A map from `groupId` to the `value` of the saved choice. */
|
|
||||||
readonly tabGroupChoices: {readonly [groupId: string]: string};
|
|
||||||
/** Set the new choice value of a group. */
|
|
||||||
readonly setTabGroupChoices: (groupId: string, newChoice: string) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Context = React.createContext<ContextValue | undefined>(undefined);
|
|
||||||
|
|
||||||
function useContextValue(): ContextValue {
|
|
||||||
const [tabGroupChoices, setChoices] = useState<{
|
|
||||||
readonly [groupId: string]: string;
|
|
||||||
}>({});
|
|
||||||
const setChoiceSyncWithLocalStorage = useCallback(
|
|
||||||
(groupId: string, newChoice: string) => {
|
|
||||||
createStorageSlot(`${TAB_CHOICE_PREFIX}${groupId}`).set(newChoice);
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
try {
|
|
||||||
const localStorageChoices: {[groupId: string]: string} = {};
|
|
||||||
listStorageKeys().forEach((storageKey) => {
|
|
||||||
if (storageKey.startsWith(TAB_CHOICE_PREFIX)) {
|
|
||||||
const groupId = storageKey.substring(TAB_CHOICE_PREFIX.length);
|
|
||||||
localStorageChoices[groupId] = createStorageSlot(storageKey).get()!;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setChoices(localStorageChoices);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const setTabGroupChoices = useCallback(
|
|
||||||
(groupId: string, newChoice: string) => {
|
|
||||||
setChoices((oldChoices) => ({...oldChoices, [groupId]: newChoice}));
|
|
||||||
setChoiceSyncWithLocalStorage(groupId, newChoice);
|
|
||||||
},
|
|
||||||
[setChoiceSyncWithLocalStorage],
|
|
||||||
);
|
|
||||||
|
|
||||||
return useMemo(
|
|
||||||
() => ({tabGroupChoices, setTabGroupChoices}),
|
|
||||||
[tabGroupChoices, setTabGroupChoices],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TabGroupChoiceProvider({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: ReactNode;
|
|
||||||
}): JSX.Element {
|
|
||||||
const value = useContextValue();
|
|
||||||
return <Context.Provider value={value}>{children}</Context.Provider>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useTabGroupChoice(): ContextValue {
|
|
||||||
const context = useContext(Context);
|
|
||||||
if (context == null) {
|
|
||||||
throw new ReactContextError('TabGroupChoiceProvider');
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
|
|
@ -8,6 +8,7 @@
|
||||||
import {useCallback, useEffect, useState} from 'react';
|
import {useCallback, useEffect, useState} from 'react';
|
||||||
import {useHistory} from '@docusaurus/router';
|
import {useHistory} from '@docusaurus/router';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
|
import type {ThemeConfig as AlgoliaThemeConfig} from '@docusaurus/theme-search-algolia';
|
||||||
|
|
||||||
const SEARCH_PARAM_QUERY = 'q';
|
const SEARCH_PARAM_QUERY = 'q';
|
||||||
|
|
||||||
|
@ -31,8 +32,11 @@ export function useSearchPage(): {
|
||||||
} {
|
} {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const {
|
const {
|
||||||
siteConfig: {baseUrl},
|
siteConfig: {baseUrl, themeConfig},
|
||||||
} = useDocusaurusContext();
|
} = useDocusaurusContext();
|
||||||
|
const {
|
||||||
|
algolia: {searchPagePath},
|
||||||
|
} = themeConfig as AlgoliaThemeConfig;
|
||||||
|
|
||||||
const [searchQuery, setSearchQueryState] = useState('');
|
const [searchQuery, setSearchQueryState] = useState('');
|
||||||
|
|
||||||
|
@ -65,10 +69,11 @@ export function useSearchPage(): {
|
||||||
const generateSearchPageLink = useCallback(
|
const generateSearchPageLink = useCallback(
|
||||||
(targetSearchQuery: string) =>
|
(targetSearchQuery: string) =>
|
||||||
// Refer to https://github.com/facebook/docusaurus/pull/2838
|
// Refer to https://github.com/facebook/docusaurus/pull/2838
|
||||||
`${baseUrl}search?${SEARCH_PARAM_QUERY}=${encodeURIComponent(
|
// Note: if searchPagePath is falsy, useSearchPage() will not be called
|
||||||
targetSearchQuery,
|
`${baseUrl}${
|
||||||
)}`,
|
searchPagePath as string
|
||||||
[baseUrl],
|
}?${SEARCH_PARAM_QUERY}=${encodeURIComponent(targetSearchQuery)}`,
|
||||||
|
[baseUrl, searchPagePath],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -24,7 +24,11 @@ export {
|
||||||
type ColorModeConfig,
|
type ColorModeConfig,
|
||||||
} from './utils/useThemeConfig';
|
} from './utils/useThemeConfig';
|
||||||
|
|
||||||
export {createStorageSlot, listStorageKeys} from './utils/storageUtils';
|
export {
|
||||||
|
createStorageSlot,
|
||||||
|
useStorageSlot,
|
||||||
|
listStorageKeys,
|
||||||
|
} from './utils/storageUtils';
|
||||||
|
|
||||||
export {useContextualSearchFilters} from './utils/searchUtils';
|
export {useContextualSearchFilters} from './utils/searchUtils';
|
||||||
|
|
||||||
|
|
|
@ -42,10 +42,8 @@ export {
|
||||||
useAnnouncementBar,
|
useAnnouncementBar,
|
||||||
} from './contexts/announcementBar';
|
} from './contexts/announcementBar';
|
||||||
|
|
||||||
export {
|
export {useTabs} from './utils/tabsUtils';
|
||||||
useTabGroupChoice,
|
export type {TabValue, TabsProps, TabItemProps} from './utils/tabsUtils';
|
||||||
TabGroupChoiceProvider,
|
|
||||||
} from './contexts/tabGroupChoice';
|
|
||||||
|
|
||||||
export {useNavbarMobileSidebar} from './contexts/navbarMobileSidebar';
|
export {useNavbarMobileSidebar} from './contexts/navbarMobileSidebar';
|
||||||
export {useNavbarSecondaryMenu} from './contexts/navbarSecondaryMenu/display';
|
export {useNavbarSecondaryMenu} from './contexts/navbarSecondaryMenu/display';
|
||||||
|
@ -82,7 +80,11 @@ export {useLocationChange} from './utils/useLocationChange';
|
||||||
|
|
||||||
export {useLocalPathname} from './utils/useLocalPathname';
|
export {useLocalPathname} from './utils/useLocalPathname';
|
||||||
|
|
||||||
export {useHistoryPopHandler} from './utils/historyUtils';
|
export {
|
||||||
|
useHistoryPopHandler,
|
||||||
|
useHistorySelector,
|
||||||
|
useQueryStringValue,
|
||||||
|
} from './utils/historyUtils';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
useFilteredAndTreeifiedTOC,
|
useFilteredAndTreeifiedTOC,
|
||||||
|
|
|
@ -7,8 +7,11 @@
|
||||||
|
|
||||||
import {useEffect} from 'react';
|
import {useEffect} from 'react';
|
||||||
import {useHistory} from '@docusaurus/router';
|
import {useHistory} from '@docusaurus/router';
|
||||||
|
// @ts-expect-error: TODO temporary until React 18 upgrade
|
||||||
|
import {useSyncExternalStore} from 'use-sync-external-store/shim';
|
||||||
import {useEvent} from './reactUtils';
|
import {useEvent} from './reactUtils';
|
||||||
import type {Location, Action} from 'history';
|
|
||||||
|
import type {History, Location, Action} from 'history';
|
||||||
|
|
||||||
type HistoryBlockHandler = (location: Location, action: Action) => void | false;
|
type HistoryBlockHandler = (location: Location, action: Action) => void | false;
|
||||||
|
|
||||||
|
@ -43,3 +46,28 @@ export function useHistoryPopHandler(handler: HistoryBlockHandler): void {
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permits to efficiently subscribe to a slice of the history
|
||||||
|
* See https://thisweekinreact.com/articles/useSyncExternalStore-the-underrated-react-api
|
||||||
|
* @param selector
|
||||||
|
*/
|
||||||
|
export function useHistorySelector<Value>(
|
||||||
|
selector: (history: History<unknown>) => Value,
|
||||||
|
): Value {
|
||||||
|
const history = useHistory();
|
||||||
|
return useSyncExternalStore(history.listen, () => selector(history));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permits to efficiently subscribe to a specific querystring value
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
export function useQueryStringValue(key: string | null): string | null {
|
||||||
|
return useHistorySelector((history) => {
|
||||||
|
if (key === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new URLSearchParams(history.location.search).get(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -5,12 +5,46 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {useCallback, useRef} from 'react';
|
||||||
|
// @ts-expect-error: TODO temp error until React 18 upgrade
|
||||||
|
import {useSyncExternalStore} from 'use-sync-external-store/shim';
|
||||||
|
|
||||||
const StorageTypes = ['localStorage', 'sessionStorage', 'none'] as const;
|
const StorageTypes = ['localStorage', 'sessionStorage', 'none'] as const;
|
||||||
|
|
||||||
export type StorageType = typeof StorageTypes[number];
|
export type StorageType = typeof StorageTypes[number];
|
||||||
|
|
||||||
const DefaultStorageType: StorageType = 'localStorage';
|
const DefaultStorageType: StorageType = 'localStorage';
|
||||||
|
|
||||||
|
// window.addEventListener('storage') only works for different windows...
|
||||||
|
// so for current window we have to dispatch the event manually
|
||||||
|
// Now we can listen for both cross-window / current-window storage changes!
|
||||||
|
// see https://stackoverflow.com/a/71177640/82609
|
||||||
|
// see https://stackoverflow.com/questions/26974084/listen-for-changes-with-localstorage-on-the-same-window
|
||||||
|
function dispatchChangeEvent({
|
||||||
|
key,
|
||||||
|
oldValue,
|
||||||
|
newValue,
|
||||||
|
storage,
|
||||||
|
}: {
|
||||||
|
key: string;
|
||||||
|
oldValue: string | null;
|
||||||
|
newValue: string | null;
|
||||||
|
storage: Storage;
|
||||||
|
}) {
|
||||||
|
const event = document.createEvent('StorageEvent');
|
||||||
|
event.initStorageEvent(
|
||||||
|
'storage',
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
key,
|
||||||
|
oldValue,
|
||||||
|
newValue,
|
||||||
|
window.location.href,
|
||||||
|
storage,
|
||||||
|
);
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will return `null` if browser storage is unavailable (like running Docusaurus
|
* Will return `null` if browser storage is unavailable (like running Docusaurus
|
||||||
* in an iframe). This should NOT be called in SSR.
|
* in an iframe). This should NOT be called in SSR.
|
||||||
|
@ -58,12 +92,14 @@ export type StorageSlot = {
|
||||||
get: () => string | null;
|
get: () => string | null;
|
||||||
set: (value: string) => void;
|
set: (value: string) => void;
|
||||||
del: () => void;
|
del: () => void;
|
||||||
|
listen: (onChange: (event: StorageEvent) => void) => () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const NoopStorageSlot: StorageSlot = {
|
const NoopStorageSlot: StorageSlot = {
|
||||||
get: () => null,
|
get: () => null,
|
||||||
set: () => {},
|
set: () => {},
|
||||||
del: () => {},
|
del: () => {},
|
||||||
|
listen: () => () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fail-fast, as storage APIs should not be used during the SSR process
|
// Fail-fast, as storage APIs should not be used during the SSR process
|
||||||
|
@ -78,6 +114,7 @@ Please only call storage APIs in effects and event handlers.`);
|
||||||
get: throwError,
|
get: throwError,
|
||||||
set: throwError,
|
set: throwError,
|
||||||
del: throwError,
|
del: throwError,
|
||||||
|
listen: throwError,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,39 +135,103 @@ export function createStorageSlot(
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return createServerStorageSlot(key);
|
return createServerStorageSlot(key);
|
||||||
}
|
}
|
||||||
const browserStorage = getBrowserStorage(options?.persistence);
|
const storage = getBrowserStorage(options?.persistence);
|
||||||
if (browserStorage === null) {
|
if (storage === null) {
|
||||||
return NoopStorageSlot;
|
return NoopStorageSlot;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
get: () => {
|
get: () => {
|
||||||
try {
|
try {
|
||||||
return browserStorage.getItem(key);
|
return storage.getItem(key);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Docusaurus storage error, can't get key=${key}`, err);
|
console.error(`Docusaurus storage error, can't get key=${key}`, err);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
set: (value) => {
|
set: (newValue) => {
|
||||||
try {
|
try {
|
||||||
browserStorage.setItem(key, value);
|
const oldValue = storage.getItem(key);
|
||||||
|
storage.setItem(key, newValue);
|
||||||
|
dispatchChangeEvent({
|
||||||
|
key,
|
||||||
|
oldValue,
|
||||||
|
newValue,
|
||||||
|
storage,
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(
|
console.error(
|
||||||
`Docusaurus storage error, can't set ${key}=${value}`,
|
`Docusaurus storage error, can't set ${key}=${newValue}`,
|
||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
del: () => {
|
del: () => {
|
||||||
try {
|
try {
|
||||||
browserStorage.removeItem(key);
|
const oldValue = storage.getItem(key);
|
||||||
|
storage.removeItem(key);
|
||||||
|
dispatchChangeEvent({key, oldValue, newValue: null, storage});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Docusaurus storage error, can't delete key=${key}`, err);
|
console.error(`Docusaurus storage error, can't delete key=${key}`, err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
listen: (onChange) => {
|
||||||
|
try {
|
||||||
|
const listener = (event: StorageEvent) => {
|
||||||
|
if (event.storageArea === storage && event.key === key) {
|
||||||
|
onChange(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('storage', listener);
|
||||||
|
return () => window.removeEventListener('storage', listener);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
`Docusaurus storage error, can't listen for changes of key=${key}`,
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useStorageSlot(
|
||||||
|
key: string | null,
|
||||||
|
options?: {persistence?: StorageType},
|
||||||
|
): [string | null, StorageSlot] {
|
||||||
|
// Not ideal but good enough: assumes storage slot config is constant
|
||||||
|
const storageSlot = useRef(() => {
|
||||||
|
if (key === null) {
|
||||||
|
return NoopStorageSlot;
|
||||||
|
}
|
||||||
|
return createStorageSlot(key, options);
|
||||||
|
}).current();
|
||||||
|
|
||||||
|
const listen: StorageSlot['listen'] = useCallback(
|
||||||
|
(onChange) => {
|
||||||
|
// Do not try to add a listener during SSR
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
return storageSlot.listen(onChange);
|
||||||
|
},
|
||||||
|
[storageSlot],
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentValue = useSyncExternalStore(
|
||||||
|
listen,
|
||||||
|
() => {
|
||||||
|
// TODO this check should be useless after React 18
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return storageSlot.get();
|
||||||
|
},
|
||||||
|
() => null,
|
||||||
|
);
|
||||||
|
|
||||||
|
return [currentValue, storageSlot];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all the keys currently stored in browser storage,
|
* Returns a list of all the keys currently stored in browser storage,
|
||||||
* or an empty list if browser storage can't be accessed.
|
* or an empty list if browser storage can't be accessed.
|
||||||
|
|
266
packages/docusaurus-theme-common/src/utils/tabsUtils.tsx
Normal file
266
packages/docusaurus-theme-common/src/utils/tabsUtils.tsx
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, {
|
||||||
|
isValidElement,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useMemo,
|
||||||
|
type ReactNode,
|
||||||
|
type ReactElement,
|
||||||
|
} from 'react';
|
||||||
|
import {useHistory} from '@docusaurus/router';
|
||||||
|
import {useQueryStringValue} from '@docusaurus/theme-common/internal';
|
||||||
|
import {duplicates, useStorageSlot} from '../index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TabValue is the "config" of a given Tab
|
||||||
|
* Provided through <Tabs> "values" prop or through the children <TabItem> props
|
||||||
|
*/
|
||||||
|
export interface TabValue {
|
||||||
|
readonly value: string;
|
||||||
|
readonly label?: string;
|
||||||
|
readonly attributes?: {[key: string]: unknown};
|
||||||
|
readonly default?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TabsProps {
|
||||||
|
readonly lazy?: boolean;
|
||||||
|
readonly block?: boolean;
|
||||||
|
readonly children: readonly ReactElement<TabItemProps>[];
|
||||||
|
readonly defaultValue?: string | null;
|
||||||
|
readonly values?: readonly TabValue[];
|
||||||
|
readonly groupId?: string;
|
||||||
|
readonly className?: string;
|
||||||
|
readonly queryString?: string | boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TabItemProps {
|
||||||
|
readonly children: ReactNode;
|
||||||
|
readonly value: string;
|
||||||
|
readonly default?: boolean;
|
||||||
|
readonly label?: string;
|
||||||
|
readonly hidden?: boolean;
|
||||||
|
readonly className?: string;
|
||||||
|
readonly attributes?: {[key: string]: unknown};
|
||||||
|
}
|
||||||
|
|
||||||
|
// A very rough duck type, but good enough to guard against mistakes while
|
||||||
|
// allowing customization
|
||||||
|
function isTabItem(
|
||||||
|
comp: ReactElement<object>,
|
||||||
|
): comp is ReactElement<TabItemProps> {
|
||||||
|
return 'value' in comp.props;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureValidChildren(children: TabsProps['children']) {
|
||||||
|
return React.Children.map(children, (child) => {
|
||||||
|
if (isValidElement(child) && isTabItem(child)) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
// child.type.name will give non-sensical values in prod because of
|
||||||
|
// minification, but we assume it won't throw in prod.
|
||||||
|
throw new Error(
|
||||||
|
`Docusaurus error: Bad <Tabs> child <${
|
||||||
|
// @ts-expect-error: guarding against unexpected cases
|
||||||
|
typeof child.type === 'string' ? child.type : child.type.name
|
||||||
|
}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractChildrenTabValues(children: TabsProps['children']): TabValue[] {
|
||||||
|
return ensureValidChildren(children).map(
|
||||||
|
({props: {value, label, attributes, default: isDefault}}) => ({
|
||||||
|
value,
|
||||||
|
label,
|
||||||
|
attributes,
|
||||||
|
default: isDefault,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureNoDuplicateValue(values: readonly TabValue[]) {
|
||||||
|
const dup = duplicates(values, (a, b) => a.value === b.value);
|
||||||
|
if (dup.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Docusaurus error: Duplicate values "${dup
|
||||||
|
.map((a) => a.value)
|
||||||
|
.join(', ')}" found in <Tabs>. Every value needs to be unique.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function useTabValues(
|
||||||
|
props: Pick<TabsProps, 'values' | 'children'>,
|
||||||
|
): readonly TabValue[] {
|
||||||
|
const {values: valuesProp, children} = props;
|
||||||
|
return useMemo(() => {
|
||||||
|
const values = valuesProp ?? extractChildrenTabValues(children);
|
||||||
|
ensureNoDuplicateValue(values);
|
||||||
|
return values;
|
||||||
|
}, [valuesProp, children]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidValue({
|
||||||
|
value,
|
||||||
|
tabValues,
|
||||||
|
}: {
|
||||||
|
value: string | null | undefined;
|
||||||
|
tabValues: readonly TabValue[];
|
||||||
|
}) {
|
||||||
|
return tabValues.some((a) => a.value === value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInitialStateValue({
|
||||||
|
defaultValue,
|
||||||
|
tabValues,
|
||||||
|
}: {
|
||||||
|
defaultValue: TabsProps['defaultValue'];
|
||||||
|
tabValues: readonly TabValue[];
|
||||||
|
}): string {
|
||||||
|
if (tabValues.length === 0) {
|
||||||
|
throw new Error(
|
||||||
|
'Docusaurus error: the <Tabs> component requires at least one <TabItem> children component',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (defaultValue) {
|
||||||
|
// Warn user about passing incorrect defaultValue as prop.
|
||||||
|
if (!isValidValue({value: defaultValue, tabValues})) {
|
||||||
|
throw new Error(
|
||||||
|
`Docusaurus error: The <Tabs> has a defaultValue "${defaultValue}" but none of its children has the corresponding value. Available values are: ${tabValues
|
||||||
|
.map((a) => a.value)
|
||||||
|
.join(
|
||||||
|
', ',
|
||||||
|
)}. If you intend to show no default tab, use defaultValue={null} instead.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
const defaultTabValue =
|
||||||
|
tabValues.find((tabValue) => tabValue.default) ?? tabValues[0];
|
||||||
|
if (!defaultTabValue) {
|
||||||
|
throw new Error('Unexpected error: 0 tabValues');
|
||||||
|
}
|
||||||
|
return defaultTabValue.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStorageKey(groupId: string | undefined) {
|
||||||
|
if (!groupId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return `docusaurus.tab.${groupId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getQueryStringKey({
|
||||||
|
queryString = false,
|
||||||
|
groupId,
|
||||||
|
}: Pick<TabsProps, 'queryString' | 'groupId'>) {
|
||||||
|
if (typeof queryString === 'string') {
|
||||||
|
return queryString;
|
||||||
|
}
|
||||||
|
if (queryString === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (queryString === true && !groupId) {
|
||||||
|
throw new Error(
|
||||||
|
`Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return groupId ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useTabQueryString({
|
||||||
|
queryString = false,
|
||||||
|
groupId,
|
||||||
|
}: Pick<TabsProps, 'queryString' | 'groupId'>) {
|
||||||
|
const history = useHistory();
|
||||||
|
const key = getQueryStringKey({queryString, groupId});
|
||||||
|
const value = useQueryStringValue(key);
|
||||||
|
|
||||||
|
const setValue = useCallback(
|
||||||
|
(newValue: string) => {
|
||||||
|
if (!key) {
|
||||||
|
return; // no-op
|
||||||
|
}
|
||||||
|
const searchParams = new URLSearchParams(history.location.search);
|
||||||
|
searchParams.set(key, newValue);
|
||||||
|
history.replace({...history.location, search: searchParams.toString()});
|
||||||
|
},
|
||||||
|
[key, history],
|
||||||
|
);
|
||||||
|
|
||||||
|
return [value, setValue] as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useTabStorage({groupId}: Pick<TabsProps, 'groupId'>) {
|
||||||
|
const key = getStorageKey(groupId);
|
||||||
|
const [value, storageSlot] = useStorageSlot(key);
|
||||||
|
|
||||||
|
const setValue = useCallback(
|
||||||
|
(newValue: string) => {
|
||||||
|
if (!key) {
|
||||||
|
return; // no-op
|
||||||
|
}
|
||||||
|
storageSlot.set(newValue);
|
||||||
|
},
|
||||||
|
[key, storageSlot],
|
||||||
|
);
|
||||||
|
|
||||||
|
return [value, setValue] as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTabs(props: TabsProps): {
|
||||||
|
selectedValue: string;
|
||||||
|
selectValue: (value: string) => void;
|
||||||
|
tabValues: readonly TabValue[];
|
||||||
|
} {
|
||||||
|
const {defaultValue, queryString = false, groupId} = props;
|
||||||
|
const tabValues = useTabValues(props);
|
||||||
|
|
||||||
|
const [selectedValue, setSelectedValue] = useState(() =>
|
||||||
|
getInitialStateValue({defaultValue, tabValues}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [queryStringValue, setQueryString] = useTabQueryString({
|
||||||
|
queryString,
|
||||||
|
groupId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [storageValue, setStorageValue] = useTabStorage({
|
||||||
|
groupId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// We sync valid querystring/storage value to state on change + hydration
|
||||||
|
const valueToSync = (() => {
|
||||||
|
const value = queryStringValue ?? storageValue;
|
||||||
|
if (!isValidValue({value, tabValues})) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
})();
|
||||||
|
useEffect(() => {
|
||||||
|
if (valueToSync) {
|
||||||
|
setSelectedValue(valueToSync);
|
||||||
|
}
|
||||||
|
}, [valueToSync]);
|
||||||
|
|
||||||
|
const selectValue = useCallback(
|
||||||
|
(newValue: string) => {
|
||||||
|
if (!isValidValue({value: newValue, tabValues})) {
|
||||||
|
throw new Error(`Can't select invalid tab value=${newValue}`);
|
||||||
|
}
|
||||||
|
setSelectedValue(newValue);
|
||||||
|
setQueryString(newValue);
|
||||||
|
setStorageValue(newValue);
|
||||||
|
},
|
||||||
|
[setQueryString, setStorageValue, tabValues],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {selectedValue, selectValue, tabValues};
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/theme-live-codeblock",
|
"name": "@docusaurus/theme-live-codeblock",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Docusaurus live code block component.",
|
"description": "Docusaurus live code block component.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "src/theme-live-codeblock.d.ts",
|
"types": "src/theme-live-codeblock.d.ts",
|
||||||
|
@ -23,10 +23,10 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/theme-common": "2.2.0",
|
"@docusaurus/theme-common": "2.3.0",
|
||||||
"@docusaurus/theme-translations": "2.2.0",
|
"@docusaurus/theme-translations": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"@philpl/buble": "^0.19.7",
|
"@philpl/buble": "^0.19.7",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@types/buble": "^0.20.1"
|
"@types/buble": "^0.20.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/theme-mermaid",
|
"name": "@docusaurus/theme-mermaid",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Mermaid components for Docusaurus.",
|
"description": "Mermaid components for Docusaurus.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "src/theme-mermaid.d.ts",
|
"types": "src/theme-mermaid.d.ts",
|
||||||
|
@ -33,18 +33,17 @@
|
||||||
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/module-type-aliases": "2.2.0",
|
"@docusaurus/module-type-aliases": "2.3.0",
|
||||||
"@docusaurus/theme-common": "2.2.0",
|
"@docusaurus/theme-common": "2.3.0",
|
||||||
"@docusaurus/types": "2.2.0",
|
"@docusaurus/types": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"@mdx-js/react": "^1.6.22",
|
"@mdx-js/react": "^1.6.22",
|
||||||
"mermaid": "^9.1.1",
|
"mermaid": "^9.2.2",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/mdx-js__react": "^1.5.5",
|
"@types/mdx-js__react": "^1.5.5",
|
||||||
"@types/mermaid": "^8.2.9",
|
|
||||||
"react-test-renderer": "^17.0.2"
|
"react-test-renderer": "^17.0.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
|
|
||||||
import {useMemo} from 'react';
|
import {useMemo} from 'react';
|
||||||
import {useColorMode, useThemeConfig} from '@docusaurus/theme-common';
|
import {useColorMode, useThemeConfig} from '@docusaurus/theme-common';
|
||||||
import mermaid from 'mermaid';
|
import mermaid, {type MermaidConfig} from 'mermaid';
|
||||||
import type mermaidAPI from 'mermaid/mermaidAPI';
|
|
||||||
import type {ThemeConfig} from '@docusaurus/theme-mermaid';
|
import type {ThemeConfig} from '@docusaurus/theme-mermaid';
|
||||||
|
|
||||||
// Stable className to allow users to easily target with CSS
|
// Stable className to allow users to easily target with CSS
|
||||||
|
@ -18,7 +17,7 @@ export function useMermaidThemeConfig(): ThemeConfig['mermaid'] {
|
||||||
return (useThemeConfig() as unknown as ThemeConfig).mermaid;
|
return (useThemeConfig() as unknown as ThemeConfig).mermaid;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useMermaidConfig(): mermaidAPI.Config {
|
export function useMermaidConfig(): MermaidConfig {
|
||||||
const {colorMode} = useColorMode();
|
const {colorMode} = useColorMode();
|
||||||
const mermaidThemeConfig = useMermaidThemeConfig();
|
const mermaidThemeConfig = useMermaidThemeConfig();
|
||||||
|
|
||||||
|
@ -33,7 +32,7 @@ export function useMermaidConfig(): mermaidAPI.Config {
|
||||||
|
|
||||||
export function useMermaidSvg(
|
export function useMermaidSvg(
|
||||||
txt: string,
|
txt: string,
|
||||||
mermaidConfigParam?: mermaidAPI.Config,
|
mermaidConfigParam?: MermaidConfig,
|
||||||
): string {
|
): string {
|
||||||
/*
|
/*
|
||||||
For flexibility, we allow the hook to receive a custom Mermaid config
|
For flexibility, we allow the hook to receive a custom Mermaid config
|
||||||
|
|
|
@ -7,14 +7,13 @@
|
||||||
|
|
||||||
import {Joi} from '@docusaurus/utils-validation';
|
import {Joi} from '@docusaurus/utils-validation';
|
||||||
import type {ThemeConfig} from '@docusaurus/theme-mermaid';
|
import type {ThemeConfig} from '@docusaurus/theme-mermaid';
|
||||||
import type mermaidAPI from 'mermaid/mermaidAPI';
|
|
||||||
import type {ThemeConfigValidationContext} from '@docusaurus/types';
|
import type {ThemeConfigValidationContext} from '@docusaurus/types';
|
||||||
|
|
||||||
export const DEFAULT_THEME_CONFIG: ThemeConfig = {
|
export const DEFAULT_THEME_CONFIG: ThemeConfig = {
|
||||||
mermaid: {
|
mermaid: {
|
||||||
theme: {
|
theme: {
|
||||||
dark: 'dark' as mermaidAPI.Theme,
|
dark: 'dark',
|
||||||
light: 'default' as mermaidAPI.Theme,
|
light: 'default',
|
||||||
},
|
},
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,6 +10,6 @@
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"target": "esnext"
|
"target": "esnext"
|
||||||
},
|
},
|
||||||
"include": ["src/theme", "src/*.d.ts"],
|
"include": ["src/client", "src/theme", "src/*.d.ts"],
|
||||||
"exclude": ["**/__tests__/**"]
|
"exclude": ["**/__tests__/**"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,5 +10,5 @@
|
||||||
"outDir": "lib"
|
"outDir": "lib"
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"exclude": ["src/theme", "**/__tests__/**"]
|
"exclude": ["src/client", "src/theme", "**/__tests__/**"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@docusaurus/theme-search-algolia",
|
"name": "@docusaurus/theme-search-algolia",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"description": "Algolia search component for Docusaurus.",
|
"description": "Algolia search component for Docusaurus.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"sideEffects": [
|
"sideEffects": [
|
||||||
|
@ -34,13 +34,13 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docsearch/react": "^3.1.1",
|
"@docsearch/react": "^3.1.1",
|
||||||
"@docusaurus/core": "2.2.0",
|
"@docusaurus/core": "2.3.0",
|
||||||
"@docusaurus/logger": "2.2.0",
|
"@docusaurus/logger": "2.3.0",
|
||||||
"@docusaurus/plugin-content-docs": "2.2.0",
|
"@docusaurus/plugin-content-docs": "2.3.0",
|
||||||
"@docusaurus/theme-common": "2.2.0",
|
"@docusaurus/theme-common": "2.3.0",
|
||||||
"@docusaurus/theme-translations": "2.2.0",
|
"@docusaurus/theme-translations": "2.3.0",
|
||||||
"@docusaurus/utils": "2.2.0",
|
"@docusaurus/utils": "2.3.0",
|
||||||
"@docusaurus/utils-validation": "2.2.0",
|
"@docusaurus/utils-validation": "2.3.0",
|
||||||
"algoliasearch": "^4.13.1",
|
"algoliasearch": "^4.13.1",
|
||||||
"algoliasearch-helper": "^3.10.0",
|
"algoliasearch-helper": "^3.10.0",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
"utility-types": "^3.10.0"
|
"utility-types": "^3.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/module-type-aliases": "2.2.0"
|
"@docusaurus/module-type-aliases": "2.3.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.4 || ^17.0.0",
|
"react": "^16.8.4 || ^17.0.0",
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {validateThemeConfig, DEFAULT_CONFIG} from '../validateThemeConfig';
|
import {DEFAULT_CONFIG, validateThemeConfig} from '../validateThemeConfig';
|
||||||
import type {Joi} from '@docusaurus/utils-validation';
|
import type {Joi} from '@docusaurus/utils-validation';
|
||||||
|
|
||||||
function testValidateThemeConfig(themeConfig: {[key: string]: unknown}) {
|
function testValidateThemeConfig(themeConfig: {[key: string]: unknown}) {
|
||||||
|
@ -121,6 +121,53 @@ describe('validateThemeConfig', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('replaceSearchResultPathname', () => {
|
||||||
|
it('escapes from string', () => {
|
||||||
|
const algolia = {
|
||||||
|
appId: 'BH4D9OD16A',
|
||||||
|
indexName: 'index',
|
||||||
|
apiKey: 'apiKey',
|
||||||
|
replaceSearchResultPathname: {
|
||||||
|
from: '/docs/some-\\special-.[regexp]{chars*}',
|
||||||
|
to: '/abc',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(testValidateThemeConfig({algolia})).toEqual({
|
||||||
|
algolia: {
|
||||||
|
...DEFAULT_CONFIG,
|
||||||
|
...algolia,
|
||||||
|
replaceSearchResultPathname: {
|
||||||
|
from: '/docs/some\\x2d\\\\special\\x2d\\.\\[regexp\\]\\{chars\\*\\}',
|
||||||
|
to: '/abc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts from regexp to string', () => {
|
||||||
|
const algolia = {
|
||||||
|
appId: 'BH4D9OD16A',
|
||||||
|
indexName: 'index',
|
||||||
|
apiKey: 'apiKey',
|
||||||
|
replaceSearchResultPathname: {
|
||||||
|
from: /^\/docs\/(?:1\.0|next)/,
|
||||||
|
to: '/abc',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(testValidateThemeConfig({algolia})).toEqual({
|
||||||
|
algolia: {
|
||||||
|
...DEFAULT_CONFIG,
|
||||||
|
...algolia,
|
||||||
|
replaceSearchResultPathname: {
|
||||||
|
from: '^\\/docs\\/(?:1\\.0|next)',
|
||||||
|
to: '/abc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('searchParameters.facetFilters search config', () => {
|
it('searchParameters.facetFilters search config', () => {
|
||||||
const algolia = {
|
const algolia = {
|
||||||
appId: 'BH4D9OD16A',
|
appId: 'BH4D9OD16A',
|
||||||
|
|
|
@ -5,4 +5,6 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export {useAlgoliaThemeConfig} from './useAlgoliaThemeConfig';
|
||||||
export {useAlgoliaContextualFacetFilters} from './useAlgoliaContextualFacetFilters';
|
export {useAlgoliaContextualFacetFilters} from './useAlgoliaContextualFacetFilters';
|
||||||
|
export {useSearchResultUrlProcessor} from './useSearchResultUrlProcessor';
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
|
import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
|
||||||
|
|
||||||
|
export function useAlgoliaThemeConfig(): ThemeConfig {
|
||||||
|
const {
|
||||||
|
siteConfig: {themeConfig},
|
||||||
|
} = useDocusaurusContext();
|
||||||
|
return themeConfig as ThemeConfig;
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {useCallback} from 'react';
|
||||||
|
import {isRegexpStringMatch} from '@docusaurus/theme-common';
|
||||||
|
import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
|
||||||
|
import {useAlgoliaThemeConfig} from './useAlgoliaThemeConfig';
|
||||||
|
import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
|
||||||
|
|
||||||
|
function replacePathname(
|
||||||
|
pathname: string,
|
||||||
|
replaceSearchResultPathname: ThemeConfig['algolia']['replaceSearchResultPathname'],
|
||||||
|
): string {
|
||||||
|
return replaceSearchResultPathname
|
||||||
|
? pathname.replaceAll(
|
||||||
|
new RegExp(replaceSearchResultPathname.from, 'g'),
|
||||||
|
replaceSearchResultPathname.to,
|
||||||
|
)
|
||||||
|
: pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the search result url from Algolia to its final form, ready to be
|
||||||
|
* navigated to or used as a link
|
||||||
|
*/
|
||||||
|
export function useSearchResultUrlProcessor(): (url: string) => string {
|
||||||
|
const {withBaseUrl} = useBaseUrlUtils();
|
||||||
|
const {
|
||||||
|
algolia: {externalUrlRegex, replaceSearchResultPathname},
|
||||||
|
} = useAlgoliaThemeConfig();
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
(url: string) => {
|
||||||
|
const parsedURL = new URL(url);
|
||||||
|
|
||||||
|
// Algolia contains an external domain => navigate to URL
|
||||||
|
if (isRegexpStringMatch(externalUrlRegex, parsedURL.href)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise => transform to relative URL for SPA navigation
|
||||||
|
const relativeUrl = `${parsedURL.pathname + parsedURL.hash}`;
|
||||||
|
|
||||||
|
return withBaseUrl(
|
||||||
|
replacePathname(relativeUrl, replaceSearchResultPathname),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[withBaseUrl, externalUrlRegex, replaceSearchResultPathname],
|
||||||
|
);
|
||||||
|
}
|
|
@ -17,13 +17,23 @@ declare module '@docusaurus/theme-search-algolia' {
|
||||||
indexName: string;
|
indexName: string;
|
||||||
searchParameters: {[key: string]: unknown};
|
searchParameters: {[key: string]: unknown};
|
||||||
searchPagePath: string | false | null;
|
searchPagePath: string | false | null;
|
||||||
|
replaceSearchResultPathname?: {
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export type UserThemeConfig = DeepPartial<ThemeConfig>;
|
export type UserThemeConfig = DeepPartial<ThemeConfig>;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@docusaurus/theme-search-algolia/client' {
|
declare module '@docusaurus/theme-search-algolia/client' {
|
||||||
|
import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
|
||||||
|
|
||||||
|
export function useAlgoliaThemeConfig(): ThemeConfig;
|
||||||
|
|
||||||
export function useAlgoliaContextualFacetFilters(): [string, string[]];
|
export function useAlgoliaContextualFacetFilters(): [string, string[]];
|
||||||
|
|
||||||
|
export function useSearchResultUrlProcessor(): (url: string) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/SearchPage' {
|
declare module '@theme/SearchPage' {
|
||||||
|
|
|
@ -5,20 +5,23 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {useState, useRef, useCallback, useMemo} from 'react';
|
import React, {useCallback, useMemo, useRef, useState} from 'react';
|
||||||
import {createPortal} from 'react-dom';
|
import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
||||||
import {useHistory} from '@docusaurus/router';
|
|
||||||
import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
|
|
||||||
import Link from '@docusaurus/Link';
|
|
||||||
import Head from '@docusaurus/Head';
|
import Head from '@docusaurus/Head';
|
||||||
|
import Link from '@docusaurus/Link';
|
||||||
|
import {useHistory} from '@docusaurus/router';
|
||||||
import {isRegexpStringMatch} from '@docusaurus/theme-common';
|
import {isRegexpStringMatch} from '@docusaurus/theme-common';
|
||||||
import {useSearchPage} from '@docusaurus/theme-common/internal';
|
import {useSearchPage} from '@docusaurus/theme-common/internal';
|
||||||
import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
|
import {
|
||||||
import {useAlgoliaContextualFacetFilters} from '@docusaurus/theme-search-algolia/client';
|
useAlgoliaContextualFacetFilters,
|
||||||
|
useSearchResultUrlProcessor,
|
||||||
|
} from '@docusaurus/theme-search-algolia/client';
|
||||||
import Translate from '@docusaurus/Translate';
|
import Translate from '@docusaurus/Translate';
|
||||||
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
|
import {createPortal} from 'react-dom';
|
||||||
import translations from '@theme/SearchTranslations';
|
import translations from '@theme/SearchTranslations';
|
||||||
|
|
||||||
|
import type {AutocompleteState} from '@algolia/autocomplete-core';
|
||||||
import type {
|
import type {
|
||||||
DocSearchModal as DocSearchModalType,
|
DocSearchModal as DocSearchModalType,
|
||||||
DocSearchModalProps,
|
DocSearchModalProps,
|
||||||
|
@ -28,7 +31,6 @@ import type {
|
||||||
StoredDocSearchHit,
|
StoredDocSearchHit,
|
||||||
} from '@docsearch/react/dist/esm/types';
|
} from '@docsearch/react/dist/esm/types';
|
||||||
import type {SearchClient} from 'algoliasearch/lite';
|
import type {SearchClient} from 'algoliasearch/lite';
|
||||||
import type {AutocompleteState} from '@algolia/autocomplete-core';
|
|
||||||
|
|
||||||
type DocSearchProps = Omit<
|
type DocSearchProps = Omit<
|
||||||
DocSearchModalProps,
|
DocSearchModalProps,
|
||||||
|
@ -88,6 +90,7 @@ function DocSearch({
|
||||||
...props
|
...props
|
||||||
}: DocSearchProps) {
|
}: DocSearchProps) {
|
||||||
const {siteMetadata} = useDocusaurusContext();
|
const {siteMetadata} = useDocusaurusContext();
|
||||||
|
const processSearchResultUrl = useSearchResultUrlProcessor();
|
||||||
|
|
||||||
const contextualSearchFacetFilters =
|
const contextualSearchFacetFilters =
|
||||||
useAlgoliaContextualFacetFilters() as FacetFilters;
|
useAlgoliaContextualFacetFilters() as FacetFilters;
|
||||||
|
@ -107,7 +110,6 @@ function DocSearch({
|
||||||
facetFilters,
|
facetFilters,
|
||||||
};
|
};
|
||||||
|
|
||||||
const {withBaseUrl} = useBaseUrlUtils();
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const searchContainer = useRef<HTMLDivElement | null>(null);
|
const searchContainer = useRef<HTMLDivElement | null>(null);
|
||||||
const searchButtonRef = useRef<HTMLButtonElement>(null);
|
const searchButtonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
@ -172,20 +174,14 @@ function DocSearch({
|
||||||
|
|
||||||
const transformItems = useRef<DocSearchModalProps['transformItems']>(
|
const transformItems = useRef<DocSearchModalProps['transformItems']>(
|
||||||
(items) =>
|
(items) =>
|
||||||
items.map((item) => {
|
props.transformItems
|
||||||
// If Algolia contains a external domain, we should navigate without
|
? // Custom transformItems
|
||||||
// relative URL
|
props.transformItems(items)
|
||||||
if (isRegexpStringMatch(externalUrlRegex, item.url)) {
|
: // Default transformItems
|
||||||
return item;
|
items.map((item) => ({
|
||||||
}
|
...item,
|
||||||
|
url: processSearchResultUrl(item.url),
|
||||||
// We transform the absolute URL into a relative URL.
|
})),
|
||||||
const url = new URL(item.url);
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
url: withBaseUrl(`${url.pathname}${url.hash}`),
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
).current;
|
).current;
|
||||||
|
|
||||||
const resultsFooterComponent: DocSearchProps['resultsFooterComponent'] =
|
const resultsFooterComponent: DocSearchProps['resultsFooterComponent'] =
|
||||||
|
|
|
@ -7,32 +7,34 @@
|
||||||
|
|
||||||
/* eslint-disable jsx-a11y/no-autofocus */
|
/* eslint-disable jsx-a11y/no-autofocus */
|
||||||
|
|
||||||
import React, {useEffect, useState, useReducer, useRef} from 'react';
|
import React, {useEffect, useReducer, useRef, useState} from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import algoliaSearch from 'algoliasearch/lite';
|
|
||||||
import algoliaSearchHelper from 'algoliasearch-helper';
|
import algoliaSearchHelper from 'algoliasearch-helper';
|
||||||
|
import algoliaSearch from 'algoliasearch/lite';
|
||||||
|
|
||||||
|
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
||||||
import Head from '@docusaurus/Head';
|
import Head from '@docusaurus/Head';
|
||||||
import Link from '@docusaurus/Link';
|
import Link from '@docusaurus/Link';
|
||||||
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
import {useAllDocsData} from '@docusaurus/plugin-content-docs/client';
|
||||||
import {
|
import {
|
||||||
HtmlClassNameProvider,
|
HtmlClassNameProvider,
|
||||||
usePluralForm,
|
|
||||||
isRegexpStringMatch,
|
|
||||||
useEvent,
|
useEvent,
|
||||||
|
usePluralForm,
|
||||||
} from '@docusaurus/theme-common';
|
} from '@docusaurus/theme-common';
|
||||||
import {
|
import {
|
||||||
useTitleFormatter,
|
|
||||||
useSearchPage,
|
useSearchPage,
|
||||||
|
useTitleFormatter,
|
||||||
} from '@docusaurus/theme-common/internal';
|
} from '@docusaurus/theme-common/internal';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
||||||
import {useAllDocsData} from '@docusaurus/plugin-content-docs/client';
|
|
||||||
import Translate, {translate} from '@docusaurus/Translate';
|
import Translate, {translate} from '@docusaurus/Translate';
|
||||||
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
|
import {
|
||||||
|
useAlgoliaThemeConfig,
|
||||||
|
useSearchResultUrlProcessor,
|
||||||
|
} from '@docusaurus/theme-search-algolia/client';
|
||||||
import Layout from '@theme/Layout';
|
import Layout from '@theme/Layout';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
|
|
||||||
|
|
||||||
// Very simple pluralization: probably good enough for now
|
// Very simple pluralization: probably good enough for now
|
||||||
function useDocumentsFoundPlural() {
|
function useDocumentsFoundPlural() {
|
||||||
|
@ -156,12 +158,12 @@ type ResultDispatcher =
|
||||||
|
|
||||||
function SearchPageContent(): JSX.Element {
|
function SearchPageContent(): JSX.Element {
|
||||||
const {
|
const {
|
||||||
siteConfig: {themeConfig},
|
|
||||||
i18n: {currentLocale},
|
i18n: {currentLocale},
|
||||||
} = useDocusaurusContext();
|
} = useDocusaurusContext();
|
||||||
const {
|
const {
|
||||||
algolia: {appId, apiKey, indexName, externalUrlRegex},
|
algolia: {appId, apiKey, indexName},
|
||||||
} = themeConfig as ThemeConfig;
|
} = useAlgoliaThemeConfig();
|
||||||
|
const processSearchResultUrl = useSearchResultUrlProcessor();
|
||||||
const documentsFoundPlural = useDocumentsFoundPlural();
|
const documentsFoundPlural = useDocumentsFoundPlural();
|
||||||
|
|
||||||
const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers();
|
const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers();
|
||||||
|
@ -244,16 +246,12 @@ function SearchPageContent(): JSX.Element {
|
||||||
_highlightResult: {hierarchy: {[key: string]: {value: string}}};
|
_highlightResult: {hierarchy: {[key: string]: {value: string}}};
|
||||||
_snippetResult: {content?: {value: string}};
|
_snippetResult: {content?: {value: string}};
|
||||||
}) => {
|
}) => {
|
||||||
const parsedURL = new URL(url);
|
|
||||||
const titles = Object.keys(hierarchy).map((key) =>
|
const titles = Object.keys(hierarchy).map((key) =>
|
||||||
sanitizeValue(hierarchy[key]!.value),
|
sanitizeValue(hierarchy[key]!.value),
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: titles.pop()!,
|
title: titles.pop()!,
|
||||||
url: isRegexpStringMatch(externalUrlRegex, parsedURL.href)
|
url: processSearchResultUrl(url),
|
||||||
? parsedURL.href
|
|
||||||
: parsedURL.pathname + parsedURL.hash,
|
|
||||||
summary: snippet.content
|
summary: snippet.content
|
||||||
? `${sanitizeValue(snippet.content.value)}...`
|
? `${sanitizeValue(snippet.content.value)}...`
|
||||||
: '',
|
: '',
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {escapeRegexp} from '@docusaurus/utils';
|
||||||
import {Joi} from '@docusaurus/utils-validation';
|
import {Joi} from '@docusaurus/utils-validation';
|
||||||
import type {
|
import type {
|
||||||
ThemeConfig,
|
ThemeConfig,
|
||||||
|
@ -39,6 +40,19 @@ export const Schema = Joi.object<ThemeConfig>({
|
||||||
.try(Joi.boolean().invalid(true), Joi.string())
|
.try(Joi.boolean().invalid(true), Joi.string())
|
||||||
.allow(null)
|
.allow(null)
|
||||||
.default(DEFAULT_CONFIG.searchPagePath),
|
.default(DEFAULT_CONFIG.searchPagePath),
|
||||||
|
replaceSearchResultPathname: Joi.object({
|
||||||
|
from: Joi.custom((from) => {
|
||||||
|
if (typeof from === 'string') {
|
||||||
|
return escapeRegexp(from);
|
||||||
|
} else if (from instanceof RegExp) {
|
||||||
|
return from.source;
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`it should be a RegExp or a string, but received ${from}`,
|
||||||
|
);
|
||||||
|
}).required(),
|
||||||
|
to: Joi.string().required(),
|
||||||
|
}).optional(),
|
||||||
})
|
})
|
||||||
.label('themeConfig.algolia')
|
.label('themeConfig.algolia')
|
||||||
.required()
|
.required()
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "تبديل فئة الشريط الجاني القابل للاغلاق '{label}'",
|
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "تبديل فئة الشريط الجاني القابل للاغلاق '{label}'",
|
||||||
"theme.ErrorPageContent.title": "هذه الصفحة لا تستجيب.",
|
"theme.ErrorPageContent.title": "هذه الصفحة لا تستجيب.",
|
||||||
"theme.ErrorPageContent.tryAgain": "المحاولة مجددا",
|
"theme.ErrorPageContent.tryAgain": "المحاولة مجددا",
|
||||||
|
"theme.NavBar.navAriaLabel": "Main",
|
||||||
"theme.NotFound.p1": "لم نتمكن من العثور على ما كنت تبحث عنه.",
|
"theme.NotFound.p1": "لم نتمكن من العثور على ما كنت تبحث عنه.",
|
||||||
"theme.NotFound.p2": "يرجى الاتصال بمالك الموقع الذي ربطك بعنوان URL الأصلي وإخباره بأن الارتباط الخاص به معطل.",
|
"theme.NotFound.p2": "يرجى الاتصال بمالك الموقع الذي ربطك بعنوان URL الأصلي وإخباره بأن الارتباط الخاص به معطل.",
|
||||||
"theme.NotFound.title": "الصفحة غير موجودة",
|
"theme.NotFound.title": "الصفحة غير موجودة",
|
||||||
|
@ -35,7 +36,7 @@
|
||||||
"theme.colorToggle.ariaLabel.mode.dark": "الوضع الداكن",
|
"theme.colorToggle.ariaLabel.mode.dark": "الوضع الداكن",
|
||||||
"theme.colorToggle.ariaLabel.mode.light": "الوضع الفاتح",
|
"theme.colorToggle.ariaLabel.mode.light": "الوضع الفاتح",
|
||||||
"theme.common.editThisPage": "تعديل هذه الصفحة",
|
"theme.common.editThisPage": "تعديل هذه الصفحة",
|
||||||
"theme.common.headingLinkTitle": "ارتباط مباشر بالعنوان",
|
"theme.common.headingLinkTitle": "ارتباط مباشر بالعنوان {heading}",
|
||||||
"theme.common.skipToMainContent": "انتقل إلى المحتوى الرئيسي",
|
"theme.common.skipToMainContent": "انتقل إلى المحتوى الرئيسي",
|
||||||
"theme.docs.DocCard.categoryDescription": "{count} مواد",
|
"theme.docs.DocCard.categoryDescription": "{count} مواد",
|
||||||
"theme.docs.breadcrumbs.home": "الرئيسية",
|
"theme.docs.breadcrumbs.home": "الرئيسية",
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
"theme.docs.sidebar.collapseButtonTitle": "طي الشريط الجانبي",
|
"theme.docs.sidebar.collapseButtonTitle": "طي الشريط الجانبي",
|
||||||
"theme.docs.sidebar.expandButtonAriaLabel": "توسيع الشريط الجانبي",
|
"theme.docs.sidebar.expandButtonAriaLabel": "توسيع الشريط الجانبي",
|
||||||
"theme.docs.sidebar.expandButtonTitle": "توسيع الشريط الجانبي",
|
"theme.docs.sidebar.expandButtonTitle": "توسيع الشريط الجانبي",
|
||||||
|
"theme.docs.sidebar.navAriaLabel": "Docs sidebar",
|
||||||
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Toggle navigation bar",
|
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Toggle navigation bar",
|
||||||
"theme.docs.tagDocListPageTitle": "{nDocsTagged} مستند موسوم بـ \"{tagName}\"",
|
"theme.docs.tagDocListPageTitle": "{nDocsTagged} مستند موسوم بـ \"{tagName}\"",
|
||||||
"theme.docs.tagDocListPageTitle.nDocsTagged": "مستند موسوم واحد|{count} مستندات موسومة",
|
"theme.docs.tagDocListPageTitle.nDocsTagged": "مستند موسوم واحد|{count} مستندات موسومة",
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
"theme.ErrorPageContent.title": "This page crashed.",
|
"theme.ErrorPageContent.title": "This page crashed.",
|
||||||
"theme.ErrorPageContent.title___DESCRIPTION": "The title of the fallback page when the page crashed",
|
"theme.ErrorPageContent.title___DESCRIPTION": "The title of the fallback page when the page crashed",
|
||||||
"theme.ErrorPageContent.tryAgain": "Try again",
|
"theme.ErrorPageContent.tryAgain": "Try again",
|
||||||
"theme.ErrorPageContent.tryAgain___DESCRIPTION": "The label of the button to try again when the page crashed",
|
"theme.ErrorPageContent.tryAgain___DESCRIPTION": "The label of the button to try again rendering when the React error boundary captures an error",
|
||||||
|
"theme.NavBar.navAriaLabel": "Main",
|
||||||
|
"theme.NavBar.navAriaLabel___DESCRIPTION": "The ARIA label for the main navigation",
|
||||||
"theme.NotFound.p1": "We could not find what you were looking for.",
|
"theme.NotFound.p1": "We could not find what you were looking for.",
|
||||||
"theme.NotFound.p1___DESCRIPTION": "The first paragraph of the 404 page",
|
"theme.NotFound.p1___DESCRIPTION": "The first paragraph of the 404 page",
|
||||||
"theme.NotFound.p2": "Please contact the owner of the site that linked you to the original URL and let them know their link is broken.",
|
"theme.NotFound.p2": "Please contact the owner of the site that linked you to the original URL and let them know their link is broken.",
|
||||||
|
@ -71,7 +73,7 @@
|
||||||
"theme.colorToggle.ariaLabel.mode.light___DESCRIPTION": "The name for the light color mode",
|
"theme.colorToggle.ariaLabel.mode.light___DESCRIPTION": "The name for the light color mode",
|
||||||
"theme.common.editThisPage": "Edit this page",
|
"theme.common.editThisPage": "Edit this page",
|
||||||
"theme.common.editThisPage___DESCRIPTION": "The link label to edit the current page",
|
"theme.common.editThisPage___DESCRIPTION": "The link label to edit the current page",
|
||||||
"theme.common.headingLinkTitle": "Direct link to heading",
|
"theme.common.headingLinkTitle": "Direct link to {heading}",
|
||||||
"theme.common.headingLinkTitle___DESCRIPTION": "Title for link to heading",
|
"theme.common.headingLinkTitle___DESCRIPTION": "Title for link to heading",
|
||||||
"theme.common.skipToMainContent": "Skip to main content",
|
"theme.common.skipToMainContent": "Skip to main content",
|
||||||
"theme.common.skipToMainContent___DESCRIPTION": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation",
|
"theme.common.skipToMainContent___DESCRIPTION": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation",
|
||||||
|
@ -97,6 +99,8 @@
|
||||||
"theme.docs.sidebar.expandButtonAriaLabel___DESCRIPTION": "The ARIA label and title attribute for expand button of doc sidebar",
|
"theme.docs.sidebar.expandButtonAriaLabel___DESCRIPTION": "The ARIA label and title attribute for expand button of doc sidebar",
|
||||||
"theme.docs.sidebar.expandButtonTitle": "Expand sidebar",
|
"theme.docs.sidebar.expandButtonTitle": "Expand sidebar",
|
||||||
"theme.docs.sidebar.expandButtonTitle___DESCRIPTION": "The ARIA label and title attribute for expand button of doc sidebar",
|
"theme.docs.sidebar.expandButtonTitle___DESCRIPTION": "The ARIA label and title attribute for expand button of doc sidebar",
|
||||||
|
"theme.docs.sidebar.navAriaLabel": "Docs sidebar",
|
||||||
|
"theme.docs.sidebar.navAriaLabel___DESCRIPTION": "The ARIA label for the sidebar navigation",
|
||||||
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Toggle navigation bar",
|
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Toggle navigation bar",
|
||||||
"theme.docs.sidebar.toggleSidebarButtonAriaLabel___DESCRIPTION": "The ARIA label for hamburger menu button of mobile navigation",
|
"theme.docs.sidebar.toggleSidebarButtonAriaLabel___DESCRIPTION": "The ARIA label for hamburger menu button of mobile navigation",
|
||||||
"theme.docs.tagDocListPageTitle": "{nDocsTagged} with \"{tagName}\"",
|
"theme.docs.tagDocListPageTitle": "{nDocsTagged} with \"{tagName}\"",
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
|
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
|
||||||
"theme.ErrorPageContent.title": "This page crashed.",
|
"theme.ErrorPageContent.title": "This page crashed.",
|
||||||
"theme.ErrorPageContent.tryAgain": "Try again",
|
"theme.ErrorPageContent.tryAgain": "Try again",
|
||||||
|
"theme.NavBar.navAriaLabel": "Main",
|
||||||
"theme.NotFound.p1": "আপনি যা খুঁজছিলেন তা আমরা খুঁজে পাইনি।",
|
"theme.NotFound.p1": "আপনি যা খুঁজছিলেন তা আমরা খুঁজে পাইনি।",
|
||||||
"theme.NotFound.p2": "দয়া করে সাইটের মালিকের সাথে যোগাযোগ করুন যা আপনাকে মূল URL এর সাথে যুক্ত করেছে এবং তাদের লিঙ্কটি ভাঙ্গা রয়েছে তা তাদের জানান।",
|
"theme.NotFound.p2": "দয়া করে সাইটের মালিকের সাথে যোগাযোগ করুন যা আপনাকে মূল URL এর সাথে যুক্ত করেছে এবং তাদের লিঙ্কটি ভাঙ্গা রয়েছে তা তাদের জানান।",
|
||||||
"theme.NotFound.title": "পেজটি খুঁজে পাওয়া যায়নি",
|
"theme.NotFound.title": "পেজটি খুঁজে পাওয়া যায়নি",
|
||||||
|
@ -35,7 +36,7 @@
|
||||||
"theme.colorToggle.ariaLabel.mode.dark": "dark mode",
|
"theme.colorToggle.ariaLabel.mode.dark": "dark mode",
|
||||||
"theme.colorToggle.ariaLabel.mode.light": "light mode",
|
"theme.colorToggle.ariaLabel.mode.light": "light mode",
|
||||||
"theme.common.editThisPage": "এই পেজটি এডিট করুন",
|
"theme.common.editThisPage": "এই পেজটি এডিট করুন",
|
||||||
"theme.common.headingLinkTitle": "হেডিং এর সঙ্গে সরাসরি লিংকড",
|
"theme.common.headingLinkTitle": "{heading} এর সঙ্গে সরাসরি লিংকড",
|
||||||
"theme.common.skipToMainContent": "স্কিপ করে মূল কন্টেন্ট এ যান",
|
"theme.common.skipToMainContent": "স্কিপ করে মূল কন্টেন্ট এ যান",
|
||||||
"theme.docs.DocCard.categoryDescription": "{count} items",
|
"theme.docs.DocCard.categoryDescription": "{count} items",
|
||||||
"theme.docs.breadcrumbs.home": "Home page",
|
"theme.docs.breadcrumbs.home": "Home page",
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
"theme.docs.sidebar.collapseButtonTitle": "সাইডবারটি সঙ্কুচিত করুন",
|
"theme.docs.sidebar.collapseButtonTitle": "সাইডবারটি সঙ্কুচিত করুন",
|
||||||
"theme.docs.sidebar.expandButtonAriaLabel": "সাইডবারটি প্রসারিত করুন",
|
"theme.docs.sidebar.expandButtonAriaLabel": "সাইডবারটি প্রসারিত করুন",
|
||||||
"theme.docs.sidebar.expandButtonTitle": "সাইডবারটি প্রসারিত করুন",
|
"theme.docs.sidebar.expandButtonTitle": "সাইডবারটি প্রসারিত করুন",
|
||||||
|
"theme.docs.sidebar.navAriaLabel": "Docs sidebar",
|
||||||
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Toggle navigation bar",
|
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Toggle navigation bar",
|
||||||
"theme.docs.tagDocListPageTitle": "{nDocsTagged} with \"{tagName}\"",
|
"theme.docs.tagDocListPageTitle": "{nDocsTagged} with \"{tagName}\"",
|
||||||
"theme.docs.tagDocListPageTitle.nDocsTagged": "One doc tagged|{count} docs tagged",
|
"theme.docs.tagDocListPageTitle.nDocsTagged": "One doc tagged|{count} docs tagged",
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
|
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
|
||||||
"theme.ErrorPageContent.title": "This page crashed.",
|
"theme.ErrorPageContent.title": "This page crashed.",
|
||||||
"theme.ErrorPageContent.tryAgain": "Try again",
|
"theme.ErrorPageContent.tryAgain": "Try again",
|
||||||
|
"theme.NavBar.navAriaLabel": "Main",
|
||||||
"theme.NotFound.p1": "Nepodařilo se nám najít co jste hledal(a).",
|
"theme.NotFound.p1": "Nepodařilo se nám najít co jste hledal(a).",
|
||||||
"theme.NotFound.p2": "Kontaktujte prosím vlastníka webu, který vás odkázal na původní URL a upozorněte ho, že jejich odkaz nefunguje.",
|
"theme.NotFound.p2": "Kontaktujte prosím vlastníka webu, který vás odkázal na původní URL a upozorněte ho, že jejich odkaz nefunguje.",
|
||||||
"theme.NotFound.title": "Stránka nenalezena",
|
"theme.NotFound.title": "Stránka nenalezena",
|
||||||
|
@ -35,7 +36,7 @@
|
||||||
"theme.colorToggle.ariaLabel.mode.dark": "dark mode",
|
"theme.colorToggle.ariaLabel.mode.dark": "dark mode",
|
||||||
"theme.colorToggle.ariaLabel.mode.light": "light mode",
|
"theme.colorToggle.ariaLabel.mode.light": "light mode",
|
||||||
"theme.common.editThisPage": "Upravit tuto stránku",
|
"theme.common.editThisPage": "Upravit tuto stránku",
|
||||||
"theme.common.headingLinkTitle": "Přímý odkaz na nadpis",
|
"theme.common.headingLinkTitle": "Přímý odkaz na {heading}",
|
||||||
"theme.common.skipToMainContent": "Přeskočit na hlavní obsah",
|
"theme.common.skipToMainContent": "Přeskočit na hlavní obsah",
|
||||||
"theme.docs.DocCard.categoryDescription": "{count} items",
|
"theme.docs.DocCard.categoryDescription": "{count} items",
|
||||||
"theme.docs.breadcrumbs.home": "Home page",
|
"theme.docs.breadcrumbs.home": "Home page",
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
"theme.docs.sidebar.collapseButtonTitle": "Zavřít postranní lištu",
|
"theme.docs.sidebar.collapseButtonTitle": "Zavřít postranní lištu",
|
||||||
"theme.docs.sidebar.expandButtonAriaLabel": "Otevřít postranní lištu",
|
"theme.docs.sidebar.expandButtonAriaLabel": "Otevřít postranní lištu",
|
||||||
"theme.docs.sidebar.expandButtonTitle": "Otevřít postranní lištu",
|
"theme.docs.sidebar.expandButtonTitle": "Otevřít postranní lištu",
|
||||||
|
"theme.docs.sidebar.navAriaLabel": "Docs sidebar",
|
||||||
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Toggle navigation bar",
|
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Toggle navigation bar",
|
||||||
"theme.docs.tagDocListPageTitle": "{nDocsTagged} with \"{tagName}\"",
|
"theme.docs.tagDocListPageTitle": "{nDocsTagged} with \"{tagName}\"",
|
||||||
"theme.docs.tagDocListPageTitle.nDocsTagged": "One doc tagged|{count} docs tagged",
|
"theme.docs.tagDocListPageTitle.nDocsTagged": "One doc tagged|{count} docs tagged",
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
|
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Toggle the collapsible sidebar category '{label}'",
|
||||||
"theme.ErrorPageContent.title": "This page crashed.",
|
"theme.ErrorPageContent.title": "This page crashed.",
|
||||||
"theme.ErrorPageContent.tryAgain": "Try again",
|
"theme.ErrorPageContent.tryAgain": "Try again",
|
||||||
|
"theme.NavBar.navAriaLabel": "Main",
|
||||||
"theme.NotFound.p1": "Vi kunne ikke finde det, du søgte.",
|
"theme.NotFound.p1": "Vi kunne ikke finde det, du søgte.",
|
||||||
"theme.NotFound.p2": "Venligst kontakt ejeren til webstedet, som førte dig frem denne URL, og informer dem om at linket ikke virker.",
|
"theme.NotFound.p2": "Venligst kontakt ejeren til webstedet, som førte dig frem denne URL, og informer dem om at linket ikke virker.",
|
||||||
"theme.NotFound.title": "Siden blev ikke fundet",
|
"theme.NotFound.title": "Siden blev ikke fundet",
|
||||||
|
@ -35,7 +36,7 @@
|
||||||
"theme.colorToggle.ariaLabel.mode.dark": "dark mode",
|
"theme.colorToggle.ariaLabel.mode.dark": "dark mode",
|
||||||
"theme.colorToggle.ariaLabel.mode.light": "light mode",
|
"theme.colorToggle.ariaLabel.mode.light": "light mode",
|
||||||
"theme.common.editThisPage": "Rediger denne side",
|
"theme.common.editThisPage": "Rediger denne side",
|
||||||
"theme.common.headingLinkTitle": "Direkte link til overskrift",
|
"theme.common.headingLinkTitle": "Direkte link til {heading}",
|
||||||
"theme.common.skipToMainContent": "Hop til hovedindhold",
|
"theme.common.skipToMainContent": "Hop til hovedindhold",
|
||||||
"theme.docs.DocCard.categoryDescription": "{count} items",
|
"theme.docs.DocCard.categoryDescription": "{count} items",
|
||||||
"theme.docs.breadcrumbs.home": "Home page",
|
"theme.docs.breadcrumbs.home": "Home page",
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
"theme.docs.sidebar.collapseButtonTitle": "Sammenlæg sidenavigation",
|
"theme.docs.sidebar.collapseButtonTitle": "Sammenlæg sidenavigation",
|
||||||
"theme.docs.sidebar.expandButtonAriaLabel": "Udvid sidenavigation",
|
"theme.docs.sidebar.expandButtonAriaLabel": "Udvid sidenavigation",
|
||||||
"theme.docs.sidebar.expandButtonTitle": "Udvid sidenavigation",
|
"theme.docs.sidebar.expandButtonTitle": "Udvid sidenavigation",
|
||||||
|
"theme.docs.sidebar.navAriaLabel": "Docs sidebar",
|
||||||
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Toggle navigation bar",
|
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Toggle navigation bar",
|
||||||
"theme.docs.tagDocListPageTitle": "{nDocsTagged} with \"{tagName}\"",
|
"theme.docs.tagDocListPageTitle": "{nDocsTagged} with \"{tagName}\"",
|
||||||
"theme.docs.tagDocListPageTitle.nDocsTagged": "One doc tagged|{count} docs tagged",
|
"theme.docs.tagDocListPageTitle.nDocsTagged": "One doc tagged|{count} docs tagged",
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Umschalten der Seitenleiste mit einklappbarer Kategorie '{label}'",
|
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Umschalten der Seitenleiste mit einklappbarer Kategorie '{label}'",
|
||||||
"theme.ErrorPageContent.title": "Die Seite ist abgestürzt.",
|
"theme.ErrorPageContent.title": "Die Seite ist abgestürzt.",
|
||||||
"theme.ErrorPageContent.tryAgain": "Nochmal versuchen",
|
"theme.ErrorPageContent.tryAgain": "Nochmal versuchen",
|
||||||
|
"theme.NavBar.navAriaLabel": "Main",
|
||||||
"theme.NotFound.p1": "Wir konnten nicht finden, wonach Sie gesucht haben.",
|
"theme.NotFound.p1": "Wir konnten nicht finden, wonach Sie gesucht haben.",
|
||||||
"theme.NotFound.p2": "Bitte kontaktieren Sie den Besitzer der Seite, die Sie mit der ursprünglichen URL verlinkt hat, und teilen Sie ihm mit, dass der Link nicht mehr funktioniert.",
|
"theme.NotFound.p2": "Bitte kontaktieren Sie den Besitzer der Seite, die Sie mit der ursprünglichen URL verlinkt hat, und teilen Sie ihm mit, dass der Link nicht mehr funktioniert.",
|
||||||
"theme.NotFound.title": "Seite nicht gefunden",
|
"theme.NotFound.title": "Seite nicht gefunden",
|
||||||
|
@ -35,7 +36,7 @@
|
||||||
"theme.colorToggle.ariaLabel.mode.dark": "dunkler Modus",
|
"theme.colorToggle.ariaLabel.mode.dark": "dunkler Modus",
|
||||||
"theme.colorToggle.ariaLabel.mode.light": "heller Modus",
|
"theme.colorToggle.ariaLabel.mode.light": "heller Modus",
|
||||||
"theme.common.editThisPage": "Diese Seite bearbeiten",
|
"theme.common.editThisPage": "Diese Seite bearbeiten",
|
||||||
"theme.common.headingLinkTitle": "Direkter Link zur Überschrift",
|
"theme.common.headingLinkTitle": "Direkter Link zur {heading}",
|
||||||
"theme.common.skipToMainContent": "Zum Hauptinhalt springen",
|
"theme.common.skipToMainContent": "Zum Hauptinhalt springen",
|
||||||
"theme.docs.DocCard.categoryDescription": "{count} Einträge",
|
"theme.docs.DocCard.categoryDescription": "{count} Einträge",
|
||||||
"theme.docs.breadcrumbs.home": "Home page",
|
"theme.docs.breadcrumbs.home": "Home page",
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
"theme.docs.sidebar.collapseButtonTitle": "Seitenleiste einklappen",
|
"theme.docs.sidebar.collapseButtonTitle": "Seitenleiste einklappen",
|
||||||
"theme.docs.sidebar.expandButtonAriaLabel": "Seitenleiste ausklappen",
|
"theme.docs.sidebar.expandButtonAriaLabel": "Seitenleiste ausklappen",
|
||||||
"theme.docs.sidebar.expandButtonTitle": "Seitenleiste ausklappen",
|
"theme.docs.sidebar.expandButtonTitle": "Seitenleiste ausklappen",
|
||||||
|
"theme.docs.sidebar.navAriaLabel": "Docs sidebar",
|
||||||
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Toggle navigation bar",
|
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Toggle navigation bar",
|
||||||
"theme.docs.tagDocListPageTitle": "{nDocsTagged} mit \"{tagName}\"",
|
"theme.docs.tagDocListPageTitle": "{nDocsTagged} mit \"{tagName}\"",
|
||||||
"theme.docs.tagDocListPageTitle.nDocsTagged": "Ein doc getaggt|{count} docs getaggt",
|
"theme.docs.tagDocListPageTitle.nDocsTagged": "Ein doc getaggt|{count} docs getaggt",
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue