Skip to content

Commit

Permalink
feat(umi-plugin): add styles, info area, group feature for side menu (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
PeachScript committed Nov 27, 2019
1 parent 7748662 commit 8ba10bb
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 29 deletions.
1 change: 1 addition & 0 deletions packages/umi-plugin-father-doc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"remark-parse": "^7.0.1",
"remark-rehype": "^5.0.0",
"sylvanas": "^0.4.2",
"umi": "^2.12.3",
"umi-build-dev": "^1.16.5",
"umi-plugin-react": "^1.13.1",
"umi-types": "^0.5.4",
Expand Down
28 changes: 21 additions & 7 deletions packages/umi-plugin-father-doc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import getRouteConfig from './routes/getRouteConfig';
import getMenuFromRoutes from'./routes/getMenuFromRoutes';

export interface IFatherDocOpts {
name?: string;
title?: string;
logo?: URL;
desc?: string;
include?: string[];
routes?: {
path: IRoute['path'];
Expand All @@ -34,10 +35,17 @@ function docConfigPlugin() {

export default function (api: IApi, opts: IFatherDocOpts) {
// apply default options
opts = Object.assign({
name: require(path.join(api.paths.cwd, 'package.json')).name,
include: ['docs'],
}, (api.config as any).doc, opts);
opts = Object.assign(
{
title: require(path.join(api.paths.cwd, 'package.json')).name,
include: ['docs'],
},
{
routes: api.config.routes
},
(api.config as any).doc,
opts,
);

const routeConfig = getRouteConfig(api.paths, opts);

Expand All @@ -48,7 +56,7 @@ export default function (api: IApi, opts: IFatherDocOpts) {
api.registerPlugin({
id: require.resolve('umi-plugin-react'),
apply: require('umi-plugin-react').default,
opts: { title: { defaultTitle: opts.name } },
opts: { title: { defaultTitle: opts.title } },
});

// repalce default routes with generated routes
Expand Down Expand Up @@ -82,7 +90,13 @@ export default function (api: IApi, opts: IFatherDocOpts) {
module
}, { menu: { items: ${
JSON.stringify(getMenuFromRoutes(routeConfig[0].routes)).replace(/\"/g, '\'')
} }, ...props })`;
} }, title: '${
opts.title
}', logo: '${
opts.logo || ''
}', desc: '${
opts.desc || ''
}', ...props })`;
}

return ret;
Expand Down
58 changes: 53 additions & 5 deletions packages/umi-plugin-father-doc/src/routes/getMenuFromRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,74 @@ import path from 'path';
import { IApi } from 'umi-types';

export interface IMenuItem {
path: string;
/**
* menu 的路由,仅该 menu 不为 group 时才存在
*/
path?: string;
/**
* group 前缀,仅该 menu 为 group 时才存在
*/
prefix?: string;
title: string;
meta?: { [key: string]: any };
children: IMenuItem[];
children?: IMenuItem[];
}

export default (routes: IApi['routes']) : IMenuItem[] => {
const menu: IMenuItem[] = [];
let menu: IMenuItem[] = [];

// convert routes to menu
routes.forEach((route) => {

if (route.path) {
menu.push({
path: route.path,
title: route.meta.title || path.parse(route.component as string).name,
meta: route.meta,
children: [],
});
}
});

// group by menu frontmatter
const [ungroup, groupedMapping] = menu.reduce((result, item) => {
if (item.meta?.group?.title) {
const key = item.meta.group.title;

if (result[1][key]) {
result[1][key].push(item);
} else {
result[1][key] = [item];
}
} else {
result[0].push(item);
}

return result;
}, [[], {}]);

menu = ungroup.concat(
Object
.keys(groupedMapping)
.map(key => {

return {
title: key,
prefix: groupedMapping[key].find(item => item.meta.group.path).meta.group.path,
children: groupedMapping[key],
meta: {
// use child order as group order
order: groupedMapping[key].find(item => item.meta.order)?.meta?.order || undefined,
},
}
})
);

// sort menu
menu.sort((prev, next) => {
const prevOrder = typeof(prev.meta.order) === 'number' ? prev.meta.order : 0;
const nextOrder = typeof(next.meta.order) === 'number' ? next.meta.order : 0;

return (prevOrder === nextOrder) ? 0 : (nextOrder - prevOrder);
});

return menu;
}
7 changes: 6 additions & 1 deletion packages/umi-plugin-father-doc/src/routes/getRouteConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default (paths: IApi['paths'], opts: IFatherDocOpts): IRoute[] => {
path: '/',
component: path.join(__dirname, '../themes/default/layout.js'),
routes: [],
title: opts.name,
title: opts.title,
}];

if (opts.routes) {
Expand Down Expand Up @@ -47,6 +47,11 @@ export default (paths: IApi['paths'], opts: IFatherDocOpts): IRoute[] => {
if (route.meta.title) {
route.title = route.meta.title;
}

// apply group path
if (route.meta.group?.path) {
route.path = path.join(route.meta.group.path, route.path);
}
});

return config;
Expand Down
104 changes: 102 additions & 2 deletions packages/umi-plugin-father-doc/src/themes/default/layout.less
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
@import './markdown.less';

@s-menu-width: 300px;
@s-menu-width: 260px;
@s-content-margin: 64px;
@img-logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIQAAACCCAMAAACww5CIAAACf1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8YkP8AAAACCxMamv/6+voaGhoXi/YYjv8aoP8cq/8dr/8bo/8cqP8bpv8Ykv8drv8BAwUcrP8Zlf8Xjf/s7OzLy8scp/8anP8ZmP/d3d0BBArg4ODT09O7u7sEGCsKCgoanf8YlP/8/Pz09PTIyMgMTIV1dXUGKEVEREQ0NDQODg4GBgYdsv8dsf8Zl//m5uYVgOXj4+MWgtfW1tYTc87BwcERbLWzs7Ovr6+np6cQX6OgoKCTk5MMSXlwcHBra2tiYmIVFRUetf/39/fp6ekWhOkXi+QVfNvY2NjPz88TdcUSb7u6urq3t7cPYK0NUJGQkJCLi4ttbW0JO2cINFtVVVVRUVEHMFEHLEs6OjoEHDEiIiIcHBwXj/vx8fEWh+4Sb8gRbL+rq6upqakOVZiWlpaJiYmGhoYMSIF9fX15eXkKPnQLRHJMTExHR0c9PT0FHzkqKiomJiYEFyUBBw8bovfu7u4Wht4UedsUeMrFxcW9vb0RZrOkpKSampoPXZqAgIALQmtlZWUJOGJZWVkIMFcFIUExMTEwMDAtLS0DEh8Zl/v4+PgXj/QWhvEWhvAYku8YjuwUfNcUfNAVfc0RaLkSaKsRZ6kPWqENUYlbW1sCEBhkSPCkAAAAOHRSTlMA87y4BeKrltbFnUDo0MCup6D67t7ayZKGemtmWS8rEwLNso1wVEpFGaR+UDUlHwmBYls5i1oN/DMym4YAAAfTSURBVHjaxNndS1NxHMfxX5s6t1Kz1KzsuazMnqjgyxv03ovtQrYxUBEfLkREVBQf0AsFBRUUQvEiSVFQ0YsuiiIiqKC/oH+o31lzjtPZg55zttfVNnbx5ffw+X53pmx5UFl2+XLZ4zpVOPWlJFTntYyiBwF/VbX39Sv9upYU9/QHjbXe6qqayrrnylXXi0kov3GVuFiMuNqbHhIu3FcuuohZZ+jDh7mdXkwqlGtKMGmOSFzrGiYe5ZL4+vdsd/SHFyYxtIQlIdiD4ftCa39osTlxRtzwHO1tUOLm0XYk6T3asMRtdKHdUs6qv+L1l/vKgak2SYjqN+1yYg2G5NgR4Pd5/F7fk9sO3YhSkoYkaW40KCk2Rj9KUoikqmtOn8YpydE6J7xFyq5yUhxIjvZJcUfZ5EOb6oxGQmPdtEQlR4Mxupc6IoOdzWiVypabaF1BiesIS876OiSufRXtvO0DcSi2dAN+ZcclYFZsCaOps3nYUOKprDTiSWzqAioCnpIX9ep03pxkw7jYtMWx0pdn7Jb2i1jixN3cM6OGFCti0zgpyopOsw6xiZHoyHIPLIhNHdD7bWR+c7znFD3+PNp+vxhmRkNi28BoWAzBPbQHKhdlQLe4ogsoVTl4ijYjrmiKATdUdvfjh9Ely8DVHFvWe3HJMBBQ2QWAd+KSeeBxjtuxKC7ZzG07Ht0DusQlfwDfs2wZ4b2EYVBcESHO81BlcIWESXHFV7Qss5aXY1FxRSj7L7QAhv3tsaVBMVn8Ou1MFUtjW3sYKjL0jO6QWJiA7iZxysBbtDplpRT4KZbQWkUbHRMnGFUUKwuNaH1iaRJ+Tf8bDbqcWJH2HuCV+l9DpkuxtdsuGlpYHNAJ1FqNMjnE9QocOXJCPwJ309zPT9la8e5yUJwwC/jTBNWQ5EkIqEyzHROSJzvWSeFDW5M8OUArsdgMq2EmanOyGB4WSyMYAhZp2TwkJouw2mZvmusUSwtraA//m7DXZ8SsBxiQM5tGSxNuv3+ZU/NmIpfN9qDXxp1sO4LDNrE202J6cHE1TVq2f1uNiA39K9/7JJ0JwGe6nvOSZ4OA1/R0bFbyrBWoMUX2nOTZAOA3pcSXjFW7UOJnU17VAYeZv98pTvsB1KsTRVXAtqQVA/rFWSNo11SKiuRYZeknEBRn7WJ4rZKuX8pcROvBj6g4rLUZQ8NJYBo2Jb/ax2KkhKYf6I1I3oWngKqUhfgkBTCL1pics1elICaS/5Y9jk+XBdEBeJKhHZGCCLZAWTIkBqQgNlr+NbGi2wHgS1tTAbQNAxW3i1R58WWgd725ANZ7gXPFNaqagrvwt1t7aW0qiOIAPlErPqJCq6JWrW8r1ar1xf0n4NxnnpCELEKyCNmkJZSQRSCbQltooS4sVApiC10U2kWhFRUEEdGF4vuNH8g7c9NQ2pjepPcB/r5ADjlnzp2ZM+QMXHeYb+1WfO5hi5QfveYe33XJ4+d8a3MNQHbI75KhMt9z9wF4FRNcIi3wO94bAHJiQHCHNgmgh3QD8D1MCK6I+KeNCUgbgFFRcEX8Qwhov014o/juUlEoxeqrgpsA7oWp4AZprnpv1ANgShFcoU4a+36jMgOuVGYmnuJ1Wb0hKWqCC8QCgI4dqyfRbNCFoqDBX7Xz6C0AS660K3UKQCdhuqAbdqFT+B8mAXQTbhtbpM7ng4Yn1oytOwFMu5AP9QGAa4Qz8lFwvFWIH6G7Qjijc8/LDueDyvd4z151EYBvwOF+lRFTAK6TGi+ACWdLk0ozANqvkpojAFJKRnCSlFt3m8pLc9bJTylVn64ty9rJfEl1cpVKbH3uJ2v1QleUqOCI2h9xeeP0aVqLCA4JSLk6s7hu6CbkqOAIGpyB7iRZ5xLvFWlHEkITyjK/41/v9h0AC3lngpCz0PXWf0yDUcmBhFDt0T/flx8CkNL8VLAZjUhvAHSQek5AtyALdqP5e9BdbPCkZsbuFRKVvlRHs/W1AfC902yNgoriWwCeqw1fSL+J2VkWNBF8vckr6mPQ3ZcjtkVBA/3z4Ju6Bs5ANzck2BQFpUMTxlVZQ4ege95vUxRUHoPOe5s01OWBbryf2hEFDX4Fc4Vs4gaYZ3ZEQeXBJPgMcFPnwYzJVmeE6jGsGCNAE/rAlPIBamkMQv9YCLpzxJRjYMr5BLXyg5EvgTlKTOoEkw2LUct6dTz4ojqCNO04mMm4ZE150mhMuQ+jHppwAUxqUM5QK9qkPLIE5jhpygkvmHJYiW45FaL8IwmdZy9pUtc2MK9HtvgloZngJyMVp3tJ846ASb7Q1NYrg1JN+ukDs4e05LwHTO5bUKG0tRBEeXAKzJ3rpEXdB8C9fBIWKW0hhOBIBdy2K6R11zvALY6EFYE21yHF4OdKEkz7ObIlXXvAhV4OquoApaYbpCo9qayA29lLturibhimSgOSFjG1ILRwYnwShn09xArnT8PwdnHML6n+hl+2gD8Wjj+rLMOwq49Y5dZpVKUWS++VcCwdCdT5/Uhck5SH45VpVO3qJFbq2Y5Vvly2VBgQY5KqKWI6HY+n06KiqVJMSQyP/37wB6v29xGrnThyEDWh5dyr+fJscbQw/OjRcGG0OFvO3n+QSqKm7exlYgsvNgolkyFs1HGV2OQgTGsjNjnVBtO8Owj3nwbhgWnttgWxy2PaoWaC+AuAXqWYKHupMgAAAABJRU5ErkJggg==';

body {
margin: 0;
Expand All @@ -23,7 +24,106 @@ body {
left: 0;
bottom: 0;
width: @s-menu-width;
border-right: 1px solid #eee;
background-color: #F2F5FA;
box-sizing: border-box;
overflow: auto;

// shadow
&::after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
display: block;
width: 20px;
background: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.03));
}

.menuHeader {
padding-top: 40px;
text-align: center;
border-bottom: 1px solid @c-border;

.logo {
display: inline-block;
width: 66px;
height: 65px;
background: url(@img-logo) no-repeat 0/contain;
}

h1 {
margin: 10px 0 0;
color: @c-heading;
font-weight: 500;
line-height: 1.40625;
}

p {
margin: 0 0 10px;
color: lighten(@c-secondary, 10%);
}
}

> ul {
list-style: none;
margin: 0;
padding: 8px 0;

> li {
> a {
@c-active-bg: #E8ECF4;

display: block;
padding: 0 28px;
color: @c-heading;
font-size: 16px;
line-height: 46px;
transition: background 0.3s;

&:hover {
background: lighten(@c-active-bg, 2%);
}

&:active {
background: lighten(@c-active-bg, 1%);
}

&:global(.active) {
color: @c-primary;
background: #E8ECF4;
}

// disable click for group link
&[data-group] {
pointer-events: none;;
}
}

ul {
list-style: none;
margin: 6px 0;
padding: 0 28px 0 42px;

li a {
font-size: 14px;
line-height: 2;
color: @c-secondary;

&:hover {
color: lighten(@c-primary, 5%);
}

&:active {
color: lighten(@c-primary, 3%);
}

&:global(.active) {
color: @c-primary;
}
}
}
}
}
}
}
65 changes: 51 additions & 14 deletions packages/umi-plugin-father-doc/src/themes/default/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,75 @@
/// <reference path="../typings/global.d.ts" />
/// <reference path="../typings/typings.d.ts" />

import React, { Component, MouseEvent } from 'react';
import { IMenuItem } from '../../routes/getMenuFromRoutes';
import React, { Component } from 'react';
import Link from 'umi/link';
import NavLink from 'umi/navlink';
import 'prismjs/themes/prism.css';
import { IMenuItem } from '../../routes/getMenuFromRoutes';
import styles from './layout.less';

export interface ILayoutProps {
title: string;
logo?: string;
desc?: string;
menu: {
items: IMenuItem[];
};
}

export default class Layout extends Component<ILayoutProps> {
handleMenuItemClick = (e: MouseEvent, path: string) => {
window.g_history.push(path);
e.preventDefault();
}

render () {
const { children, menu } = this.props;
const { children, menu, logo, title, desc } = this.props;

return (
<div className={styles.wrapper}>
<div className={styles.menu}>
<div className={styles.menuHeader}>
<Link
to="/"
className={styles.logo}
style={{
backgroundImage: logo && `url('${logo}')`,
}}
/>
<h1>{title}</h1>
<p>{desc}</p>
</div>
<ul>
{menu.items.map((item) => (
<li>
<a
href={item.path}
onClick={e => this.handleMenuItemClick(e, item.path)}
>
{item.title}
</a>
{
item.path
? (
// render single routes
<NavLink to={item.path} exact>
{item.title}
</NavLink>
)
: (
// render child routes
<>
{/* use NavLink for active, but disable click by css */}
<NavLink
to={item.prefix}
data-group
>
{item.title}
</NavLink>
{item.children && item.children.length && (
<ul>
<li>
{item.children.map((child) => (
<NavLink to={child.path} exact>
{child.title}
</NavLink>
))}
</li>
</ul>
)}
</>
)
}
</li>
))}
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,5 +143,10 @@
opacity: 0.9;
}
}

// images
img {
max-width: 100%;
}
}
}

0 comments on commit 8ba10bb

Please sign in to comment.