feat(v2): support external links and linking to docs from other sidebars (#1052)

* feat(sidebar): support external links and linking to docs from other sidebars

* Update styles.css
This commit is contained in:
Sviatoslav 2018-10-25 07:01:39 +03:00 committed by Yangshun Tay
parent edde297504
commit a2d3f26722
8 changed files with 466 additions and 139 deletions

View file

@ -1,52 +1,66 @@
// build the docs meta such as next, previous, category and sidebar
module.exports = function createOrder(allSidebars = {}) {
const order = {};
if (!allSidebars) {
return order;
}
Object.keys(allSidebars).forEach(sidebar => {
const categories = allSidebars[sidebar];
let ids = [];
Object.keys(allSidebars).forEach(sidebarId => {
const sidebar = allSidebars[sidebarId];
const ids = [];
const categoryOrder = [];
const subCategoryOrder = [];
Object.keys(categories).forEach(category => {
if (Array.isArray(categories[category])) {
ids = ids.concat(categories[category]);
// eslint-disable-next-line
for (let i = 0; i < categories[category].length; i++) {
categoryOrder.push(category);
subCategoryOrder.push(undefined);
const indexItems = ({items, categoryLabel, subCategoryLabel}) => {
items.forEach(item => {
switch (item.type) {
case 'category':
indexItems({
items: item.items,
categoryLabel: categoryLabel || item.label,
subCategoryLabel: categoryLabel && item.label,
});
break;
case 'ref':
case 'link':
// refs and links should not be shown in navigation
break;
case 'doc':
ids.push(item.id);
categoryOrder.push(categoryLabel);
subCategoryOrder.push(subCategoryLabel);
break;
default:
throw new Error(
`Unknown item type: ${item.type}. Item: ${JSON.stringify(item)}`,
);
}
} else {
Object.keys(categories[category]).forEach(subCategory => {
ids = ids.concat(categories[category][subCategory]);
});
};
// eslint-disable-next-line
for (let i = 0; i < categories[category][subCategory].length; i++) {
categoryOrder.push(category);
subCategoryOrder.push(subCategory);
}
});
}
});
indexItems({items: sidebar});
// eslint-disable-next-line
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
let previous;
let next;
if (i > 0) previous = ids[i - 1];
if (i < ids.length - 1) next = ids[i + 1];
if (i > 0) {
previous = ids[i - 1];
}
if (i < ids.length - 1) {
next = ids[i + 1];
}
order[id] = {
previous,
next,
sidebar,
sidebar: sidebarId,
category: categoryOrder[i],
subCategory: subCategoryOrder[i],
};
}
});
return order;
};

View file

@ -2,6 +2,110 @@ const fs = require('fs-extra');
const path = require('path');
const {idx} = require('../utils');
/**
* Check that item contains only allowed keys
*
* @param {Object} item
* @param {Array<string>} keys
*/
function assertItem(item, keys) {
const unknownKeys = Object.keys(item).filter(
key => !keys.includes(key) && key !== 'type',
);
if (unknownKeys.length) {
throw new Error(
`Unknown sidebar item keys: ${unknownKeys}. Item: ${JSON.stringify(
item,
)}`,
);
}
}
/**
* Normalizes recursively category and all its children. Ensures, that at the end
* each item will be an object with the corresponding type
*
* @param {Array<Object>} category
* @param {number} [level=0]
*
* @return {Array<Object>}
*/
function normalizeCategory(category, level = 0) {
if (level === 2) {
throw new Error(
`Can not process ${
category.label
} category. Categories can be nested only one level deep.`,
);
}
assertItem(category, ['items', 'label']);
if (!Array.isArray(category.items)) {
throw new Error(
`Error loading ${category.label} category. Category items must be array.`,
);
}
const items = category.items.map(item => {
switch (item.type) {
case 'category':
return normalizeCategory(item, level + 1);
case 'link':
assertItem(item, ['href', 'label']);
break;
case 'ref':
assertItem(item, ['id', 'label']);
break;
default:
if (typeof item === 'string') {
return {
type: 'doc',
id: item,
};
}
if (item.type !== 'doc') {
throw new Error(`Unknown sidebar item type: ${item.type}`);
}
assertItem(item, ['id', 'label']);
break;
}
return item;
});
return {...category, items};
}
/**
* Converts sidebars object to mapping to arrays of sidebar item objects
*
* @param {{[key: string]: Object}} sidebars
*
* @return {{[key: string]: Array<Object>}}
*/
function normalizeSidebar(sidebars) {
return Object.entries(sidebars).reduce((acc, [sidebarId, sidebar]) => {
let normalizedSidebar = sidebar;
if (!Array.isArray(sidebar)) {
// convert sidebar to a more generic structure
normalizedSidebar = Object.entries(sidebar).map(([label, items]) => ({
type: 'category',
label,
items,
}));
}
acc[sidebarId] = normalizedSidebar.map(item => normalizeCategory(item));
return acc;
}, {});
}
module.exports = function loadSidebars({siteDir, env}, deleteCache = true) {
let allSidebars = {};
@ -34,5 +138,6 @@ module.exports = function loadSidebars({siteDir, env}, deleteCache = true) {
});
}
}
return allSidebars;
return normalizeSidebar(allSidebars);
};