This commit is contained in:
sup39 2023-02-16 00:03:53 +09:00
commit a5bd51d572
25 changed files with 3198 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
node_modules/

2
README.md Normal file
View file

@ -0,0 +1,2 @@
# supMDX-nav
Nav component for supMDX

15
dist/Nav.d.ts vendored Normal file
View file

@ -0,0 +1,15 @@
import React from 'react';
import type { NavEntryInfo } from './NavEntry';
import { HeadingInfo } from './NavBase';
import { NavHeaderConfig } from './NavHeader';
export type NavConfig = {
site: NavHeaderConfig;
nav: NavEntryInfo[];
};
export type NavProps = {
children?: React.ReactNode;
headings: HeadingInfo[];
pathname: string;
config: NavConfig;
};
export declare function Nav({ config, children, ...props }: NavProps): JSX.Element;

26
dist/Nav.jsx vendored Normal file
View file

@ -0,0 +1,26 @@
"use strict";
'use client';
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = require("react");
var NavBase_1 = require("./NavBase");
var NavHeader_1 = require("./NavHeader");
function Nav(_a) {
var config = _a.config, children = _a.children, props = __rest(_a, ["config", "children"]);
var _b = (0, react_1.useState)(false), navFold = _b[0], setNavFold = _b[1];
return (<NavBase_1.NavBase className={navFold ? 'open' : ''} entries={config.nav} {...props}>
<NavHeader_1.NavHeader config={config.site} onToggleFold={function () { return setNavFold(function (e) { return !e; }); }}/>
{children}
</NavBase_1.NavBase>);
}
exports.default = Nav;

15
dist/NavBase.d.ts vendored Normal file
View file

@ -0,0 +1,15 @@
import React from 'react';
import { NavEntryInfo } from './NavEntry';
export type HeadingInfo = {
tagName: string;
label: string;
id: string;
};
export type NavBaseProps = {
children?: React.ReactNode;
headings: HeadingInfo[];
pathname: string;
entries: NavEntryInfo[];
className?: string;
};
export declare function NavBase({ children, headings, pathname, entries, className, }: NavBaseProps): JSX.Element;

22
dist/NavBase.jsx vendored Normal file
View file

@ -0,0 +1,22 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.NavBase = void 0;
var NavEntry_1 = require("./NavEntry");
function NavBase(_a) {
var children = _a.children, headings = _a.headings, pathname = _a.pathname, entries = _a.entries, className = _a.className;
var headingsJSX = <ul className=''>{headings.map(function (_a) {
var label = _a.label, id = _a.id;
return <li key={id}>
<a href={'#' + id}>{label}</a>
</li>;
})}</ul>;
return <nav className={className}>
{children}
<div className='nav-root'>
{entries.map(function (entry) { return <NavEntry_1.NavEntry key={entry.link} entry={entry} dir={'/'} here={pathname}/>; })}
{headingsJSX}
</div>
</nav>;
}
exports.NavBase = NavBase;

12
dist/NavEntry.d.ts vendored Normal file
View file

@ -0,0 +1,12 @@
/// <reference types="react" />
export type NavEntryInfo = {
label: string;
link: string;
children?: NavEntryInfo[];
};
export declare function NavEntry<Body>({ entry: { label, link, children }, dir, here, children: childrenJSX, }: {
entry: NavEntryInfo;
dir: string;
here: string;
children?: Body;
}): JSX.Element;

25
dist/NavEntry.jsx vendored Normal file
View file

@ -0,0 +1,25 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.NavEntry = void 0;
var react_1 = require("react");
var link_1 = require("next/link");
function NavEntry(_a) {
var _b = _a.entry, label = _b.label, link = _b.link, children = _b.children, dir = _a.dir, here = _a.here, childrenJSX = _a.children;
var href = dir + link;
var isHere = href.replace(/\/$/, '') === here; // remove trailing slash
var isRHere = isHere || here.startsWith(href); // here or is children
var _c = (0, react_1.useState)(isRHere), toggle = _c[0], setToggle = _c[1];
var entryCls = 'nav-entry' + (isHere ? ' nav-here' : '');
return (children === null || children === void 0 ? void 0 : children.length) ? <div className={'nav-dir' + (toggle ? ' nav-fold-open' : '')}><>
<div className={entryCls}>
<link_1.default href={href}>{label}</link_1.default>
<svg viewBox="0 0 8 8" onClick={function () { return setToggle(function (e) { return !e; }); }}><polyline points="6 3 4 5 2 3"></polyline></svg>
</div>
{isHere ? childrenJSX : <></>}
<div className='nav-dir-child'>{children.map(function (entry) { return <NavEntry key={entry.link} entry={entry} dir={href} here={here}>{childrenJSX}</NavEntry>; })}</div>
</></div> : <div className={entryCls}>
<link_1.default href={href}>{label}</link_1.default>
</div>;
}
exports.NavEntry = NavEntry;

16
dist/NavHeader.d.ts vendored Normal file
View file

@ -0,0 +1,16 @@
/// <reference types="react" />
export type NavHeaderConfig = {
title: string;
subtitle?: string;
icon?: string | {
href: string;
alt?: string;
size?: number;
width?: number;
height?: number;
};
};
export declare function NavHeader({ config, onToggleFold }: {
config: NavHeaderConfig;
onToggleFold?: () => void;
}): JSX.Element;

22
dist/NavHeader.jsx vendored Normal file
View file

@ -0,0 +1,22 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.NavHeader = void 0;
var link_1 = require("next/link");
function NavHeader(_a) {
var _b, _c, _d, _e, _f;
var config = _a.config, onToggleFold = _a.onToggleFold;
var title = config.title, subtitle = config.subtitle, icon0 = config.icon;
var icon = typeof icon0 === 'string' ? { href: icon0 } : icon0;
return <header>
<link_1.default href="/" id="icon-link">
<img className="icon" src={icon === null || icon === void 0 ? void 0 : icon.href} alt={(_b = icon === null || icon === void 0 ? void 0 : icon.alt) !== null && _b !== void 0 ? _b : 'icon'} width={(_d = (_c = icon === null || icon === void 0 ? void 0 : icon.width) !== null && _c !== void 0 ? _c : icon === null || icon === void 0 ? void 0 : icon.size) !== null && _d !== void 0 ? _d : 96} height={(_f = (_e = icon === null || icon === void 0 ? void 0 : icon.height) !== null && _e !== void 0 ? _e : icon === null || icon === void 0 ? void 0 : icon.size) !== null && _f !== void 0 ? _f : 96}/>
<div className="icon-text">
<div>{title}</div>
<div>{subtitle}</div>
</div>
</link_1.default>
<div className="menu-toggle" onClick={onToggleFold}/>
</header>;
}
exports.NavHeader = NavHeader;

4
dist/index.d.ts vendored Normal file
View file

@ -0,0 +1,4 @@
export * from './Nav';
export * from './NavBase';
export * from './NavEntry';
export * from './NavHeader';

97
dist/index.esm.js vendored Normal file
View file

@ -0,0 +1,97 @@
import React, { useState } from 'react';
import Link from 'next/link';
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
function NavEntry(_a) {
var _b = _a.entry, label = _b.label, link = _b.link, children = _b.children, dir = _a.dir, here = _a.here, childrenJSX = _a.children;
var href = dir + link;
var isHere = href.replace(/\/$/, '') === here; // remove trailing slash
var isRHere = isHere || here.startsWith(href); // here or is children
var _c = useState(isRHere), toggle = _c[0], setToggle = _c[1];
var entryCls = 'nav-entry' + (isHere ? ' nav-here' : '');
return (children === null || children === void 0 ? void 0 : children.length) ? React.createElement("div", { className: 'nav-dir' + (toggle ? ' nav-fold-open' : '') },
React.createElement(React.Fragment, null,
React.createElement("div", { className: entryCls },
React.createElement(Link, { href: href }, label),
React.createElement("svg", { viewBox: "0 0 8 8", onClick: function () { return setToggle(function (e) { return !e; }); } },
React.createElement("polyline", { points: "6 3 4 5 2 3" }))),
isHere ? childrenJSX : React.createElement(React.Fragment, null),
React.createElement("div", { className: 'nav-dir-child' }, children.map(function (entry) { return React.createElement(NavEntry, { key: entry.link, entry: entry, dir: href, here: here }, childrenJSX); })))) : React.createElement("div", { className: entryCls },
React.createElement(Link, { href: href }, label));
}
function NavBase(_a) {
var children = _a.children, headings = _a.headings, pathname = _a.pathname, entries = _a.entries, className = _a.className;
var headingsJSX = React.createElement("ul", { className: "" }, headings.map(function (_a) {
var label = _a.label, id = _a.id;
return React.createElement("li", { key: id },
React.createElement("a", { href: '#' + id }, label));
}));
return React.createElement("nav", { className: className },
children,
React.createElement("div", { className: "nav-root" },
entries.map(function (entry) { return React.createElement(NavEntry, { key: entry.link, entry: entry, dir: '/', here: pathname }); }),
headingsJSX));
}
function NavHeader(_a) {
var _b, _c, _d, _e, _f;
var config = _a.config, onToggleFold = _a.onToggleFold;
var title = config.title, subtitle = config.subtitle, icon0 = config.icon;
var icon = typeof icon0 === 'string' ? { href: icon0 } : icon0;
return React.createElement("header", null,
React.createElement(Link, { href: "/", id: "icon-link" },
(icon === null || icon === void 0 ? void 0 : icon.href) && React.createElement("img", { className: "icon", src: icon === null || icon === void 0 ? void 0 : icon.href, alt: (_b = icon === null || icon === void 0 ? void 0 : icon.alt) !== null && _b !== void 0 ? _b : 'icon', width: (_d = (_c = icon === null || icon === void 0 ? void 0 : icon.width) !== null && _c !== void 0 ? _c : icon === null || icon === void 0 ? void 0 : icon.size) !== null && _d !== void 0 ? _d : 96, height: (_f = (_e = icon === null || icon === void 0 ? void 0 : icon.height) !== null && _e !== void 0 ? _e : icon === null || icon === void 0 ? void 0 : icon.size) !== null && _f !== void 0 ? _f : 96 }),
React.createElement("div", { className: "icon-text" },
React.createElement("div", null, title),
React.createElement("div", null, subtitle))),
React.createElement("div", { className: "menu-toggle", onClick: onToggleFold }));
}
function Nav(_a) {
var config = _a.config, children = _a.children, props = __rest(_a, ["config", "children"]);
var _b = useState(false), navFold = _b[0], setNavFold = _b[1];
return (React.createElement(NavBase, __assign({ className: navFold ? 'open' : '', entries: config.nav }, props),
React.createElement(NavHeader, { config: config.site, onToggleFold: function () { return setNavFold(function (e) { return !e; }); } }),
children));
}
export { Nav, NavBase, NavEntry, NavHeader };
//# sourceMappingURL=index.esm.js.map

1
dist/index.esm.js.map vendored Normal file

File diff suppressed because one or more lines are too long

102
dist/index.js vendored Normal file
View file

@ -0,0 +1,102 @@
'use strict';
var React = require('react');
var Link = require('next/link');
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
function NavEntry(_a) {
var _b = _a.entry, label = _b.label, link = _b.link, children = _b.children, dir = _a.dir, here = _a.here, childrenJSX = _a.children;
var href = dir + link;
var isHere = href.replace(/\/$/, '') === here; // remove trailing slash
var isRHere = isHere || here.startsWith(href); // here or is children
var _c = React.useState(isRHere), toggle = _c[0], setToggle = _c[1];
var entryCls = 'nav-entry' + (isHere ? ' nav-here' : '');
return (children === null || children === void 0 ? void 0 : children.length) ? React.createElement("div", { className: 'nav-dir' + (toggle ? ' nav-fold-open' : '') },
React.createElement(React.Fragment, null,
React.createElement("div", { className: entryCls },
React.createElement(Link, { href: href }, label),
React.createElement("svg", { viewBox: "0 0 8 8", onClick: function () { return setToggle(function (e) { return !e; }); } },
React.createElement("polyline", { points: "6 3 4 5 2 3" }))),
isHere ? childrenJSX : React.createElement(React.Fragment, null),
React.createElement("div", { className: 'nav-dir-child' }, children.map(function (entry) { return React.createElement(NavEntry, { key: entry.link, entry: entry, dir: href, here: here }, childrenJSX); })))) : React.createElement("div", { className: entryCls },
React.createElement(Link, { href: href }, label));
}
function NavBase(_a) {
var children = _a.children, headings = _a.headings, pathname = _a.pathname, entries = _a.entries, className = _a.className;
var headingsJSX = React.createElement("ul", { className: "" }, headings.map(function (_a) {
var label = _a.label, id = _a.id;
return React.createElement("li", { key: id },
React.createElement("a", { href: '#' + id }, label));
}));
return React.createElement("nav", { className: className },
children,
React.createElement("div", { className: "nav-root" },
entries.map(function (entry) { return React.createElement(NavEntry, { key: entry.link, entry: entry, dir: '/', here: pathname }); }),
headingsJSX));
}
function NavHeader(_a) {
var _b, _c, _d, _e, _f;
var config = _a.config, onToggleFold = _a.onToggleFold;
var title = config.title, subtitle = config.subtitle, icon0 = config.icon;
var icon = typeof icon0 === 'string' ? { href: icon0 } : icon0;
return React.createElement("header", null,
React.createElement(Link, { href: "/", id: "icon-link" },
(icon === null || icon === void 0 ? void 0 : icon.href) && React.createElement("img", { className: "icon", src: icon === null || icon === void 0 ? void 0 : icon.href, alt: (_b = icon === null || icon === void 0 ? void 0 : icon.alt) !== null && _b !== void 0 ? _b : 'icon', width: (_d = (_c = icon === null || icon === void 0 ? void 0 : icon.width) !== null && _c !== void 0 ? _c : icon === null || icon === void 0 ? void 0 : icon.size) !== null && _d !== void 0 ? _d : 96, height: (_f = (_e = icon === null || icon === void 0 ? void 0 : icon.height) !== null && _e !== void 0 ? _e : icon === null || icon === void 0 ? void 0 : icon.size) !== null && _f !== void 0 ? _f : 96 }),
React.createElement("div", { className: "icon-text" },
React.createElement("div", null, title),
React.createElement("div", null, subtitle))),
React.createElement("div", { className: "menu-toggle", onClick: onToggleFold }));
}
function Nav(_a) {
var config = _a.config, children = _a.children, props = __rest(_a, ["config", "children"]);
var _b = React.useState(false), navFold = _b[0], setNavFold = _b[1];
return (React.createElement(NavBase, __assign({ className: navFold ? 'open' : '', entries: config.nav }, props),
React.createElement(NavHeader, { config: config.site, onToggleFold: function () { return setNavFold(function (e) { return !e; }); } }),
children));
}
exports.Nav = Nav;
exports.NavBase = NavBase;
exports.NavEntry = NavEntry;
exports.NavHeader = NavHeader;
//# sourceMappingURL=index.js.map

1
dist/index.js.map vendored Normal file

File diff suppressed because one or more lines are too long

20
dist/index.jsx vendored Normal file
View file

@ -0,0 +1,20 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./Nav"), exports);
__exportStar(require("./NavBase"), exports);
__exportStar(require("./NavEntry"), exports);
__exportStar(require("./NavHeader"), exports);

49
package.json Normal file
View file

@ -0,0 +1,49 @@
{
"name": "@sup39/mdx-nav",
"version": "0.1.0",
"author": "sup39 <dev@sup39.dev>",
"repository": "github.com:sup39/supMDX-nav",
"description": "Nav component for supMDX",
"license": "MIT",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"files": [
"dist/*"
],
"scripts": {
"build": "rollup -c",
"lint": "eslint --ext .ts --ext .tsx src/",
"pre-commit:add": "git add -u"
},
"eslintConfig": {
"env": {
"es6": true
},
"extends": [
"@sup39/typescript"
]
},
"devDependencies": {
"@babel/core": "^7.20.12",
"@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-typescript": "^11.0.0",
"@sup39/eslint-config-typescript": "^0.1.5",
"@types/react": "^18.0.28",
"eslint": "^8.34.0",
"pre-commit": "^1.2.2",
"rollup": "^3.15.0",
"typescript": "^4.9.5"
},
"pre-commit": [
"lint",
"build",
"pre-commit:add"
],
"peerDependencies": {
"next": "^13",
"react": "^18"
}
}

32
rollup.config.mjs Normal file
View file

@ -0,0 +1,32 @@
import typescript from '@rollup/plugin-typescript';
import babel from '@rollup/plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
export default {
input: 'src/index.tsx',
output: [
{
file: 'dist/index.js',
format: 'cjs',
sourcemap: true,
},
{
file: 'dist/index.esm.js',
format: 'esm',
sourcemap: true,
},
],
plugins: [
resolve(),
typescript(),
babel({
babelHelpers: 'bundled',
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
'@babel/preset-react',
],
exclude: 'node_modules/**',
}),
],
external: ['react', 'react-dom', 'next/link'],
};

25
src/Nav.tsx Normal file
View file

@ -0,0 +1,25 @@
import React, {useState} from 'react';
import type {NavEntryInfo} from './NavEntry';
import {NavBase, HeadingInfo} from './NavBase';
import {NavHeader, NavHeaderConfig} from './NavHeader';
export type NavConfig = {
site: NavHeaderConfig
nav: NavEntryInfo[]
}
export type NavProps = {
children?: React.ReactNode
headings: HeadingInfo[]
pathname: string
config: NavConfig
};
export function Nav({config, children, ...props}: NavProps) {
const [navFold, setNavFold] = useState(false);
return (
<NavBase className={navFold ? 'open' : ''} entries={config.nav} {...props}>
<NavHeader config={config.site} onToggleFold={()=>setNavFold(e=>!e)} />
{children}
</NavBase>
);
}

31
src/NavBase.tsx Normal file
View file

@ -0,0 +1,31 @@
import React from 'react';
import {NavEntry, NavEntryInfo} from './NavEntry';
export type HeadingInfo = {
tagName: string;
label: string;
id: string;
};
export type NavBaseProps = {
children?: React.ReactNode
headings: HeadingInfo[]
pathname: string
entries: NavEntryInfo[]
className?: string
}
export function NavBase({
children, headings, pathname, entries, className,
}: NavBaseProps) {
const headingsJSX = <ul className="">{headings.map(({label, id}) => <li key={id}>
<a href={'#'+id}>{label}</a>
</li>)}</ul>;
return <nav className={className}>
{children}
<div className="nav-root">
{entries.map(entry => <NavEntry key={entry.link} entry={entry} dir={'/'} here={pathname} />)}
{headingsJSX}
</div>
</nav>;
}

30
src/NavEntry.tsx Normal file
View file

@ -0,0 +1,30 @@
import React, {useState} from 'react';
import Link from 'next/link';
export type NavEntryInfo = {
label: string
link: string
children?: NavEntryInfo[]
};
export function NavEntry<Body, >({
entry: {label, link, children}, dir, here, children: childrenJSX,
}: {entry: NavEntryInfo, dir: string, here: string, children?: Body}) {
const href = dir+link;
const isHere = href.replace(/\/$/, '')===here; // remove trailing slash
const isRHere = isHere || here.startsWith(href); // here or is children
const [toggle, setToggle] = useState(isRHere);
const entryCls = 'nav-entry'+(isHere ? ' nav-here' : '');
return children?.length ? <div className={'nav-dir'+(toggle ? ' nav-fold-open' : '')}><>
<div className={entryCls}>
<Link href={href}>{label}</Link>
<svg viewBox="0 0 8 8" onClick={()=>setToggle(e=>!e)}><polyline points="6 3 4 5 2 3"></polyline></svg>
</div>
{isHere ? childrenJSX : <></>}
<div className='nav-dir-child'>{
children.map(entry => <NavEntry key={entry.link} entry={entry} dir={href} here={here}>{childrenJSX}</NavEntry>)
}</div>
</></div> : <div className={entryCls}>
<Link href={href}>{label}</Link>
</div>;
}

37
src/NavHeader.tsx Normal file
View file

@ -0,0 +1,37 @@
import React from 'react';
import Link from 'next/link';
export type NavHeaderConfig = {
title: string
subtitle?: string
icon?: string | {
href: string
alt?: string
size?: number
width?: number
height?: number
}
};
export function NavHeader({config, onToggleFold}: {
config: NavHeaderConfig
onToggleFold?: ()=>void
}) {
const {title, subtitle, icon: icon0} = config;
const icon = typeof icon0 === 'string' ? {href: icon0} : icon0;
return <header>
<Link href="/" id="icon-link">
{icon?.href && <img className="icon"
src={icon?.href}
alt={icon?.alt ?? 'icon'}
width={icon?.width ?? icon?.size ?? 96}
height={icon?.height ?? icon?.size ?? 96}
/>}
<div className="icon-text">
<div>{title}</div>
<div>{subtitle}</div>
</div>
</Link>
<div className="menu-toggle" onClick={onToggleFold} />
</header>;
}

4
src/index.tsx Normal file
View file

@ -0,0 +1,4 @@
export * from './Nav';
export * from './NavBase';
export * from './NavEntry';
export * from './NavHeader';

26
tsconfig.json Normal file
View file

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "es5",
"module": "es6",
"outDir": "dist",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"skipLibCheck": true,
"strict": true,
"declaration": true,
"moduleResolution": "node",
"esModuleInterop": true,
"sourceMap": true,
"jsx": "react"
},
"include": [
"src/*.ts",
"src/*.tsx"
],
"exclude": [
"node_modules"
]
}

2583
yarn.lock Normal file

File diff suppressed because it is too large Load diff