first commit
This commit is contained in:
commit
197cae9a54
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
31
bolt.config.ts
Normal file
31
bolt.config.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
const pkg = require("./package.json");
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 文档版本
|
||||||
|
*/
|
||||||
|
version: pkg.version,
|
||||||
|
/**
|
||||||
|
* <title>
|
||||||
|
*/
|
||||||
|
title: "API文档 • Bolt Design",
|
||||||
|
/**
|
||||||
|
* logo 区域标题
|
||||||
|
*/
|
||||||
|
subTitle: "Bolt Design",
|
||||||
|
/**
|
||||||
|
* 配置自定义页面页面
|
||||||
|
*/
|
||||||
|
customPages: [
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
src: "Welcome",
|
||||||
|
title: "首页",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/intro",
|
||||||
|
src: "Intro",
|
||||||
|
title: "文档",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
13
index.html
Normal file
13
index.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/logo.png" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + React + TS</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
12290
package-lock.json
generated
Normal file
12290
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
49
package.json
Normal file
49
package.json
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"name": "bolt",
|
||||||
|
"private": true,
|
||||||
|
"description": "基于 mdx-react 的自动化组件、文档脚手架",
|
||||||
|
"version": "1.2.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "pnpm run build:lib && pnpm run build:doc",
|
||||||
|
"build:lib": "tsc -p ./tsconfig.lib.json && vite build --mode lib && pnpm build:less",
|
||||||
|
"build:doc": "tsc -p ./tsconfig.prod.json && vite build --mode doc",
|
||||||
|
"build:less": "esno plugin/build/autocss.ts",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@mapbox/rehype-prism": "^0.8.0",
|
||||||
|
"@mdx-js/react": "^2.1.3",
|
||||||
|
"@mdx-js/rollup": "^2.1.3",
|
||||||
|
"antd": "^4.23.5",
|
||||||
|
"less": "^4.1.3",
|
||||||
|
"markdown-toc": "^1.2.0",
|
||||||
|
"object-hash": "3.0.0",
|
||||||
|
"prismjs": "^1.29.0",
|
||||||
|
"rc-tooltip": "^5.2.2",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-loadable": "^5.5.0",
|
||||||
|
"react-router": "^6.3.0",
|
||||||
|
"react-router-dom": "^6.3.0",
|
||||||
|
"react-scroll": "^1.8.7",
|
||||||
|
"react-tooltip": "^4.2.21"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@ricons/fluent": "^0.12.0",
|
||||||
|
"@types/node": "^18.7.9",
|
||||||
|
"@types/object-hash": "2.2.1",
|
||||||
|
"@types/prismjs": "^1.26.0",
|
||||||
|
"@types/react": "^18.0.17",
|
||||||
|
"@types/react-dom": "^18.0.6",
|
||||||
|
"@types/react-scroll": "1.8.4",
|
||||||
|
"@vitejs/plugin-react": "^2.0.1",
|
||||||
|
"cpy": "^9.0.1",
|
||||||
|
"fast-glob": "^3.2.12",
|
||||||
|
"terser": "^5.15.1",
|
||||||
|
"typescript": "^4.7.4",
|
||||||
|
"vite": "^3.0.7",
|
||||||
|
"vite-plugin-dts": "^1.6.5"
|
||||||
|
}
|
||||||
|
}
|
49
plugin/build/autocss.ts
Normal file
49
plugin/build/autocss.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* css 分包,自动引入到rollup 打包后的 组件 index.js 中
|
||||||
|
* e.g Buttom/index.js
|
||||||
|
* before import './index.less'
|
||||||
|
* after import './index.css'
|
||||||
|
*/
|
||||||
|
|
||||||
|
import cpy from "cpy";
|
||||||
|
import { resolve, dirname } from "path";
|
||||||
|
import { promises as fs } from "fs";
|
||||||
|
import less from "less";
|
||||||
|
import glob from "fast-glob";
|
||||||
|
|
||||||
|
const rootPath = process.cwd();
|
||||||
|
const sourceDir = resolve(rootPath, "src");
|
||||||
|
//lib文件目录
|
||||||
|
const targetLib = resolve(rootPath, "dist/ui");
|
||||||
|
|
||||||
|
//src目录
|
||||||
|
const srcDir = resolve(rootPath, "src");
|
||||||
|
|
||||||
|
const buildLess = async () => {
|
||||||
|
//直接将less文件复制到打包后目录
|
||||||
|
await cpy([`${sourceDir}/**/*.less`], targetLib);
|
||||||
|
|
||||||
|
//获取打包后.less文件目录(lib和es一样)
|
||||||
|
const lessFiles = await glob("**/*.less", { cwd: srcDir, onlyFiles: true });
|
||||||
|
|
||||||
|
//遍历含有less的目录
|
||||||
|
for (let path in lessFiles) {
|
||||||
|
const filePath = `${srcDir}/${lessFiles[path]}`;
|
||||||
|
//获取less文件字符串
|
||||||
|
const lessCode = await fs.readFile(filePath, "utf-8");
|
||||||
|
//将less解析成css
|
||||||
|
|
||||||
|
const code = await less.render(lessCode, {
|
||||||
|
//指定src下对应less文件的文件夹为目录
|
||||||
|
paths: [srcDir, dirname(filePath)],
|
||||||
|
});
|
||||||
|
|
||||||
|
//拿到.css后缀path
|
||||||
|
const cssPath = lessFiles[path].replace(".less", ".css");
|
||||||
|
|
||||||
|
//将css写入对应目录
|
||||||
|
await fs.writeFile(resolve(targetLib, cssPath), code.css);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
buildLess();
|
4
plugin/build/autopkg.ts
Normal file
4
plugin/build/autopkg.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/**
|
||||||
|
* 给 dist 构建文件夹组件生成 package.json 发包用
|
||||||
|
*/
|
||||||
|
const buildPkg = () => {};
|
48
plugin/build/vite.conf.ts
Normal file
48
plugin/build/vite.conf.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建 doc 网页 vite构建配置
|
||||||
|
*/
|
||||||
|
const docPage = { outDir: "dist/doc" };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建组件 lib vite构建配置 组件按需
|
||||||
|
*/
|
||||||
|
const componentLib = {
|
||||||
|
minify: true, // boolean | 'terser' | 'esbuild'
|
||||||
|
sourcemap: true, // 输出单独 source文件
|
||||||
|
brotliSize: true, // 生成压缩大小报告
|
||||||
|
cssCodeSplit: false, // css 从 js 组件中分离出去
|
||||||
|
rollupOptions: {
|
||||||
|
external: ["react", "react-dom", /\.less/], // 忽略js 中的 less 文件
|
||||||
|
output: [
|
||||||
|
{
|
||||||
|
format: "es",
|
||||||
|
entryFileNames: "[name].js",
|
||||||
|
preserveModules: true,
|
||||||
|
dir: "dist/ui",
|
||||||
|
preserveModulesRoot: "src",
|
||||||
|
// assetFileNames: (assetInfo) => processAssets(assetInfo),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
lib: {
|
||||||
|
entry: path.resolve(process.cwd(), "src/index.ts"),
|
||||||
|
name: "Bolt", // 暴露的全局变量名称
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const build = (mode: string, config = {}) => {
|
||||||
|
switch (mode) {
|
||||||
|
case "doc":
|
||||||
|
config = docPage;
|
||||||
|
break;
|
||||||
|
case "lib":
|
||||||
|
config = componentLib;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
config = {};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
};
|
78
plugin/common/ApiTable.tsx
Normal file
78
plugin/common/ApiTable.tsx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { IPropMeta } from "../types";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
propList: IPropMeta[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type IHeader = {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可以拓展更多的annotation tag
|
||||||
|
*/
|
||||||
|
const tableHeader: IHeader = {
|
||||||
|
name: "参数名",
|
||||||
|
description: "说明",
|
||||||
|
type: "类型",
|
||||||
|
default: "默认值",
|
||||||
|
required: "必须",
|
||||||
|
version: "版本",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ApiTable(props: IProps) {
|
||||||
|
const [propList, setPropList] = useState<IPropMeta[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
props.propList?.length > 0 ? setPropList(props.propList) : setPropList([]);
|
||||||
|
}, [props.propList]);
|
||||||
|
|
||||||
|
const format = (prop: string, v: any) => {
|
||||||
|
if (prop === "version") return !v ? "" : "v" + v;
|
||||||
|
if (typeof v === "boolean") return !v ? "" : "" + v;
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="anchor-api-table api-table-container">
|
||||||
|
<h1>API</h1>
|
||||||
|
<table className="api-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{Object.keys(tableHeader).map((prop) => {
|
||||||
|
return (
|
||||||
|
<th key={prop}>
|
||||||
|
<div>{tableHeader[prop]}</div>
|
||||||
|
{/* <div className="annotation-prop">@{prop}</div> */}
|
||||||
|
</th>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{propList?.length > 0 ? (
|
||||||
|
propList.map((m) => {
|
||||||
|
return (
|
||||||
|
<tr key={m.name}>
|
||||||
|
{Object.keys(tableHeader).map((prop) => {
|
||||||
|
return <td key={prop}>{format(prop, (m as any)[prop])}</td>;
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<tr key={"without-data"}>
|
||||||
|
<td
|
||||||
|
style={{ textAlign: "center", fontWeight: "normal" }}
|
||||||
|
colSpan={Object.keys(tableHeader).length}
|
||||||
|
>
|
||||||
|
该组件 props.ts 缺失, 或未定义任何 interface
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
59
plugin/common/Aside.tsx
Normal file
59
plugin/common/Aside.tsx
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useLocation, useNavigate } from "react-router";
|
||||||
|
import { IRoute } from "../types";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
menus: IRoute[];
|
||||||
|
subTitle: string;
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Aside(props: IProps) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
const version = props.version;
|
||||||
|
const [menuList, setMenuList] = useState<IRoute[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.menus?.length > 0) setMenuList(props.menus);
|
||||||
|
}, [props.menus]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const path = location.pathname.replace(/\//, "");
|
||||||
|
const isCompoPath = menuList.map((i) => i.path).includes(path);
|
||||||
|
if (!isCompoPath)
|
||||||
|
setMenuList((p) => p.map((i) => ({ ...i, active: false })));
|
||||||
|
}, [location.pathname]);
|
||||||
|
|
||||||
|
const toggle = (route: IRoute) => {
|
||||||
|
setMenuList((prev) => {
|
||||||
|
return prev.map((r: IRoute) => {
|
||||||
|
r.active = r.path === route.path;
|
||||||
|
return r;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
navigate(route.path);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside>
|
||||||
|
<div className="logo">
|
||||||
|
<img src="logo.png" />
|
||||||
|
<span data-version={version}>{props.subTitle}</span>
|
||||||
|
</div>
|
||||||
|
<h4>组件({menuList.length})</h4>
|
||||||
|
<ul className="nav-list">
|
||||||
|
{menuList.map((route: IRoute) => (
|
||||||
|
<li key={route.path}>
|
||||||
|
<a
|
||||||
|
className={route.active ? "active" : ""}
|
||||||
|
onClick={() => toggle(route)}
|
||||||
|
>
|
||||||
|
{route.name}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
);
|
||||||
|
}
|
53
plugin/common/Code.tsx
Normal file
53
plugin/common/Code.tsx
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import Code20Regular from "@ricons/fluent/Code20Regular";
|
||||||
|
import Prism from "prismjs";
|
||||||
|
import "prismjs/plugins/line-numbers/prism-line-numbers";
|
||||||
|
import "prismjs/plugins/line-numbers/prism-line-numbers.css";
|
||||||
|
// import "prismjs/components/prism";
|
||||||
|
import "prismjs/components/prism-javascript";
|
||||||
|
import "prismjs/components/prism-typescript";
|
||||||
|
import "prismjs/components/prism-bash";
|
||||||
|
import "prismjs/components/prism-css";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
/**
|
||||||
|
* @description prism 渲染的代码文档字符串
|
||||||
|
*/
|
||||||
|
raw: string;
|
||||||
|
/**
|
||||||
|
* 语言类型
|
||||||
|
*/
|
||||||
|
lang: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Code(props: IProps) {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const codeRef = useRef<any>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Prism.highlightElement(codeRef.current);
|
||||||
|
}, [props.raw]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="code-container">
|
||||||
|
<div style={{ paddingTop: "10px", textAlign: "right" }}>
|
||||||
|
<span
|
||||||
|
data-placement="示例代码"
|
||||||
|
className={`btn ${visible ? "active" : ""}`}
|
||||||
|
onClick={() => setVisible((v) => !v)}
|
||||||
|
>
|
||||||
|
<Code20Regular />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<pre
|
||||||
|
className="line-numbers"
|
||||||
|
data-lang={props.lang}
|
||||||
|
style={{ display: visible ? "block" : "none" }}
|
||||||
|
>
|
||||||
|
<code ref={codeRef} className={`language-${props.lang}`}>
|
||||||
|
{props.raw}
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
65
plugin/common/MdToc.tsx
Normal file
65
plugin/common/MdToc.tsx
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { TToc } from "../types";
|
||||||
|
import { scroller } from "react-scroll";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
children?: any;
|
||||||
|
toc: TToc[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const tocEndItem: TToc = {
|
||||||
|
content: "API",
|
||||||
|
slug: "API",
|
||||||
|
lvl: 1,
|
||||||
|
anchor: "anchor-api-table",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Toc(props: IProps) {
|
||||||
|
const [contentTable, setContentTable] = useState<TToc[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const tocWithApiItem = [...props.toc, tocEndItem];
|
||||||
|
props.toc?.length > 0
|
||||||
|
? setContentTable(tocWithApiItem.map((i) => ({ ...i, active: false })))
|
||||||
|
: setContentTable([]);
|
||||||
|
}, [props.toc]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击右侧 toc 跳转
|
||||||
|
*/
|
||||||
|
const onClickTocItem = (anchor: string) => {
|
||||||
|
setContentTable((p) =>
|
||||||
|
p?.map((i) => ({ ...i, active: i.anchor === anchor }))
|
||||||
|
);
|
||||||
|
scroller.scrollTo(anchor, {
|
||||||
|
duration: 800,
|
||||||
|
delay: 0,
|
||||||
|
smooth: "easeInOutQuart",
|
||||||
|
offset: -28,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="toc curtain-bottom">
|
||||||
|
<h4>快速导航</h4>
|
||||||
|
{contentTable && (
|
||||||
|
<ul className="hidden-scrollbar">
|
||||||
|
{contentTable.map((item: TToc) => {
|
||||||
|
return (
|
||||||
|
<li key={item.content}>
|
||||||
|
<div
|
||||||
|
onClick={() => onClickTocItem(item.anchor)}
|
||||||
|
className={`toc-lvl toc-lvl-${item.lvl} ${
|
||||||
|
item.active ? "active" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{item.content}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
65
plugin/common/Nav.tsx
Normal file
65
plugin/common/Nav.tsx
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import Search20Regular from "@ricons/fluent/Search20Regular";
|
||||||
|
import { useLocation, useNavigate } from "react-router";
|
||||||
|
import { ICustomPage } from "../types";
|
||||||
|
interface IProps {
|
||||||
|
customPages: ICustomPage[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface INavItem extends ICustomPage {
|
||||||
|
active: boolean;
|
||||||
|
key: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Nav(props: IProps) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
const [navList, setNavList] = useState<INavItem[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const navList = props.customPages.map((i: ICustomPage) => ({
|
||||||
|
...i,
|
||||||
|
key: i.path,
|
||||||
|
active: false,
|
||||||
|
}));
|
||||||
|
setNavList(navList);
|
||||||
|
}, [props.customPages]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 路由变化同步页面文字 active 状态
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
const path = location.pathname;
|
||||||
|
setNavList((p) => p.map((i) => ({ ...i, active: path === i.path })));
|
||||||
|
}, [location.pathname]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击切换页面
|
||||||
|
*/
|
||||||
|
const onClickNavItem = (key: string) => {
|
||||||
|
const nav = navList.find((i: INavItem) => i.key === key);
|
||||||
|
setNavList((p: INavItem[]) =>
|
||||||
|
p.map((i: INavItem) => ({ ...i, active: key === i.key }))
|
||||||
|
);
|
||||||
|
navigate(nav!.path);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav>
|
||||||
|
<div className="start"></div>
|
||||||
|
<div className="end">
|
||||||
|
<div className="search-container">
|
||||||
|
<Search20Regular />
|
||||||
|
<input type="text" placeholder="搜索 / 待开发" />
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
{navList.map((item) => (
|
||||||
|
<li key={item.key} onClick={() => onClickNavItem(item.key)}>
|
||||||
|
<span className={item.active ? "active" : ""}>{item.title}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
534
plugin/common/style/index.less
Normal file
534
plugin/common/style/index.less
Normal file
|
@ -0,0 +1,534 @@
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--color-grey: #f2f3f5;
|
||||||
|
--color-grey-lighter: #f7f8fa;
|
||||||
|
--color-text-1: #1d2129;
|
||||||
|
--color-text-2: #4e5969;
|
||||||
|
--color-text-3: rgb(118, 124, 130);
|
||||||
|
--color-primary: rgba(22, 93, 255, 1);
|
||||||
|
--color-primary-lighter: rgba(106, 161, 255, 1);
|
||||||
|
--color-primary-lighter-5: rgba(106, 161, 255, 0.1);
|
||||||
|
--color-primary-hover: rgba(22, 93, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "./prism-laserwave.less";
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1;
|
||||||
|
font-family: Inter, "-apple-system", BlinkMacSystemFont, "PingFang SC",
|
||||||
|
"Hiragino Sans GB", "noto sans", "Microsoft YaHei", "Helvetica Neue",
|
||||||
|
Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--color-primary-lighter);
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.curtain-top {
|
||||||
|
position: relative;
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
top: 20px;
|
||||||
|
z-index: 10;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 32px;
|
||||||
|
background: linear-gradient(to top, transparent, #fff 70%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.curtain-bottom {
|
||||||
|
position: relative;
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 10;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 32px;
|
||||||
|
background: linear-gradient(to bottom, transparent, #fff 70%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-scrollbar {
|
||||||
|
position: relative;
|
||||||
|
overflow-y: auto;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
margin-top: 24px;
|
||||||
|
padding: 48px;
|
||||||
|
color: #282f38;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid var(--color-grey);
|
||||||
|
border-radius: 2px 2px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-container {
|
||||||
|
nav {
|
||||||
|
padding: 0 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: fixed;
|
||||||
|
left: 260px;
|
||||||
|
right: 0;
|
||||||
|
height: 60px;
|
||||||
|
// border-bottom: 1px solid var(--color-grey);
|
||||||
|
backdrop-filter: saturate(50%) blur(8px);
|
||||||
|
background: rgba(255, 255, 255, 0.7);
|
||||||
|
z-index: 199401;
|
||||||
|
|
||||||
|
.end {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.search-container {
|
||||||
|
position: relative;
|
||||||
|
margin-right: 40px;
|
||||||
|
height: 32px;
|
||||||
|
width: 180px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
position: absolute;
|
||||||
|
left: 5px;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -10px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
transition: all 0.25s ease;
|
||||||
|
padding-left: 25px;
|
||||||
|
padding-right: 10px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--color-grey-lighter);
|
||||||
|
&:focus {
|
||||||
|
background-color: var(--color-grey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
li {
|
||||||
|
margin-right: 30px;
|
||||||
|
&:last-of-type {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
user-select: none;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aside {
|
||||||
|
transition: all 0.15s ease-in-out;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 260px;
|
||||||
|
border-right: 1px solid var(--color-grey);
|
||||||
|
padding: 0 20px;
|
||||||
|
overflow: auto;
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
height: 40px;
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
&::after {
|
||||||
|
margin-left: 10px;
|
||||||
|
padding: 0 2px;
|
||||||
|
line-height: 1.3;
|
||||||
|
border-radius: 3px;
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
content: attr(data-version);
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--color-primary);
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--color-primary);
|
||||||
|
background: var(--color-primary-lighter-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> h4 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px 0;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
> header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 10px 10px 30px;
|
||||||
|
img {
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.nav-list {
|
||||||
|
li {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
&:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--color-grey-lighter);
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
display: inline-block;
|
||||||
|
display: block;
|
||||||
|
padding-left: 20px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
line-height: 40px;
|
||||||
|
text-decoration: none;
|
||||||
|
&.active {
|
||||||
|
color: var(--color-primary);
|
||||||
|
background-color: var(--color-primary-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> main {
|
||||||
|
padding: 20px 40px 0 40px;
|
||||||
|
margin-left: 260px;
|
||||||
|
margin-right: 180px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-loading {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: #fff;
|
||||||
|
z-index: 1023;
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-top-color: var(--color-primary-lighter);
|
||||||
|
border-bottom-color: var(--color-primary-lighter);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: rotation 1s ease infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-lang] {
|
||||||
|
position: relative;
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 1em;
|
||||||
|
right: 1em;
|
||||||
|
content: attr(data-lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotation {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
position: relative;
|
||||||
|
transition: all 0.25s ease-in-out;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border: 1px solid #e5e6eb;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
z-index: 2;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 4px 10px #0000001a;
|
||||||
|
&::before {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
content: attr(data-placement);
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
left: -65px;
|
||||||
|
width: 60px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 4px 0;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-container {
|
||||||
|
> h1,
|
||||||
|
> h2,
|
||||||
|
> h3,
|
||||||
|
> h4,
|
||||||
|
> h5,
|
||||||
|
> h6 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 48px 0 12px 0;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
> ul {
|
||||||
|
list-style-position: inside;
|
||||||
|
list-style-type: square;
|
||||||
|
> li {
|
||||||
|
line-height: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style-type: circle;
|
||||||
|
}
|
||||||
|
|
||||||
|
> blockquote {
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 16px;
|
||||||
|
color: var(--color-primary);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--color-primary);
|
||||||
|
background: var(--color-primary-lighter-5);
|
||||||
|
> p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> p,
|
||||||
|
li {
|
||||||
|
margin: 0;
|
||||||
|
line-height: 2;
|
||||||
|
code {
|
||||||
|
padding: 2px 4px;
|
||||||
|
margin: 0 2px;
|
||||||
|
font-size: 85%;
|
||||||
|
color: #3c81a6;
|
||||||
|
background-color: #d4eefc;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-family: Inter, "-apple-system", BlinkMacSystemFont, "PingFang SC",
|
||||||
|
"Hiragino Sans GB", "noto sans", "Microsoft YaHei", "Helvetica Neue",
|
||||||
|
Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-table-container {
|
||||||
|
> h1 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 48px 0 12px 0;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
.api-table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
table-layout: fixed;
|
||||||
|
border: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
tr {
|
||||||
|
th {
|
||||||
|
background: #f2f3f5;
|
||||||
|
&:nth-of-type(1) {
|
||||||
|
width: 10%;
|
||||||
|
border-radius: 8px 0 0 8px;
|
||||||
|
}
|
||||||
|
&:nth-of-type(2) {
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
&:nth-of-type(3) {
|
||||||
|
width: 21%;
|
||||||
|
}
|
||||||
|
&:nth-of-type(4) {
|
||||||
|
width: 8%;
|
||||||
|
}
|
||||||
|
&:nth-of-type(5) {
|
||||||
|
width: 8%;
|
||||||
|
}
|
||||||
|
&:nth-of-type(6) {
|
||||||
|
width: 8%;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
.annotation-prop {
|
||||||
|
padding-top: 3px;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #989898;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
&:hover {
|
||||||
|
background: rgba(60, 90, 100, 0.04);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
line-height: 20px;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
&:nth-of-type(1) {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
&:nth-of-type(2) {
|
||||||
|
color: var(--color-text-3);
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
&:nth-of-type(3),
|
||||||
|
&:nth-of-type(4) {
|
||||||
|
color: #40b4c4;
|
||||||
|
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
&:nth-of-type(5) {
|
||||||
|
color: #eb64b9;
|
||||||
|
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc {
|
||||||
|
position: fixed;
|
||||||
|
width: 180px;
|
||||||
|
top: 120px;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background: #fff;
|
||||||
|
h4 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
height: 100%;
|
||||||
|
border-left: 1px solid var(--color-grey);
|
||||||
|
li {
|
||||||
|
.toc-lvl {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
display: block;
|
||||||
|
padding-right: 20px;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--color-grey-lighter);
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
color: var(--color-primary);
|
||||||
|
border-left-color: var(--color-primary);
|
||||||
|
background-color: var(--color-primary-hover);
|
||||||
|
}
|
||||||
|
&-1 {
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
&-2 {
|
||||||
|
padding-left: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-container {
|
||||||
|
.btn {
|
||||||
|
&.active {
|
||||||
|
background: #27212e;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
133
plugin/common/style/prism-laserwave.less
Normal file
133
plugin/common/style/prism-laserwave.less
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* Laserwave Theme originally by Jared Jones for Visual Studio Code
|
||||||
|
* https://github.com/Jaredk3nt/laserwave
|
||||||
|
*
|
||||||
|
* Ported for PrismJS by Simon Jespersen [https://github.com/simjes]
|
||||||
|
*/
|
||||||
|
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
background: #27212e;
|
||||||
|
color: #ffffff;
|
||||||
|
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; /* this is the default */
|
||||||
|
/* The following properties are standard, please leave them as they are */
|
||||||
|
font-size: 1em;
|
||||||
|
direction: ltr;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
-moz-tab-size: 2;
|
||||||
|
-o-tab-size: 2;
|
||||||
|
tab-size: 2;
|
||||||
|
/* The following properties are also standard */
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
code[class*="language-"]::-moz-selection,
|
||||||
|
code[class*="language-"] ::-moz-selection,
|
||||||
|
pre[class*="language-"]::-moz-selection,
|
||||||
|
pre[class*="language-"] ::-moz-selection {
|
||||||
|
background: #eb64b927;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code[class*="language-"]::selection,
|
||||||
|
code[class*="language-"] ::selection,
|
||||||
|
pre[class*="language-"]::selection,
|
||||||
|
pre[class*="language-"] ::selection {
|
||||||
|
background: #eb64b927;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Properties specific to code blocks */
|
||||||
|
pre[class*="language-"] {
|
||||||
|
padding: 1em; /* this is standard */
|
||||||
|
margin: 0.5em 0; /* this is the default */
|
||||||
|
overflow: auto; /* this is standard */
|
||||||
|
border-radius: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Properties specific to inline code */
|
||||||
|
:not(pre) > code[class*="language-"] {
|
||||||
|
padding: 0.2em 0.3em;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
white-space: normal; /* this is standard */
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment,
|
||||||
|
.token.prolog,
|
||||||
|
.token.cdata {
|
||||||
|
color: #91889b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.punctuation {
|
||||||
|
color: #7b6995;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.builtin,
|
||||||
|
.token.constant,
|
||||||
|
.token.boolean {
|
||||||
|
color: #ffe261;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.number {
|
||||||
|
color: #b381c5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important,
|
||||||
|
.token.atrule,
|
||||||
|
.token.property,
|
||||||
|
.token.keyword {
|
||||||
|
color: #40b4c4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.doctype,
|
||||||
|
.token.operator,
|
||||||
|
.token.inserted,
|
||||||
|
.token.tag,
|
||||||
|
.token.class-name,
|
||||||
|
.token.symbol {
|
||||||
|
color: #74dfc4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.attr-name,
|
||||||
|
.token.function,
|
||||||
|
.token.deleted,
|
||||||
|
.token.selector {
|
||||||
|
color: #eb64b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.attr-value,
|
||||||
|
.token.regex,
|
||||||
|
.token.char,
|
||||||
|
.token.string {
|
||||||
|
color: #b4dce7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.entity,
|
||||||
|
.token.url,
|
||||||
|
.token.variable {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following rules are pretty similar across themes, but feel free to adjust them */
|
||||||
|
.token.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.entity {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.namespace {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
210
plugin/common/style/prism-material-oceanic.less
Normal file
210
plugin/common/style/prism-material-oceanic.less
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
color: #c3cee3;
|
||||||
|
background: #2a2d3e;
|
||||||
|
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||||
|
font-size: 1em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
code[class*="language-"]::-moz-selection,
|
||||||
|
pre[class*="language-"]::-moz-selection,
|
||||||
|
code[class*="language-"] ::-moz-selection,
|
||||||
|
pre[class*="language-"] ::-moz-selection {
|
||||||
|
background: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
code[class*="language-"]::selection,
|
||||||
|
pre[class*="language-"]::selection,
|
||||||
|
code[class*="language-"] ::selection,
|
||||||
|
pre[class*="language-"] ::selection {
|
||||||
|
background: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(pre) > code[class*="language-"] {
|
||||||
|
white-space: normal;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
padding: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"] {
|
||||||
|
overflow: auto;
|
||||||
|
position: relative;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
padding: 1.25em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-css > code,
|
||||||
|
.language-sass > code,
|
||||||
|
.language-scss > code {
|
||||||
|
color: #fd9170;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*="language-"] .namespace {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.atrule {
|
||||||
|
color: #c792ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.attr-name {
|
||||||
|
color: #ffcb6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.attr-value {
|
||||||
|
color: #c3e88d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.attribute {
|
||||||
|
color: #c3e88d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.boolean {
|
||||||
|
color: #c792ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.builtin {
|
||||||
|
color: #ffcb6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.cdata {
|
||||||
|
color: #80cbc4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.char {
|
||||||
|
color: #80cbc4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.class {
|
||||||
|
color: #ffcb6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.class-name {
|
||||||
|
color: #f2ff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.color {
|
||||||
|
color: #f2ff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment {
|
||||||
|
color: #546e7a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.constant {
|
||||||
|
color: #c792ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.deleted {
|
||||||
|
color: #f07178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.doctype {
|
||||||
|
color: #546e7a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.entity {
|
||||||
|
color: #f07178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.function {
|
||||||
|
color: #c792ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.hexcode {
|
||||||
|
color: #f2ff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.id {
|
||||||
|
color: #c792ea;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important {
|
||||||
|
color: #c792ea;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.inserted {
|
||||||
|
color: #80cbc4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.keyword {
|
||||||
|
color: #c792ea;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.number {
|
||||||
|
color: #fd9170;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.operator {
|
||||||
|
color: #89ddff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.prolog {
|
||||||
|
color: #546e7a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.property {
|
||||||
|
color: #80cbc4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.pseudo-class {
|
||||||
|
color: #c3e88d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.pseudo-element {
|
||||||
|
color: #c3e88d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.punctuation {
|
||||||
|
color: #89ddff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.regex {
|
||||||
|
color: #f2ff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.selector {
|
||||||
|
color: #f07178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.string {
|
||||||
|
color: #c3e88d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.symbol {
|
||||||
|
color: #c792ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.tag {
|
||||||
|
color: #f07178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.unit {
|
||||||
|
color: #f07178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.url {
|
||||||
|
color: #fd9170;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.variable {
|
||||||
|
color: #f07178;
|
||||||
|
}
|
158
plugin/common/style/prism-night-owl.less
Normal file
158
plugin/common/style/prism-night-owl.less
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) 2018 Sarah Drasner
|
||||||
|
* Sarah Drasner's[@sdras] Night Owl
|
||||||
|
* Ported by Sara vieria [@SaraVieira]
|
||||||
|
* Added by Souvik Mandal [@SimpleIndian]
|
||||||
|
*/
|
||||||
|
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
color: #d6deeb;
|
||||||
|
font-family: Menlo, Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-size: 1em;
|
||||||
|
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"]::-moz-selection,
|
||||||
|
pre[class*="language-"] ::-moz-selection,
|
||||||
|
code[class*="language-"]::-moz-selection,
|
||||||
|
code[class*="language-"] ::-moz-selection {
|
||||||
|
text-shadow: none;
|
||||||
|
background: rgba(29, 59, 83, 0.99);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"]::selection,
|
||||||
|
pre[class*="language-"] ::selection,
|
||||||
|
code[class*="language-"]::selection,
|
||||||
|
code[class*="language-"] ::selection {
|
||||||
|
text-shadow: none;
|
||||||
|
background: rgba(29, 59, 83, 0.99);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code blocks */
|
||||||
|
pre[class*="language-"] {
|
||||||
|
padding: 1em;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(pre) > code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
color: white;
|
||||||
|
background: #011627;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(pre) > code[class*="language-"] {
|
||||||
|
padding: 0.1em;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment,
|
||||||
|
.token.prolog,
|
||||||
|
.token.cdata {
|
||||||
|
color: rgb(99, 119, 119);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.punctuation {
|
||||||
|
color: rgb(199, 146, 234);
|
||||||
|
}
|
||||||
|
|
||||||
|
.namespace {
|
||||||
|
color: rgb(178, 204, 214);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.deleted {
|
||||||
|
color: rgba(239, 83, 80, 0.56);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.symbol,
|
||||||
|
.token.property {
|
||||||
|
color: rgb(128, 203, 196);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.tag,
|
||||||
|
.token.operator,
|
||||||
|
.token.keyword {
|
||||||
|
color: rgb(127, 219, 202);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.boolean {
|
||||||
|
color: rgb(255, 88, 116);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.number {
|
||||||
|
color: rgb(247, 140, 108);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.constant,
|
||||||
|
.token.function,
|
||||||
|
.token.builtin,
|
||||||
|
.token.char {
|
||||||
|
color: rgb(130, 170, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.selector,
|
||||||
|
.token.doctype {
|
||||||
|
color: rgb(199, 146, 234);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.attr-name,
|
||||||
|
.token.inserted {
|
||||||
|
color: rgb(173, 219, 103);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.string,
|
||||||
|
.token.url,
|
||||||
|
.token.entity,
|
||||||
|
.language-css .token.string,
|
||||||
|
.style .token.string {
|
||||||
|
color: rgb(173, 219, 103);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.class-name,
|
||||||
|
.token.atrule,
|
||||||
|
.token.attr-value {
|
||||||
|
color: rgb(255, 203, 139);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.regex,
|
||||||
|
.token.important,
|
||||||
|
.token.variable {
|
||||||
|
color: rgb(214, 222, 235);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important,
|
||||||
|
.token.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
428
plugin/common/style/prism-one-light.less
Normal file
428
plugin/common/style/prism-one-light.less
Normal file
|
@ -0,0 +1,428 @@
|
||||||
|
/**
|
||||||
|
* One Light theme for prism.js
|
||||||
|
* Based on Atom's One Light theme: https://github.com/atom/atom/tree/master/packages/one-light-syntax
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One Light colours (accurate as of commit eb064bf on 19 Feb 2021)
|
||||||
|
* From colors.less
|
||||||
|
* --mono-1: hsl(230, 8%, 24%);
|
||||||
|
* --mono-2: hsl(230, 6%, 44%);
|
||||||
|
* --mono-3: hsl(230, 4%, 64%)
|
||||||
|
* --hue-1: hsl(198, 99%, 37%);
|
||||||
|
* --hue-2: hsl(221, 87%, 60%);
|
||||||
|
* --hue-3: hsl(301, 63%, 40%);
|
||||||
|
* --hue-4: hsl(119, 34%, 47%);
|
||||||
|
* --hue-5: hsl(5, 74%, 59%);
|
||||||
|
* --hue-5-2: hsl(344, 84%, 43%);
|
||||||
|
* --hue-6: hsl(35, 99%, 36%);
|
||||||
|
* --hue-6-2: hsl(35, 99%, 40%);
|
||||||
|
* --syntax-fg: hsl(230, 8%, 24%);
|
||||||
|
* --syntax-bg: hsl(230, 1%, 98%);
|
||||||
|
* --syntax-gutter: hsl(230, 1%, 62%);
|
||||||
|
* --syntax-guide: hsla(230, 8%, 24%, 0.2);
|
||||||
|
* --syntax-accent: hsl(230, 100%, 66%);
|
||||||
|
* From syntax-variables.less
|
||||||
|
* --syntax-selection-color: hsl(230, 1%, 90%);
|
||||||
|
* --syntax-gutter-background-color-selected: hsl(230, 1%, 90%);
|
||||||
|
* --syntax-cursor-line: hsla(230, 8%, 24%, 0.05);
|
||||||
|
*/
|
||||||
|
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
background: hsl(230, 1%, 98%);
|
||||||
|
color: hsl(230, 8%, 24%);
|
||||||
|
font-family: Menlo, Consolas, "DejaVu Sans Mono", monospace;
|
||||||
|
direction: ltr;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
line-height: 1.4;
|
||||||
|
-moz-tab-size: 2;
|
||||||
|
-o-tab-size: 2;
|
||||||
|
tab-size: 2;
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selection */
|
||||||
|
code[class*="language-"]::-moz-selection,
|
||||||
|
code[class*="language-"] *::-moz-selection,
|
||||||
|
pre[class*="language-"] *::-moz-selection {
|
||||||
|
background: hsl(230, 1%, 90%);
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code[class*="language-"]::selection,
|
||||||
|
code[class*="language-"] *::selection,
|
||||||
|
pre[class*="language-"] *::selection {
|
||||||
|
background: hsl(230, 1%, 90%);
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code blocks */
|
||||||
|
pre[class*="language-"] {
|
||||||
|
padding: 1em;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
overflow: auto;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline code */
|
||||||
|
:not(pre) > code[class*="language-"] {
|
||||||
|
padding: 0.2em 0.3em;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment,
|
||||||
|
.token.prolog,
|
||||||
|
.token.cdata {
|
||||||
|
color: hsl(230, 4%, 64%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.doctype,
|
||||||
|
.token.punctuation,
|
||||||
|
.token.entity {
|
||||||
|
color: hsl(230, 8%, 24%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.attr-name,
|
||||||
|
.token.class-name,
|
||||||
|
.token.boolean,
|
||||||
|
.token.constant,
|
||||||
|
.token.number,
|
||||||
|
.token.atrule {
|
||||||
|
color: hsl(35, 99%, 36%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.keyword {
|
||||||
|
color: hsl(301, 63%, 40%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.property,
|
||||||
|
.token.tag,
|
||||||
|
.token.symbol,
|
||||||
|
.token.deleted,
|
||||||
|
.token.important {
|
||||||
|
color: hsl(5, 74%, 59%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.selector,
|
||||||
|
.token.string,
|
||||||
|
.token.char,
|
||||||
|
.token.builtin,
|
||||||
|
.token.inserted,
|
||||||
|
.token.regex,
|
||||||
|
.token.attr-value,
|
||||||
|
.token.attr-value > .token.punctuation {
|
||||||
|
color: hsl(119, 34%, 47%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.variable,
|
||||||
|
.token.operator,
|
||||||
|
.token.function {
|
||||||
|
color: hsl(221, 87%, 60%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.url {
|
||||||
|
color: hsl(198, 99%, 37%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HTML overrides */
|
||||||
|
.token.attr-value > .token.punctuation.attr-equals,
|
||||||
|
.token.special-attr > .token.attr-value > .token.value.css {
|
||||||
|
color: hsl(230, 8%, 24%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CSS overrides */
|
||||||
|
.language-css .token.selector {
|
||||||
|
color: hsl(5, 74%, 59%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-css .token.property {
|
||||||
|
color: hsl(230, 8%, 24%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-css .token.function,
|
||||||
|
.language-css .token.url > .token.function {
|
||||||
|
color: hsl(198, 99%, 37%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-css .token.url > .token.string.url {
|
||||||
|
color: hsl(119, 34%, 47%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-css .token.important,
|
||||||
|
.language-css .token.atrule .token.rule {
|
||||||
|
color: hsl(301, 63%, 40%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JS overrides */
|
||||||
|
.language-javascript .token.operator {
|
||||||
|
color: hsl(301, 63%, 40%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-javascript .token.template-string > .token.interpolation > .token.interpolation-punctuation.punctuation {
|
||||||
|
color: hsl(344, 84%, 43%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JSON overrides */
|
||||||
|
.language-json .token.operator {
|
||||||
|
color: hsl(230, 8%, 24%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-json .token.null.keyword {
|
||||||
|
color: hsl(35, 99%, 36%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MD overrides */
|
||||||
|
.language-markdown .token.url,
|
||||||
|
.language-markdown .token.url > .token.operator,
|
||||||
|
.language-markdown .token.url-reference.url > .token.string {
|
||||||
|
color: hsl(230, 8%, 24%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.url > .token.content {
|
||||||
|
color: hsl(221, 87%, 60%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.url > .token.url,
|
||||||
|
.language-markdown .token.url-reference.url {
|
||||||
|
color: hsl(198, 99%, 37%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.blockquote.punctuation,
|
||||||
|
.language-markdown .token.hr.punctuation {
|
||||||
|
color: hsl(230, 4%, 64%);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.code-snippet {
|
||||||
|
color: hsl(119, 34%, 47%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.bold .token.content {
|
||||||
|
color: hsl(35, 99%, 36%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.italic .token.content {
|
||||||
|
color: hsl(301, 63%, 40%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.strike .token.content,
|
||||||
|
.language-markdown .token.strike .token.punctuation,
|
||||||
|
.language-markdown .token.list.punctuation,
|
||||||
|
.language-markdown .token.title.important > .token.punctuation {
|
||||||
|
color: hsl(5, 74%, 59%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* General */
|
||||||
|
.token.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment,
|
||||||
|
.token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.entity {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.namespace {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Plugin overrides */
|
||||||
|
/* Selectors should have higher specificity than those in the plugins' default stylesheets */
|
||||||
|
|
||||||
|
/* Show Invisibles plugin overrides */
|
||||||
|
.token.token.tab:not(:empty):before,
|
||||||
|
.token.token.cr:before,
|
||||||
|
.token.token.lf:before,
|
||||||
|
.token.token.space:before {
|
||||||
|
color: hsla(230, 8%, 24%, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toolbar plugin overrides */
|
||||||
|
/* Space out all buttons and move them away from the right edge of the code block */
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item {
|
||||||
|
margin-right: 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styling the buttons */
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > button,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > a,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > span {
|
||||||
|
background: hsl(230, 1%, 90%);
|
||||||
|
color: hsl(230, 6%, 44%);
|
||||||
|
padding: 0.1em 0.4em;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:hover,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:focus,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:hover,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:focus,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:hover,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:focus {
|
||||||
|
background: hsl(230, 1%, 78%); /* custom: darken(--syntax-bg, 20%) */
|
||||||
|
color: hsl(230, 8%, 24%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Line Highlight plugin overrides */
|
||||||
|
/* The highlighted line itself */
|
||||||
|
.line-highlight.line-highlight {
|
||||||
|
background: hsla(230, 8%, 24%, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default line numbers in Line Highlight plugin */
|
||||||
|
.line-highlight.line-highlight:before,
|
||||||
|
.line-highlight.line-highlight[data-end]:after {
|
||||||
|
background: hsl(230, 1%, 90%);
|
||||||
|
color: hsl(230, 8%, 24%);
|
||||||
|
padding: 0.1em 0.6em;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
box-shadow: 0 2px 0 0 rgba(0, 0, 0, 0.2); /* same as Toolbar plugin default */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hovering over a linkable line number (in the gutter area) */
|
||||||
|
/* Requires Line Numbers plugin as well */
|
||||||
|
pre[id].linkable-line-numbers.linkable-line-numbers span.line-numbers-rows > span:hover:before {
|
||||||
|
background-color: hsla(230, 8%, 24%, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Line Numbers and Command Line plugins overrides */
|
||||||
|
/* Line separating gutter from coding area */
|
||||||
|
.line-numbers.line-numbers .line-numbers-rows,
|
||||||
|
.command-line .command-line-prompt {
|
||||||
|
border-right-color: hsla(230, 8%, 24%, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stuff in the gutter */
|
||||||
|
.line-numbers .line-numbers-rows > span:before,
|
||||||
|
.command-line .command-line-prompt > span:before {
|
||||||
|
color: hsl(230, 1%, 62%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Match Braces plugin overrides */
|
||||||
|
/* Note: Outline colour is inherited from the braces */
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-1,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-5,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-9 {
|
||||||
|
color: hsl(5, 74%, 59%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-2,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-6,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-10 {
|
||||||
|
color: hsl(119, 34%, 47%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-3,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-7,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-11 {
|
||||||
|
color: hsl(221, 87%, 60%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-4,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-8,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-12 {
|
||||||
|
color: hsl(301, 63%, 40%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Diff Highlight plugin overrides */
|
||||||
|
/* Taken from https://github.com/atom/github/blob/master/styles/variables.less */
|
||||||
|
pre.diff-highlight > code .token.token.deleted:not(.prefix),
|
||||||
|
pre > code.diff-highlight .token.token.deleted:not(.prefix) {
|
||||||
|
background-color: hsla(353, 100%, 66%, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.diff-highlight > code .token.token.deleted:not(.prefix)::-moz-selection,
|
||||||
|
pre.diff-highlight > code .token.token.deleted:not(.prefix) *::-moz-selection,
|
||||||
|
pre > code.diff-highlight .token.token.deleted:not(.prefix)::-moz-selection,
|
||||||
|
pre > code.diff-highlight .token.token.deleted:not(.prefix) *::-moz-selection {
|
||||||
|
background-color: hsla(353, 95%, 66%, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.diff-highlight > code .token.token.deleted:not(.prefix)::selection,
|
||||||
|
pre.diff-highlight > code .token.token.deleted:not(.prefix) *::selection,
|
||||||
|
pre > code.diff-highlight .token.token.deleted:not(.prefix)::selection,
|
||||||
|
pre > code.diff-highlight .token.token.deleted:not(.prefix) *::selection {
|
||||||
|
background-color: hsla(353, 95%, 66%, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.diff-highlight > code .token.token.inserted:not(.prefix),
|
||||||
|
pre > code.diff-highlight .token.token.inserted:not(.prefix) {
|
||||||
|
background-color: hsla(137, 100%, 55%, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.diff-highlight > code .token.token.inserted:not(.prefix)::-moz-selection,
|
||||||
|
pre.diff-highlight > code .token.token.inserted:not(.prefix) *::-moz-selection,
|
||||||
|
pre > code.diff-highlight .token.token.inserted:not(.prefix)::-moz-selection,
|
||||||
|
pre > code.diff-highlight .token.token.inserted:not(.prefix) *::-moz-selection {
|
||||||
|
background-color: hsla(135, 73%, 55%, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.diff-highlight > code .token.token.inserted:not(.prefix)::selection,
|
||||||
|
pre.diff-highlight > code .token.token.inserted:not(.prefix) *::selection,
|
||||||
|
pre > code.diff-highlight .token.token.inserted:not(.prefix)::selection,
|
||||||
|
pre > code.diff-highlight .token.token.inserted:not(.prefix) *::selection {
|
||||||
|
background-color: hsla(135, 73%, 55%, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Previewers plugin overrides */
|
||||||
|
/* Based on https://github.com/atom-community/atom-ide-datatip/blob/master/styles/atom-ide-datatips.less and https://github.com/atom/atom/blob/master/packages/one-light-ui */
|
||||||
|
/* Border around popup */
|
||||||
|
.prism-previewer.prism-previewer:before,
|
||||||
|
.prism-previewer-gradient.prism-previewer-gradient div {
|
||||||
|
border-color: hsl(0, 0, 95%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Angle and time should remain as circles and are hence not included */
|
||||||
|
.prism-previewer-color.prism-previewer-color:before,
|
||||||
|
.prism-previewer-gradient.prism-previewer-gradient div,
|
||||||
|
.prism-previewer-easing.prism-previewer-easing:before {
|
||||||
|
border-radius: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Triangles pointing to the code */
|
||||||
|
.prism-previewer.prism-previewer:after {
|
||||||
|
border-top-color: hsl(0, 0, 95%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prism-previewer-flipped.prism-previewer-flipped.after {
|
||||||
|
border-bottom-color: hsl(0, 0, 95%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Background colour within the popup */
|
||||||
|
.prism-previewer-angle.prism-previewer-angle:before,
|
||||||
|
.prism-previewer-time.prism-previewer-time:before,
|
||||||
|
.prism-previewer-easing.prism-previewer-easing {
|
||||||
|
background: hsl(0, 0%, 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For angle, this is the positive area (eg. 90deg will display one quadrant in this colour) */
|
||||||
|
/* For time, this is the alternate colour */
|
||||||
|
.prism-previewer-angle.prism-previewer-angle circle,
|
||||||
|
.prism-previewer-time.prism-previewer-time circle {
|
||||||
|
stroke: hsl(230, 8%, 24%);
|
||||||
|
stroke-opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stroke colours of the handle, direction point, and vector itself */
|
||||||
|
.prism-previewer-easing.prism-previewer-easing circle,
|
||||||
|
.prism-previewer-easing.prism-previewer-easing path,
|
||||||
|
.prism-previewer-easing.prism-previewer-easing line {
|
||||||
|
stroke: hsl(230, 8%, 24%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill colour of the handle */
|
||||||
|
.prism-previewer-easing.prism-previewer-easing circle {
|
||||||
|
fill: transparent;
|
||||||
|
}
|
193
plugin/initialize.ts
Normal file
193
plugin/initialize.ts
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs";
|
||||||
|
import TsToJson from "./ts2json";
|
||||||
|
import toc from "markdown-toc";
|
||||||
|
import objHash from "object-hash";
|
||||||
|
|
||||||
|
const hash = (str: string) => "" + objHash(str).substring(0, 6);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* props 注释元信息
|
||||||
|
*/
|
||||||
|
interface IPropMeta {
|
||||||
|
/**
|
||||||
|
* 声明属性是否必须 对应 ts ?
|
||||||
|
*/
|
||||||
|
required?: boolean;
|
||||||
|
/**
|
||||||
|
* @name 参数名称
|
||||||
|
*/
|
||||||
|
name?: string;
|
||||||
|
/**
|
||||||
|
* @type 类型
|
||||||
|
*/
|
||||||
|
type?: string;
|
||||||
|
/**
|
||||||
|
* @description 说明
|
||||||
|
*/
|
||||||
|
description?: string;
|
||||||
|
/**
|
||||||
|
* @default 默认值
|
||||||
|
*/
|
||||||
|
default?: string;
|
||||||
|
/**
|
||||||
|
* @version 版本
|
||||||
|
*/
|
||||||
|
version?: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IProp2jsonItem {
|
||||||
|
[key: string]: {
|
||||||
|
props: IPropMeta[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRoute {
|
||||||
|
active: false;
|
||||||
|
path: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITree {
|
||||||
|
/**
|
||||||
|
* 组件🎄
|
||||||
|
*/
|
||||||
|
compoTree: ICompoTree;
|
||||||
|
/**
|
||||||
|
* 文件🎄,最小粒度更新
|
||||||
|
*/
|
||||||
|
fileTree?: {};
|
||||||
|
/**
|
||||||
|
* 路由🎄,根据 menuTree 生成
|
||||||
|
*/
|
||||||
|
routerList: IRoute[];
|
||||||
|
/**
|
||||||
|
* 插件配置
|
||||||
|
*/
|
||||||
|
config?: {};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ITocItem {
|
||||||
|
/**
|
||||||
|
* 锚点的 hash
|
||||||
|
*/
|
||||||
|
anchor?: string;
|
||||||
|
/**
|
||||||
|
* mardkown h标签 innerHTML
|
||||||
|
*/
|
||||||
|
content: string;
|
||||||
|
i: 0;
|
||||||
|
lvl: 1;
|
||||||
|
seen: 0;
|
||||||
|
/**
|
||||||
|
* 小写 innerHTML
|
||||||
|
*/
|
||||||
|
slug: "hi";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ts interface 转 json
|
||||||
|
*/
|
||||||
|
const tsParser = new TsToJson();
|
||||||
|
/**
|
||||||
|
* 组件开发目录
|
||||||
|
*/
|
||||||
|
const root = path.resolve(process.cwd(), "src/components");
|
||||||
|
/**
|
||||||
|
* 文件映射,最小粒度更新 For HMR
|
||||||
|
*/
|
||||||
|
const fileTree = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICompoTree Item
|
||||||
|
*/
|
||||||
|
interface ICompoMetadata {
|
||||||
|
/**
|
||||||
|
* 组件 props 信息
|
||||||
|
*/
|
||||||
|
props: IPropMeta[] | [];
|
||||||
|
/**
|
||||||
|
* markdown 文件目录 table of content
|
||||||
|
*/
|
||||||
|
toc: ITocItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件🎄
|
||||||
|
*/
|
||||||
|
interface ICompoTree {
|
||||||
|
[key: string]: ICompoMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 markdown 内容生成目录 table of content
|
||||||
|
* @param {string} content markdown 内容
|
||||||
|
*/
|
||||||
|
const md2toc = (content: string) => {
|
||||||
|
if (!content) return [];
|
||||||
|
return toc(content).json;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成路由
|
||||||
|
* @param {ICompoTree} compoTree 菜单
|
||||||
|
*/
|
||||||
|
const processRouter = (compoTree: ICompoTree): IRoute[] => {
|
||||||
|
return Object.keys(compoTree).map((t) => ({
|
||||||
|
active: false,
|
||||||
|
path: t.toLowerCase(),
|
||||||
|
name: t,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* props.ts 转换成 json 对象
|
||||||
|
* @param {string} path 组件 props.ts 文件路径
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const parsePropsTs = (path: string) => {
|
||||||
|
const prop2json: IProp2jsonItem = fs.existsSync(path)
|
||||||
|
? tsParser.parse(path)
|
||||||
|
: {};
|
||||||
|
return Object.values(prop2json)?.[0]?.props ?? [];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 mdx 文件中的目录
|
||||||
|
* @param {string} path 组件 mdx 文件路径
|
||||||
|
*/
|
||||||
|
const parseToc = (path: string) => {
|
||||||
|
const isFile = fs.statSync(path).isFile();
|
||||||
|
const mdxContent = isFile ? fs.readFileSync(path).toString() : "";
|
||||||
|
// 注入锚点 hash
|
||||||
|
const toc = md2toc(mdxContent).map((i) => ({
|
||||||
|
...i,
|
||||||
|
anchor: `anchor-${hash(i.content)}`,
|
||||||
|
}));
|
||||||
|
return toc;
|
||||||
|
};
|
||||||
|
|
||||||
|
const walkCompoMetadata = (dirs: string[]): ICompoTree => {
|
||||||
|
const compoTree: ICompoTree = {};
|
||||||
|
dirs.forEach((dir) => {
|
||||||
|
const mdxFile = path.resolve(root, dir, "index.mdx");
|
||||||
|
const propFile = path.resolve(root, dir, "props.ts");
|
||||||
|
fileTree[mdxFile] = dir;
|
||||||
|
fileTree[propFile] = dir;
|
||||||
|
const toc = parseToc(mdxFile);
|
||||||
|
const props = parsePropsTs(propFile);
|
||||||
|
compoTree[dir] = { toc, props };
|
||||||
|
});
|
||||||
|
return compoTree;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
* @returns menuTree: 目录树, 包含markdownHeadings | propsTree: 组件 api 树
|
||||||
|
*/
|
||||||
|
export const initialize = (): ITree => {
|
||||||
|
const dirs = fs.existsSync(root) ? fs.readdirSync(root) : [];
|
||||||
|
const compoTree = walkCompoMetadata(dirs);
|
||||||
|
const routerList = processRouter(compoTree);
|
||||||
|
return { compoTree, fileTree, routerList };
|
||||||
|
};
|
213
plugin/ts2json.ts
Normal file
213
plugin/ts2json.ts
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
import ts from "typescript";
|
||||||
|
|
||||||
|
export interface DocsType {
|
||||||
|
name?: string;
|
||||||
|
type?: string;
|
||||||
|
description?: string;
|
||||||
|
default?: string;
|
||||||
|
required?: boolean;
|
||||||
|
version?: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Interfaces {
|
||||||
|
name?: {
|
||||||
|
description?: string;
|
||||||
|
/** 是否不渲染 markdown */
|
||||||
|
ignore?: string;
|
||||||
|
props?: DocsType[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ts Interface 类型定义转换为 json
|
||||||
|
*/
|
||||||
|
export default class TsToJson {
|
||||||
|
checker: any;
|
||||||
|
interfaces: any;
|
||||||
|
fileNames: any;
|
||||||
|
types: any;
|
||||||
|
|
||||||
|
_createProgram(fileNames: string[], options: ts.CompilerOptions) {
|
||||||
|
this.interfaces = {};
|
||||||
|
this.types = {};
|
||||||
|
|
||||||
|
// 使用filename中的根文件名构建一个程序;
|
||||||
|
const program = ts.createProgram(fileNames, options);
|
||||||
|
// 获取检查器,我们将使用它来查找更多关于 ast 的信息
|
||||||
|
this.checker = program.getTypeChecker();
|
||||||
|
|
||||||
|
this.fileNames = fileNames;
|
||||||
|
|
||||||
|
// 访问程序中的每个sourceFile
|
||||||
|
for (const sourceFile of program.getSourceFiles()) {
|
||||||
|
if (!sourceFile.isDeclarationFile) {
|
||||||
|
// 遍历树以搜索 interface 和 type
|
||||||
|
ts.forEachChild(sourceFile, (node) => this._visitAst(node));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问查找导出的节点
|
||||||
|
* @param node
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
_visitAst(node: ts.Node) {
|
||||||
|
// 只考虑导出的节点
|
||||||
|
if (!this._isNodeExported(node)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ts.SyntaxKind[node.kind]) {
|
||||||
|
case "InterfaceDeclaration":
|
||||||
|
this.generateInterfaceDeclaration(node);
|
||||||
|
break;
|
||||||
|
case "TypeAliasDeclaration":
|
||||||
|
this.generateTypeAliasDeclaration(node);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 Interface 类型 json 数据
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
generateInterfaceDeclaration(node: ts.Node) {
|
||||||
|
if (
|
||||||
|
// @ts-ignore
|
||||||
|
node.parent?.fileName.replace(/\\/g, "/") !==
|
||||||
|
this.fileNames[0].replace(/\\/g, "/")
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
const newNode = node as ts.InterfaceDeclaration;
|
||||||
|
const symbol = this.checker.getSymbolAtLocation(newNode.name);
|
||||||
|
const { escapedName } = symbol;
|
||||||
|
const { name = escapedName as string, ...rest } = this._getDocs(symbol);
|
||||||
|
this.interfaces[name] = {
|
||||||
|
...rest,
|
||||||
|
props: [],
|
||||||
|
};
|
||||||
|
if (newNode.heritageClauses) {
|
||||||
|
// 是否有extends
|
||||||
|
const firstHeritageClause = newNode.heritageClauses[0];
|
||||||
|
const firstHeritageClauseType = firstHeritageClause.types![0];
|
||||||
|
const extendsType = this.checker.getTypeAtLocation(
|
||||||
|
firstHeritageClauseType.expression
|
||||||
|
);
|
||||||
|
if (extendsType?.symbol?.members && extendsType.symbol.members.size > 0) {
|
||||||
|
// 接口 extends 接口(Interface)
|
||||||
|
extendsType.symbol.members.forEach((member: any) => {
|
||||||
|
this.interfaces[name].props.push(this._serializeSymbol(member));
|
||||||
|
});
|
||||||
|
} else if (firstHeritageClauseType) {
|
||||||
|
console.log("暂时不支持接口继承Type类型");
|
||||||
|
// 接口 extends 类型(Type)
|
||||||
|
// const type = this.checker.typeToTypeNode(
|
||||||
|
// this.checker.getTypeAtLocation(firstHeritageClauseType),
|
||||||
|
// firstHeritageClauseType,
|
||||||
|
// ts.NodeBuilderFlags.InTypeAlias
|
||||||
|
// // ts.TypeFormatFlags.InTypeAlias
|
||||||
|
// );
|
||||||
|
// console.log(firstHeritageClauseType.getFullText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
symbol.members.forEach((member: any) => {
|
||||||
|
this.interfaces[name].props.push(this._serializeSymbol(member));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 Type 类型 json 数据
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
generateTypeAliasDeclaration(node: ts.Node) {
|
||||||
|
const type = this.checker.typeToString(
|
||||||
|
this.checker.getTypeAtLocation(node),
|
||||||
|
node,
|
||||||
|
ts.TypeFormatFlags.InTypeAlias
|
||||||
|
);
|
||||||
|
const name = (node as any).name.escapedText;
|
||||||
|
this.types[name] = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将symbol序列化为json对象
|
||||||
|
* @param symbol
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
_serializeSymbol(symbol: ts.Symbol): DocsType | undefined {
|
||||||
|
if (!symbol) return;
|
||||||
|
const docs = this._getDocs(symbol);
|
||||||
|
const questionToken = (symbol as any).valueDeclaration?.questionToken;
|
||||||
|
const type = this.checker.typeToString(
|
||||||
|
this.checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration)
|
||||||
|
);
|
||||||
|
let newType = type;
|
||||||
|
if (type.indexOf("|") > 0) {
|
||||||
|
newType = type
|
||||||
|
.split("|")
|
||||||
|
.map((item: any) => {
|
||||||
|
const hasType = this.types[item.trim()];
|
||||||
|
if (hasType) {
|
||||||
|
return ` ${hasType} `;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
.join("|");
|
||||||
|
} else if (this.types[type]) {
|
||||||
|
newType = this.types[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: symbol.getName(),
|
||||||
|
required: !questionToken,
|
||||||
|
type: newType,
|
||||||
|
...docs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取jsDoc信息
|
||||||
|
* @param symbol
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
_getDocs(symbol: ts.Symbol): DocsType {
|
||||||
|
const docs: DocsType = {};
|
||||||
|
if (
|
||||||
|
symbol.getJsDocTags &&
|
||||||
|
Array.isArray(symbol.getJsDocTags(this.checker))
|
||||||
|
) {
|
||||||
|
symbol.getJsDocTags(this.checker).forEach((tag) => {
|
||||||
|
(docs as any)[tag.name] = tag.text && tag.text[0].text;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果在文件外部可见,则为True,否则为false
|
||||||
|
* @param node
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
|
_isNodeExported(node: ts.Node): boolean {
|
||||||
|
return (
|
||||||
|
(ts.getCombinedModifierFlags(node as ts.Declaration) &
|
||||||
|
ts.ModifierFlags.Export) !==
|
||||||
|
0 ||
|
||||||
|
(!!node.parent && node.parent.kind === ts.SyntaxKind.SourceFile)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(
|
||||||
|
fileNames: string,
|
||||||
|
options: ts.CompilerOptions = {
|
||||||
|
target: ts.ScriptTarget.ES5,
|
||||||
|
module: ts.ModuleKind.CommonJS,
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
this._createProgram([fileNames], options);
|
||||||
|
return this.interfaces;
|
||||||
|
}
|
||||||
|
}
|
116
plugin/types/index.ts
Normal file
116
plugin/types/index.ts
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
export interface ITocItem {
|
||||||
|
/**
|
||||||
|
* 锚点的 hash
|
||||||
|
*/
|
||||||
|
anchor: string;
|
||||||
|
/**
|
||||||
|
* mardkown h标签 innerHTML
|
||||||
|
*/
|
||||||
|
content: string;
|
||||||
|
i: 0;
|
||||||
|
lvl: 1;
|
||||||
|
seen: 0;
|
||||||
|
/**
|
||||||
|
* 小写 innerHTML
|
||||||
|
*/
|
||||||
|
slug: "hi";
|
||||||
|
/**
|
||||||
|
* 高亮
|
||||||
|
*/
|
||||||
|
active?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* props 注释元信息
|
||||||
|
*/
|
||||||
|
export interface IPropMeta {
|
||||||
|
/**
|
||||||
|
* 声明属性是否必须 对应 ts ?
|
||||||
|
*/
|
||||||
|
required?: boolean;
|
||||||
|
/**
|
||||||
|
* @name 参数名称
|
||||||
|
*/
|
||||||
|
name?: string;
|
||||||
|
/**
|
||||||
|
* @type 类型
|
||||||
|
*/
|
||||||
|
type?: string;
|
||||||
|
/**
|
||||||
|
* @description 说明
|
||||||
|
*/
|
||||||
|
description?: string;
|
||||||
|
/**
|
||||||
|
* @default 默认值
|
||||||
|
*/
|
||||||
|
default?: string;
|
||||||
|
/**
|
||||||
|
* @version 版本
|
||||||
|
*/
|
||||||
|
version?: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件🎄
|
||||||
|
*/
|
||||||
|
export interface ICompoTree {
|
||||||
|
[key: string]: ICompoMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICompoTree Item
|
||||||
|
*/
|
||||||
|
export interface ICompoMetadata {
|
||||||
|
/**
|
||||||
|
* 组件 props 信息
|
||||||
|
*/
|
||||||
|
props: IPropMeta[] | [];
|
||||||
|
/**
|
||||||
|
* markdown 文件目录 table of content
|
||||||
|
*/
|
||||||
|
toc: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITree {
|
||||||
|
/**
|
||||||
|
* 组件🎄
|
||||||
|
*/
|
||||||
|
compoTree: ICompoTree;
|
||||||
|
/**
|
||||||
|
* 文件🎄,最小粒度更新
|
||||||
|
*/
|
||||||
|
fileTree?: {};
|
||||||
|
/**
|
||||||
|
* 路由🎄,根据 menuTree 生成
|
||||||
|
*/
|
||||||
|
routerList: IRoute[];
|
||||||
|
/**
|
||||||
|
* 插件配置
|
||||||
|
*/
|
||||||
|
config?: {
|
||||||
|
title: string;
|
||||||
|
subTitle: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRoute {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
active: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TToc = {
|
||||||
|
content: string;
|
||||||
|
slug: string;
|
||||||
|
lvl: 1 | 2;
|
||||||
|
i?: number;
|
||||||
|
seen?: number;
|
||||||
|
active?: boolean;
|
||||||
|
anchor: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ICustomPage {
|
||||||
|
path: string;
|
||||||
|
src: string;
|
||||||
|
title: string;
|
||||||
|
}
|
77
plugin/vite-plugin-bolt.ts
Normal file
77
plugin/vite-plugin-bolt.ts
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import { initialize, ITree } from "./initialize";
|
||||||
|
|
||||||
|
interface IMozzieOptions {
|
||||||
|
virtualModuleName?: string;
|
||||||
|
config?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IMeta {
|
||||||
|
name: string;
|
||||||
|
required: boolean;
|
||||||
|
type: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRoute {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
active: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IConfigs {
|
||||||
|
config: {
|
||||||
|
title: string; // document title
|
||||||
|
subTitle: string; // 文档标题
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Bolt(options: IMozzieOptions) {
|
||||||
|
const { config = {} } = options;
|
||||||
|
const virtualModuleId = "virtual:@vite/plugin-bolt";
|
||||||
|
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
||||||
|
const TREE: ITree = {
|
||||||
|
compoTree: {},
|
||||||
|
fileTree: {},
|
||||||
|
routerList: [],
|
||||||
|
config: {},
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
name: "@vite/plugin-bolt",
|
||||||
|
enforce: "pre",
|
||||||
|
resolveId(id: string) {
|
||||||
|
if (id === virtualModuleId) {
|
||||||
|
return resolvedVirtualModuleId;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
load(id: string) {
|
||||||
|
if (id === resolvedVirtualModuleId) {
|
||||||
|
const { compoTree, fileTree, routerList } = initialize();
|
||||||
|
TREE.fileTree = fileTree;
|
||||||
|
TREE.compoTree = compoTree;
|
||||||
|
TREE.routerList = routerList;
|
||||||
|
TREE.config = config;
|
||||||
|
// build 后也存在 config
|
||||||
|
return `export const TREE = ${JSON.stringify(TREE)}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleHotUpdate(ctx: any) {
|
||||||
|
const { file, server } = ctx;
|
||||||
|
//! 开发阶段:
|
||||||
|
//* 由于 上面load方法 只会触发一次,导致写到前端的 TREE 对象,无法更改
|
||||||
|
//* 当动态增减 components 组件,默认会先使用 TREE,然后触发 client:refresh 方法,获取到最新的 compoTree
|
||||||
|
//* 导致左侧菜单会变化,目前采用 loading 遮住这个跳变的过程
|
||||||
|
server.ws.send("server:fullUpdate", initialize());
|
||||||
|
|
||||||
|
//! return [] 会让 mdx-plugin 不更新
|
||||||
|
},
|
||||||
|
configureServer(server) {
|
||||||
|
server.ws.on("client:refresh", (data, client) => {
|
||||||
|
client.send("client:refresh:response", initialize());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
transformIndexHtml(html) {
|
||||||
|
const { title } = config;
|
||||||
|
return html.replace(/<title>(.*?)<\/title>/, `<title>${title}</title>`);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
5386
pnpm-lock.yaml
Normal file
5386
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/logo.png
Normal file
BIN
public/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
24
readme.md
Normal file
24
readme.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# quick start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
# 构建产物
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
|
- dist 文件夹 `doc` 是文档站,直接部署到 `web`容器。路由 `history`模式,配合 nginx 开启 `nginx 伪静态`
|
||||||
|
|
||||||
|
- dist 文件夹 `ui` 为 组件库 ,直接`ESModule`引入项目
|
||||||
|
|
||||||
|
- `src/pages` 文件夹自定义页面,`bolt.config.ts` 配置引入
|
||||||
|
|
||||||
|
- `src/components` 为组件开发目录,基本组成:
|
||||||
|
|
||||||
|
- index.tsx 组件的代码
|
||||||
|
- index.less 组件的样式
|
||||||
|
- props.ts 组件 api 接口
|
||||||
|
- index.mdx 组件文档说明
|
174
src/App.tsx
Normal file
174
src/App.tsx
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
import { MDXProvider } from "@mdx-js/react";
|
||||||
|
import { TREE } from "@bolt/config";
|
||||||
|
import { lazy, Suspense, useEffect, useState } from "react";
|
||||||
|
import { Route, Routes, useLocation } from "react-router";
|
||||||
|
import {
|
||||||
|
ICompoTree,
|
||||||
|
ICustomPage,
|
||||||
|
IPropMeta,
|
||||||
|
IRoute,
|
||||||
|
ITocItem,
|
||||||
|
ITree,
|
||||||
|
} from "../plugin/types";
|
||||||
|
import ApiTable from "../plugin/common/ApiTable";
|
||||||
|
import Aside from "../plugin/common/Aside";
|
||||||
|
import objHash from "object-hash";
|
||||||
|
import Toc from "../plugin/common/MdToc";
|
||||||
|
import Nav from "../plugin/common/Nav";
|
||||||
|
|
||||||
|
const hash = (str: string) => "" + objHash(str).substring(0, 6);
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const location = useLocation();
|
||||||
|
const [customPages, setCustomPages] = useState<ICustomPage[]>(
|
||||||
|
TREE.config.customPages
|
||||||
|
);
|
||||||
|
const [compoTree, setCompoTree] = useState<ICompoTree>(TREE.compoTree);
|
||||||
|
const [routerList, setRouterList] = useState<IRoute[]>(TREE.routerList);
|
||||||
|
const [toc, setToc] = useState<ITocItem[]>([]);
|
||||||
|
const [propsTable, setPropsTable] = useState<IPropMeta[]>([]);
|
||||||
|
const [isPage, setIsPage] = useState(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端刷新通知 vite-server
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (import.meta.hot) import.meta.hot.send("client:refresh");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端刷新 vite-server 返回新的 TREE
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (import.meta.hot) {
|
||||||
|
import.meta.hot.on("client:refresh:response", (response: ITree) => {
|
||||||
|
console.log("refresh", response);
|
||||||
|
const { routerList, compoTree } = response;
|
||||||
|
setRouterList(routerList);
|
||||||
|
setCompoTree(compoTree);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vite-server 监听到文件改动
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (import.meta.hot) {
|
||||||
|
import.meta.hot.on("server:fullUpdate", (response: ITree) => {
|
||||||
|
console.log("fullUpdate", response);
|
||||||
|
const { routerList, compoTree } = response;
|
||||||
|
setRouterList(routerList);
|
||||||
|
setCompoTree(compoTree);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getCurrent = () => {
|
||||||
|
const path = location.pathname.replace(/\//, "");
|
||||||
|
const route = routerList.find((i) => i.path === path);
|
||||||
|
const compoName = route?.name ?? "";
|
||||||
|
setIsPage(compoName === "");
|
||||||
|
return { path, route, compoName };
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { compoName } = getCurrent();
|
||||||
|
if (!!compoName) {
|
||||||
|
const { toc, props } = compoTree[compoName];
|
||||||
|
setPropsTable(props);
|
||||||
|
setToc(toc);
|
||||||
|
} else {
|
||||||
|
setToc([]);
|
||||||
|
}
|
||||||
|
}, [compoTree, location.pathname]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setRouterList((p) =>
|
||||||
|
p.map((i) => ({ ...i, active: location.pathname.indexOf(i.path) > -1 }))
|
||||||
|
);
|
||||||
|
}, [compoTree]);
|
||||||
|
|
||||||
|
const H1 = (args: any) => {
|
||||||
|
const anchorHash = "anchor-" + hash(args.children);
|
||||||
|
const attr = { name: anchorHash };
|
||||||
|
return <h1 {...attr}>{args.children}</h1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const H2 = (args: any) => {
|
||||||
|
const anchorHash = "anchor-" + hash(args.children);
|
||||||
|
const attr = { name: anchorHash };
|
||||||
|
return <h2 {...attr}>{args.children}</h2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const lazyLoad = (modulePath: string) => {
|
||||||
|
const Module = lazy(() =>
|
||||||
|
new Promise((resolve) => setTimeout(resolve, 150)).then(() =>
|
||||||
|
import(`./components/${modulePath}/index.mdx`).then((m) => m)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<div className="page-loading" />}>
|
||||||
|
<MDXProvider components={{ h1: H1, h2: H2 }}>
|
||||||
|
<Module />
|
||||||
|
</MDXProvider>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const lazyLoadPage = (src: string) => {
|
||||||
|
const Module = lazy(() =>
|
||||||
|
new Promise((resolve) => setTimeout(resolve, 150)).then(() =>
|
||||||
|
import(`./pages/${src}.mdx`).then((m) => m)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<div className="page-loading" />}>
|
||||||
|
<MDXProvider components={{ h1: H1, h2: H2 }}>
|
||||||
|
<Module />
|
||||||
|
</MDXProvider>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="root-container">
|
||||||
|
{/* nav */}
|
||||||
|
<Nav customPages={customPages} />
|
||||||
|
{/* 左侧导航栏 */}
|
||||||
|
<Aside
|
||||||
|
menus={routerList}
|
||||||
|
subTitle={TREE.config.subTitle}
|
||||||
|
version={TREE.config.version}
|
||||||
|
/>
|
||||||
|
<main>
|
||||||
|
<article className="markdown-container">
|
||||||
|
<Routes>
|
||||||
|
{/* 自定义页面 */}
|
||||||
|
{customPages.map((page: ICustomPage) => (
|
||||||
|
<Route
|
||||||
|
key={page.path}
|
||||||
|
path={page.path}
|
||||||
|
element={lazyLoadPage(page.src)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{/* 组件 */}
|
||||||
|
{routerList.map((route: IRoute) => (
|
||||||
|
<Route
|
||||||
|
key={route.name}
|
||||||
|
path={route.path}
|
||||||
|
element={lazyLoad(route.name)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Routes>
|
||||||
|
</article>
|
||||||
|
{/* 组件 props 表 */}
|
||||||
|
{!isPage && <ApiTable propList={propsTable} />}
|
||||||
|
{/* table of content */}
|
||||||
|
{toc.length > 0 && <Toc toc={toc} />}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
1
src/assets/common.less
Normal file
1
src/assets/common.less
Normal file
|
@ -0,0 +1 @@
|
||||||
|
@import "./var.less";
|
13
src/assets/var.less
Normal file
13
src/assets/var.less
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
:root {
|
||||||
|
--bolt-primary-color: #1890ff;
|
||||||
|
--bolt-primary-color-hover: #40a9ff;
|
||||||
|
--bolt-primary-color-active: #096dd9;
|
||||||
|
--bolt-primary-color-1: #e6f7ff;
|
||||||
|
--bolt-primary-color-2: #bae7ff;
|
||||||
|
--bolt-primary-color-3: #91d5ff;
|
||||||
|
--bolt-primary-color-4: #69c0ff;
|
||||||
|
--bolt-primary-color-5: #40a9ff;
|
||||||
|
--bolt-primary-color-6: #1890ff;
|
||||||
|
--bolt-primary-color-7: #096dd9;
|
||||||
|
--bolt-border-radius: 2px;
|
||||||
|
}
|
14
src/components/Button/demo/demo1.tsx
Normal file
14
src/components/Button/demo/demo1.tsx
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import Button from "..";
|
||||||
|
|
||||||
|
export default function test() {
|
||||||
|
return (
|
||||||
|
<div className="box">
|
||||||
|
<p>
|
||||||
|
<Button size={"small"}>小按钮</Button>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<Button size={"large"}>大按钮</Button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
42
src/components/Button/index.less
Normal file
42
src/components/Button/index.less
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
@import "../../assets/common.less";
|
||||||
|
|
||||||
|
button {
|
||||||
|
transition: all .25s ease-in-out;
|
||||||
|
position: relative;
|
||||||
|
font-size: 13px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 4px 15px;
|
||||||
|
background: var(--bolt-primary-color);
|
||||||
|
border: 0;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: var(--bolt-border-radius);
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background: var(--bolt-primary-color-hover);
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
background: var(--bolt-primary-color-active);
|
||||||
|
}
|
||||||
|
&.halo {
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border-color: var(--bolt-primary-color-hover);
|
||||||
|
border-radius: 2px;
|
||||||
|
animation: ani-halo 0.25s 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ani-halo {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0px rgb(24 144 255 / 60%);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 4px rgb(24 144 255 / 0%);
|
||||||
|
}
|
||||||
|
}
|
114
src/components/Button/index.mdx
Normal file
114
src/components/Button/index.mdx
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
import Code from "../../../plugin/common/Code.tsx";
|
||||||
|
import Demo1 from "./demo/demo1.tsx";
|
||||||
|
import Demo1Code from "./demo/demo1.tsx?raw";
|
||||||
|
|
||||||
|
# Hi
|
||||||
|
|
||||||
|
按钮组件测试
|
||||||
|
|
||||||
|
<Demo1 />
|
||||||
|
<Code raw={Demo1Code} lang="ts" />
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
间距预设大、中、小三种大小。[链接](/input)
|
||||||
|
|
||||||
|
通过设置 `size`、`large`、`middle` 分别把间距设为大、中间距。若不设置 `size`, 则间距为小。
|
||||||
|
|
||||||
|
## 使用 useage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# npm install pnpm -g
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
```css
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 测试 toc
|
||||||
|
|
||||||
|
是的吗,好厉害
|
||||||
|
|
||||||
|
## JJ
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const a = 1;
|
||||||
|
const a = 3;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Emphasis
|
||||||
|
|
||||||
|
**This is bold text**
|
||||||
|
|
||||||
|
**This is bold text**
|
||||||
|
|
||||||
|
_This is italic text_
|
||||||
|
|
||||||
|
_This is italic text_
|
||||||
|
|
||||||
|
~~Strikethrough~~
|
||||||
|
|
||||||
|
## Blockquotes
|
||||||
|
|
||||||
|
> Blockquotes can also be nested...
|
||||||
|
>
|
||||||
|
> > by using additional greater-than signs right next to each other...
|
||||||
|
> >
|
||||||
|
> > > ..or with spaces between arrows.
|
||||||
|
|
||||||
|
## Lists
|
||||||
|
|
||||||
|
Unordered
|
||||||
|
|
||||||
|
- Create a list by starting a line with `+`, `-`, or `*`
|
||||||
|
- Sub-lists are made by indenting 2 spaces:
|
||||||
|
- Marker character change forces new list start:
|
||||||
|
- Ac tristique libero volutpat at
|
||||||
|
* Facilisis in pretium nisl aliquet
|
||||||
|
- Nulla volutpat aliquam velit
|
||||||
|
- Very easy!
|
||||||
|
|
||||||
|
Ordered
|
||||||
|
|
||||||
|
1. Lorem ipsum dolor sit amet
|
||||||
|
2. Consectetur adipiscing elit
|
||||||
|
3. Integer molestie lorem at massa
|
||||||
|
|
||||||
|
4. You can use sequential numbers...
|
||||||
|
5. ...or keep all the numbers as `1.`
|
||||||
|
|
||||||
|
Start numbering with offset:
|
||||||
|
|
||||||
|
57. foo
|
||||||
|
1. bar
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
Inline `code`
|
||||||
|
|
||||||
|
Indented code
|
||||||
|
|
||||||
|
// Some comments
|
||||||
|
line 1 of code
|
||||||
|
line 2 of code
|
||||||
|
line 3 of code
|
||||||
|
|
||||||
|
Block code "fences"
|
||||||
|
|
||||||
|
```
|
||||||
|
Sample text here...
|
||||||
|
```
|
||||||
|
|
||||||
|
Syntax highlighting
|
||||||
|
|
||||||
|
```js
|
||||||
|
var foo = function (bar) {
|
||||||
|
return bar++;
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(foo(5));
|
||||||
|
```
|
35
src/components/Button/index.tsx
Normal file
35
src/components/Button/index.tsx
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
import "./index.less";
|
||||||
|
import { IButton } from "./props";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button组件
|
||||||
|
*/
|
||||||
|
export default function Button(props: IButton) {
|
||||||
|
const sizeTable = {
|
||||||
|
small: "12px",
|
||||||
|
large: "16px",
|
||||||
|
};
|
||||||
|
const [animation, setAnimation] = useState(false);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
setAnimation(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setAnimation(false);
|
||||||
|
}, 250);
|
||||||
|
console.log("click event handler");
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
<button
|
||||||
|
onClick={onClick}
|
||||||
|
className={animation ? "halo" : ""}
|
||||||
|
style={{ fontSize: sizeTable[props.size] }}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
29
src/components/Button/props.ts
Normal file
29
src/components/Button/props.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
type TSize = "small" | "large";
|
||||||
|
|
||||||
|
export interface IButton {
|
||||||
|
children: string;
|
||||||
|
/**
|
||||||
|
* @description 这是描述住按钮尺寸大小的属性,非常的奈一丝,克苏鲁,san 值拉满
|
||||||
|
* @default small
|
||||||
|
*/
|
||||||
|
size: TSize;
|
||||||
|
|
||||||
|
type?: "primary" | "danger";
|
||||||
|
|
||||||
|
omg?: 1 | 2 | 3;
|
||||||
|
|
||||||
|
and?: Array<number> | Array<string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 禁用
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
disabled?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 点击按钮
|
||||||
|
* @param {Event} e 点击dom对象
|
||||||
|
* @version 3.0
|
||||||
|
*/
|
||||||
|
onClick?: (e: Event, callback: () => {}) => {};
|
||||||
|
}
|
8
src/components/Input/demo/demo1.tsx
Normal file
8
src/components/Input/demo/demo1.tsx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import Input from "..";
|
||||||
|
export default function demo1() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Input size="small" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
3
src/components/Input/index.less
Normal file
3
src/components/Input/index.less
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.bolt-input {
|
||||||
|
border: 1px solid red;
|
||||||
|
}
|
15
src/components/Input/index.mdx
Normal file
15
src/components/Input/index.mdx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import Code from "../../../plugin/common/Code.tsx";
|
||||||
|
import Demo1 from "./demo/demo1.tsx";
|
||||||
|
import Demo1Code from "./demo/demo1.tsx?raw";
|
||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
<Demo1 />
|
||||||
|
<Code raw={Demo1Code} lang="ts" />
|
||||||
|
|
||||||
|
开动小脑静,冷静的思考下,这个问题一定可以不被解决掉
|
||||||
|
|
||||||
|
- 开发阶段 HMR 会重置路由的状态
|
||||||
|
- 切换路由懒加载, markdown 文字区域目录生成的问题
|
||||||
|
|
||||||
|
> 冲啊!Fighting 渣渣,我到河北省来
|
6
src/components/Input/index.tsx
Normal file
6
src/components/Input/index.tsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { IInput } from "./props";
|
||||||
|
import "./index.less";
|
||||||
|
|
||||||
|
export default function Input(props: IInput) {
|
||||||
|
return <input className="bolt-input" type="text" placeholder="待封装" />;
|
||||||
|
}
|
7
src/components/Input/props.ts
Normal file
7
src/components/Input/props.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export interface IInput {
|
||||||
|
/**
|
||||||
|
* @description大小的属性
|
||||||
|
* @default small
|
||||||
|
*/
|
||||||
|
size: "small" | "large";
|
||||||
|
}
|
0
src/components/Slider/index.mdx
Normal file
0
src/components/Slider/index.mdx
Normal file
3
src/components/Switch/index.mdx
Normal file
3
src/components/Switch/index.mdx
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# switch
|
||||||
|
|
||||||
|
1112222
|
5
src/components/Switch/index.tsx
Normal file
5
src/components/Switch/index.tsx
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { ISwitch } from "./props";
|
||||||
|
|
||||||
|
export default function Switch(props: ISwitch) {
|
||||||
|
return <div>Switch</div>;
|
||||||
|
}
|
11
src/components/Switch/props.ts
Normal file
11
src/components/Switch/props.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export interface ISwitch {
|
||||||
|
/**
|
||||||
|
* @description 开关
|
||||||
|
*/
|
||||||
|
checked: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 测试2
|
||||||
|
*/
|
||||||
|
test: 1 | 2 | 3
|
||||||
|
}
|
5
src/index.ts
Normal file
5
src/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import Button from "./components/Button";
|
||||||
|
import Input from "./components/Input";
|
||||||
|
import Switch from "./components/Switch";
|
||||||
|
|
||||||
|
export { Button, Input, Switch };
|
13
src/main.tsx
Normal file
13
src/main.tsx
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import App from "./App";
|
||||||
|
import "../plugin/common/style/index.less";
|
||||||
|
import { BrowserRouter as Router } from "react-router-dom";
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
|
// 会执行两次
|
||||||
|
// <React.StrictMode>
|
||||||
|
<Router>
|
||||||
|
<App />
|
||||||
|
</Router>
|
||||||
|
// </React.StrictMode>
|
||||||
|
);
|
3
src/pages/Intro.mdx
Normal file
3
src/pages/Intro.mdx
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# 介绍
|
||||||
|
|
||||||
|
滴滴滴滴
|
46
src/pages/Welcome.mdx
Normal file
46
src/pages/Welcome.mdx
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# Bolt
|
||||||
|
|
||||||
|
Bolt 是快速简单的组件库开发脚手架,用于构建快速组件库、文档。你可以在开发`react组件库`的同时, 无缝地编写`组件文档`, 实时热更新
|
||||||
|
|
||||||
|
# ✨ 特性
|
||||||
|
|
||||||
|
- 🌈 使用 `markdown` 语法编写组件文档
|
||||||
|
- ⚙️ 使用 `mdx` 扩展了 `markdown` 的能力, 你可以将 `Jsx.Element` 引入 `markdown`
|
||||||
|
- 🛡 摒弃 `PropTypes`, 使用 `typescript` 约束组件 `props`, 组件文档自动化生成相应 `API`
|
||||||
|
- 📦 借助 `vite`、`rollup`能力, `文档站`,`组件库`一并构建产出
|
||||||
|
|
||||||
|
# MDX
|
||||||
|
|
||||||
|
`MDX` 允许在降价内容中使用 JSX。您可以导入组件, 例如交互式图表或警报, 并将其嵌入到内容中。
|
||||||
|
|
||||||
|
```md
|
||||||
|
import Code from "./Code.tsx";
|
||||||
|
|
||||||
|
# Test
|
||||||
|
|
||||||
|
mdx 文件引入 react 组件示例
|
||||||
|
|
||||||
|
<Code raw={Demo1Code} lang="ts" />
|
||||||
|
```
|
||||||
|
|
||||||
|
# 项目结构
|
||||||
|
|
||||||
|
```bash
|
||||||
|
├── bolt.config.ts # bolt 基本配置
|
||||||
|
├── dist
|
||||||
|
│ ├── doc # 文档静态资源构建产物
|
||||||
|
│ └── ui # 组件库构建产物
|
||||||
|
├── src
|
||||||
|
│ ├── pages # 自定义页面
|
||||||
|
│ │ ├── Intro.mdx
|
||||||
|
│ │ └── Welcome.mdx
|
||||||
|
│ ├── assets # 静态资源
|
||||||
|
│ │ └── common.less
|
||||||
|
│ ├── components # 组件开发目录
|
||||||
|
│ │ ├── Button
|
||||||
|
│ │ │ ├── index.less # 样式
|
||||||
|
│ │ │ ├── index.mdx # 组件文档
|
||||||
|
│ │ │ ├── index.tsx # 组件
|
||||||
|
│ │ │ └── props.ts # 组件 props
|
||||||
|
│ ├── index.ts # 组件是否打包进组件库
|
||||||
|
```
|
9
src/vite-env.d.ts
vendored
Normal file
9
src/vite-env.d.ts
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module "@bolt/config" {
|
||||||
|
export const TREE;
|
||||||
|
}
|
||||||
|
declare module "*.mdx" {
|
||||||
|
let MDXComponent: (props: any) => JSX.Element;
|
||||||
|
export default MDXComponent;
|
||||||
|
}
|
39
tsconfig.json
Normal file
39
tsconfig.json
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": [
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable",
|
||||||
|
"ESNext"
|
||||||
|
],
|
||||||
|
"allowJs": false,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src",
|
||||||
|
"lib",
|
||||||
|
"common",
|
||||||
|
"plugin/vite-plugin-mozzie",
|
||||||
|
"plugin/ui",
|
||||||
|
"plugin/types",
|
||||||
|
],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"path": {
|
||||||
|
"@bolt/config": "virtual:@vite/plugin-bolt",
|
||||||
|
"@": "src"
|
||||||
|
}
|
||||||
|
}
|
13
tsconfig.lib.json
Normal file
13
tsconfig.lib.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": true, // 输出d.ts
|
||||||
|
"noEmit": true, // 生输出编译文件
|
||||||
|
"removeComments": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"outDir": "./dist/BoltUI"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/components/**/*.tsx",
|
||||||
|
"src/components/**/*.ts",
|
||||||
|
],
|
||||||
|
}
|
14
tsconfig.node.json
Normal file
14
tsconfig.node.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"vite.config.ts",
|
||||||
|
"bolt.config.ts",
|
||||||
|
"plugin",
|
||||||
|
"plugin/build",
|
||||||
|
]
|
||||||
|
}
|
9
tsconfig.prod.json
Normal file
9
tsconfig.prod.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": false, // 输出d.ts
|
||||||
|
"noEmit": true, // 生输出编译文件
|
||||||
|
"removeComments": true,
|
||||||
|
"outDir": "./dist/doc"
|
||||||
|
},
|
||||||
|
}
|
62
vite.config.ts
Normal file
62
vite.config.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import Bolt from "./plugin/vite-plugin-bolt";
|
||||||
|
import rehypePrism from "@mapbox/rehype-prism";
|
||||||
|
import { build } from "./plugin/build/vite.conf";
|
||||||
|
import dts from "vite-plugin-dts";
|
||||||
|
import BoltConfig from "./bolt.config";
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig(async ({ mode }): Promise<{}> => {
|
||||||
|
const mdx = await import("@mdx-js/rollup");
|
||||||
|
return {
|
||||||
|
server: {
|
||||||
|
watch: {
|
||||||
|
ignored: ["!**/dist/**"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
process.env.MODE !== "production"
|
||||||
|
? react({
|
||||||
|
jsxRuntime: "classic",
|
||||||
|
})
|
||||||
|
: react(),
|
||||||
|
mdx.default({
|
||||||
|
jsxRuntime: "automatic",
|
||||||
|
providerImportSource: "@mdx-js/react",
|
||||||
|
rehypePlugins: [rehypePrism], // syntax highlight会compile pre > code
|
||||||
|
remarkPlugins: [],
|
||||||
|
}),
|
||||||
|
Bolt({ config: BoltConfig }),
|
||||||
|
mode === "lib" &&
|
||||||
|
dts({
|
||||||
|
entryRoot: "src",
|
||||||
|
outputDir: "dist/ui",
|
||||||
|
}),
|
||||||
|
mode !== "doc" && {
|
||||||
|
name: "style",
|
||||||
|
generateBundle(config, bundle) {
|
||||||
|
//这里可以获取打包后的文件目录以及代码code
|
||||||
|
const keys = Object.keys(bundle);
|
||||||
|
for (const key of keys) {
|
||||||
|
const bundler: any = bundle[key as any];
|
||||||
|
//rollup内置方法,将所有输出文件code中的.less换成.css
|
||||||
|
//TODO 导致sourcemap 文件丢失,待解决
|
||||||
|
this.emitFile({
|
||||||
|
type: "asset",
|
||||||
|
fileName: key, //文件名名不变
|
||||||
|
source: bundler.code.replace(/\.less/g, ".css"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
].filter(Boolean),
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@bolt/config": "virtual:@vite/plugin-bolt",
|
||||||
|
"@": "src",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
build: build(mode),
|
||||||
|
};
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user