diff --git a/packages/docz-core/src/commands/args.ts b/packages/docz-core/src/commands/args.ts index a84ec1afe..9b7370d8f 100644 --- a/packages/docz-core/src/commands/args.ts +++ b/packages/docz-core/src/commands/args.ts @@ -24,6 +24,10 @@ const getInitialDescription = (pkg: any): string => export type Env = 'production' | 'development' export type ThemeConfig = Record +type SubMenuConfig = string[] +export interface MenuConfig { name: string, docs: SubMenuConfig } +export type RootMenuConfig = Array | null + export interface HtmlContext { lang: string favicon?: string @@ -75,6 +79,7 @@ export interface Config extends Argv { hastPlugins: any[] themeConfig: ThemeConfig htmlContext: HtmlContext + menu: RootMenuConfig modifyBundlerConfig(config: C, dev: boolean, args: Config): C modifyBabelRc(babelrc: BabelRC, args: Config): BabelRC } diff --git a/packages/docz-core/src/states/config.ts b/packages/docz-core/src/states/config.ts index 19746b5dc..1be985873 100644 --- a/packages/docz-core/src/states/config.ts +++ b/packages/docz-core/src/states/config.ts @@ -5,7 +5,7 @@ import equal from 'fast-deep-equal' import get from 'lodash.get' import { Params, State } from '../DataServer' -import { Config, ThemeConfig } from '../commands/args' +import { Config, RootMenuConfig, ThemeConfig } from '../commands/args' import { getRepoUrl } from '../utils/repo-info' import * as paths from '../config/paths' @@ -13,6 +13,7 @@ interface Payload { title: string description: string ordering: string + menu: RootMenuConfig themeConfig: ThemeConfig version: string | null repository: string | null @@ -27,6 +28,7 @@ const getInitialConfig = (config: Config): Payload => { title: config.title, description: config.description, themeConfig: config.themeConfig, + menu: config.menu, ordering: config.ordering, version: get(pkg, 'version'), repository: repoUrl, diff --git a/packages/docz-core/src/utils/load-config.ts b/packages/docz-core/src/utils/load-config.ts index 18a39ba05..12acaf468 100644 --- a/packages/docz-core/src/utils/load-config.ts +++ b/packages/docz-core/src/utils/load-config.ts @@ -22,6 +22,7 @@ export const loadConfig = (args: Config): Config => { hastPlugins: [], themeConfig: {}, htmlContext: defaultHtmlContext, + menu: null, modifyBundlerConfig: (config: any) => config, modifyBabelRc: (babelrc: BabelRC) => babelrc, }) diff --git a/packages/docz/src/components/Docs.tsx b/packages/docz/src/components/Docs.tsx index d928814f6..1ff39a9fc 100644 --- a/packages/docz/src/components/Docs.tsx +++ b/packages/docz/src/components/Docs.tsx @@ -2,18 +2,69 @@ import * as React from 'react' import { Children } from 'react' import sort from 'array-sort' -import { state, Entry, EntryMap, Config } from '../state' +import { state, Entry, EntryMap, Config, MenuConfig } from '../state' import { entriesSelector } from './DocPreview' import { configSelector } from './ThemeConfig' export const isFn = (value: any): boolean => typeof value === 'function' -const sortBy = (a: any, b: any, reverse?: boolean) => { +const compare = (a: any, b: any, reverse?: boolean) => { if (a < b) return reverse ? 1 : -1 if (a > b) return reverse ? -1 : 1 return 0 } +const UNKNOWN_POS = Infinity + +const comparePositionInConfig = ( + a: string, + b: string, + menu: string | null, + config: Config +) => { + + if(config.menu) { + const orderedMenuList = config.menu.map(m => { + return typeof m === 'string' ? m : m.name + }) + + if(menu) { + const menuPos = findPos(menu, orderedMenuList) + + if(menuPos !== UNKNOWN_POS && typeof config.menu[menuPos] === 'object') { + const menuConfig = config.menu[menuPos] as MenuConfig + const orderedList = menuConfig.docs + + return compare( + findPos(a, orderedList), + findPos(b, orderedList) + ) + } + } else { + return compare( + findPos(a, orderedMenuList), + findPos(b, orderedMenuList) + ) + } + } + return 0 +} + +const compareEntryPositionInConfig = (a: Entry, b: Entry, config: Config) => { + if(a.menu === b.menu) { + return comparePositionInConfig(a.name, b.name, a.menu, config) + } + return 0 +} + +const findPos = (name: string, orderedList: string[] | null) => { + if(!orderedList) { + return UNKNOWN_POS + } + const pos = orderedList.findIndex(item => item === name) + return pos !== -1 ? pos : UNKNOWN_POS +} + const menuFromEntries = (entries: Entry[]) => Array.from( new Set( @@ -49,13 +100,18 @@ export const Docs: React.SFC = ({ children }) => { const arr = Object.values(entries) const menusArr = menuFromEntries(arr) - const menus = sort(menusArr, (a: Entry, b: Entry) => sortBy(a, b)) + const menus = sort( + menusArr, + (a: string, b: string) => comparePositionInConfig(a, b, null, config), + (a: string, b: string) => compare(a, b) + ) const descending = config.ordering === 'descending' const docs: Entry[] = sort( arr, - (a: Entry, b: Entry) => sortBy(a.order, b.order, descending), - (a: Entry, b: Entry) => sortBy(a.name, b.name) + (a: Entry, b: Entry) => compareEntryPositionInConfig(a, b, config), + (a: Entry, b: Entry) => compare(a.order, b.order, descending), + (a: Entry, b: Entry) => compare(a.name, b.name) ) return Children.only( diff --git a/packages/docz/src/state.ts b/packages/docz/src/state.ts index 73138486d..cc18b6cbc 100644 --- a/packages/docz/src/state.ts +++ b/packages/docz/src/state.ts @@ -34,11 +34,16 @@ export interface ThemeConfig { [key: string]: any } +type SubMenuConfig = string[] +export interface MenuConfig { name: string, docs: SubMenuConfig } +export type RootMenuConfig = Array | null + export interface Config { title: string description: string ordering: string themeConfig: ThemeConfig + menu: RootMenuConfig version: string | null repository: string | null native: boolean