dumi 2.0.0-beta.16 → 2.0.0-beta.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/client/theme-api/index.d.ts +1 -0
  2. package/dist/client/theme-api/index.js +1 -0
  3. package/dist/client/theme-api/openCodeSandbox.d.ts +9 -0
  4. package/dist/client/theme-api/openCodeSandbox.js +110 -0
  5. package/dist/client/theme-api/types.d.ts +4 -2
  6. package/dist/client/theme-api/utils.d.ts +6 -0
  7. package/dist/client/theme-api/utils.js +15 -0
  8. package/dist/constants.d.ts +1 -0
  9. package/dist/constants.js +3 -0
  10. package/dist/features/configPlugins/index.js +3 -1
  11. package/dist/features/derivative.js +6 -0
  12. package/dist/features/routes.js +2 -9
  13. package/dist/loaders/markdown/index.js +15 -2
  14. package/dist/loaders/markdown/transformer/rehypeDemo.js +4 -4
  15. package/dist/loaders/markdown/transformer/remarkMeta.js +2 -0
  16. package/package.json +2 -3
  17. package/theme-default/layouts/DocLayout/index.js +5 -1
  18. package/theme-default/layouts/DocLayout/index.less +18 -0
  19. package/theme-default/locales/en-US.json +1 -1
  20. package/theme-default/locales/zh-CN.json +1 -1
  21. package/theme-default/slots/Content/index.less +7 -1
  22. package/theme-default/slots/Features/index.js +6 -2
  23. package/theme-default/slots/Features/index.less +9 -1
  24. package/theme-default/slots/Footer/index.d.ts +4 -0
  25. package/theme-default/slots/Footer/index.js +18 -0
  26. package/theme-default/slots/Footer/index.less +19 -0
  27. package/theme-default/slots/Hero/index.less +9 -5
  28. package/theme-default/slots/PreviewerActions/index.js +5 -2
  29. package/theme-default/slots/Sidebar/index.js +3 -2
  30. package/theme-default/slots/Toc/index.less +4 -0
  31. package/theme.d.ts +0 -1
@@ -3,6 +3,7 @@ export { useSiteData } from './context';
3
3
  export { DumiDemo } from './DumiDemo';
4
4
  export { DumiDemoGrid } from './DumiDemoGrid';
5
5
  export { DumiPage } from './DumiPage';
6
+ export { openCodeSandbox } from './openCodeSandbox';
6
7
  export type { IPreviewerProps } from './types';
7
8
  export { useAtomAssets } from './useAtomAssets';
8
9
  export { useLocale } from './useLocale';
@@ -3,6 +3,7 @@ export { useSiteData } from "./context";
3
3
  export { DumiDemo } from "./DumiDemo";
4
4
  export { DumiDemoGrid } from "./DumiDemoGrid";
5
5
  export { DumiPage } from "./DumiPage";
6
+ export { openCodeSandbox } from "./openCodeSandbox";
6
7
  export { useAtomAssets } from "./useAtomAssets";
7
8
  export { useLocale } from "./useLocale";
8
9
  export { useNavData } from "./useNavData";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * use CodeSandbox.io
3
+ * @param data previewer opts
4
+ * @param opts the api that CodeSandbox calls when creating the demo
5
+ * @note return a open function for open demo on codesandbox.io
6
+ */
7
+ export declare const openCodeSandbox: (data: IPreviewerProps, opts?: {
8
+ api?: string;
9
+ }) => void;
@@ -0,0 +1,110 @@
1
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
2
+
3
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
4
+
5
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
6
+
7
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
8
+
9
+ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
10
+
11
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
12
+
13
+ import { getParameters } from 'codesandbox/lib/api/define';
14
+ import { genReactRenderCode } from "./utils";
15
+ var CSB_API_ENDPOINT = 'https://codesandbox.io/api/v1/sandboxes/define';
16
+ /**
17
+ * get serialized data that use to submit to codesandbox.io
18
+ * @param opts previewer props
19
+ */
20
+
21
+ function getCSBData(opts) {
22
+ var _opts$asset$dependenc, _react, _deps$_react, _reactDom, _deps$_reactDom;
23
+
24
+ var isTSX = Boolean((_opts$asset$dependenc = opts.asset.dependencies) === null || _opts$asset$dependenc === void 0 ? void 0 : _opts$asset$dependenc['index.tsx']);
25
+ var ext = isTSX ? '.tsx' : '.jsx';
26
+ var files = {};
27
+ var deps = {};
28
+ var entryFileName = "index".concat(ext);
29
+ Object.entries(opts.asset.dependencies).forEach(function (_ref) {
30
+ var _ref2 = _slicedToArray(_ref, 2),
31
+ name = _ref2[0],
32
+ _ref2$ = _ref2[1],
33
+ type = _ref2$.type,
34
+ value = _ref2$.value;
35
+
36
+ if (type === 'NPM') {
37
+ // generate dependencies
38
+ deps[name] = value;
39
+ } else {
40
+ // append other imported local files
41
+ files[name === entryFileName ? "App".concat(ext) : name] = {
42
+ content: value,
43
+ isBinary: false
44
+ };
45
+ }
46
+ }); // add react、react-dom dependency
47
+
48
+ (_deps$_react = deps[_react = 'react']) !== null && _deps$_react !== void 0 ? _deps$_react : deps[_react] = 'latest';
49
+ (_deps$_reactDom = deps[_reactDom = 'react-dom']) !== null && _deps$_reactDom !== void 0 ? _deps$_reactDom : deps[_reactDom] = deps.react; // append sandbox.config.json
50
+
51
+ files['sandbox.config.json'] = {
52
+ content: JSON.stringify({
53
+ template: isTSX ? 'create-react-app-typescript' : 'create-react-app'
54
+ }, null, 2),
55
+ isBinary: false
56
+ }; // append package.json
57
+
58
+ files['package.json'] = {
59
+ content: JSON.stringify({
60
+ name: opts.title,
61
+ description: opts.description || 'An auto-generated demo by dumi',
62
+ main: entryFileName,
63
+ dependencies: deps,
64
+ // add TypeScript dependency if required, must in devDeps to avoid csb compile error
65
+ devDependencies: isTSX ? {
66
+ typescript: '^4'
67
+ } : {}
68
+ }, null, 2),
69
+ isBinary: false
70
+ }; // append index.html
71
+
72
+ files['index.html'] = {
73
+ content: '<div style="margin: 16px;" id="root"></div>',
74
+ isBinary: false
75
+ }; // append entry file
76
+
77
+ files[entryFileName] = {
78
+ content: genReactRenderCode(deps.react),
79
+ isBinary: false
80
+ };
81
+ return getParameters({
82
+ files: files
83
+ });
84
+ }
85
+ /**
86
+ * use CodeSandbox.io
87
+ * @param data previewer opts
88
+ * @param opts the api that CodeSandbox calls when creating the demo
89
+ * @note return a open function for open demo on codesandbox.io
90
+ */
91
+
92
+
93
+ export var openCodeSandbox = function openCodeSandbox(data, opts) {
94
+ var _data$assets;
95
+
96
+ var form = document.createElement('form');
97
+ var input = document.createElement('input');
98
+ var CSBData = getCSBData(data);
99
+ form.method = 'POST';
100
+ form.target = '_blank';
101
+ form.style.display = 'none';
102
+ form.action = (opts === null || opts === void 0 ? void 0 : opts.api) || CSB_API_ENDPOINT;
103
+ form.appendChild(input);
104
+ form.setAttribute('data-demo', ((_data$assets = data.assets) === null || _data$assets === void 0 ? void 0 : _data$assets.id) || '');
105
+ input.name = 'parameters';
106
+ input.value = CSBData;
107
+ document.body.appendChild(form);
108
+ form.submit();
109
+ form.remove();
110
+ };
@@ -10,9 +10,9 @@ export interface IPreviewerProps {
10
10
  */
11
11
  description?: string;
12
12
  /**
13
- * file path of current demo
13
+ * filename of current demo
14
14
  */
15
- filePath?: string;
15
+ filename?: string;
16
16
  /**
17
17
  * use iframe to render demo
18
18
  */
@@ -79,6 +79,7 @@ export interface IRouteMeta {
79
79
  tocDepth?: number;
80
80
  };
81
81
  atomId?: string;
82
+ filename?: string;
82
83
  [key: string]: any;
83
84
  };
84
85
  toc: {
@@ -148,6 +149,7 @@ export interface IThemeConfig {
148
149
  children?: INavItem[];
149
150
  })[];
150
151
  sidebar?: Record<string, ISidebarGroup[]>;
152
+ footer?: string;
151
153
  [key: string]: any;
152
154
  }
153
155
  export declare type IRoutesById = Record<string, {
@@ -1,2 +1,8 @@
1
1
  import type { IRoutesById } from './types';
2
2
  export declare const useLocaleDocRoutes: () => IRoutesById;
3
+ /**
4
+ * 在 react 18 中需要新的 render 方式,这个函数用来处理不同的 jsx 模式。
5
+ * @param version react version
6
+ * @returns code string
7
+ */
8
+ export declare const genReactRenderCode: (version: string) => string;
@@ -41,4 +41,19 @@ export var useLocaleDocRoutes = function useLocaleDocRoutes() {
41
41
  localeDocRoutes = _useState2[0];
42
42
 
43
43
  return localeDocRoutes;
44
+ };
45
+ /**
46
+ * 在 react 18 中需要新的 render 方式,这个函数用来处理不同的 jsx 模式。
47
+ * @param version react version
48
+ * @returns code string
49
+ */
50
+
51
+ export var genReactRenderCode = function genReactRenderCode(version) {
52
+ var annotation = "/**\n * This is an auto-generated demo by dumi\n * if you think it is not working as expected,\n * please report the issue at\n * https://github.com/umijs/dumi/issues\n */";
53
+
54
+ if (version.startsWith('18.') || version === 'latest') {
55
+ return "".concat(annotation, "\n\nimport React from 'react';\nimport { createRoot } from \"react-dom/client\";\nimport App from \"./App\";\n\nconst rootElement = document.getElementById(\"root\");\nconst root = createRoot(rootElement);\n\nroot.render(<App />);");
56
+ }
57
+
58
+ return "".concat(annotation, "\n \nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\n \nReactDOM.render(\n <App />,\n document.getElementById('root'),\n);");
44
59
  };
@@ -1,5 +1,6 @@
1
1
  export declare const LOCAL_DUMI_DIR = ".dumi";
2
2
  export declare const LOCAL_THEME_DIR: string;
3
+ export declare const LOCAL_PAGES_DIR: string;
3
4
  export declare const THEME_PREFIX = "dumi-theme-";
4
5
  export declare const SP_ROUTE_PREFIX = "~";
5
6
  export declare const PICKED_PKG_FIELDS: {
package/dist/constants.js CHANGED
@@ -21,6 +21,7 @@ var constants_exports = {};
21
21
  __export(constants_exports, {
22
22
  CLIENT_DEPS: () => CLIENT_DEPS,
23
23
  LOCAL_DUMI_DIR: () => LOCAL_DUMI_DIR,
24
+ LOCAL_PAGES_DIR: () => LOCAL_PAGES_DIR,
24
25
  LOCAL_THEME_DIR: () => LOCAL_THEME_DIR,
25
26
  PICKED_PKG_FIELDS: () => PICKED_PKG_FIELDS,
26
27
  SP_ROUTE_PREFIX: () => SP_ROUTE_PREFIX,
@@ -29,6 +30,7 @@ __export(constants_exports, {
29
30
  module.exports = __toCommonJS(constants_exports);
30
31
  var LOCAL_DUMI_DIR = ".dumi";
31
32
  var LOCAL_THEME_DIR = `${LOCAL_DUMI_DIR}/theme`;
33
+ var LOCAL_PAGES_DIR = `${LOCAL_DUMI_DIR}/pages`;
32
34
  var THEME_PREFIX = "dumi-theme-";
33
35
  var SP_ROUTE_PREFIX = "~";
34
36
  var PICKED_PKG_FIELDS = {
@@ -57,6 +59,7 @@ var CLIENT_DEPS = [
57
59
  0 && (module.exports = {
58
60
  CLIENT_DEPS,
59
61
  LOCAL_DUMI_DIR,
62
+ LOCAL_PAGES_DIR,
60
63
  LOCAL_THEME_DIR,
61
64
  PICKED_PKG_FIELDS,
62
65
  SP_ROUTE_PREFIX,
@@ -30,7 +30,9 @@ var configPlugins_default = (api) => {
30
30
  atomDirs: [{ type: "component", dir: "src" }],
31
31
  codeBlockMode: "active"
32
32
  },
33
- themeConfig: {}
33
+ themeConfig: {
34
+ footer: `Copyright \xA9 ${new Date().getFullYear()} | Powered by <a href="https://d.umijs.org" target="_blank" rel="noreferrer">dumi</a>`
35
+ }
34
36
  };
35
37
  const schemas = (0, import_schema.getSchemas)();
36
38
  for (const key of Object.keys(schemas)) {
@@ -68,6 +68,12 @@ var derivative_default = (api) => {
68
68
  });
69
69
  }
70
70
  }
71
+ if (api.userConfig.conventionRoutes !== false) {
72
+ memo.conventionRoutes = {
73
+ base: import_path.default.join(api.cwd, import_constants.LOCAL_PAGES_DIR),
74
+ exclude: [/(\/|^)(\.|_\/)/]
75
+ };
76
+ }
71
77
  memo.hash = true;
72
78
  return memo;
73
79
  });
@@ -58,15 +58,8 @@ var routes_default = (api) => {
58
58
  { dir: "docs" }
59
59
  ]
60
60
  ].map(({ dir }) => import_path.default.join(api.cwd, dir, "**/*.md"));
61
- const pagesDir = import_path.default.join(api.cwd, ".dumi/pages");
62
61
  api.describe({ key: "dumi:routes" });
63
62
  api.addTmpGenerateWatcherPaths(() => extraWatchPaths);
64
- api.modifyConfig((memo) => {
65
- memo.conventionRoutes = {
66
- base: pagesDir
67
- };
68
- return memo;
69
- });
70
63
  api.modifyDefaultConfig((memo) => {
71
64
  if (api.userConfig.resolve) {
72
65
  const keys = ["docDirs", "atomDirs"];
@@ -117,7 +110,7 @@ var routes_default = (api) => {
117
110
  };
118
111
  }
119
112
  Object.entries(pages).forEach(([, route]) => {
120
- route.file = (0, import_plugin_utils.winPath)(import_path.default.resolve(pagesDir, route.file));
113
+ route.file = (0, import_plugin_utils.winPath)(import_path.default.resolve(api.config.conventionRoutes.base, route.file));
121
114
  flatRoute(route);
122
115
  routes[route.id] = route;
123
116
  });
@@ -125,7 +118,7 @@ var routes_default = (api) => {
125
118
  const base = import_path.default.join(api.cwd, dir);
126
119
  const dirRoutes = (0, import_core.getConventionRoutes)({
127
120
  base,
128
- exclude: [/.*(?<!md)$/]
121
+ exclude: [/.*(?<!md)$/, /(\/|^)(\.|_\/)/]
129
122
  });
130
123
  Object.entries(dirRoutes).forEach(([key, route]) => {
131
124
  route.id = `${dir}/${key}`;
@@ -87,16 +87,24 @@ function DumiMarkdownContent() {
87
87
  export default DumiMarkdownContent;`;
88
88
  }
89
89
  }
90
+ function getEmbedsCacheKey(embeds = []) {
91
+ return JSON.stringify(embeds.map((file) => `${file}:${import_fs.default.statSync(file).mtimeMs}`));
92
+ }
90
93
  var deferrer = {};
94
+ var embedsMapping = {};
91
95
  function mdLoader(content) {
92
96
  const opts = this.getOptions();
93
97
  const cb = this.async();
94
98
  const cache = (0, import_utils.getCache)("md-loader");
95
- const cacheKey = [
99
+ const baseCacheKey = [
96
100
  this.resourcePath,
97
101
  import_fs.default.statSync(this.resourcePath).mtimeMs,
98
102
  JSON.stringify(import_plugin_utils.lodash.omit(opts, ["mode", "builtins", "onResolveDemos"]))
99
103
  ].join(":");
104
+ const cacheKey = [
105
+ baseCacheKey,
106
+ getEmbedsCacheKey(embedsMapping[this.resourcePath])
107
+ ].join(":");
100
108
  const cacheRet = cache.getSync(cacheKey, "");
101
109
  if (cacheRet) {
102
110
  cb(null, emit.call(this, opts, cacheRet));
@@ -112,7 +120,12 @@ function mdLoader(content) {
112
120
  fileAbsPath: this.resourcePath
113
121
  });
114
122
  deferrer[cacheKey].then((ret) => {
115
- cache.setSync(cacheKey, ret);
123
+ const finalCacheKey = [
124
+ baseCacheKey,
125
+ getEmbedsCacheKey(ret.meta.embeds)
126
+ ].join(":");
127
+ embedsMapping[this.resourcePath] = ret.meta.embeds || [];
128
+ cache.setSync(finalCacheKey, ret);
116
129
  cb(null, emit.call(this, opts, ret));
117
130
  }).catch(cb);
118
131
  }
@@ -58,7 +58,7 @@ function getCodeLang(node, opts) {
58
58
  }
59
59
  function getCodeId(cwd, fileAbsPath, localId, atomId) {
60
60
  const prefix = atomId || (0, import_utils.getRoutePathFromFsPath)(import_path.default.relative(cwd, fileAbsPath)).replace(/\//g, "-");
61
- return [prefix, "demo", localId].filter(Boolean).join("-");
61
+ return [prefix.toLowerCase(), "demo", localId.toLowerCase()].filter(Boolean).join("-");
62
62
  }
63
63
  function tryMarkDemoNode(node, opts) {
64
64
  var _a, _b;
@@ -140,13 +140,13 @@ function rehypeDemo(opts) {
140
140
  let component = "";
141
141
  if (codeType === "external") {
142
142
  parseOpts.fileAbsPath = codeNode.properties.src;
143
- parseOpts.id = getCodeId(opts.cwd, opts.fileAbsPath, import_path.default.parse(parseOpts.fileAbsPath).name);
143
+ parseOpts.id = getCodeId(opts.cwd, opts.fileAbsPath, import_path.default.parse(parseOpts.fileAbsPath).name, vFile.data.frontmatter.atomId);
144
144
  component = `React.lazy(() => import('${(0, import_plugin_utils.winPath)(parseOpts.fileAbsPath)}?techStack=${techStack.name}'))`;
145
145
  codeNode.properties.title = codeValue || void 0;
146
- (_a2 = codeNode.properties).filePath ?? (_a2.filePath = (0, import_plugin_utils.winPath)(import_path.default.relative(opts.cwd, parseOpts.fileAbsPath)));
146
+ (_a2 = codeNode.properties).filename ?? (_a2.filename = (0, import_plugin_utils.winPath)(import_path.default.relative(opts.cwd, parseOpts.fileAbsPath)));
147
147
  } else {
148
148
  parseOpts.fileAbsPath = opts.fileAbsPath.replace(".md", ".tsx");
149
- parseOpts.id = getCodeId(opts.cwd, opts.fileAbsPath, String(index++));
149
+ parseOpts.id = getCodeId(opts.cwd, opts.fileAbsPath, String(index++), vFile.data.frontmatter.atomId);
150
150
  component = techStack.transformCode(codeValue, {
151
151
  type: "code-block",
152
152
  fileAbsPath: opts.fileAbsPath
@@ -52,6 +52,8 @@ function remarkMeta(opts) {
52
52
  const guessAtomId = getGuessAtomId(opts);
53
53
  vFile.data.frontmatter = {
54
54
  title: "",
55
+ toc: "menu",
56
+ filename: (0, import_plugin_utils.winPath)(import_path.default.relative(opts.cwd, opts.fileAbsPath)),
55
57
  ...guessAtomId && { atomId: guessAtomId }
56
58
  };
57
59
  visit(tree, "yaml", (node) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dumi",
3
- "version": "2.0.0-beta.16",
3
+ "version": "2.0.0-beta.17",
4
4
  "description": "Framework for developing UI components",
5
5
  "keywords": [],
6
6
  "license": "MIT",
@@ -12,7 +12,6 @@
12
12
  "dist",
13
13
  "compiled",
14
14
  "theme-default",
15
- "theme.d.ts",
16
15
  "index.d.ts"
17
16
  ],
18
17
  "scripts": {
@@ -59,7 +58,7 @@
59
58
  "@types/mdast": "^3.0.10",
60
59
  "@umijs/bundler-utils": "^4.0.27",
61
60
  "@umijs/core": "^4.0.27",
62
- "@umijs/utils": "^4.0.27",
61
+ "codesandbox": "^2.2.3",
63
62
  "deepmerge": "^4.2.2",
64
63
  "dumi-afx-deps": "^1.0.0-alpha.1",
65
64
  "dumi-assets-types": "2.0.0-alpha.0",
@@ -1,9 +1,11 @@
1
1
  import { useIntl, useOutlet, useRouteMeta } from 'dumi';
2
2
  import Content from 'dumi/theme/slots/Content';
3
3
  import Features from 'dumi/theme/slots/Features';
4
+ import Footer from 'dumi/theme/slots/Footer';
4
5
  import Header from 'dumi/theme/slots/Header';
5
6
  import Hero from 'dumi/theme/slots/Hero';
6
7
  import Sidebar from 'dumi/theme/slots/Sidebar';
8
+ import Toc from 'dumi/theme/slots/Toc';
7
9
  import React from 'react';
8
10
  import Helmet from 'react-helmet';
9
11
  import "./index.less";
@@ -34,7 +36,9 @@ var DocLayout = function DocLayout() {
34
36
  }), fm.keywords && /*#__PURE__*/React.createElement("meta", {
35
37
  property: "og:keywords",
36
38
  content: fm.keywords.join(',')
37
- })), /*#__PURE__*/React.createElement(Header, null), /*#__PURE__*/React.createElement(Hero, null), /*#__PURE__*/React.createElement(Features, null), /*#__PURE__*/React.createElement("main", null, /*#__PURE__*/React.createElement(Sidebar, null), /*#__PURE__*/React.createElement(Content, null, outlet)));
39
+ })), /*#__PURE__*/React.createElement(Header, null), /*#__PURE__*/React.createElement(Hero, null), /*#__PURE__*/React.createElement(Features, null), /*#__PURE__*/React.createElement("main", null, /*#__PURE__*/React.createElement(Sidebar, null), /*#__PURE__*/React.createElement(Content, null, outlet, /*#__PURE__*/React.createElement(Footer, null)), fm.toc === 'content' && /*#__PURE__*/React.createElement("div", {
40
+ className: "dumi-default-doc-layout-toc-wrapper"
41
+ }, /*#__PURE__*/React.createElement("h4", null, "TABLE OF CONTENTS"), /*#__PURE__*/React.createElement(Toc, null))));
38
42
  };
39
43
 
40
44
  export default DocLayout;
@@ -19,5 +19,23 @@ body {
19
19
  flex: 1;
20
20
  max-width: 100%;
21
21
  }
22
+
23
+ > .@{prefix}-doc-layout-toc-wrapper {
24
+ position: sticky;
25
+ top: @s-header-height + 30px;
26
+ width: @s-sidebar-width;
27
+ margin-left: 24px;
28
+ max-height: 80vh;
29
+ overflow: auto;
30
+ overscroll-behavior: contain;
31
+ -webkit-overflow-scrolling: touch;
32
+
33
+ > h4 {
34
+ margin: 0 0 8px;
35
+ color: @c-text-note;
36
+ font-size: 13px;
37
+ line-height: 1;
38
+ }
39
+ }
22
40
  }
23
41
  }
@@ -3,7 +3,7 @@
3
3
  "header.search.placeholder": "Type keywords...",
4
4
  "previewer.actions.code.expand": "Show Code",
5
5
  "previewer.actions.code.shrink": "Hide Code",
6
- "previewer.actions.codesandbox": "Open in CodeSandbox (Not implemented)",
6
+ "previewer.actions.codesandbox": "Open in CodeSandbox",
7
7
  "previewer.actions.codepen": "Open in CodePen (Not implemented)",
8
8
  "previewer.actions.separate": "Open in separate page",
9
9
  "404.title": "PAGE NOT FOUND",
@@ -3,7 +3,7 @@
3
3
  "header.search.placeholder": "输入关键字搜索...",
4
4
  "previewer.actions.code.expand": "展开代码",
5
5
  "previewer.actions.code.shrink": "收起代码",
6
- "previewer.actions.codesandbox": "在 CodeSandbox 中打开(未实现)",
6
+ "previewer.actions.codesandbox": "在 CodeSandbox 中打开",
7
7
  "previewer.actions.codepen": "在 CodePen 中打开(未实现)",
8
8
  "previewer.actions.separate": "在独立页面中打开",
9
9
  "404.title": "页面未找到",
@@ -45,13 +45,19 @@
45
45
 
46
46
  .@{prefix}-content {
47
47
  flex: 1;
48
+ min-width: 0;
48
49
  max-width: 100%;
50
+ box-sizing: border-box;
49
51
 
50
52
  &:not([data-no-sidebar]) {
51
- padding: @s-content-padding;
53
+ padding: @s-content-padding @s-content-padding 0;
52
54
  background-color: #fff;
53
55
  border-top-left-radius: 10px;
54
56
  border-top-right-radius: 10px;
55
57
  box-shadow: 0 8px 24px 0 rgb(0 0 0 / 5%);
56
58
  }
59
+
60
+ .@{prefix}-header + main > & {
61
+ min-height: calc(100vh - @s-header-height);
62
+ }
57
63
  }
@@ -11,7 +11,7 @@ var Features = function Features() {
11
11
  return Boolean((_frontmatter$features = frontmatter.features) === null || _frontmatter$features === void 0 ? void 0 : _frontmatter$features.length) ? /*#__PURE__*/React.createElement("div", {
12
12
  className: "dumi-default-features" // auto render 2 or 3 cols by feature count
13
13
  ,
14
- "data-cols": [2, 3].find(function (n) {
14
+ "data-cols": [3, 2].find(function (n) {
15
15
  return frontmatter.features.length % n === 0;
16
16
  }) || 3
17
17
  }, frontmatter.features.map(function (_ref) {
@@ -21,7 +21,11 @@ var Features = function Features() {
21
21
  return /*#__PURE__*/React.createElement("div", {
22
22
  key: title,
23
23
  className: "dumi-default-features-item"
24
- }, emoji && /*#__PURE__*/React.createElement("i", null, emoji), title && /*#__PURE__*/React.createElement("h3", null, title), description && /*#__PURE__*/React.createElement("p", null, description));
24
+ }, emoji && /*#__PURE__*/React.createElement("i", null, emoji), title && /*#__PURE__*/React.createElement("h3", null, title), description && /*#__PURE__*/React.createElement("p", {
25
+ dangerouslySetInnerHTML: {
26
+ __html: description
27
+ }
28
+ }));
25
29
  })) : null;
26
30
  };
27
31
 
@@ -36,7 +36,7 @@
36
36
  margin: 4px 0;
37
37
  color: #2c4262;
38
38
  font-weight: normal;
39
- font-size: 22px;
39
+ font-size: 20px;
40
40
  }
41
41
 
42
42
  > p {
@@ -44,6 +44,14 @@
44
44
  color: #61728a;
45
45
  font-size: 16px;
46
46
  line-height: 1.475;
47
+
48
+ a {
49
+ color: @c-primary;
50
+
51
+ &:not(:hover) {
52
+ text-decoration: none;
53
+ }
54
+ }
47
55
  }
48
56
  }
49
57
  }
@@ -0,0 +1,4 @@
1
+ import { type FC } from 'react';
2
+ import './index.less';
3
+ declare const Footer: FC;
4
+ export default Footer;
@@ -0,0 +1,18 @@
1
+ import { useSiteData } from 'dumi';
2
+ import React from 'react';
3
+ import "./index.less";
4
+
5
+ var Footer = function Footer() {
6
+ var _useSiteData = useSiteData(),
7
+ themeConfig = _useSiteData.themeConfig;
8
+
9
+ if (!themeConfig.footer) return null;
10
+ return /*#__PURE__*/React.createElement("div", {
11
+ className: "dumi-default-footer",
12
+ dangerouslySetInnerHTML: {
13
+ __html: themeConfig.footer
14
+ }
15
+ });
16
+ };
17
+
18
+ export default Footer;
@@ -0,0 +1,19 @@
1
+ @import (reference) '../../styles/variables.less';
2
+
3
+ .@{prefix}-footer {
4
+ margin-top: @s-content-padding;
5
+ border-top: 1px solid @c-border-light;
6
+ color: @c-text-note;
7
+ font-size: 15px;
8
+ line-height: 26px;
9
+ text-align: center;
10
+ padding: @s-content-padding * 0.6 0;
11
+
12
+ a {
13
+ color: @c-primary;
14
+
15
+ &:not(:hover) {
16
+ text-decoration: none;
17
+ }
18
+ }
19
+ }
@@ -9,6 +9,10 @@
9
9
  text-align: center;
10
10
  box-sizing: border-box;
11
11
 
12
+ + * {
13
+ position: relative;
14
+ }
15
+
12
16
  &::before {
13
17
  content: '';
14
18
  position: absolute;
@@ -26,7 +30,7 @@
26
30
  > p {
27
31
  margin: 32px;
28
32
  color: #4e6789;
29
- font-size: 24px;
33
+ font-size: 20px;
30
34
  line-height: 1.6;
31
35
  }
32
36
 
@@ -37,12 +41,12 @@
37
41
 
38
42
  > a {
39
43
  display: inline-block;
40
- height: 56px;
41
- font-size: 20px;
42
- line-height: 56px;
44
+ height: 52px;
45
+ font-size: 18px;
46
+ line-height: 52px;
43
47
  text-decoration: none;
44
48
  min-width: 168px;
45
- border-radius: 28px;
49
+ border-radius: 26px;
46
50
  box-sizing: border-box;
47
51
  transition: opacity 0.2s;
48
52
 
@@ -12,7 +12,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
12
12
 
13
13
  import { ReactComponent as IconCodeSandbox } from '@ant-design/icons-svg/inline-svg/outlined/code-sandbox.svg';
14
14
  import { ReactComponent as IconCodePen } from '@ant-design/icons-svg/inline-svg/outlined/codepen.svg';
15
- import { useIntl } from 'dumi';
15
+ import { openCodeSandbox, useIntl } from 'dumi';
16
16
  import SourceCode from 'dumi/theme/builtins/SourceCode';
17
17
  import PreviewerActionsExtra from 'dumi/theme/slots/PreviewerActionsExtra';
18
18
  import Tabs from 'rc-tabs';
@@ -75,7 +75,10 @@ var PreviewerActions = function PreviewerActions(props) {
75
75
  type: "button",
76
76
  "data-dumi-tooltip": intl.formatMessage({
77
77
  id: 'previewer.actions.codesandbox'
78
- })
78
+ }),
79
+ onClick: function onClick() {
80
+ return openCodeSandbox(props);
81
+ }
79
82
  }, /*#__PURE__*/React.createElement(IconCodeSandbox, null)), !((_props$disabledAction2 = props.disabledActions) !== null && _props$disabledAction2 !== void 0 && _props$disabledAction2.includes('CODEPEN')) && /*#__PURE__*/React.createElement("button", {
80
83
  className: "dumi-default-previewer-action-btn",
81
84
  type: "button",
@@ -1,4 +1,4 @@
1
- import { NavLink, useLocation, useSidebarData } from 'dumi';
1
+ import { NavLink, useLocation, useRouteMeta, useSidebarData } from 'dumi';
2
2
  import Toc from 'dumi/theme/slots/Toc';
3
3
  import React from 'react';
4
4
  import "./index.less";
@@ -7,6 +7,7 @@ var Sidebar = function Sidebar() {
7
7
  var _useLocation = useLocation(),
8
8
  pathname = _useLocation.pathname;
9
9
 
10
+ var meta = useRouteMeta();
10
11
  var sidebar = useSidebarData();
11
12
  return sidebar && /*#__PURE__*/React.createElement("div", {
12
13
  className: "dumi-default-sidebar"
@@ -21,7 +22,7 @@ var Sidebar = function Sidebar() {
21
22
  to: child.link,
22
23
  title: child.title,
23
24
  end: true
24
- }, child.title), child.link === pathname && /*#__PURE__*/React.createElement(Toc, null));
25
+ }, child.title), child.link === pathname && meta.frontmatter.toc === 'menu' && /*#__PURE__*/React.createElement(Toc, null));
25
26
  }));
26
27
  }));
27
28
  };
@@ -23,6 +23,10 @@
23
23
  overflow: hidden;
24
24
  text-overflow: ellipsis;
25
25
 
26
+ &:hover {
27
+ color: @c-text;
28
+ }
29
+
26
30
  &.active {
27
31
  margin-left: -1px;
28
32
  color: @c-text;
package/theme.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from './dist/client/theme-api';