dumi 2.2.0-rc.1 → 2.2.1-alpha.0

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.
@@ -20,7 +20,7 @@ var DemoErrorBoundary = function DemoErrorBoundary(props) {
20
20
  }, props.children);
21
21
  };
22
22
 
23
- export var DumiDemo = function DumiDemo(props) {
23
+ export var DumiDemo = /*#__PURE__*/React.memo(function (props) {
24
24
  var _useSiteData = useSiteData(),
25
25
  demos = _useSiteData.demos,
26
26
  historyType = _useSiteData.historyType;
@@ -45,4 +45,7 @@ export var DumiDemo = function DumiDemo(props) {
45
45
  props.previewerProps.demoUrl || // when use hash route, browser can automatically handle relative paths starting with #
46
46
  "".concat(isHashRoute ? "#" : '').concat(basename).concat(SP_ROUTE_PREFIX, "demos/").concat(props.demo.id)
47
47
  }, props.previewerProps), props.previewerProps.iframe ? null : /*#__PURE__*/React.createElement(DemoErrorBoundary, null, /*#__PURE__*/createElement(component)));
48
- };
48
+ }, function (prev, next) {
49
+ // compare length for performance
50
+ return JSON.stringify(prev).length === JSON.stringify(next).length;
51
+ });
@@ -14,8 +14,13 @@ interface ISiteContext {
14
14
  components: Record<string, AtomComponentAsset>;
15
15
  locales: ILocalesConfig;
16
16
  themeConfig: IThemeConfig;
17
+ hostname?: string;
17
18
  loading: boolean;
18
19
  setLoading: (status: boolean) => void;
20
+ /**
21
+ * private field, do not use it in your code
22
+ */
23
+ _2_level_nav_available: boolean;
19
24
  }
20
25
  export declare const SiteContext: import("react").Context<ISiteContext>;
21
26
  export declare const useSiteData: () => ISiteContext;
@@ -8,7 +8,8 @@ export var SiteContext = /*#__PURE__*/createContext({
8
8
  locales: [],
9
9
  themeConfig: {},
10
10
  loading: false,
11
- setLoading: function setLoading() {}
11
+ setLoading: function setLoading() {},
12
+ _2_level_nav_available: true
12
13
  });
13
14
  export var useSiteData = function useSiteData() {
14
15
  return useContext(SiteContext);
@@ -59,7 +59,7 @@ export interface IRouteMeta {
59
59
  nav?: string | {
60
60
  title?: string;
61
61
  order?: number;
62
- parent?: Omit<IRouteMeta['frontmatter']['nav'], 'parent'>;
62
+ second?: Omit<IRouteMeta['frontmatter']['nav'], 'second'>;
63
63
  };
64
64
  group?: string | {
65
65
  title?: string;
@@ -103,7 +103,7 @@ export interface IRouteMeta {
103
103
  depth: number;
104
104
  title: string;
105
105
  /**
106
- * private field, will be removed in the future
106
+ * private field, do not use it in your code
107
107
  */
108
108
  _debug_demo?: boolean;
109
109
  }[];
@@ -136,7 +136,7 @@ export interface IRouteMeta {
136
136
  };
137
137
  }[];
138
138
  /**
139
- * private field, will be removed in the future
139
+ * private field, do not use it in your code
140
140
  */
141
141
  _atom_route?: boolean;
142
142
  }
@@ -203,8 +203,8 @@ export interface IThemeConfig {
203
203
  switch: boolean;
204
204
  };
205
205
  nprogress?: boolean;
206
- socialLinks: {
207
- [key in SocialTypes]: string;
206
+ socialLinks?: {
207
+ [key in SocialTypes]?: string;
208
208
  };
209
209
  [key: string]: any;
210
210
  }
@@ -47,7 +47,8 @@ export var useNavData = function useNavData() {
47
47
  var routes = useLocaleDocRoutes();
48
48
 
49
49
  var _useSiteData = useSiteData(),
50
- themeConfig = _useSiteData.themeConfig;
50
+ themeConfig = _useSiteData.themeConfig,
51
+ is2LevelNav = _useSiteData._2_level_nav_available;
51
52
 
52
53
  var sidebar = useFullSidebarData();
53
54
  var sidebarDataComparer = useRouteDataComparer();
@@ -91,36 +92,36 @@ export var useNavData = function useNavData() {
91
92
  parentPath = _ref8[1],
92
93
  restPath = _ref8[2];
93
94
 
94
- var isNestedNav = Boolean(restPath);
95
+ var isNestedNav = Boolean(restPath) && is2LevelNav;
95
96
 
96
97
  var _Object$values$reduce = Object.values(routes).reduce(function (ret, route) {
97
98
  // find routes which within the nav path
98
99
  if (route.path.startsWith(link.slice(1))) {
99
100
  pickRouteSortMeta(ret[0], 'nav', route.meta.frontmatter); // generate parent meta for nested nav
100
101
 
101
- if (isNestedNav) pickRouteSortMeta(ret[1], 'nav.parent', route.meta.frontmatter);
102
+ if (isNestedNav) pickRouteSortMeta(ret[1], 'nav.second', route.meta.frontmatter);
102
103
  }
103
104
 
104
105
  return ret;
105
106
  }, [{}, {}]),
106
107
  _Object$values$reduce2 = _slicedToArray(_Object$values$reduce, 2),
107
- rootMeta = _Object$values$reduce2[0],
108
- parentMeta = _Object$values$reduce2[1];
108
+ firstMeta = _Object$values$reduce2[0],
109
+ secondMeta = _Object$values$reduce2[1];
109
110
 
110
111
  if (isNestedNav) {
111
- var _parentMeta$title, _ret$parentPath, _parent$children;
112
+ var _firstMeta$title, _ret$parentPath, _second$children;
112
113
 
113
- // fallback to use parent path as title
114
- (_parentMeta$title = parentMeta.title) !== null && _parentMeta$title !== void 0 ? _parentMeta$title : parentMeta.title = parentPath.slice(1).replace(/^[a-z]/, function (s) {
114
+ // fallback to use parent path as 1-level nav title
115
+ (_firstMeta$title = firstMeta.title) !== null && _firstMeta$title !== void 0 ? _firstMeta$title : firstMeta.title = parentPath.slice(1).replace(/^[a-z]/, function (s) {
115
116
  return s.toUpperCase();
116
117
  }); // handle nested nav item as parent children
117
118
 
118
- var parent = (_ret$parentPath = ret[parentPath]) !== null && _ret$parentPath !== void 0 ? _ret$parentPath : ret[parentPath] = genNavItem(parentMeta, groups, parentPath);
119
- (_parent$children = parent.children) !== null && _parent$children !== void 0 ? _parent$children : parent.children = [];
120
- ret[parentPath].children.push(genNavItem(rootMeta, groups, link, groups[0].children[0].link));
119
+ var second = (_ret$parentPath = ret[parentPath]) !== null && _ret$parentPath !== void 0 ? _ret$parentPath : ret[parentPath] = genNavItem(firstMeta, groups, parentPath);
120
+ (_second$children = second.children) !== null && _second$children !== void 0 ? _second$children : second.children = [];
121
+ ret[parentPath].children.push(genNavItem(secondMeta, groups, link, groups[0].children[0].link));
121
122
  } else {
122
123
  // handle root nav item
123
- ret[link] = genNavItem(rootMeta, groups, link, groups[0].children[0].link);
124
+ ret[link] = genNavItem(firstMeta, groups, link, groups[0].children[0].link);
124
125
  }
125
126
 
126
127
  return ret;
@@ -39,15 +39,18 @@ var getLocaleClearPath = function getLocaleClearPath(routePath, locale) {
39
39
  */
40
40
 
41
41
 
42
- function getRouteParentPath(path, meta) {
42
+ function getRouteParentPath(path, _ref) {
43
43
  var _meta$frontmatter$fil;
44
44
 
45
- var isIndexRoute = ((_meta$frontmatter$fil = meta.frontmatter.filename) === null || _meta$frontmatter$fil === void 0 ? void 0 : _meta$frontmatter$fil.endsWith('index.md')) && !meta._atom_route;
46
- var paths = path.split('/');
45
+ var meta = _ref.meta,
46
+ is2LevelNav = _ref.is2LevelNav;
47
+ var isIndexDocRoute = ((_meta$frontmatter$fil = meta.frontmatter.filename) === null || _meta$frontmatter$fil === void 0 ? void 0 : _meta$frontmatter$fil.endsWith('index.md')) && !meta._atom_route && is2LevelNav;
48
+ var paths = path.split('/') // strip end slash
49
+ .filter(Boolean);
47
50
  var sliceEnd = Math.min(Math.max( // increase 1 level if route file is index.md
48
- isIndexRoute ? paths.length : paths.length - 1, // least 1-level
49
- 1), // up to 2-level
50
- 2);
51
+ isIndexDocRoute ? paths.length : paths.length - 1, // least 1-level
52
+ 1), // up to 2-level when use conventional 2-level nav
53
+ is2LevelNav ? 2 : Infinity);
51
54
  return paths.slice(0, sliceEnd).join('/');
52
55
  }
53
56
  /**
@@ -60,7 +63,8 @@ export var useFullSidebarData = function useFullSidebarData() {
60
63
  var routes = useLocaleDocRoutes();
61
64
 
62
65
  var _useSiteData = useSiteData(),
63
- themeConfig = _useSiteData.themeConfig;
66
+ themeConfig = _useSiteData.themeConfig,
67
+ is2LevelNav = _useSiteData._2_level_nav_available;
64
68
 
65
69
  var sidebarDataComparer = useRouteDataComparer();
66
70
 
@@ -82,7 +86,10 @@ export var useFullSidebarData = function useFullSidebarData() {
82
86
  // a/b => /a/b (if route file is a/b/index.md)
83
87
  // a/b/c => /a/b
84
88
  var parentPath = "/".concat(route.path.replace(clearPath, function (s) {
85
- return getRouteParentPath(s, route.meta);
89
+ return getRouteParentPath(s, {
90
+ is2LevelNav: is2LevelNav,
91
+ meta: route.meta
92
+ });
86
93
  }));
87
94
 
88
95
  var _pickRouteSortMeta = pickRouteSortMeta({
@@ -109,10 +116,10 @@ export var useFullSidebarData = function useFullSidebarData() {
109
116
  return ret;
110
117
  }, {}); // destruct sidebar data into sidebar config
111
118
 
112
- var sidebarConfig = Object.entries(data).reduce(function (ret, _ref) {
113
- var _ref2 = _slicedToArray(_ref, 2),
114
- navPath = _ref2[0],
115
- groups = _ref2[1];
119
+ var sidebarConfig = Object.entries(data).reduce(function (ret, _ref2) {
120
+ var _ref3 = _slicedToArray(_ref2, 2),
121
+ navPath = _ref3[0],
122
+ groups = _ref3[1];
116
123
 
117
124
  ret[navPath] = Object.values(groups).sort(sidebarDataComparer); // sort group children by order or title
118
125
 
@@ -181,10 +188,10 @@ export var useTreeSidebarData = function useTreeSidebarData() {
181
188
  var data = Object.entries(original) // match from the deepest level
182
189
  .sort(function (a, b) {
183
190
  return b[0].split('/').length - a[0].split('/').length;
184
- }).reduce(function (ret, _ref3) {
185
- var _ref4 = _slicedToArray(_ref3, 2),
186
- path = _ref4[0],
187
- data = _ref4[1];
191
+ }).reduce(function (ret, _ref4) {
192
+ var _ref5 = _slicedToArray(_ref4, 2),
193
+ path = _ref5[0],
194
+ data = _ref5[1];
188
195
 
189
196
  var parent = path.replace(/\/[^/]+$/, '');
190
197
 
@@ -237,6 +244,9 @@ export var useSidebarData = function useSidebarData() {
237
244
  var locale = useLocale();
238
245
  var sidebar = useFullSidebarData();
239
246
 
247
+ var _useSiteData2 = useSiteData(),
248
+ is2LevelNav = _useSiteData2._2_level_nav_available;
249
+
240
250
  var _useLocation = useLocation(),
241
251
  pathname = _useLocation.pathname;
242
252
 
@@ -249,7 +259,10 @@ export var useSidebarData = function useSidebarData() {
249
259
  // /en-US/a/b/ => /en-US/a (also strip trailing /)
250
260
 
251
261
  var parentPath = clearPath ? pathname.replace(clearPath, function (s) {
252
- return getRouteParentPath(s, meta);
262
+ return getRouteParentPath(s, {
263
+ is2LevelNav: is2LevelNav,
264
+ meta: meta
265
+ });
253
266
  }) : pathname;
254
267
  return parentPath ? sidebar[parentPath] : [];
255
268
  };
@@ -1,4 +1,4 @@
1
- import { useLayoutEffect } from 'react';
1
+ import { useEffect } from 'react';
2
2
  import type { ILocale, INav, INavItem, IRouteMeta, IRoutesById, IUserNavValue } from './types';
3
3
  export declare const useLocaleDocRoutes: () => IRoutesById;
4
4
  /**
@@ -7,7 +7,7 @@ export declare const useLocaleDocRoutes: () => IRoutesById;
7
7
  * @returns code string
8
8
  */
9
9
  export declare const genReactRenderCode: (version: string) => string;
10
- export declare const useIsomorphicLayoutEffect: typeof useLayoutEffect;
10
+ export declare const useIsomorphicLayoutEffect: typeof useEffect;
11
11
  /**
12
12
  * common comparer for sidebar/nav items
13
13
  */
@@ -20,5 +20,5 @@ export declare const useRouteDataComparer: <T extends {
20
20
  /**
21
21
  * common util for pick meta to sort sidebar/nav items
22
22
  */
23
- export declare const pickRouteSortMeta: (original: Partial<Pick<INavItem, 'order' | 'title'>>, field: 'nav' | 'nav.parent' | 'group', fm: IRouteMeta['frontmatter']) => Partial<Pick<INavItem, "title" | "order">>;
23
+ export declare const pickRouteSortMeta: (original: Partial<Pick<INavItem, 'order' | 'title'>>, field: 'nav' | 'nav.second' | 'group', fm: IRouteMeta['frontmatter']) => Partial<Pick<INavItem, "title" | "order">>;
24
24
  export declare function getLocaleNav(nav: IUserNavValue | INav, locale: ILocale): import("./types").IUserNavItems;
@@ -83,7 +83,7 @@ export var useRouteDataComparer = function useRouteDataComparer() {
83
83
  export var pickRouteSortMeta = function pickRouteSortMeta(original, field, fm) {
84
84
  var _sub$order;
85
85
 
86
- var sub = field === 'nav.parent' ? _typeof(fm.nav) === 'object' ? fm.nav.parent : {} : fm[field];
86
+ var sub = field === 'nav.second' ? _typeof(fm.nav) === 'object' ? fm.nav.second : {} : fm[field];
87
87
 
88
88
  switch (_typeof(sub)) {
89
89
  case 'object':
@@ -15,3 +15,4 @@ export declare const PICKED_PKG_FIELDS: {
15
15
  authors: string;
16
16
  };
17
17
  export declare const USELESS_TMP_FILES: string[];
18
+ export declare const VERSION_2_LEVEL_NAV = "^2.2.0";
package/dist/constants.js CHANGED
@@ -27,7 +27,8 @@ __export(constants_exports, {
27
27
  PREFERS_COLOR_LS_KEY: () => PREFERS_COLOR_LS_KEY,
28
28
  SP_ROUTE_PREFIX: () => SP_ROUTE_PREFIX,
29
29
  THEME_PREFIX: () => THEME_PREFIX,
30
- USELESS_TMP_FILES: () => USELESS_TMP_FILES
30
+ USELESS_TMP_FILES: () => USELESS_TMP_FILES,
31
+ VERSION_2_LEVEL_NAV: () => VERSION_2_LEVEL_NAV
31
32
  });
32
33
  module.exports = __toCommonJS(constants_exports);
33
34
  var LOCAL_DUMI_DIR = ".dumi";
@@ -47,6 +48,7 @@ var PICKED_PKG_FIELDS = {
47
48
  authors: ""
48
49
  };
49
50
  var USELESS_TMP_FILES = ["tsconfig.json", "typings.d.ts"];
51
+ var VERSION_2_LEVEL_NAV = "^2.2.0";
50
52
  // Annotate the CommonJS export names for ESM import in node:
51
53
  0 && (module.exports = {
52
54
  LOCAL_DUMI_DIR,
@@ -57,5 +59,6 @@ var USELESS_TMP_FILES = ["tsconfig.json", "typings.d.ts"];
57
59
  PREFERS_COLOR_LS_KEY,
58
60
  SP_ROUTE_PREFIX,
59
61
  THEME_PREFIX,
60
- USELESS_TMP_FILES
62
+ USELESS_TMP_FILES,
63
+ VERSION_2_LEVEL_NAV
61
64
  });
@@ -50,7 +50,7 @@ var sitemap_default = (api) => {
50
50
  const writeStream = import_fs.default.createWriteStream(import_path.default.join(api.paths.absOutputPath, "sitemap.xml"));
51
51
  smis.pipe(writeStream);
52
52
  Object.values(api.appData.routes).forEach((route) => {
53
- if (!exclude.includes(route.path) && ![":", "*"].some((char) => route.path.includes(char))) {
53
+ if (!exclude.includes(route.path) && ![":", "*"].some((char) => route.path.includes(char)) && route.path !== "" && !route.isLayout) {
54
54
  smis.write({ url: route.path });
55
55
  }
56
56
  });
@@ -34,13 +34,17 @@ var import_derivative = require("../derivative");
34
34
  var import_loader = __toESM(require("./loader"));
35
35
  var DEFAULT_THEME_PATH = import_path.default.join(__dirname, "../../../theme-default");
36
36
  function getPkgThemeName(api) {
37
+ if (process.env.DUMI_THEME) {
38
+ const themePkg = require(import_path.default.join(process.env.DUMI_THEME, "package.json"));
39
+ return themePkg.name;
40
+ }
37
41
  const validDeps = Object.assign({}, api.pkg.dependencies, api.pkg.devDependencies);
38
42
  const pkgThemeName = Object.keys(validDeps).find((pkg) => pkg.split("/").pop().startsWith(import_constants.THEME_PREFIX));
39
43
  return pkgThemeName;
40
44
  }
41
45
  function getPkgThemePath(api) {
42
46
  const pkgThemeName = getPkgThemeName(api);
43
- return pkgThemeName && import_path.default.dirname(import_plugin_utils.resolve.sync(`${pkgThemeName}/package.json`, {
47
+ return process.env.DUMI_THEME || pkgThemeName && import_path.default.dirname(import_plugin_utils.resolve.sync(`${process.env.DUMI_THEME || pkgThemeName}/package.json`, {
44
48
  basedir: api.cwd,
45
49
  preserveSymlinks: true
46
50
  }));
@@ -51,6 +55,13 @@ function getModuleExports(modulePath) {
51
55
  content: import_fs.default.readFileSync(modulePath, "utf-8")
52
56
  })[1];
53
57
  }
58
+ function checkMinor2ByPkg(pkg) {
59
+ var _a, _b, _c;
60
+ if ((_a = pkg.name) == null ? void 0 : _a.startsWith("@examples/"))
61
+ return true;
62
+ const ver = ((_b = pkg.peerDependencies) == null ? void 0 : _b.dumi) || ((_c = pkg.devDependencies) == null ? void 0 : _c.dumi) || "^2.0.0";
63
+ return import_plugin_utils.semver.subset(ver, import_constants.VERSION_2_LEVEL_NAV);
64
+ }
54
65
  var theme_default = (api) => {
55
66
  const defaultThemeData = (0, import_loader.default)(DEFAULT_THEME_PATH);
56
67
  const pkgThemePath = getPkgThemePath(api);
@@ -105,6 +116,13 @@ var theme_default = (api) => {
105
116
  return memo;
106
117
  }
107
118
  });
119
+ api.modifyAppData((memo) => {
120
+ memo._2LevelNavAvailable = checkMinor2ByPkg(api.pkg);
121
+ if (pkgThemePath && !memo._2LevelNavAvailable) {
122
+ memo._2LevelNavAvailable = checkMinor2ByPkg(require(import_path.default.join(pkgThemePath, "package.json")));
123
+ }
124
+ return memo;
125
+ });
108
126
  api.modifyConfig((memo) => {
109
127
  if (localThemeData) {
110
128
  themeMapKeys.forEach((key) => {
@@ -137,7 +155,7 @@ var theme_default = (api) => {
137
155
  return memo;
138
156
  });
139
157
  api.onGenerateFiles(() => {
140
- var _a, _b, _c, _d;
158
+ var _a, _b, _c, _d, _e;
141
159
  themeMapKeys.forEach((key) => {
142
160
  Object.values(originalThemeData[key] || {}).forEach((item) => {
143
161
  if (item.source === "dumi")
@@ -214,14 +232,16 @@ export default function DumiContextWrapper() {
214
232
  locales,
215
233
  loading,
216
234
  setLoading,
235
+ hostname: ${JSON.stringify((_b = api.config.sitemap) == null ? void 0 : _b.hostname)},
217
236
  themeConfig: ${JSON.stringify(Object.assign(import_plugin_utils.lodash.pick(api.config, "logo", "description", "title"), api.config.themeConfig))},
237
+ _2_level_nav_available: ${api.appData._2LevelNavAvailable},
218
238
  }}>
219
239
  {outlet}
220
240
  </SiteContext.Provider>
221
241
  );
222
242
  }`
223
243
  });
224
- const primaryColor = typeof ((_b = api.config) == null ? void 0 : _b.theme) === "object" ? (_d = (_c = api.config) == null ? void 0 : _c.theme) == null ? void 0 : _d["@c-primary"] : "#1677ff";
244
+ const primaryColor = typeof ((_c = api.config) == null ? void 0 : _c.theme) === "object" ? (_e = (_d = api.config) == null ? void 0 : _d.theme) == null ? void 0 : _e["@c-primary"] : "#1677ff";
225
245
  api.writeTmpFile({
226
246
  noPluginDir: true,
227
247
  path: "dumi/theme/nprogress.css",
@@ -172,7 +172,7 @@ function rehypeDemo(opts) {
172
172
  parseOpts.fileAbsPath = (0, import_plugin_utils.winPath)(codeNode.properties.src);
173
173
  let localId = ((_b = codeNode.properties) == null ? void 0 : _b.id) ?? import_path.default.parse(parseOpts.fileAbsPath.replace(/\/index\.(j|t)sx?$/, "")).name;
174
174
  parseOpts.id = getCodeId(opts.cwd, opts.fileAbsPath, localId, vFile.data.frontmatter.atomId);
175
- component = `React.lazy(() => import( /* webpackChunkName: "${chunkName}" */ '${(0, import_plugin_utils.winPath)(parseOpts.fileAbsPath)}?techStack=${techStack.name}'))`;
175
+ component = `React.memo(React.lazy(() => import( /* webpackChunkName: "${chunkName}" */ '${(0, import_plugin_utils.winPath)(parseOpts.fileAbsPath)}?techStack=${techStack.name}')))`;
176
176
  if (codeValue)
177
177
  codeNode.properties.title = codeValue;
178
178
  (_c = codeNode.properties).filename ?? (_c.filename = (0, import_plugin_utils.winPath)(import_path.default.relative(opts.cwd, parseOpts.fileAbsPath)));
@@ -58,9 +58,9 @@ var ReactTechStack = class {
58
58
  type: "es6"
59
59
  }
60
60
  });
61
- return `React.lazy(async () => {
61
+ return `React.memo(React.lazy(async () => {
62
62
  ${code}
63
- })`;
63
+ }))`;
64
64
  }
65
65
  return raw;
66
66
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dumi",
3
- "version": "2.2.0-rc.1",
3
+ "version": "2.2.1-alpha.0",
4
4
  "description": "📖 Documentation Generator of React Component",
5
5
  "keywords": [
6
6
  "generator",
@@ -29,10 +29,12 @@ var DocLayout = function DocLayout() {
29
29
  var sidebar = useSidebarData();
30
30
 
31
31
  var _useLocation = useLocation(),
32
- hash = _useLocation.hash;
32
+ hash = _useLocation.hash,
33
+ pathname = _useLocation.pathname;
33
34
 
34
35
  var _useSiteData = useSiteData(),
35
- loading = _useSiteData.loading;
36
+ loading = _useSiteData.loading,
37
+ hostname = _useSiteData.hostname;
36
38
 
37
39
  var _useState = useState(false),
38
40
  _useState2 = _slicedToArray(_useState, 2),
@@ -80,9 +82,15 @@ var DocLayout = function DocLayout() {
80
82
  }), fm.keywords && /*#__PURE__*/React.createElement("meta", {
81
83
  name: "keywords",
82
84
  content: fm.keywords.join(',')
83
- }), fm.keywords && /*#__PURE__*/React.createElement("meta", {
84
- property: "og:keywords",
85
- content: fm.keywords.join(',')
85
+ }), fm.keywords && fm.keywords.map(function (keyword) {
86
+ return /*#__PURE__*/React.createElement("meta", {
87
+ key: keyword,
88
+ property: "article:tag",
89
+ content: keyword
90
+ });
91
+ }), hostname && /*#__PURE__*/React.createElement("link", {
92
+ rel: "canonical",
93
+ href: hostname + pathname
86
94
  })), /*#__PURE__*/React.createElement(Header, null), /*#__PURE__*/React.createElement(Hero, null), /*#__PURE__*/React.createElement(Features, null), showSidebar && /*#__PURE__*/React.createElement("div", {
87
95
  className: "dumi-default-doc-layout-mobile-bar"
88
96
  }, /*#__PURE__*/React.createElement("button", {
@@ -10,11 +10,11 @@ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Sy
10
10
 
11
11
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
12
12
 
13
- import HeaderExtra from "../HeadeExtra";
14
13
  import { ReactComponent as IconClose } from '@ant-design/icons-svg/inline-svg/outlined/close.svg';
15
14
  import { ReactComponent as IconMenu } from '@ant-design/icons-svg/inline-svg/outlined/menu.svg';
16
15
  import { useRouteMeta, useSiteData } from 'dumi';
17
16
  import ColorSwitch from 'dumi/theme/slots/ColorSwitch';
17
+ import HeaderExtra from 'dumi/theme/slots/HeaderExtra';
18
18
  import LangSwitch from 'dumi/theme/slots/LangSwitch';
19
19
  import Logo from 'dumi/theme/slots/Logo';
20
20
  import Navbar from 'dumi/theme/slots/Navbar';
@@ -36,6 +36,7 @@
36
36
  line-height: 1;
37
37
  border: 0;
38
38
  background-color: transparent;
39
+ cursor: pointer;
39
40
 
40
41
  @{dark-selector} & {
41
42
  color: @c-text-secondary-dark;
@@ -75,7 +75,7 @@ var NavbarContent = function NavbarContent(_ref2) {
75
75
  var data = _ref2.data;
76
76
  return /*#__PURE__*/React.createElement(React.Fragment, null, data.map(function (item) {
77
77
  return /*#__PURE__*/React.createElement("li", {
78
- key: item.activePath
78
+ key: item.activePath || item.link || item.title
79
79
  }, item.link && /^(\w+:)\/\/|^(mailto|tel):/.test(item.link) ? /*#__PURE__*/React.createElement("a", {
80
80
  href: item.link,
81
81
  target: "_blank",
@@ -78,8 +78,10 @@
78
78
  }
79
79
  }
80
80
 
81
- &[data-collapsed] > svg {
82
- transform: rotate(180deg);
81
+ @media @mobile {
82
+ &[data-collapsed] > svg {
83
+ transform: rotate(180deg);
84
+ }
83
85
  }
84
86
 
85
87
  @media @desktop {
@@ -122,7 +124,7 @@
122
124
  padding: 0 18px;
123
125
  color: @c-text-secondary;
124
126
  font-size: 15px;
125
- line-height: 1.5;
127
+ line-height: 1.6;
126
128
  text-align: left;
127
129
 
128
130
  @media @mobile {
@@ -1,11 +1,12 @@
1
1
  import { type IPreviewerProps } from 'dumi';
2
- import { type FC } from 'react';
2
+ import { type FC, type ReactNode } from 'react';
3
3
  import './index.less';
4
4
  export interface IPreviewerActionsProps extends IPreviewerProps {
5
5
  /**
6
6
  * disabled actions
7
7
  */
8
8
  disabledActions?: ('CSB' | 'STACKBLITZ' | 'EXTERNAL' | 'HTML2SKETCH')[];
9
+ extra?: ReactNode;
9
10
  forceShowCode?: boolean;
10
11
  demoContainer: HTMLDivElement | HTMLIFrameElement;
11
12
  }
@@ -159,7 +159,7 @@ var PreviewerActions = function PreviewerActions(props) {
159
159
  "data-dumi-tooltip": intl.formatMessage({
160
160
  id: 'previewer.actions.separate'
161
161
  })
162
- }, /*#__PURE__*/React.createElement(IconExternalLink, null)), /*#__PURE__*/React.createElement(PreviewerActionsExtra, props), !props.forceShowCode && /*#__PURE__*/React.createElement("button", {
162
+ }, /*#__PURE__*/React.createElement(IconExternalLink, null)), props.extra, /*#__PURE__*/React.createElement(PreviewerActionsExtra, props), !props.forceShowCode && /*#__PURE__*/React.createElement("button", {
163
163
  className: "dumi-default-previewer-action-btn",
164
164
  type: "button",
165
165
  onClick: function onClick() {