From 0a6a7d0984ebbc83270c0cc76dbb8f52a29d803a Mon Sep 17 00:00:00 2001 From: liuziyang Date: Tue, 7 Jul 2020 13:17:33 +0800 Subject: [PATCH 1/5] feature: add docs site --- README.md | 20 +- docs/CONTRIBUTING_zh-CN.md | 1 + docs/I18N_zh-CN.md | 1 + docs/MARKDOWN_zh-CN.md | 1 + docs/THEME_zh-CN.md | 1 + package.json | 3 +- site/.editorconfig | 23 ++ site/.eslintrc.js | 8 + site/.gitignore | 4 + site/README.md | 19 ++ site/assets/zanui-logo.png | Bin 0 -> 13350 bytes site/babel.config.js | 40 +++ site/configs/demo.config.js | 14 + site/configs/doc.config.js | 154 +++++++++++ site/constants.js | 8 + site/markdown-loader/card-wrapper.js | 16 ++ site/markdown-loader/highlight.js | 9 + site/markdown-loader/index.js | 67 +++++ site/markdown-loader/link-open.js | 18 ++ site/package.json | 92 +++++++ site/postcss.config.js | 7 + site/scripts/deploy.sh | 3 + site/scripts/dev.js | 52 ++++ site/scripts/gather.js | 58 ++++ site/src/.eslintrc | 5 + site/src/404.html | 41 +++ site/src/common/styles/base.scss | 48 ++++ site/src/common/styles/highlight.scss | 82 ++++++ site/src/common/styles/index.scss | 17 ++ site/src/common/styles/variable.scss | 29 ++ site/src/desktop/App.js | 95 +++++++ site/src/desktop/components/content/index.js | 72 +++++ .../desktop/components/content/styles.scss | 260 ++++++++++++++++++ site/src/desktop/components/header/index.js | 80 ++++++ site/src/desktop/components/header/style.scss | 134 +++++++++ .../src/desktop/components/i18n/CNWrapper.tsx | 18 ++ .../src/desktop/components/i18n/USWrapper.tsx | 18 ++ site/src/desktop/components/i18n/index.ts | 2 + site/src/desktop/components/i18n/types.ts | 5 + site/src/desktop/components/loadable/index.js | 15 + .../desktop/components/loadable/loading.js | 36 +++ .../desktop/components/loadable/style.scss | 55 ++++ .../desktop/components/router-context-type.js | 11 + .../desktop/components/scroll-to-top/index.js | 16 ++ .../components/search-box/ResultList.js | 123 +++++++++ .../components/search-box/constants.js | 2 + .../desktop/components/search-box/index.js | 218 +++++++++++++++ .../desktop/components/search-box/search.js | 25 ++ .../desktop/components/search-box/style.scss | 59 ++++ site/src/desktop/components/side-nav/index.js | 88 ++++++ .../desktop/components/side-nav/style.scss | 84 ++++++ site/src/desktop/index.html | 127 +++++++++ site/src/desktop/index.js | 32 +++ site/src/desktop/router.config.js | 32 +++ site/src/desktop/styles/docs.scss | 20 ++ site/src/desktop/styles/index.scss | 1 + site/src/simulator/App.js | 13 + site/src/simulator/index.html | 127 +++++++++ site/src/simulator/index.js | 30 ++ site/tsconfig.json | 23 ++ site/webpack/webpack.config.js | 207 ++++++++++++++ site/webpack/webpack.dev.config.js | 32 +++ site/webpack/webpack.prd.config.js | 47 ++++ src/components/Button/README.zh-CN.md | 155 +++++++++++ src/components/Cell/README.zh-CN.md | 1 + src/components/Field/README.zh-CN.md | 1 + src/components/Icons/README.zh-CN.md | 1 + src/components/Navbar/README.zh-CN.md | 1 + src/components/Popup/README.zh-CN.md | 1 + src/components/Rate/README.zh-CN.md | 1 + src/components/Search/README.zh-CN.md | 1 + src/components/Tag/README.zh-CN.md | 1 + 72 files changed, 3099 insertions(+), 12 deletions(-) create mode 100644 docs/CONTRIBUTING_zh-CN.md create mode 100644 docs/I18N_zh-CN.md create mode 100644 docs/MARKDOWN_zh-CN.md create mode 100644 docs/THEME_zh-CN.md create mode 100644 site/.editorconfig create mode 100644 site/.eslintrc.js create mode 100644 site/.gitignore create mode 100644 site/README.md create mode 100644 site/assets/zanui-logo.png create mode 100644 site/babel.config.js create mode 100644 site/configs/demo.config.js create mode 100644 site/configs/doc.config.js create mode 100644 site/constants.js create mode 100644 site/markdown-loader/card-wrapper.js create mode 100644 site/markdown-loader/highlight.js create mode 100644 site/markdown-loader/index.js create mode 100644 site/markdown-loader/link-open.js create mode 100644 site/package.json create mode 100644 site/postcss.config.js create mode 100755 site/scripts/deploy.sh create mode 100644 site/scripts/dev.js create mode 100644 site/scripts/gather.js create mode 100644 site/src/.eslintrc create mode 100644 site/src/404.html create mode 100644 site/src/common/styles/base.scss create mode 100644 site/src/common/styles/highlight.scss create mode 100644 site/src/common/styles/index.scss create mode 100644 site/src/common/styles/variable.scss create mode 100644 site/src/desktop/App.js create mode 100644 site/src/desktop/components/content/index.js create mode 100644 site/src/desktop/components/content/styles.scss create mode 100644 site/src/desktop/components/header/index.js create mode 100644 site/src/desktop/components/header/style.scss create mode 100644 site/src/desktop/components/i18n/CNWrapper.tsx create mode 100644 site/src/desktop/components/i18n/USWrapper.tsx create mode 100644 site/src/desktop/components/i18n/index.ts create mode 100644 site/src/desktop/components/i18n/types.ts create mode 100644 site/src/desktop/components/loadable/index.js create mode 100644 site/src/desktop/components/loadable/loading.js create mode 100644 site/src/desktop/components/loadable/style.scss create mode 100644 site/src/desktop/components/router-context-type.js create mode 100644 site/src/desktop/components/scroll-to-top/index.js create mode 100644 site/src/desktop/components/search-box/ResultList.js create mode 100644 site/src/desktop/components/search-box/constants.js create mode 100644 site/src/desktop/components/search-box/index.js create mode 100644 site/src/desktop/components/search-box/search.js create mode 100644 site/src/desktop/components/search-box/style.scss create mode 100644 site/src/desktop/components/side-nav/index.js create mode 100644 site/src/desktop/components/side-nav/style.scss create mode 100644 site/src/desktop/index.html create mode 100644 site/src/desktop/index.js create mode 100644 site/src/desktop/router.config.js create mode 100644 site/src/desktop/styles/docs.scss create mode 100644 site/src/desktop/styles/index.scss create mode 100644 site/src/simulator/App.js create mode 100644 site/src/simulator/index.html create mode 100644 site/src/simulator/index.js create mode 100644 site/tsconfig.json create mode 100644 site/webpack/webpack.config.js create mode 100644 site/webpack/webpack.dev.config.js create mode 100644 site/webpack/webpack.prd.config.js create mode 100644 src/components/Button/README.zh-CN.md create mode 100644 src/components/Cell/README.zh-CN.md create mode 100644 src/components/Field/README.zh-CN.md create mode 100644 src/components/Icons/README.zh-CN.md create mode 100644 src/components/Navbar/README.zh-CN.md create mode 100644 src/components/Popup/README.zh-CN.md create mode 100644 src/components/Rate/README.zh-CN.md create mode 100644 src/components/Search/README.zh-CN.md create mode 100644 src/components/Tag/README.zh-CN.md diff --git a/README.md b/README.md index 11b471611..bd67a4240 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,23 @@ # **Vant React** -[![npm version](https://badge.fury.io/js/vant-react.svg)](https://badge.fury.io/js/vant-react) -[![NPM](https://img.shields.io/npm/l/vant-react)](LICENSE) -![Test CI](https://github.com/mxdi9i7/vant-react/workflows/Test%20CI/badge.svg) -[![Netlify Status](https://api.netlify.com/api/v1/badges/30ddabc0-3eb6-4530-ab08-58db247a2b48/deploy-status)](https://vant.bctc.io) -[![Storybook](https://cdn.jsdelivr.net/gh/storybookjs/brand@master/badge/badge-storybook.svg)](https://vant.bctc.io) +npm version +NPM +Test CI +Netlify Status +Storybook Lightweight Mobile UI Components built on Typescript and React in under 2kb! -## **Features** +### **Features** * Support Typescript * 60+ Reusable components * 100% Storybook coverage: [https://vant.bctc.io](https://vant.bctc.io) * Extensive documentation and demos -## Install +### Install -```text +``` bash # Using npm npm i vant-react -S @@ -25,9 +25,9 @@ npm i vant-react -S yarn add vant-react ``` -## Quickstart +### Quickstart -```text +``` js import React from 'react'; import { Button } from 'vant-react'; import 'vant-react/dist/index.css'; diff --git a/docs/CONTRIBUTING_zh-CN.md b/docs/CONTRIBUTING_zh-CN.md new file mode 100644 index 000000000..cfbca5ac4 --- /dev/null +++ b/docs/CONTRIBUTING_zh-CN.md @@ -0,0 +1 @@ +## 如何参与 \ No newline at end of file diff --git a/docs/I18N_zh-CN.md b/docs/I18N_zh-CN.md new file mode 100644 index 000000000..fe57d0e98 --- /dev/null +++ b/docs/I18N_zh-CN.md @@ -0,0 +1 @@ +## 国际化 diff --git a/docs/MARKDOWN_zh-CN.md b/docs/MARKDOWN_zh-CN.md new file mode 100644 index 000000000..2a18abb8f --- /dev/null +++ b/docs/MARKDOWN_zh-CN.md @@ -0,0 +1 @@ +## 组件文档如何编写 diff --git a/docs/THEME_zh-CN.md b/docs/THEME_zh-CN.md new file mode 100644 index 000000000..254266ca3 --- /dev/null +++ b/docs/THEME_zh-CN.md @@ -0,0 +1 @@ +## 定制主题 diff --git a/package.json b/package.json index 9c4a39b16..8e56f81ca 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,7 @@ "test:lint": "run-s lint", "test:unit": "cross-env CI=1 react-scripts test --env=jsdom --passWithNoTests", "test:watch": "react-scripts test --env=jsdom", - "predeploy": "cd example && yarn install && yarn run build", - "deploy": "gh-pages -d example/build", + "deploy": "cd site && yarn deploy", "lint": "eslint --ext .tsx ./src", "lint:watch": "esw --watch --fix --ext .tsx ./src", "storybook": "start-storybook -p 9009", diff --git a/site/.editorconfig b/site/.editorconfig new file mode 100644 index 000000000..39cb692c8 --- /dev/null +++ b/site/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +indent_style = space +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[*.js] +indent_size = 2 + +[*.vue] +indent_size = 2 + +[*.css] +indent_size = 2 + +[Makefile] +indent_style = tab diff --git a/site/.eslintrc.js b/site/.eslintrc.js new file mode 100644 index 000000000..fef4a2e8b --- /dev/null +++ b/site/.eslintrc.js @@ -0,0 +1,8 @@ +module.exports = { + root: true, + env: { + browser: true, + }, + extends: ['../eslintrc.react.js.base.js'], + ignorePatterns: ['node_modules/', 'dist/', 'src/nav.js'], +}; diff --git a/site/.gitignore b/site/.gitignore new file mode 100644 index 000000000..01d50dc80 --- /dev/null +++ b/site/.gitignore @@ -0,0 +1,4 @@ +dist +node_modules +src/nav.js +.awcache diff --git a/site/README.md b/site/README.md new file mode 100644 index 000000000..f3fde3329 --- /dev/null +++ b/site/README.md @@ -0,0 +1,19 @@ +## 开发 + +```bash +yarn + +npm run dev +``` + +浏览器打开[http://127.0.0.1:4396](http://127.0.0.1:4396) + +## 部署 + +`yarn run deploy` + +默认情况下生成的静态文件会部署到当前仓库的 `gh-pages` 分支上,环境变量来修改仓库。 + +## Notes + +Github doesn't support SPAs, the `404.html` is a workaround. Do NOT remove it. diff --git a/site/assets/zanui-logo.png b/site/assets/zanui-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ffbbde2b57e91129171e293a0a05428de6b0dfee GIT binary patch literal 13350 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 KMg#PA)c*kJ_lzh2 literal 0 HcmV?d00001 diff --git a/site/babel.config.js b/site/babel.config.js new file mode 100644 index 000000000..a22e85ee5 --- /dev/null +++ b/site/babel.config.js @@ -0,0 +1,40 @@ +module.exports = function(api) { + api.cache(true); + + const envSpecificPlugins = + process.env.NODE_ENV === 'development' + ? ['react-loadable/babel', 'react-hot-loader/babel'] + : []; + + return { + presets: [ + [ + '@babel/preset-env', + { + useBuiltIns: 'entry', + corejs: 3, + }, + ], + '@babel/preset-react', + ], + + plugins: [ + 'transform-property-literals', + 'transform-member-expression-literals', + '@babel/plugin-proposal-export-namespace-from', + '@babel/plugin-proposal-export-default-from', + '@babel/plugin-proposal-class-properties', + '@babel/plugin-syntax-dynamic-import', + [ + '@babel/plugin-transform-runtime', + { + corejs: false, + helpers: true, + regenerator: true, + useESModules: false, + }, + ], + ...envSpecificPlugins, + ], + }; +}; diff --git a/site/configs/demo.config.js b/site/configs/demo.config.js new file mode 100644 index 000000000..8ed889e17 --- /dev/null +++ b/site/configs/demo.config.js @@ -0,0 +1,14 @@ + +import DemoLoadable from '../src/desktop/components/Loadable'; + +export default { + button: DemoLoadable({ loader: () => import('../../src/components/Button/demo') }), + cell: DemoLoadable({ loader: () => import('../../src/components/Button/demo') }), + field: DemoLoadable({ loader: () => import('../../src/components/Button/demo') }), + icons: DemoLoadable({ loader: () => import('../../src/components/Button/demo') }), + navbar: DemoLoadable({ loader: () => import('../../src/components/Button/demo') }), + popup: DemoLoadable({ loader: () => import('../../src/components/Button/demo') }), + rate: DemoLoadable({ loader: () => import('../../src/components/Button/demo') }), + search: DemoLoadable({ loader: () => import('../../src/components/Button/demo') }), + tag: DemoLoadable({ loader: () => import('../../src/components/Button/demo') }) +}; diff --git a/site/configs/doc.config.js b/site/configs/doc.config.js new file mode 100644 index 000000000..687cd73fb --- /dev/null +++ b/site/configs/doc.config.js @@ -0,0 +1,154 @@ +import pkgJson from '../../package.json'; +import DocLoadable from '../src/desktop/components/loadable'; + +export const { version } = pkgJson; + +export const versions = [version]; + +export const github = 'https://github.com/youzan/vant'; + +export default { + 'zh-CN': { + header: { + logo: { + image: 'https://img.yzcdn.cn/vant/logo.png', + title: 'Vant React', + href: '#/' + }, + nav: { + lang: { + text: 'En', + from: 'zh-CN', + to: 'en-US' + }, + logoLink: [ + { + image: 'https://b.yzcdn.cn/vant/logo/github.svg', + url: github + } + ] + } + }, + + nav: [ + { + name: '开发指南', + list: [ + { + title: '快速上手', + path: 'guides/install', + source: + DocLoadable({ loader: () => import('../../README.md') }), + }, + { + title: '更新日志', + path: 'guides/changelog', + source: + DocLoadable({ loader: () => import('../../changelog.md') }), + }, + { + title: 'Github 日志', + path: 'guides/github_changelog', + hidden: true, + source: + DocLoadable({ loader: () => import('../../README.md') }), + }, + { + title: '如何参与', + path: 'guides/contribute', + source: + DocLoadable({ loader: () => import('../../docs/CONTRIBUTING_zh-CN.md') }), + }, + { + title: '文档规范', + path: 'guides/markdown', + source: + DocLoadable({ loader: () => import('../../docs/MARKDOWN_zh-CN.md') }), + }, + ], + }, + { + name: '基础组件', + list: [ + { + title: 'Button 按钮', + path: 'api/button', + source: + DocLoadable({ loader: () => import('../../src/components/Button/README.zh-CN.md') }), + }, + { + title: 'Cell 单元格', + path: 'api/cell', + source: + DocLoadable({ loader: () => import('../../src/components/Cell/README.zh-CN.md') }), + }, + { + title: 'Field 输入框', + path: 'api/field', + source: + DocLoadable({ loader: () => import('../../src/components/Field/README.zh-CN.md') }), + }, + { + title: 'Icon 图标', + path: 'api/icons', + source: + DocLoadable({ loader: () => import('../../src/components/Icons/README.zh-CN.md') }), + }, + { + title: 'Navbar 导航栏', + path: 'api/navbar', + source: + DocLoadable({ loader: () => import('../../src/components/Navbar/README.zh-CN.md') }), + }, + { + title: 'Popup 弹出层', + path: 'api/popup', + source: + DocLoadable({ loader: () => import('../../src/components/Popup/README.zh-CN.md') }), + }, + { + title: 'Rate 评分', + path: 'api/rate', + source: + DocLoadable({ loader: () => import('../../src/components/Rate/README.zh-CN.md') }), + }, + { + title: 'Search 搜索', + path: 'api/search', + source: + DocLoadable({ loader: () => import('../../src/components/Search/README.zh-CN.md') }), + }, + { + title: 'Tag 标记', + path: 'api/tag', + source: + DocLoadable({ loader: () => import('../../src/components/Tag/README.zh-CN.md') }), + } + ], + }, + ] + }, + 'en-US': { + header: { + logo: { + image: + 'https://img.yzcdn.cn/vant/logo.png', + title: 'Vant React', + href: '#/' + }, + nav: { + lang: { + text: '中文', + from: 'en-US', + to: 'zh-CN' + }, + logoLink: [ + { + image: 'https://b.yzcdn.cn/vant/logo/github.svg', + url: github + } + ] + } + }, + } +}; diff --git a/site/constants.js b/site/constants.js new file mode 100644 index 000000000..294b170d9 --- /dev/null +++ b/site/constants.js @@ -0,0 +1,8 @@ +exports.prefix = getPrefix(); + +function getPrefix() { + if (process.env.NODE_ENV === 'production') { + return '/vant-react/'; + } + return '/'; +} diff --git a/site/markdown-loader/card-wrapper.js b/site/markdown-loader/card-wrapper.js new file mode 100644 index 000000000..6216cdef9 --- /dev/null +++ b/site/markdown-loader/card-wrapper.js @@ -0,0 +1,16 @@ +module.exports = function cardWrapper(html) { + const group = html + .replace(/

{ + if (fragment.indexOf('${fragment}`; + } + + return fragment; + }) + .join(''); +}; diff --git a/site/markdown-loader/highlight.js b/site/markdown-loader/highlight.js new file mode 100644 index 000000000..9adb9eecf --- /dev/null +++ b/site/markdown-loader/highlight.js @@ -0,0 +1,9 @@ +const hljs = require('highlight.js'); + +module.exports = function highlight(str, lang) { + if (lang && hljs.getLanguage(lang)) { + return hljs.highlight(lang, str, true).value; + } + + return ''; +}; diff --git a/site/markdown-loader/index.js b/site/markdown-loader/index.js new file mode 100644 index 000000000..827e395de --- /dev/null +++ b/site/markdown-loader/index.js @@ -0,0 +1,67 @@ +const loaderUtils = require('loader-utils'); +const MarkdownIt = require('markdown-it'); +const markdownItAnchor = require('markdown-it-anchor'); +const markdownCheckbox = require('markdown-it-checkbox'); +const frontMatter = require('front-matter'); +const highlight = require('./highlight'); +const cardWrapper = require('./card-wrapper'); +const linkOpen = require('./link-open'); +const { slugify } = require('transliteration'); + + +function wrapper(content) { + content = cardWrapper(content); + content = escape(content); + + return ` + import * as React from 'react'; + + function RawHtmlRenderer(props) { + return
; + } + + class ReactMarkdownComponent extends React.PureComponent { + + render() { + return + } + } + + export default ReactMarkdownComponent; +`; +} + +const parser = new MarkdownIt({ + html: true, + highlight, +}).use(markdownItAnchor, { + level: 2, + slugify, +}).use(markdownCheckbox, { + divClass: 'van-doc-checkbox' +}); + +module.exports = function(source) { + let options = loaderUtils.getOptions(this) || {}; + this.cacheable && this.cacheable(); + + options = { + wrapper, + linkOpen: true, + ...options, + }; + + let fm; + + if (options.enableMetaData) { + fm = frontMatter(source); + source = fm.body; + } + + if (options.linkOpen) { + linkOpen(parser); + } + + return options.wrapper(parser.render(source), fm); +}; + diff --git a/site/markdown-loader/link-open.js b/site/markdown-loader/link-open.js new file mode 100644 index 000000000..0e513226a --- /dev/null +++ b/site/markdown-loader/link-open.js @@ -0,0 +1,18 @@ +// add target="_blank" to all links +module.exports = function linkOpen(md) { + const defaultRender = + md.renderer.rules.link_open || + function(tokens, idx, options, env, self) { + return self.renderToken(tokens, idx, options); + }; + + md.renderer.rules.link_open = function(tokens, idx, options, env, self) { + const aIndex = tokens[idx].attrIndex('target'); + + if (aIndex < 0) { + tokens[idx].attrPush(['target', '_blank']); // add new attribute + } + + return defaultRender(tokens, idx, options, env, self); + }; +}; diff --git a/site/package.json b/site/package.json new file mode 100644 index 000000000..52b1d03c0 --- /dev/null +++ b/site/package.json @@ -0,0 +1,92 @@ +{ + "name": "vant-docs", + "version": "0.0.1", + "description": "doc site generator for vant react", + "private": true, + "keywords": [ + "docs", + "react", + "vant" + ], + "license": "MIT", + "scripts": { + "prepare-build": "rm -rf dist", + "dev": "cross-env NODE_ENV=development node scripts/dev.js", + "webpack": "cross-env NODE_ENV=production webpack --progress --hide-modules --config webpack/webpack.prd.config.js", + "build": "yarn prepare-build && yarn webpack", + "deploy": "yarn install && yarn build && ./scripts/deploy.sh" + }, + "dependencies": { + "@babel/runtime": "^7.1.5", + "classnames": "^2.2.5", + "core-js": "3", + "fibers": "^4.0.2", + "fuse.js": "^3.2.0", + "highlight.js": "^10.1.1", + "lodash": "^4.17.15", + "prismjs": "1.8.4", + "prop-types": "^15.6.0", + "react": "16.11.x", + "react-beautiful-dnd": "^10.0.4", + "react-dom": "16.11.x", + "react-loadable": "^5.3.0", + "react-router-dom": "^4.1.1", + "transliteration": "^2.1.11" + }, + "devDependencies": { + "@babel/core": "^7.1.6", + "@babel/parser": "^7.1.6", + "@babel/plugin-proposal-class-properties": "^7.1.0", + "@babel/plugin-proposal-export-default-from": "^7.0.0", + "@babel/plugin-proposal-export-namespace-from": "^7.0.0", + "@babel/plugin-syntax-dynamic-import": "^7.0.0", + "@babel/plugin-transform-runtime": "^7.1.0", + "@babel/preset-env": "^7.1.6", + "@babel/preset-react": "^7.0.0", + "@babel/types": "^7.1.6", + "@hot-loader/react-dom": "16.11.x", + "@types/react": "^16.9.41", + "@types/react-dom": "^16.9.8", + "autoprefixer": "^9.4.6", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.5", + "babel-plugin-module-resolver": "^3.1.1", + "babel-plugin-transform-member-expression-literals": "^6.9.4", + "babel-plugin-transform-property-literals": "^6.9.4", + "babel-plugin-transform-remove-console": "^6.9.4", + "babel-plugin-transform-remove-debugger": "^6.9.4", + "cache-loader": "^4.1.0", + "colors": "^1.1.2", + "coveralls": "^3.0.3", + "cross-env": "^4.0.0", + "css-loader": "^1.0.1", + "favicons-webpack-plugin": "^0.0.9", + "front-matter": "^4.0.2", + "gh-pages": "1.2.0", + "html-loader": "^0.5.5", + "html-webpack-plugin": "^3.2.0", + "lint-staged": "^8.0.5", + "loader-utils": "^2.0.0", + "markdown-doc-loader": "2.1.3", + "markdown-it": "^11.0.0", + "markdown-it-anchor": "^5.3.0", + "markdown-it-checkbox": "^1.1.0", + "mini-css-extract-plugin": "^0.4.5", + "postcss-loader": "^3.0.0", + "progress-bar-webpack-plugin": "^1.11.0", + "ramda": "^0.25.0", + "react-hot-loader": "^4.6.3", + "react-markdown-doc-loader": "^3.0.0", + "sass": "^1.22.7", + "sass-lint": "^1.12.1", + "sass-loader": "^7.1.0", + "style-loader": "^0.23.1", + "thread-loader": "^2.1.2", + "typescript": "~3.7.2", + "url-loader": "^1.1.2", + "webpack": "^4.26.0", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.10", + "webpack-merge": "^4.1.4" + } +} diff --git a/site/postcss.config.js b/site/postcss.config.js new file mode 100644 index 000000000..4732bdac6 --- /dev/null +++ b/site/postcss.config.js @@ -0,0 +1,7 @@ +/* eslint-disable global-require */ + +module.exports = { + plugins: [ + require('autoprefixer')(), + ], +}; diff --git a/site/scripts/deploy.sh b/site/scripts/deploy.sh new file mode 100755 index 000000000..10628851b --- /dev/null +++ b/site/scripts/deploy.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +gh-pages -d dist diff --git a/site/scripts/dev.js b/site/scripts/dev.js new file mode 100644 index 000000000..a20bbca5c --- /dev/null +++ b/site/scripts/dev.js @@ -0,0 +1,52 @@ +const path = require('path'); +const webpack = require('webpack'); +const WebpackDevServer = require('webpack-dev-server'); +const cp = require('child_process'); + +const webpackConfig = require('../webpack/webpack.dev.config.js'); + +const cmds = { + wind32: 'start', + linux: 'xdg-open', + darwin: 'open', +}; + +let onceMark = true; + +const compiler = webpack(webpackConfig); + +// compiler.apply( +// new ProgressPlugin((percentage, message, ...args) => { +// // { +// // format: ' build [:bar] :percent (:elapsed seconds)', +// // clear: false, +// // width: 60, +// // } +// // e.g. Output each progress message directly to the console: +// console.info(percentage, message, ...args); +// }) +// ); + +compiler.plugin('done', () => { + if (onceMark) { + cp.exec(`${cmds[process.platform]} http://127.0.0.1:4396`); + } + onceMark = false; +}); + +const server = new WebpackDevServer(compiler, { + stats: { + colors: true, + }, + hot: true, + contentBase: path.resolve(__dirname, '../dist'), + publicPath: '/', + disableHostCheck: true, + inline: true, + historyApiFallback: true, +}); + +server.listen(4396, '0.0.0.0', () => { + // eslint-disable-next-line + console.log('\n Starting server on http://localhost:4396 \n'); +}); diff --git a/site/scripts/gather.js b/site/scripts/gather.js new file mode 100644 index 000000000..ca12932cf --- /dev/null +++ b/site/scripts/gather.js @@ -0,0 +1,58 @@ +/* eslint-disable */ +const { resolve, relative } = require('path'); + +// fs.exists is deprecated, but sync version is still available in Node v8.8.1, so I use it. +const { + readdirSync, + writeFileSync, +} = require('fs'); + +const { + __, + concat, + curry, + filter, + find, + forEach, + map, + merge, + omit, + pipe, + prop, + propEq, +} = require('ramda'); +const fm = require('front-matter'); + +const componentsSrc = resolve(__dirname, '../../src/components'); + + +const isDir = path => { + try { + readdirSync(path); + } catch (e) { + return false; + } + return true; +}; + +function gather() { + const components = readdirSync(componentsSrc); + + const demoConfig = {}; + + components.forEach(item => { + demoConfig[item.toLowerCase()] = `DemoLoadable({ loader: () => import('../../src/components/${item}') })`; + }) + + console.log(JSON.stringify(demoConfig)) + + writeFileSync( + resolve(__dirname, '../configs/demo.config.js'), + ` +import DemoLoadable from '../src/desktop/components/Loadable'; + +export default ${JSON.stringify(demoConfig)}; + `.replace(/\"(DemoLoadable\(\{.+\}\))\"/g, `$1`)); + } + +gather(); diff --git a/site/src/.eslintrc b/site/src/.eslintrc new file mode 100644 index 000000000..e5a34aec6 --- /dev/null +++ b/site/src/.eslintrc @@ -0,0 +1,5 @@ +{ + "env": { + "browser": true + } +} diff --git a/site/src/404.html b/site/src/404.html new file mode 100644 index 000000000..6f6c1a035 --- /dev/null +++ b/site/src/404.html @@ -0,0 +1,41 @@ + + + + + Redirecting... + + + + + diff --git a/site/src/common/styles/base.scss b/site/src/common/styles/base.scss new file mode 100644 index 000000000..4d36e60eb --- /dev/null +++ b/site/src/common/styles/base.scss @@ -0,0 +1,48 @@ +@import './variable.scss'; + +body { + min-width: 1100px; + margin: 0; + overflow-x: auto; + color: $van-doc-black; + font-size: 16px; + font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, + Segoe UI, Arial, Roboto, 'PingFang SC', 'Hiragino Sans GB', + 'Microsoft Yahei', sans-serif; + background-color: $van-doc-background-color; + -webkit-font-smoothing: antialiased; +} + +p { + margin: 0; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; + font-size: inherit; +} + +ul, +ol { + margin: 0; + padding: 0; + list-style: none; +} + +a { + text-decoration: none; +} + +.van-doc-row { + width: 100%; + + @media (min-width: $van-doc-row-max-width) { + width: $van-doc-row-max-width; + margin: 0 auto; + } +} diff --git a/site/src/common/styles/highlight.scss b/site/src/common/styles/highlight.scss new file mode 100644 index 000000000..684386b55 --- /dev/null +++ b/site/src/common/styles/highlight.scss @@ -0,0 +1,82 @@ +@import './variable.scss'; + +code { + position: relative; + display: block; + overflow-x: auto; + color: $van-doc-code-color; + font-weight: 400; + font-size: 13.4px; + font-family: $van-doc-code-font-family; + line-height: 26px; + white-space: pre-wrap; + word-wrap: break-word; + -webkit-font-smoothing: auto; +} + +pre { + margin: 20px 0 0; + + + p { + margin-top: 20px; + } +} + +.hljs { + display: block; + padding: 0.5em; + overflow-x: auto; + background: #fff; +} + +.hljs-subst { + color: $van-doc-code-color; +} + +.hljs-string, +.hljs-meta, +.hljs-symbol, +.hljs-template-tag, +.hljs-template-variable, +.hljs-addition { + color: $van-doc-green; +} + +.hljs-comment, +.hljs-quote { + color: #999; +} + +.hljs-params, +.hljs-keyword, +.hljs-attribute { + color: $van-doc-purple; +} + +.hljs-deletion, +.hljs-variable, +.hljs-number, +.hljs-regexp, +.hljs-literal, +.hljs-bullet, +.hljs-link { + color: #eb6f6f; +} + +.hljs-attr, +.hljs-selector-tag, +.hljs-title, +.hljs-section, +.hljs-built_in, +.hljs-doctag, +.hljs-type, +.hljs-name, +.hljs-selector-id, +.hljs-selector-class, +.hljs-strong { + color: #4994df; +} + +.hljs-emphasis { + font-style: italic; +} diff --git a/site/src/common/styles/index.scss b/site/src/common/styles/index.scss new file mode 100644 index 000000000..1d234f02c --- /dev/null +++ b/site/src/common/styles/index.scss @@ -0,0 +1,17 @@ +@import './variable.scss'; +@import './base.scss'; +@import './highlight.scss'; + +.van-doc-container { + overflow: hidden; + box-sizing: border-box; + padding-left: $van-doc-nav-width; + + &--with-simulator { + padding-right: calc($van-doc-simulator-width + $van-doc-padding); + + @media (max-width: 1300px) { + padding-right: calc($van-doc-simulator-small-width + $van-doc-padding); + } + } +} \ No newline at end of file diff --git a/site/src/common/styles/variable.scss b/site/src/common/styles/variable.scss new file mode 100644 index 000000000..d45b0ac4c --- /dev/null +++ b/site/src/common/styles/variable.scss @@ -0,0 +1,29 @@ +$van-doc-black: #323233; +$van-doc-blue: #1989fa; +$van-doc-purple: #8080ff; +$van-doc-fuchsia: #a7419e; +$van-doc-green: #4fc08d; +$van-doc-text-color: #34495e; +$van-doc-text-light-blue: rgba(69, 90, 100, 0.6); +$van-doc-background-color: #f7f8fa; +$van-doc-grey: #999; +$van-doc-dark-grey: #666; +$van-doc-light-grey: #ccc; +$van-doc-border-color: #f1f4f8; +$van-doc-code-color: #58727e; +$van-doc-code-background-color: #f1f4f8; +$van-doc-code-font-family: 'Source Code Pro', 'Monaco', 'Inconsolata', monospace; +$van-doc-padding: 30px; +$van-doc-row-max-width: 1680px; +$van-doc-nav-width: 220px; +$van-doc-border-radius: 12px; + +// header +$van-doc-header-top-height: 60px; +$van-doc-header-bottom-height: 50px; + +// simulator +$van-doc-simulator-width: 360px; +$van-doc-simulator-small-width: 320px; +$van-doc-simulator-height: 620px; +$van-doc-simulator-small-height: 560px; diff --git a/site/src/desktop/App.js b/site/src/desktop/App.js new file mode 100644 index 000000000..c557854b3 --- /dev/null +++ b/site/src/desktop/App.js @@ -0,0 +1,95 @@ +import React, { Component } from 'react'; +import { + BrowserRouter as Router, + Route, + Switch, + Redirect, +} from 'react-router-dom'; + +import ScrollToTop from './components/scroll-to-top'; +import { version as pkgVersion } from '../../configs/doc.config'; +import docConfigs from '../../configs/doc.config'; +import { registerRoute } from './router.config'; +import { prefix } from '../../constants'; +import CNWrapper from './components/i18n/CNWrapper'; +// import USWrapper from './components/USWrapper'; + +window.addEventListener('click', e => { + if (e.target instanceof HTMLAnchorElement) { + const { href } = e.target; + if (/(apidoc|formulr)/.test(href)) { + e.preventDefault(); + window.open(href, '_blank', 'noopener,noreferrer'); + } + } +}); + +// one-dimentional array +// 第二个参数作为处理路由分块的夹层暂时存在,后续会修复。 +const routeData = { + 'zh-CN': registerRoute(docConfigs['zh-CN'].nav, '/zh'), + // 'en-US': registerRoute(navData['en-US'], '/en'), +}; + + +export default class App extends Component { + state = { + i18n: 'zh-CN', + }; + + changeI18N = target => { + this.setState({ + i18n: target, + }); + }; + + render() { + const { i18n } = this.state; + const sideNavData = docConfigs[i18n].nav; + const passthrough = i18nStr => ({ + // 奥利奥,路由路径中的夹层。 + oreo: `/${i18nStr.split('-')[0]}`, + version: pkgVersion, + sideNavData: sideNavData, + sideNavRef: this.saveSideNav, + saveFooter: this.saveFooter, + changeI18N: this.changeI18N, + prefix, + i18n, + }); + + // 通过 basename 控制前缀,不要放到每一层路由里去 + return ( + + + + ( + + {routeData['zh-CN'].map(renderRouter)} + + )} + /> + {/* ( + + + {routeData['en-US'].map(renderRouter)} + + + )} + /> */} + + + + + ); + } +} + +function renderRouter(data) { + const { source, path } = data; + return ; +} diff --git a/site/src/desktop/components/content/index.js b/site/src/desktop/components/content/index.js new file mode 100644 index 000000000..d487b5be4 --- /dev/null +++ b/site/src/desktop/components/content/index.js @@ -0,0 +1,72 @@ +import React, { useEffect } from 'react'; +import { withRouter } from "react-router-dom"; +import classnames from 'classnames'; + +import PageHeader from '../header'; +import SideNav from '../side-nav'; +import { prefix } from '../../../../constants'; +import './styles.scss'; + + +function Layout({ + oreo, + i18n, + children, + version, + sideNavData, + sideNavRef, + saveFooter, + location, +}) { + const { pathname } = location; + const index = pathname.indexOf(`/api`); + const withSimulator = index > -1; + const simulatorRouter = pathname.substr(index) + + const [ innerHeight, setInnerHeight ] = React.useState(window.innerHeight); + + const [ scrollY, setScrollY ] = React.useState(window.scrollY); + + const simulatorStyles = React.useMemo(() => { + const height = Math.min(640, innerHeight - 90); + return { + height: height + 'px', + } + }, [innerHeight]) + + const isFixed = React.useMemo(() => { + return scrollY > 60; + }, [scrollY]) + + React.useEffect(() => { + window.addEventListener('scroll', () => { + setScrollY(window.scrollY); + }); + window.addEventListener('resize', () => { + setInnerHeight(window.innerHeight); + }); + }, []) + + return ( +
+ + +
+
+
{children}
+
+ {withSimulator &&
+ +
} +
+
+ ); +} + +export default withRouter(Layout); diff --git a/site/src/desktop/components/content/styles.scss b/site/src/desktop/components/content/styles.scss new file mode 100644 index 000000000..5df47c8d0 --- /dev/null +++ b/site/src/desktop/components/content/styles.scss @@ -0,0 +1,260 @@ +@import '../../../common/styles/variable.scss'; + +.van-doc-content { + position: relative; + flex: 1; + padding: 0 0 75px; + + .card { + margin-bottom: 24px; + padding: 24px; + background-color: #fff; + border-radius: $van-doc-border-radius; + box-shadow: 0 8px 12px #ebedf0; + } + + a { + margin: 0 1px; + color: $van-doc-blue; + -webkit-font-smoothing: auto; + + &:hover { + color: darken($van-doc-blue, 10%); + } + + &:active { + color: darken($van-doc-blue, 20%); + } + } + + h1, + h2, + h3, + h4, + h5, + h6 { + color: $van-doc-black; + font-weight: normal; + line-height: 1.5; + + &[id] { + cursor: pointer; + } + } + + h1 { + margin: 0 0 30px; + font-size: 30px; + cursor: default; + } + + h2 { + margin: 45px 0 20px; + font-size: 22px; + } + + h3 { + margin-bottom: 16px; + font-weight: 500; + font-size: 18px; + } + + h4 { + margin: 24px 0 12px; + font-weight: 500; + font-size: 15px; + } + + h5 { + margin: 24px 0 12px; + font-weight: 500; + font-size: 14px; + } + + p { + color: $van-doc-text-color; + font-size: 14px; + line-height: 26px; + } + + table { + width: 100%; + margin-top: 12px; + color: $van-doc-text-color; + font-size: 13px; + line-height: 1.5; + border-collapse: collapse; + + th { + padding: 8px 10px; + font-weight: 500; + text-align: left; + + &:first-child { + padding-left: 0; + } + + &:last-child { + padding-right: 0; + } + } + + td { + padding: 8px; + border-top: 1px solid $van-doc-code-background-color; + + &:first-child { + padding-left: 0; + + // version tag + code { + margin: 0; + padding: 2px 6px; + color: $van-doc-blue; + font-weight: 500; + font-size: 10px; + background-color: fade($van-doc-blue, 10%); + border-radius: 20px; + } + } + + &:last-child { + padding-right: 0; + } + } + + em { + color: $van-doc-green; + font-size: 12.5px; + font-family: $van-doc-code-font-family; + font-style: normal; + -webkit-font-smoothing: auto; + } + } + + ul li, + ol li { + position: relative; + margin: 5px 0 5px 10px; + padding-left: 15px; + color: $van-doc-text-color; + font-size: 14px; + line-height: 26px; + + &::before { + position: absolute; + top: 0; + left: 0; + box-sizing: border-box; + width: 6px; + height: 6px; + margin-top: 10px; + border: 1px solid $van-doc-dark-grey; + border-radius: 50%; + content: ''; + } + } + + hr { + margin: 30px 0; + border: 0 none; + border-top: 1px solid #eee; + } + + p > code, + li > code, + table code { + display: inline; + margin: 2px 3px; + padding: 2px 5px; + font-size: 13px; + font-family: inherit; + word-break: keep-all; + background-color: #f0f2f5; + border-radius: 4px; + -webkit-font-smoothing: antialiased; + } + + p > code { + font-size: 14px; + } + + section { + padding: 30px; + overflow: hidden; + } + + blockquote { + margin: 20px 0 0; + padding: 16px; + color: rgba(52, 73, 94, 0.8); + font-weight: 500; + font-size: 14px; + background-color: #ecf9ff; + border-radius: $van-doc-border-radius; + } + + img { + width: 100%; + margin: 16px 0; + border-radius: $van-doc-border-radius; + } + + &--changelog { + strong { + display: block; + margin: 24px 0 12px; + font-weight: 500; + font-size: 15px; + } + + h3 { + + p code { + margin: 0; + } + + a { + color: inherit; + font-size: 20px; + } + } + } +} + +.van-doc-simulator { + position: absolute; + top: $van-doc-padding + $van-doc-header-top-height; + right: $van-doc-padding; + z-index: 1; + box-sizing: border-box; + width: $van-doc-simulator-width; + min-width: $van-doc-simulator-width; + overflow: hidden; + background: #fafafa; + border-radius: $van-doc-border-radius; + box-shadow: #ebedf0 0 4px 12px; + + @media (max-width: 1100px) { + right: auto; + left: 750px; + } + + @media (min-width: $van-doc-row-max-width) { + right: 50%; + margin-right: -$van-doc-row-max-width / 2 + 40px; + } + + &-fixed { + position: fixed; + top: $van-doc-padding; + } + + iframe { + display: block; + width: 100%; + } +} + +.van-doc-nav-fixed { + top: 0; +} \ No newline at end of file diff --git a/site/src/desktop/components/header/index.js b/site/src/desktop/components/header/index.js new file mode 100644 index 000000000..98e4d3cc0 --- /dev/null +++ b/site/src/desktop/components/header/index.js @@ -0,0 +1,80 @@ +import React, { Component } from 'react'; + +import SearchBox from '../search-box'; +import RouterContext from '../router-context-type'; + +import docConfigs, { versions } from '../../../../configs/doc.config'; + +import './style.scss'; + +const CONTROLLS = { + 'zh-CN': 'EN', + 'en-US': '中文', +}; + +export default class PageHeader extends Component { + static contextTypes = RouterContext; + + toggle = () => { + const { replace } = this.context.router.history; + const path = this.context.router.route.location.pathname.split('/'); + if (path[1] === 'en') { + path[1] = 'zh'; + } else { + path[1] = 'en'; + } + replace(path.join('/')); + }; + + render() { + const { i18n, sideNavData } = this.props; + const { header } = docConfigs[i18n]; + + const { nav, logo } = header; + + return ( +
+
+
+ + logo + {logo.title} + + + +
+
+
+ ); + } +} diff --git a/site/src/desktop/components/header/style.scss b/site/src/desktop/components/header/style.scss new file mode 100644 index 000000000..a0ecb8a07 --- /dev/null +++ b/site/src/desktop/components/header/style.scss @@ -0,0 +1,134 @@ +@import '../../../common/styles/variable.scss'; + +.van-doc-header { + width: 100%; + background-color: #001938; + user-select: none; + + &__top { + display: flex; + align-items: center; + height: $van-doc-header-top-height; + padding: 0 $van-doc-padding; + line-height: $van-doc-header-top-height; + + &-nav { + flex: 1; + font-size: 0; + text-align: right; + + > li { + position: relative; + display: inline-block; + vertical-align: middle; + } + + &-item { + margin-left: 20px; + } + + &-title { + display: block; + font-size: 15px; + } + } + } + + &__cube { + position: relative; + display: block; + padding: 0 12px; + color: #fff; + font-size: 14px; + font-family: 'Helvetica Neue', Arial, sans-serif; + line-height: 24px; + text-align: center; + border: 1px solid rgba(255, 255, 255, 0.7); + border-radius: 20px; + cursor: pointer; + transition: 0.3s ease-in-out; + } + + &__version { + padding-right: 20px; + + &::after { + position: absolute; + top: 7px; + right: 7px; + width: 5px; + height: 5px; + color: rgba(255, 255, 255, 0.9); + border: 1px solid; + border-color: transparent transparent currentColor currentColor; + transform: rotate(-45deg); + content: ''; + } + + &-pop { + position: absolute; + top: 30px; + right: 0; + left: 0; + z-index: 99; + color: #333; + line-height: 36px; + text-align: left; + background-color: #fff; + border-radius: $van-doc-border-radius; + box-shadow: 0 4px 12px #ebedf0; + transform-origin: top; + transition: 0.2s cubic-bezier(0.215, 0.61, 0.355, 1); + + &-item { + padding-left: 12px; + transition: 0.2s; + + &:hover { + color: $van-doc-blue; + } + } + } + } + + &__logo { + display: block; + + img, + span { + display: inline-block; + vertical-align: middle; + } + + img { + width: 24px; + margin-right: 10px; + } + + span { + color: #fff; + font-size: 22px; + } + } + + &__logo-link { + img { + display: block; + width: 26px; + height: 26px; + transition: 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); + + &:hover { + transform: scale(1.2); + } + } + } +} + +.van-doc-dropdown { + &-enter, + &-leave-active { + transform: scaleY(0); + opacity: 0; + } +} \ No newline at end of file diff --git a/site/src/desktop/components/i18n/CNWrapper.tsx b/site/src/desktop/components/i18n/CNWrapper.tsx new file mode 100644 index 000000000..91b43bf68 --- /dev/null +++ b/site/src/desktop/components/i18n/CNWrapper.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import Layout from '../content'; + +import { II18nProps } from './types'; + +export default class CNWrapper extends React.Component { + componentDidMount() { + const { changeI18N, i18n } = this.props.pass; + if (i18n !== 'zh-CN') { + changeI18N('zh-CN'); + } + } + + render() { + const { children, pass } = this.props; + return {children}; + } +} diff --git a/site/src/desktop/components/i18n/USWrapper.tsx b/site/src/desktop/components/i18n/USWrapper.tsx new file mode 100644 index 000000000..ef20a5479 --- /dev/null +++ b/site/src/desktop/components/i18n/USWrapper.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import Layout from '../content'; + +import { II18nProps } from './types'; + +export default class CNWrapper extends React.Component { + componentDidMount() { + const { changeI18N, i18n } = this.props.pass; + if (i18n !== 'en-US') { + changeI18N('en-US'); + } + } + + render() { + const { children, pass } = this.props; + return {children}; + } +} diff --git a/site/src/desktop/components/i18n/index.ts b/site/src/desktop/components/i18n/index.ts new file mode 100644 index 000000000..0ff1e8399 --- /dev/null +++ b/site/src/desktop/components/i18n/index.ts @@ -0,0 +1,2 @@ +import CNWrapper from './CNWrapper'; +import USWrapper from './USWrapper'; diff --git a/site/src/desktop/components/i18n/types.ts b/site/src/desktop/components/i18n/types.ts new file mode 100644 index 000000000..0fcc4a84e --- /dev/null +++ b/site/src/desktop/components/i18n/types.ts @@ -0,0 +1,5 @@ +export interface II18nProps { + children: React.ReactChild; + pass: any; +} + diff --git a/site/src/desktop/components/loadable/index.js b/site/src/desktop/components/loadable/index.js new file mode 100644 index 000000000..0e0bc282a --- /dev/null +++ b/site/src/desktop/components/loadable/index.js @@ -0,0 +1,15 @@ +import Loadable from 'react-loadable'; +import DocLoading from './loading'; + +export default function DocLoadable(opts) { + return Loadable( + Object.assign( + { + loading: DocLoading, + delay: 200, // Avoiding Flash Of Loading Component + timeout: 5000, // 5 seconds + }, + opts + ) + ); +} diff --git a/site/src/desktop/components/loadable/loading.js b/site/src/desktop/components/loadable/loading.js new file mode 100644 index 000000000..d5d07a6b1 --- /dev/null +++ b/site/src/desktop/components/loadable/loading.js @@ -0,0 +1,36 @@ +import React from 'react'; + +import './style.scss'; + +export default function DocLoading({ error, timedOut, pastDelay }) { + if (error) { + return ; + } + + if (timedOut) { + return ; + } + + if (pastDelay) { + return ; + } + + return null; +} + +function Loading() { + return ( +
+
+
+
+
+
+ ); +} + +function Error() { + return ( +
Oops! An error occurred.
+ ); +} diff --git a/site/src/desktop/components/loadable/style.scss b/site/src/desktop/components/loadable/style.scss new file mode 100644 index 000000000..20d5cd640 --- /dev/null +++ b/site/src/desktop/components/loadable/style.scss @@ -0,0 +1,55 @@ +@keyframes van-doc-loading-ripple { + 0% { + top: 96px; + left: 96px; + width: 0; + height: 0; + opacity: 1; + } + + 100% { + top: 18px; + left: 18px; + width: 156px; + height: 156px; + opacity: 0; + } +} + +.van-doc-loading { + margin-top: 150px; +} + +.van-doc-loading-error { + margin: 20px; + font-size: 18px; + color: #e33; +} + +.van-doc-loading-ripple div { + box-sizing: content-box; + position: absolute; + border-width: 4px; + border-style: solid; + opacity: 1; + border-radius: 50%; + animation: van-doc-loading-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) + infinite; +} + +.van-doc-loading-ripple div:nth-child(1) { + border-color: #27f; +} + +.van-doc-loading-ripple div:nth-child(2) { + border-color: #bdf; + animation-delay: -0.5s; +} + +.van-doc-loading-ripple { + position: relative; + margin: 0 auto; + width: 200px; + height: 200px; + transform: translate(-100px, -100px) scale(1) translate(100px, 100px); +} diff --git a/site/src/desktop/components/router-context-type.js b/site/src/desktop/components/router-context-type.js new file mode 100644 index 000000000..7e119f837 --- /dev/null +++ b/site/src/desktop/components/router-context-type.js @@ -0,0 +1,11 @@ +import PropTypes from 'prop-types'; + +export default { + router: PropTypes.shape({ + history: PropTypes.shape({ + push: PropTypes.func.isRequired, + replace: PropTypes.func.isRequired, + }).isRequired, + route: PropTypes.object, + }).isRequired, +}; diff --git a/site/src/desktop/components/scroll-to-top/index.js b/site/src/desktop/components/scroll-to-top/index.js new file mode 100644 index 000000000..6ab06d5fc --- /dev/null +++ b/site/src/desktop/components/scroll-to-top/index.js @@ -0,0 +1,16 @@ +import React, { Component } from 'react'; +import { withRouter } from 'react-router-dom'; + +class ScrollToTop extends Component { + componentDidUpdate(prevProps) { + if (this.props.location !== prevProps.location) { + window.scrollTo(0, 0); + } + } + + render() { + return
{this.props.children}
; + } +} + +export default withRouter(ScrollToTop); diff --git a/site/src/desktop/components/search-box/ResultList.js b/site/src/desktop/components/search-box/ResultList.js new file mode 100644 index 000000000..1288c217a --- /dev/null +++ b/site/src/desktop/components/search-box/ResultList.js @@ -0,0 +1,123 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import isEmpty from 'lodash/isEmpty'; +import cx from 'classnames'; + +import { SKIP_SCROLL } from './constants'; + +const i18n = { + 'zh-CN': { + notFound: '未找到结果', + }, + 'en-US': { + notFound: 'No results found', + }, +}; + +export default class ResultList extends Component { + static propTypes = { + matches: PropTypes.array, + locale: PropTypes.string.isRequired, + activeIndex: PropTypes.number, + redirectToResult: PropTypes.func.isRequired, + clearActiveIndex: PropTypes.func.isRequired, + }; + + componentDidUpdate() { + this.scrollActiveElementToViewport(); + } + + render() { + const { matches, locale, activeIndex, redirectToResult } = this.props; + + if (isEmpty(matches)) { + return ( +
+ {i18n[locale].notFound} +
+ ); + } + + return ( +
    + {matches.map((item, idx) => { + const { title, subtitle, path } = item; + + return ( +
  • redirectToResult(item)} + > + + {title} + + {subtitle && ( + + {subtitle} + + )} +
  • + ); + })} +
+ ); + } + + saveListNode = node => { + this.list = node; + }; + + getListItemHeight() { + if (this.list && !this.itemHeight) { + const itemNode = this.list.querySelector( + '.van-doc-search-box-result-item' + ); + if (itemNode) { + this.itemHeight = itemNode.scrollHeight; + } + } + + return this.itemHeight || 0; + } + + scrollActiveElementToViewport() { + const { activeIndex } = this.props; + const { list } = this; + if (!list || activeIndex === SKIP_SCROLL) { + return; + } + + const { scrollTop, offsetHeight } = list; + const itemHeight = this.getListItemHeight(); + const activeElementPosition = itemHeight * (activeIndex + 1); + const actualPosition = scrollTop + offsetHeight; + + // 如果高亮节点不在可见区域就滚动 + const bottomOverflow = activeElementPosition > actualPosition; + const topOverflow = + activeElementPosition - itemHeight < actualPosition - offsetHeight; + if (bottomOverflow || topOverflow) { + let scrollY; + + if (bottomOverflow) { + scrollY = + Math.ceil((activeElementPosition - offsetHeight) / itemHeight) * + itemHeight; + } else { + scrollY = + Math.ceil((activeElementPosition - itemHeight) / itemHeight) * + itemHeight; + } + + // scroll(this.list, 0, scrollY, 100); + } + } +} diff --git a/site/src/desktop/components/search-box/constants.js b/site/src/desktop/components/search-box/constants.js new file mode 100644 index 000000000..edc4eda8e --- /dev/null +++ b/site/src/desktop/components/search-box/constants.js @@ -0,0 +1,2 @@ +// 用于鼠标东时重置键盘选中的下标,此时不应该滚动位置 +export const SKIP_SCROLL = -100; diff --git a/site/src/desktop/components/search-box/index.js b/site/src/desktop/components/search-box/index.js new file mode 100644 index 000000000..a6d483be3 --- /dev/null +++ b/site/src/desktop/components/search-box/index.js @@ -0,0 +1,218 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import isEqual from 'lodash/isEqual'; +import { withRouter } from 'react-router-dom'; +import isEmpty from 'lodash/isEmpty'; + +import ResultList from './ResultList'; +import makeSearcher from './search'; +import { SKIP_SCROLL } from './constants'; + +import './style.scss'; + +const i18n = { + 'zh-CN': { + placeholder: '搜索组件...', + }, + + 'en-US': { + placeholder: 'Search components...', + }, +}; + +class SearchBox extends Component { + static propTypes = { + locale: PropTypes.string.isRequired, + navData: PropTypes.array.isRequired, + }; + + state = { + keyword: '', + activeIndex: SKIP_SCROLL, + matches: [], + resultVisible: false, + }; + + constructor(props) { + super(props); + this.buildLUT(props.navData); + } + + componentWillReceiveProps(nextProps) { + if (!isEqual(nextProps.navData, this.props.navData)) { + this.buildLUT(); + } + } + + render() { + const { keyword, matches, activeIndex, resultVisible } = this.state; + const { locale } = this.props; + + return (
+ + // + // + // + // + + // + // + // + // + ); + } + + // react-hot-loader rewrites this function into an infinite loop... lol + // if we use arrow function here + onKeywordChange(evt) { + const keyword = evt.target.value; + if (keyword !== this.state.keyword) { + this.search(keyword); + } + } + + onKeydown(evt) { + const { key } = evt; + + if (key === 'Enter') { + return setTimeout(() => { + const { matches } = this.state; + if (!isEmpty(matches)) { + let { activeIndex } = this.state; + if (activeIndex < 0) { + activeIndex = 0; + } + this.redirectToResult(matches[activeIndex]); + } + }, 0); + } + + if (key !== 'ArrowDown' && key !== 'ArrowUp') { + return; + } + + const { resultVisible } = this.state; + if (!resultVisible) { + this.setState({ + activeIndex: 0, + resultVisible: true, + }); + return; + } + + // Scroll list with arrow keys + let { activeIndex } = this.state; + if (activeIndex === SKIP_SCROLL) { + activeIndex = -1; + } + + if (key === 'ArrowDown') { + activeIndex++; + } else if (key === 'ArrowUp') { + activeIndex--; + } else { + return; + } + const { matches } = this.state; + const maxIndex = matches.length - 1; + if (activeIndex < 0) { + activeIndex = maxIndex; + } else if (activeIndex > maxIndex) { + activeIndex = 0; + } + + this.setState({ + activeIndex, + resultVisible: true, + }); + } + + onResultVisibleChange(visible) { + this.setState({ + resultVisible: visible, + }); + } + + onInputClick() { + this.search(this.state.keyword); + } + + clearActiveIndex() { + this.setState({ + activeIndex: SKIP_SCROLL, + }); + } + + buildLUT(navData) { + // Only include components + const { list } = navData[1]; + const data = list.reduce( + // eslint-disable-next-line + (lut, item) => { + lut.push(item); + return lut; + }, [] + ); + + data.sort((a, b) => { + if (a.title > b.title) { + return 1; + } + + if (a.title === b.title) { + return 0; + } + + return -1; + }); + + this.lut = makeSearcher(data); + } + + search(keyword) { + if (!this.lut) { + return; + } + + const matches = this.lut.search(keyword); + + this.setState({ + keyword, + resultVisible: true, + matches, + activeIndex: 0, + }); + } + + redirectToResult(item) { + const { path } = item; + const { history, locale } = this.props; + const prefix = locale.split('-')[0]; + + history.replace(`/${prefix}/${path}`); + this.onResultVisibleChange(false); + } +} + +export default withRouter(SearchBox); diff --git a/site/src/desktop/components/search-box/search.js b/site/src/desktop/components/search-box/search.js new file mode 100644 index 000000000..cf6281d85 --- /dev/null +++ b/site/src/desktop/components/search-box/search.js @@ -0,0 +1,25 @@ +import Fuse from 'fuse.js'; + +const options = { + shouldSort: true, + threshold: 0.5, + location: 0, + distance: 100, + maxPatternLength: 32, + minMatchCharLength: 1, + keys: ['title', 'subtitle'], +}; + +export default function makeSearcher(list) { + const fuse = new Fuse(list, options); + + return { + search(keyword) { + if (!keyword) { + return list; + } + + return fuse.search(keyword); + }, + }; +} diff --git a/site/src/desktop/components/search-box/style.scss b/site/src/desktop/components/search-box/style.scss new file mode 100644 index 000000000..fa5bbd169 --- /dev/null +++ b/site/src/desktop/components/search-box/style.scss @@ -0,0 +1,59 @@ +@import '../../../common/styles/variable.scss'; + + +.van-doc-search { + width: 200px; + height: 60px; + margin-left: 140px; + color: #fff; + font-size: 14px; + background-color: transparent; + border: none; + + &:focus { + outline: none; + } + + &::placeholder { + color: #fff; + opacity: 0.7; + } +} + +.ds-dropdown-menu { + line-height: 1.8; +} + +.algolia-autocomplete { + .algolia-docsearch-suggestion--highlight { + color: $van-doc-blue; + background-color: transparent; + } + + .algolia-docsearch-suggestion--title { + font-weight: 500; + } + + .algolia-docsearch-suggestion--text { + .algolia-docsearch-suggestion--highlight { + box-shadow: inset 0 -1px 0 0 $van-doc-blue; + } + } + + .algolia-docsearch-suggestion--category-header { + border-bottom-color: #eee; + } + + .ds-dropdown-menu [class^='ds-dataset-'] { + border: none; + } + + .ds-dropdown-menu { + top: 80% !important; + box-shadow: 0 4px 12px #ebedf0; + + &::before { + display: none; + } + } +} \ No newline at end of file diff --git a/site/src/desktop/components/side-nav/index.js b/site/src/desktop/components/side-nav/index.js new file mode 100644 index 000000000..e77d8187b --- /dev/null +++ b/site/src/desktop/components/side-nav/index.js @@ -0,0 +1,88 @@ +import React, { Component } from 'react'; +import { NavLink } from 'react-router-dom'; +import classnames from 'classnames'; + +import RouterContext from '../router-context-type'; +import { prefix } from '../../../../constants'; + +import './style.scss'; + +export default class SideNav extends Component { + static contextTypes = RouterContext; + + handleTitleClick = item => { + if (item.list[0].list[0].path) { + this.context.router.history.push( + getFullPath(this.props.base, item.list[0].list[0].path) + ); + } + }; + + parseData = (item, index) => ( +
+
+ {item.name} +
+ {item.list && item.list.map(this.parseList)} +
+ ); + + parseGroup = (group, index) => ( +
+
{group.groupName}
+ {group.list.map(this.parseList)} +
+ ); + + parseList = (navItem, index) => { + const { title, subtitle, hidden, link } = navItem; + + if (hidden) { + return null; + } + + const linkTitle = subtitle ? ( + + {title} {subtitle} + + ) : ( + title + ); + + return navItem.disabled ? null : ( +
+ {navItem.link ? ( + + {title} + + ) : ( + + {linkTitle} + + )} +
+ ); + }; + + render() { + const { data, className } = this.props; + return ( +
+ {data.map(this.parseData)} +
+ ); + } +} + +function getFullPath(base, path) { + return `${base}/${path}`; +} diff --git a/site/src/desktop/components/side-nav/style.scss b/site/src/desktop/components/side-nav/style.scss new file mode 100644 index 000000000..66b9132ec --- /dev/null +++ b/site/src/desktop/components/side-nav/style.scss @@ -0,0 +1,84 @@ +@import '../../../common/styles/variable.scss'; + +.van-doc-nav { + position: fixed; + top: 60px; + bottom: 0; + left: 0; + z-index: 1; + min-width: $van-doc-nav-width; + max-width: $van-doc-nav-width; + padding: 24px 0 72px; + overflow-y: scroll; + background-color: #fff; + box-shadow: 0 8px 12px #ebedf0; + + @media (min-width: $van-doc-row-max-width) { + left: 50%; + margin-left: -($van-doc-row-max-width / 2); + } + + &::-webkit-scrollbar { + width: 6px; + height: 6px; + background-color: transparent; + } + + &::-webkit-scrollbar-thumb { + background-color: transparent; + border-radius: 6px; + } + + &:hover::-webkit-scrollbar-thumb { + background-color: rgba(69, 90, 100, 0.2); + } + + &__group { + margin-bottom: 16px; + } + + &__title { + padding: 8px 0 8px $van-doc-padding; + color: #455a64; + font-weight: 500; + font-size: 15px; + line-height: 28px; + } + + &__item { + a { + display: block; + margin: 0; + padding: 8px 0 8px $van-doc-padding; + color: #455a64; + font-size: 14px; + line-height: 28px; + transition: color 0.2s; + + &:hover, + &.active { + color: $van-doc-green; + } + + &.active { + -webkit-font-smoothing: auto; + } + + span { + font-size: 13px; + } + } + } + + @media (max-width: 1300px) { + &__item { + a { + font-size: 13px; + } + + &:active { + font-size: 14px; + } + } + } +} \ No newline at end of file diff --git a/site/src/desktop/index.html b/site/src/desktop/index.html new file mode 100644 index 000000000..c34f8886a --- /dev/null +++ b/site/src/desktop/index.html @@ -0,0 +1,127 @@ + + + + + + + + Vant React + + + + + + + + + +
+
Loading...
+
+ + + + + diff --git a/site/src/desktop/index.js b/site/src/desktop/index.js new file mode 100644 index 000000000..c80d41492 --- /dev/null +++ b/site/src/desktop/index.js @@ -0,0 +1,32 @@ +import 'core-js/stable'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { AppContainer } from 'react-hot-loader'; + +import './styles/index.scss' + +import App from './App'; + +const render = ChildComponent => { + ReactDOM.render( + + + , + document.getElementById('app-container') // eslint-disable-line + ); +}; + +// Add a delay in dev mode to ensure styles are loaded before executing any JavaScript code +if (process.env.NODE_ENV !== 'production') { + setTimeout(() => { + render(App); + }, 500); +} else { + render(App); +} + +if (module.hot) { + module.hot.accept('./App', () => { + render(App); + }); +} diff --git a/site/src/desktop/router.config.js b/site/src/desktop/router.config.js new file mode 100644 index 000000000..9a0fdc806 --- /dev/null +++ b/site/src/desktop/router.config.js @@ -0,0 +1,32 @@ +const registerRoute = (navData, oreo = '') => { + let route = []; + + if (!navData) return route; + + function addRoute(page) { + const { path, source, title } = page; + route.push({ + path: `${oreo}/${path}`, + source, + title, + }); + } + + navData.forEach(nav => { + if (nav.list) { + nav.list.forEach(subNav => { + addRoute(subNav); + }); + } else if (nav.children) { + nav.children.forEach(subNav => { + addRoute(subNav); + }); + } else { + addRoute(nav); + } + }); + + return route; +}; + +export { registerRoute }; diff --git a/site/src/desktop/styles/docs.scss b/site/src/desktop/styles/docs.scss new file mode 100644 index 000000000..2ef781fd4 --- /dev/null +++ b/site/src/desktop/styles/docs.scss @@ -0,0 +1,20 @@ +@import '../../common/styles/index.scss'; + +.van-doc-row { + width: 100%; + + @media(min-width: $van-doc-row-max-width) { + width: $van-doc-row-max-width; + margin: 0 auto; + } +} + +.van-doc-container { + box-sizing: border-box; + padding-left: $van-doc-nav-width; + overflow: hidden; + + &--with-simulator { + padding-right: $van-doc-simulator-width + $van-doc-padding; + } +} diff --git a/site/src/desktop/styles/index.scss b/site/src/desktop/styles/index.scss new file mode 100644 index 000000000..2721c1f33 --- /dev/null +++ b/site/src/desktop/styles/index.scss @@ -0,0 +1 @@ +@import './docs.scss' \ No newline at end of file diff --git a/site/src/simulator/App.js b/site/src/simulator/App.js new file mode 100644 index 000000000..75b54ecf5 --- /dev/null +++ b/site/src/simulator/App.js @@ -0,0 +1,13 @@ +import * as React from 'react'; +import DemoConfig from '../../configs/demo.config'; + +const App = () => { + const componentName = window.location.hash.replace('#/api/', ''); + + const DemoComponent = DemoConfig[componentName]; + return ( + + ); +} + +export default App; diff --git a/site/src/simulator/index.html b/site/src/simulator/index.html new file mode 100644 index 000000000..c34f8886a --- /dev/null +++ b/site/src/simulator/index.html @@ -0,0 +1,127 @@ + + + + + + + + Vant React + + + + + + + + + +
+
Loading...
+
+ + + + + diff --git a/site/src/simulator/index.js b/site/src/simulator/index.js new file mode 100644 index 000000000..793584fc6 --- /dev/null +++ b/site/src/simulator/index.js @@ -0,0 +1,30 @@ +import 'core-js/stable'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { AppContainer } from 'react-hot-loader'; + +import App from './App'; + +const render = ChildComponent => { + ReactDOM.render( + + + , + document.getElementById('app-container') // eslint-disable-line + ); +}; + +// Add a delay in dev mode to ensure styles are loaded before executing any JavaScript code +if (process.env.NODE_ENV !== 'production') { + setTimeout(() => { + render(App); + }, 500); +} else { + render(App); +} + +if (module.hot) { + module.hot.accept('./App', () => { + render(App); + }); +} diff --git a/site/tsconfig.json b/site/tsconfig.json new file mode 100644 index 000000000..875c04865 --- /dev/null +++ b/site/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "es2015", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "outDir": "dist", + "jsx": "react", + "allowJs": true, + "lib": [ + "dom", + "es2017" + ], + }, + "exclude": [ + "lib", + "es", + ], + "include": [ + "src", + "../src", + ] +} diff --git a/site/webpack/webpack.config.js b/site/webpack/webpack.config.js new file mode 100644 index 000000000..3dc6ab789 --- /dev/null +++ b/site/webpack/webpack.config.js @@ -0,0 +1,207 @@ +const webpack = require('webpack'); +const Fiber = require('fibers'); +const sass = require('sass'); +const os = require('os'); +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const ProgressBarPlugin = require('progress-bar-webpack-plugin'); +const { prefix } = require('../constants'); + +const DEV = process.env.NODE_ENV !== 'production'; + + +module.exports = { + mode: process.env.NODE_ENV, + + output: { + path: path.resolve(__dirname, '../dist'), + filename: '[name]-[hash].js', + publicPath: prefix, + }, + + resolve: { + extensions: ['.tsx', '.ts', '.js', '.md', '.json'], + alias: Object.assign({ + vant$: path.resolve(__dirname, '../../src'), + }), + }, + + module: { + rules: [ + { + test: /\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/, + use: 'url-loader', + }, + { + test: /\.html$/, + use: 'html-loader', + }, + { + test: /\.s?css$/, + use: [ + DEV ? 'style-loader' : MiniCssExtractPlugin.loader, + 'cache-loader', + { + loader: 'css-loader', + options: { + importLoaders: 1, + sourceMap: DEV, + }, + }, + { + loader: 'postcss-loader', + options: { + sourceMap: DEV, + config: { + path: path.resolve(__dirname, '..'), + }, + }, + }, + { + loader: 'sass-loader', + options: { + sourceMap: DEV, + implementation: sass, + fiber: Fiber, + }, + }, + ], + }, + { + test: /\.jsx?$/, + exclude: /node_modules/, + use: [ + { + loader: 'babel-loader', + options: { + cacheDirectory: true, + }, + }, + ], + }, + { + test: /\.md$/, + use: [ + { + loader: 'thread-loader', + // loaders with equal options will share worker pools + options: { + // the number of spawned workers, defaults to (number of cpus - 1) or + // fallback to 1 when require('os').cpus() is undefined + workers: os.cpus() - 1, + + // number of jobs a worker processes in parallel + // defaults to 20 + workerParallelJobs: 10, + + // additional node.js arguments + workerNodeArgs: ['--max-old-space-size=1024'], + + // Allow to respawn a dead worker pool + // respawning slows down the entire compilation + // and should be set to false for development + poolRespawn: !DEV, + + // timeout for killing the worker processes when idle + // defaults to 500 (ms) + // can be set to Infinity for watching builds to keep workers alive + poolTimeout: DEV ? Infinity : 500, + + // number of jobs the poll distributes to the workers + // defaults to 200 + // decrease of less efficient but more fair distribution + poolParallelJobs: 50, + + // name of the pool + // can be used to create different pools with elsewise identical options + name: 'md-pool', + }, + }, + 'babel-loader', + { + loader: path.resolve(__dirname, '../markdown-loader') + }, + // { + // loader: 'react-markdown-doc-loader', + // options: { + // jsTemplate: path.resolve(__dirname, '../react-template.jstpl'), + // renderers: { + // markdown: 'Markdown', + // style: 'Style', + // demo: 'Demo', + // }, + // }, + // }, + // 'markdown-doc-loader', + ], + }, + { + test: /\.tsx?$/, + use: [ + { + loader: 'awesome-typescript-loader', + options: { + useCache: true, + getCustomTransformers: program => ({ + before: [], + }), + }, + }, + ], + }, + ], + }, + + plugins: [ + new webpack.EnvironmentPlugin({ + NODE_ENV: 'development', // use 'development' unless process.env.NODE_ENV is defined + VERSION: 'release', + }), + + new ProgressBarPlugin(), + + new HtmlWebpackPlugin({ + template: 'src/desktop/index.html', + filename: 'index.html', + chunks: ['vendor', 'docs'], + inject: 'body', + }), + + new HtmlWebpackPlugin({ + template: 'src/simulator/index.html', + chunks: ['vendor', 'simulator'], + filename: 'simulator.html', + inject: 'body', + }), + + new HtmlWebpackPlugin({ + template: 'src/404.html', + filename: '404.html', + chunks: [], + inject: false, + }), + + new MiniCssExtractPlugin({ + filename: DEV ? '[name].css' : '[name]-[contenthash].css', + chunkFilename: DEV ? '[id].css' : '[id].[contenthash].css', + }), + ], + + optimization: { + splitChunks: { + cacheGroups: { + vendor: { + test: /[\\/]node_modules[\\/]/, + name: 'vendor', + chunks: 'all', + }, + }, + }, + }, + + node: { + fs: 'empty', + net: 'empty', + }, +}; diff --git a/site/webpack/webpack.dev.config.js b/site/webpack/webpack.dev.config.js new file mode 100644 index 000000000..f491bf752 --- /dev/null +++ b/site/webpack/webpack.dev.config.js @@ -0,0 +1,32 @@ +const merge = require('webpack-merge'); +const webpack = require('webpack'); + +const base = require('./webpack.config'); + +module.exports = merge.smart(base, { + entry: { + docs: [ + './src/desktop/index.js', + ], + simulator: [ + './src/simulator/index.js' + ] + }, + + devServer: { + host: 'localhost', + port: 4396, + hot: true, + open: true, + }, + + devtool: 'inline-cheap-module-source-map', + + resolve: { + alias: { + 'react-dom': '@hot-loader/react-dom', + }, + }, + + plugins: [new webpack.HotModuleReplacementPlugin()], +}); diff --git a/site/webpack/webpack.prd.config.js b/site/webpack/webpack.prd.config.js new file mode 100644 index 000000000..7d2a542fb --- /dev/null +++ b/site/webpack/webpack.prd.config.js @@ -0,0 +1,47 @@ +const merge = require('webpack-merge'); +const FaviconsWebpackPlugin = require('favicons-webpack-plugin'); + +const base = require('./webpack.config'); + +module.exports = merge.smart(base, { + entry: { + docs: './src/desktop/index.js', + simulator: './src/simulator/index.js' + }, + + plugins: [ + new FaviconsWebpackPlugin({ + // Your source logo + logo: './assets/zanui-logo.png', + // The prefix for all image files (might be a folder or a name) + prefix: 'favico-[hash]-', + // Emit all stats of the generated icons + // emitStats: false, + // The name of the json containing all favicon information + // statsFilename: 'iconstats-[hash].json', + // Generate a cache file with control hashes and + // don't rebuild the favicons until those hashes change + persistentCache: true, + // Inject the html into the html-webpack-plugin + inject: true, + // favicon background color (see https://github.com/haydenbleasel/favicons#usage) + background: '#fff', + // favicon app title (see https://github.com/haydenbleasel/favicons#usage) + title: 'Vant React', + + // which icons should be generated (see https://github.com/haydenbleasel/favicons#usage) + icons: { + android: true, + appleIcon: true, + appleStartup: true, + coast: false, + favicons: true, + firefox: true, + opengraph: false, + twitter: false, + yandex: false, + windows: false, + }, + }), + ], +}); diff --git a/src/components/Button/README.zh-CN.md b/src/components/Button/README.zh-CN.md new file mode 100644 index 000000000..0ac75fea2 --- /dev/null +++ b/src/components/Button/README.zh-CN.md @@ -0,0 +1,155 @@ +# Button 按钮 + +### 引入 + +```js +import { Button } from 'vant-react'; + +``` + +## 代码演示 + +### 按钮类型 + +支持`default`、`primary`、`info`、`warning`、`danger`五种类型,默认为`default` + +```html + + + + + +``` + +### 朴素按钮 + +通过`plain`属性将按钮设置为朴素按钮,朴素按钮的文字为按钮颜色,背景为白色。 + +```html + + +``` + +### 细边框 + +设置`hairline`属性可以开启 0.5px 边框,基于伪类实现 + +```html + + +``` + +### 禁用状态 + +通过`disabled`属性来禁用按钮,禁用状态下按钮不可点击 + +```html + + +``` + +### 加载状态 + +通过`loading`属性设置按钮为加载状态,加载状态下默认会隐藏按钮文字,可以通过`loading-text`设置加载状态下的文字 + +```html + + +``` + +### 图标按钮 + +通过`icon`属性设置按钮图标,支持 Icon 组件里的所有图标,也可以传入图标 URL + +```html + + +``` + +### 按钮尺寸 + +支持`large`、`normal`、`small`、`mini`四种尺寸,默认为`normal` + +```html + + + + +``` + +### 块级元素 + +按钮在默认情况下为行内块级元素,通过`block`属性可以将按钮的元素类型设置为块级元素 + +```html + +``` + +### 页面导航 + +可以通过`url`属性进行 URL 跳转,或通过`to`属性进行路由跳转 + +```html + + +``` + +### 自定义颜色 + +通过`color`属性可以自定义按钮的颜色 + +```html + + + +``` + +## API + +### Props + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| type | 类型,可选值为 `primary` `info` `warning` `danger` | _string_ | `default` | +| size | 尺寸,可选值为 `large` `small` `mini` | _string_ | `normal` | +| text | 按钮文字 | _string_ | - | +| color `v2.1.8` | 按钮颜色,支持传入`linear-gradient`渐变色 | _string_ | - | +| icon | 左侧[图标名称](#/zh-CN/icon)或图片链接 | _string_ | - | +| icon-prefix `v2.6.0` | 图标类名前缀,同 Icon 组件的 [class-prefix 属性](#/zh-CN/icon#props) | _string_ | `van-icon` | +| tag | 根节点的 HTML 标签 | _string_ | `button` | +| native-type | 原生 button 标签的 type 属性 | _string_ | - | +| block | 是否为块级元素 | _boolean_ | `false` | +| plain | 是否为朴素按钮 | _boolean_ | `false` | +| square | 是否为方形按钮 | _boolean_ | `false` | +| round | 是否为圆形按钮 | _boolean_ | `false` | +| disabled | 是否禁用按钮 | _boolean_ | `false` | +| hairline | 是否使用 0.5px 边框 | _boolean_ | `false` | +| loading | 是否显示为加载状态 | _boolean_ | `false` | +| loading-text | 加载状态提示文字 | _string_ | - | +| loading-type | [加载图标类型](#/zh-CN/loading),可选值为`spinner` | _string_ | `circular` | +| loading-size | 加载图标大小 | _string_ | `20px` | +| url | 点击后跳转的链接地址 | _string_ | - | +| to | 点击后跳转的目标路由对象,同 vue-router 的 [to 属性](https://router.vuejs.org/zh/api/#to) | _string \| object_ | - | +| replace | 是否在跳转时替换当前页面历史 | _boolean_ | `false` | + +### Events + +| 事件名 | 说明 | 回调参数 | +| ---------- | ---------------------------------------- | ------------------- | +| click | 点击按钮,且按钮状态不为加载或禁用时触发 | _event: Event_ | +| touchstart | 开始触摸按钮时触发 | _event: TouchEvent_ | diff --git a/src/components/Cell/README.zh-CN.md b/src/components/Cell/README.zh-CN.md new file mode 100644 index 000000000..e956ba282 --- /dev/null +++ b/src/components/Cell/README.zh-CN.md @@ -0,0 +1 @@ +# Cell 单元格 \ No newline at end of file diff --git a/src/components/Field/README.zh-CN.md b/src/components/Field/README.zh-CN.md new file mode 100644 index 000000000..0eeea5d5e --- /dev/null +++ b/src/components/Field/README.zh-CN.md @@ -0,0 +1 @@ +# Field 输入框 \ No newline at end of file diff --git a/src/components/Icons/README.zh-CN.md b/src/components/Icons/README.zh-CN.md new file mode 100644 index 000000000..ae377af89 --- /dev/null +++ b/src/components/Icons/README.zh-CN.md @@ -0,0 +1 @@ +# Icon 图标 \ No newline at end of file diff --git a/src/components/Navbar/README.zh-CN.md b/src/components/Navbar/README.zh-CN.md new file mode 100644 index 000000000..99b99cb0e --- /dev/null +++ b/src/components/Navbar/README.zh-CN.md @@ -0,0 +1 @@ +# Navbar 导航栏 \ No newline at end of file diff --git a/src/components/Popup/README.zh-CN.md b/src/components/Popup/README.zh-CN.md new file mode 100644 index 000000000..23d8a72c2 --- /dev/null +++ b/src/components/Popup/README.zh-CN.md @@ -0,0 +1 @@ +# Popup 弹出层 \ No newline at end of file diff --git a/src/components/Rate/README.zh-CN.md b/src/components/Rate/README.zh-CN.md new file mode 100644 index 000000000..0593d74da --- /dev/null +++ b/src/components/Rate/README.zh-CN.md @@ -0,0 +1 @@ +# Rate 评分 \ No newline at end of file diff --git a/src/components/Search/README.zh-CN.md b/src/components/Search/README.zh-CN.md new file mode 100644 index 000000000..03502e87e --- /dev/null +++ b/src/components/Search/README.zh-CN.md @@ -0,0 +1 @@ +# Search 搜索 diff --git a/src/components/Tag/README.zh-CN.md b/src/components/Tag/README.zh-CN.md new file mode 100644 index 000000000..e2b417eed --- /dev/null +++ b/src/components/Tag/README.zh-CN.md @@ -0,0 +1 @@ +# Tag 标签 From 756b5ef9bbfb412360ea22c6ec5d0925e47025ca Mon Sep 17 00:00:00 2001 From: liuziyang Date: Fri, 10 Jul 2020 00:16:34 +0800 Subject: [PATCH 2/5] optimize document site generation --- docs/THEME_zh-CN.md | 1 - docs/changelog_zh-CN.md | 1 + ...TRIBUTING_zh-CN.md => contribute_zh-CN.md} | 0 docs/{I18N_zh-CN.md => locale_zh-CN.md} | 0 docs/quickstart_zh-CN.md | 24 +++ site/.gitignore | 3 +- site/configs/demo.config.js | 14 -- site/configs/doc.config.js | 148 ------------------ site/configs/nav.config.js | 119 ++++++++++++++ site/package.json | 5 +- site/scripts/constants.js | 15 ++ site/scripts/dev.js | 12 -- site/scripts/gather.js | 58 ------- site/scripts/gen-desktop-shared.js | 68 ++++++++ site/scripts/gen-mobile-shared.js | 97 ++++++++++++ site/scripts/gen-shared.js | 6 + site/scripts/utils.js | 47 ++++++ site/src/404.html | 41 ----- site/src/desktop/App.js | 80 +++------- site/src/desktop/components/content/index.js | 14 +- site/src/desktop/components/header/index.js | 14 +- .../src/desktop/components/i18n/CNWrapper.tsx | 18 --- .../src/desktop/components/i18n/USWrapper.tsx | 18 --- site/src/desktop/components/i18n/index.ts | 2 - site/src/desktop/components/i18n/types.ts | 5 - site/src/desktop/components/loadable/index.js | 15 -- .../desktop/components/loadable/loading.js | 36 ----- .../desktop/components/loadable/style.scss | 55 ------- .../desktop/components/router-context-type.js | 11 -- site/src/desktop/components/side-nav/index.js | 44 +----- site/src/desktop/index.html | 10 -- site/src/desktop/index.js | 11 +- site/src/desktop/router.config.js | 72 +++++---- site/src/simulator/App.js | 26 ++- site/src/simulator/pages/DemoHome.tsx | 7 + site/src/simulator/utils.js | 37 +++++ site/utils/index.js | 10 ++ site/webpack/webpack.config.js | 7 - 38 files changed, 545 insertions(+), 606 deletions(-) delete mode 100644 docs/THEME_zh-CN.md create mode 100644 docs/changelog_zh-CN.md rename docs/{CONTRIBUTING_zh-CN.md => contribute_zh-CN.md} (100%) rename docs/{I18N_zh-CN.md => locale_zh-CN.md} (100%) create mode 100644 docs/quickstart_zh-CN.md delete mode 100644 site/configs/demo.config.js create mode 100644 site/configs/nav.config.js create mode 100644 site/scripts/constants.js delete mode 100644 site/scripts/gather.js create mode 100644 site/scripts/gen-desktop-shared.js create mode 100644 site/scripts/gen-mobile-shared.js create mode 100644 site/scripts/gen-shared.js create mode 100644 site/scripts/utils.js delete mode 100644 site/src/404.html delete mode 100644 site/src/desktop/components/i18n/CNWrapper.tsx delete mode 100644 site/src/desktop/components/i18n/USWrapper.tsx delete mode 100644 site/src/desktop/components/i18n/index.ts delete mode 100644 site/src/desktop/components/i18n/types.ts delete mode 100644 site/src/desktop/components/loadable/index.js delete mode 100644 site/src/desktop/components/loadable/loading.js delete mode 100644 site/src/desktop/components/loadable/style.scss delete mode 100644 site/src/desktop/components/router-context-type.js create mode 100644 site/src/simulator/pages/DemoHome.tsx create mode 100644 site/src/simulator/utils.js create mode 100644 site/utils/index.js diff --git a/docs/THEME_zh-CN.md b/docs/THEME_zh-CN.md deleted file mode 100644 index 254266ca3..000000000 --- a/docs/THEME_zh-CN.md +++ /dev/null @@ -1 +0,0 @@ -## 定制主题 diff --git a/docs/changelog_zh-CN.md b/docs/changelog_zh-CN.md new file mode 100644 index 000000000..2d291ae49 --- /dev/null +++ b/docs/changelog_zh-CN.md @@ -0,0 +1 @@ +## 更新日志 \ No newline at end of file diff --git a/docs/CONTRIBUTING_zh-CN.md b/docs/contribute_zh-CN.md similarity index 100% rename from docs/CONTRIBUTING_zh-CN.md rename to docs/contribute_zh-CN.md diff --git a/docs/I18N_zh-CN.md b/docs/locale_zh-CN.md similarity index 100% rename from docs/I18N_zh-CN.md rename to docs/locale_zh-CN.md diff --git a/docs/quickstart_zh-CN.md b/docs/quickstart_zh-CN.md new file mode 100644 index 000000000..1e8aa0de9 --- /dev/null +++ b/docs/quickstart_zh-CN.md @@ -0,0 +1,24 @@ +## 快速上手 + +### Install + +``` bash +# Using npm +npm i vant-react -S + +# Using yarn +yarn add vant-react +``` + +### Quickstart + +``` js +import React from 'react'; +import { Button } from 'vant-react'; +import 'vant-react/dist/index.css'; + +const App = () => { + return ( +
diff --git a/site/src/desktop/components/header/index.js b/site/src/desktop/components/header/index.js index 98e4d3cc0..7e194e931 100644 --- a/site/src/desktop/components/header/index.js +++ b/site/src/desktop/components/header/index.js @@ -1,9 +1,8 @@ import React, { Component } from 'react'; - +import { withRouter } from 'react-router-dom'; import SearchBox from '../search-box'; -import RouterContext from '../router-context-type'; - -import docConfigs, { versions } from '../../../../configs/doc.config'; +import { versions } from '../../../../configs/doc.config'; +import navConfigs from '../../../../configs/nav.config'; import './style.scss'; @@ -12,8 +11,7 @@ const CONTROLLS = { 'en-US': '中文', }; -export default class PageHeader extends Component { - static contextTypes = RouterContext; +class Header extends Component { toggle = () => { const { replace } = this.context.router.history; @@ -28,7 +26,7 @@ export default class PageHeader extends Component { render() { const { i18n, sideNavData } = this.props; - const { header } = docConfigs[i18n]; + const { header } = navConfigs[i18n]; const { nav, logo } = header; @@ -78,3 +76,5 @@ export default class PageHeader extends Component { ); } } + +export default withRouter(Header); \ No newline at end of file diff --git a/site/src/desktop/components/i18n/CNWrapper.tsx b/site/src/desktop/components/i18n/CNWrapper.tsx deleted file mode 100644 index 91b43bf68..000000000 --- a/site/src/desktop/components/i18n/CNWrapper.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import * as React from 'react'; -import Layout from '../content'; - -import { II18nProps } from './types'; - -export default class CNWrapper extends React.Component { - componentDidMount() { - const { changeI18N, i18n } = this.props.pass; - if (i18n !== 'zh-CN') { - changeI18N('zh-CN'); - } - } - - render() { - const { children, pass } = this.props; - return {children}; - } -} diff --git a/site/src/desktop/components/i18n/USWrapper.tsx b/site/src/desktop/components/i18n/USWrapper.tsx deleted file mode 100644 index ef20a5479..000000000 --- a/site/src/desktop/components/i18n/USWrapper.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import * as React from 'react'; -import Layout from '../content'; - -import { II18nProps } from './types'; - -export default class CNWrapper extends React.Component { - componentDidMount() { - const { changeI18N, i18n } = this.props.pass; - if (i18n !== 'en-US') { - changeI18N('en-US'); - } - } - - render() { - const { children, pass } = this.props; - return {children}; - } -} diff --git a/site/src/desktop/components/i18n/index.ts b/site/src/desktop/components/i18n/index.ts deleted file mode 100644 index 0ff1e8399..000000000 --- a/site/src/desktop/components/i18n/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -import CNWrapper from './CNWrapper'; -import USWrapper from './USWrapper'; diff --git a/site/src/desktop/components/i18n/types.ts b/site/src/desktop/components/i18n/types.ts deleted file mode 100644 index 0fcc4a84e..000000000 --- a/site/src/desktop/components/i18n/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface II18nProps { - children: React.ReactChild; - pass: any; -} - diff --git a/site/src/desktop/components/loadable/index.js b/site/src/desktop/components/loadable/index.js deleted file mode 100644 index 0e0bc282a..000000000 --- a/site/src/desktop/components/loadable/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import Loadable from 'react-loadable'; -import DocLoading from './loading'; - -export default function DocLoadable(opts) { - return Loadable( - Object.assign( - { - loading: DocLoading, - delay: 200, // Avoiding Flash Of Loading Component - timeout: 5000, // 5 seconds - }, - opts - ) - ); -} diff --git a/site/src/desktop/components/loadable/loading.js b/site/src/desktop/components/loadable/loading.js deleted file mode 100644 index d5d07a6b1..000000000 --- a/site/src/desktop/components/loadable/loading.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; - -import './style.scss'; - -export default function DocLoading({ error, timedOut, pastDelay }) { - if (error) { - return ; - } - - if (timedOut) { - return ; - } - - if (pastDelay) { - return ; - } - - return null; -} - -function Loading() { - return ( -
-
-
-
-
-
- ); -} - -function Error() { - return ( -
Oops! An error occurred.
- ); -} diff --git a/site/src/desktop/components/loadable/style.scss b/site/src/desktop/components/loadable/style.scss deleted file mode 100644 index 20d5cd640..000000000 --- a/site/src/desktop/components/loadable/style.scss +++ /dev/null @@ -1,55 +0,0 @@ -@keyframes van-doc-loading-ripple { - 0% { - top: 96px; - left: 96px; - width: 0; - height: 0; - opacity: 1; - } - - 100% { - top: 18px; - left: 18px; - width: 156px; - height: 156px; - opacity: 0; - } -} - -.van-doc-loading { - margin-top: 150px; -} - -.van-doc-loading-error { - margin: 20px; - font-size: 18px; - color: #e33; -} - -.van-doc-loading-ripple div { - box-sizing: content-box; - position: absolute; - border-width: 4px; - border-style: solid; - opacity: 1; - border-radius: 50%; - animation: van-doc-loading-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) - infinite; -} - -.van-doc-loading-ripple div:nth-child(1) { - border-color: #27f; -} - -.van-doc-loading-ripple div:nth-child(2) { - border-color: #bdf; - animation-delay: -0.5s; -} - -.van-doc-loading-ripple { - position: relative; - margin: 0 auto; - width: 200px; - height: 200px; - transform: translate(-100px, -100px) scale(1) translate(100px, 100px); -} diff --git a/site/src/desktop/components/router-context-type.js b/site/src/desktop/components/router-context-type.js deleted file mode 100644 index 7e119f837..000000000 --- a/site/src/desktop/components/router-context-type.js +++ /dev/null @@ -1,11 +0,0 @@ -import PropTypes from 'prop-types'; - -export default { - router: PropTypes.shape({ - history: PropTypes.shape({ - push: PropTypes.func.isRequired, - replace: PropTypes.func.isRequired, - }).isRequired, - route: PropTypes.object, - }).isRequired, -}; diff --git a/site/src/desktop/components/side-nav/index.js b/site/src/desktop/components/side-nav/index.js index e77d8187b..f2c0767d5 100644 --- a/site/src/desktop/components/side-nav/index.js +++ b/site/src/desktop/components/side-nav/index.js @@ -2,21 +2,11 @@ import React, { Component } from 'react'; import { NavLink } from 'react-router-dom'; import classnames from 'classnames'; -import RouterContext from '../router-context-type'; import { prefix } from '../../../../constants'; import './style.scss'; export default class SideNav extends Component { - static contextTypes = RouterContext; - - handleTitleClick = item => { - if (item.list[0].list[0].path) { - this.context.router.history.push( - getFullPath(this.props.base, item.list[0].list[0].path) - ); - } - }; parseData = (item, index) => (
@@ -27,13 +17,6 @@ export default class SideNav extends Component {
); - parseGroup = (group, index) => ( -
-
{group.groupName}
- {group.list.map(this.parseList)} -
- ); - parseList = (navItem, index) => { const { title, subtitle, hidden, link } = navItem; @@ -51,22 +34,13 @@ export default class SideNav extends Component { return navItem.disabled ? null : (
- {navItem.link ? ( - - {title} - - ) : ( - - {linkTitle} - - )} + + {linkTitle} +
); }; @@ -82,7 +56,3 @@ export default class SideNav extends Component { ); } } - -function getFullPath(base, path) { - return `${base}/${path}`; -} diff --git a/site/src/desktop/index.html b/site/src/desktop/index.html index c34f8886a..9b30ea073 100644 --- a/site/src/desktop/index.html +++ b/site/src/desktop/index.html @@ -112,16 +112,6 @@
Loading...
- - diff --git a/site/src/desktop/index.js b/site/src/desktop/index.js index c80d41492..b8057e57d 100644 --- a/site/src/desktop/index.js +++ b/site/src/desktop/index.js @@ -12,18 +12,11 @@ const render = ChildComponent => { , - document.getElementById('app-container') // eslint-disable-line + document.getElementById('app-container') ); }; -// Add a delay in dev mode to ensure styles are loaded before executing any JavaScript code -if (process.env.NODE_ENV !== 'production') { - setTimeout(() => { - render(App); - }, 500); -} else { - render(App); -} +render(App); if (module.hot) { module.hot.accept('./App', () => { diff --git a/site/src/desktop/router.config.js b/site/src/desktop/router.config.js index 9a0fdc806..fe28f6f4b 100644 --- a/site/src/desktop/router.config.js +++ b/site/src/desktop/router.config.js @@ -1,32 +1,50 @@ -const registerRoute = (navData, oreo = '') => { - let route = []; - - if (!navData) return route; - - function addRoute(page) { - const { path, source, title } = page; - route.push({ - path: `${oreo}/${path}`, - source, - title, - }); +import configs from '../../configs/nav.config'; +import { documents } from '../../configs/site-desktop-shared'; + +function decamelize(str, sep = '-') { + return str + .replace(/([a-z\d])([A-Z])/g, '$1' + sep + '$2') + .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + sep + '$2') + .toLowerCase(); +} + +function parseName(name) { + if (name.indexOf('_') !== -1) { + const pairs = name.split('_'); + const component = pairs.shift(); + + return { + component: `${decamelize(component)}`, + lang: pairs.join('-'), + }; } - navData.forEach(nav => { - if (nav.list) { - nav.list.forEach(subNav => { - addRoute(subNav); - }); - } else if (nav.children) { - nav.children.forEach(subNav => { - addRoute(subNav); - }); - } else { - addRoute(nav); + return { + component: `${decamelize(name)}`, + lang: '', + }; +} + + +function getRoutes() { + const routes = []; + const names = Object.keys(documents); + + + + names.forEach(name => { + const { component, lang } = parseName(name); + + if (lang) { + routes.push({ + name: `${lang}/${component}`, + path: `/${lang}/${component}`, + component: documents[name], + }) } - }); + }) - return route; -}; + return routes; +} -export { registerRoute }; +export default getRoutes; diff --git a/site/src/simulator/App.js b/site/src/simulator/App.js index 75b54ecf5..5547ddabd 100644 --- a/site/src/simulator/App.js +++ b/site/src/simulator/App.js @@ -1,12 +1,28 @@ import * as React from 'react'; -import DemoConfig from '../../configs/demo.config'; +import { + HashRouter as Router, + Route, + Switch, + Redirect +} from 'react-router-dom'; +import { getRoutes } from './utils'; +import DemoHome from './pages/DemoHome'; -const App = () => { - const componentName = window.location.hash.replace('#/api/', ''); +const routes = getRoutes(); + +console.log(routes); - const DemoComponent = DemoConfig[componentName]; +const App = () => { return ( - + + + + { + routes.map(item => ) + } + + + ); } diff --git a/site/src/simulator/pages/DemoHome.tsx b/site/src/simulator/pages/DemoHome.tsx new file mode 100644 index 000000000..d4ff89a24 --- /dev/null +++ b/site/src/simulator/pages/DemoHome.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +function DemoHome() { + return
DemoHome
; +} + +export default DemoHome; \ No newline at end of file diff --git a/site/src/simulator/utils.js b/site/src/simulator/utils.js new file mode 100644 index 000000000..530d86466 --- /dev/null +++ b/site/src/simulator/utils.js @@ -0,0 +1,37 @@ +import { demos, config } from '../../configs/site-mobile-shared'; +import { decamelize } from '../../utils'; + +export function getRoutes() { + const routes = []; + const names = Object.keys(demos); + const langs = config ? Object.keys(config) : []; + + names.forEach(name => { + const component = decamelize(name); + + if (langs.length) { + langs.forEach(lang => { + routes.push({ + name: `${lang}/${component}`, + path: `/${lang}/${component}`, + component: demos[name], + meta: { + name, + lang, + }, + }); + }); + } else { + routes.push({ + name, + path: `/${component}`, + component: demos[name], + meta: { + name, + }, + }); + } + }); + + return routes; +} diff --git a/site/utils/index.js b/site/utils/index.js new file mode 100644 index 000000000..257487164 --- /dev/null +++ b/site/utils/index.js @@ -0,0 +1,10 @@ +function decamelize(str, sep = '-') { + return str + .replace(/([a-z\d])([A-Z])/g, '$1' + sep + '$2') + .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + sep + '$2') + .toLowerCase(); +} + +module.exports = { + decamelize, +} diff --git a/site/webpack/webpack.config.js b/site/webpack/webpack.config.js index 3dc6ab789..bef559d2c 100644 --- a/site/webpack/webpack.config.js +++ b/site/webpack/webpack.config.js @@ -175,13 +175,6 @@ module.exports = { inject: 'body', }), - new HtmlWebpackPlugin({ - template: 'src/404.html', - filename: '404.html', - chunks: [], - inject: false, - }), - new MiniCssExtractPlugin({ filename: DEV ? '[name].css' : '[name]-[contenthash].css', chunkFilename: DEV ? '[id].css' : '[id].[contenthash].css', From 702b8543b0b4848e71ac23cc6b1eb93540e049e5 Mon Sep 17 00:00:00 2001 From: liuziyang Date: Fri, 10 Jul 2020 23:47:24 +0800 Subject: [PATCH 3/5] fix: update document site config --- package.json | 2 +- site/tsconfig.json | 41 ++++++++++++++++++++++++++++------------- tsconfig.json | 3 ++- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 8e56f81ca..d4601e4d1 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "deploy": "cd site && yarn deploy", "lint": "eslint --ext .tsx ./src", "lint:watch": "esw --watch --fix --ext .tsx ./src", - "storybook": "start-storybook -p 9009", + "storybook": "start-storybook", "build-storybook": "build-storybook" }, "peerDependencies": { diff --git a/site/tsconfig.json b/site/tsconfig.json index 875c04865..c73ab144f 100644 --- a/site/tsconfig.json +++ b/site/tsconfig.json @@ -1,23 +1,38 @@ { "compilerOptions": { - "target": "es5", - "module": "es2015", - "moduleResolution": "node", - "allowSyntheticDefaultImports": true, "outDir": "dist", - "jsx": "react", - "allowJs": true, + "module": "esnext", "lib": [ "dom", - "es2017" + "esnext" ], + "moduleResolution": "node", + "jsx": "react", + "sourceMap": true, + "declaration": true, + "esModuleInterop": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": false, + "strictNullChecks": true, + "suppressImplicitAnyIndexErrors": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "allowSyntheticDefaultImports": true, + "target": "es5", + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true }, - "exclude": [ - "lib", - "es", - ], "include": [ - "src", - "../src", + "src" + ], + "exclude": [ + "node_modules", + "dist", ] } diff --git a/tsconfig.json b/tsconfig.json index c079083e9..90d6838cd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -34,6 +34,7 @@ "exclude": [ "node_modules", "dist", - "demo" + "demo", + "site" ] } From ee45bb4e1156f9a50063e7be4f70defd9b793d7d Mon Sep 17 00:00:00 2001 From: liuziyang Date: Sun, 19 Jul 2020 23:27:40 +0800 Subject: [PATCH 4/5] fix: update doc site config --- docs/MARKDOWN_zh-CN.md | 1 - ...{changelog_zh-CN.md => changelog.zh-CN.md} | 0 ...ontribute_zh-CN.md => contribute.zh-CN.md} | 0 docs/{locale_zh-CN.md => locale.zh-CN.md} | 0 docs/markdown.zh-CN.md | 19 +++++++++++++++++++ ...uickstart_zh-CN.md => quickstart.zh-CN.md} | 0 package.json | 2 +- site/scripts/gen-desktop-shared.js | 2 +- site/src/desktop/App.js | 6 ------ site/src/simulator/App.js | 2 -- 10 files changed, 21 insertions(+), 11 deletions(-) delete mode 100644 docs/MARKDOWN_zh-CN.md rename docs/{changelog_zh-CN.md => changelog.zh-CN.md} (100%) rename docs/{contribute_zh-CN.md => contribute.zh-CN.md} (100%) rename docs/{locale_zh-CN.md => locale.zh-CN.md} (100%) create mode 100644 docs/markdown.zh-CN.md rename docs/{quickstart_zh-CN.md => quickstart.zh-CN.md} (100%) diff --git a/docs/MARKDOWN_zh-CN.md b/docs/MARKDOWN_zh-CN.md deleted file mode 100644 index 2a18abb8f..000000000 --- a/docs/MARKDOWN_zh-CN.md +++ /dev/null @@ -1 +0,0 @@ -## 组件文档如何编写 diff --git a/docs/changelog_zh-CN.md b/docs/changelog.zh-CN.md similarity index 100% rename from docs/changelog_zh-CN.md rename to docs/changelog.zh-CN.md diff --git a/docs/contribute_zh-CN.md b/docs/contribute.zh-CN.md similarity index 100% rename from docs/contribute_zh-CN.md rename to docs/contribute.zh-CN.md diff --git a/docs/locale_zh-CN.md b/docs/locale.zh-CN.md similarity index 100% rename from docs/locale_zh-CN.md rename to docs/locale.zh-CN.md diff --git a/docs/markdown.zh-CN.md b/docs/markdown.zh-CN.md new file mode 100644 index 000000000..5bb6f60d8 --- /dev/null +++ b/docs/markdown.zh-CN.md @@ -0,0 +1,19 @@ +## 组件文档如何编写 + +### 介绍 + +文档网站分为文档展示和demo展示,文档通过收集组件目录下的 `README.[locale].md`, ` locale ` 的值是国际化的标识,和 ` ./site/configs/nav.config.js ` 中的 ` key `保持一致。 + +### 添加文档配置 + +在` ./site/configs/nav.config.js ` 添加新的文档页面配置,包括标题和` path `,标题会在左侧导航展示,` path`用来生成页面路由. + +### 添加文档文件 + +在组件目录中新增 `README.zh-CN.md`,用来展示组件文档,使用 ` markdown ` 文件语法。 + +### 添加文档 demo 文件 + +在组件目录中新增 ` demo/index.tsx ` 作为文档的页面,然后就可以愉快的编写 demo 了。 +由于每次在运行` yarn storybook `时才会收集所有文档信息生成文档配置,所以每次新增的文档文件需要重新运行` yarn storybook `。 + diff --git a/docs/quickstart_zh-CN.md b/docs/quickstart.zh-CN.md similarity index 100% rename from docs/quickstart_zh-CN.md rename to docs/quickstart.zh-CN.md diff --git a/package.json b/package.json index d4601e4d1..47a88d4c7 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "deploy": "cd site && yarn deploy", "lint": "eslint --ext .tsx ./src", "lint:watch": "esw --watch --fix --ext .tsx ./src", - "storybook": "start-storybook", + "storybook": "cd site && yarn dev && start-storybook", "build-storybook": "build-storybook" }, "peerDependencies": { diff --git a/site/scripts/gen-desktop-shared.js b/site/scripts/gen-desktop-shared.js index d8227e410..3a1b8a685 100644 --- a/site/scripts/gen-desktop-shared.js +++ b/site/scripts/gen-desktop-shared.js @@ -33,7 +33,7 @@ function resolveDocuments(components) { }) const staticDocs = glob.sync(normalizePath(path.join(DOCS_DIR, '**/*.md'))).map(p => { - const pairs = path.parse(p).name.split('_'); + const pairs = path.parse(p).name.split('.'); return { name: formatName(pairs[0], pairs[1] || 'zh-CN'), path: p, diff --git a/site/src/desktop/App.js b/site/src/desktop/App.js index 3aa32c0b7..a9df97042 100644 --- a/site/src/desktop/App.js +++ b/site/src/desktop/App.js @@ -29,18 +29,12 @@ export default class App extends Component { const { i18n } = this.state; const sideNavData = navConfig[i18n].nav; const passthrough = i18nStr => ({ - // 奥利奥,路由路径中的夹层。 - oreo: `/${i18nStr.split('-')[0]}`, version: pkgVersion, sideNavData: sideNavData, changeI18N: this.changeI18N, prefix, i18n, }); - - console.log(routes) - - // 通过 basename 控制前缀,不要放到每一层路由里去 return ( diff --git a/site/src/simulator/App.js b/site/src/simulator/App.js index 5547ddabd..b74490f20 100644 --- a/site/src/simulator/App.js +++ b/site/src/simulator/App.js @@ -10,8 +10,6 @@ import DemoHome from './pages/DemoHome'; const routes = getRoutes(); -console.log(routes); - const App = () => { return ( From bfbf77ebaeeb2a01c23547493049a231b599b49f Mon Sep 17 00:00:00 2001 From: liuziyang Date: Sun, 19 Jul 2020 23:31:15 +0800 Subject: [PATCH 5/5] fix: update ./doc/markdown.md --- docs/markdown.zh-CN.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/markdown.zh-CN.md b/docs/markdown.zh-CN.md index 5bb6f60d8..bf130bfc7 100644 --- a/docs/markdown.zh-CN.md +++ b/docs/markdown.zh-CN.md @@ -4,6 +4,10 @@ 文档网站分为文档展示和demo展示,文档通过收集组件目录下的 `README.[locale].md`, ` locale ` 的值是国际化的标识,和 ` ./site/configs/nav.config.js ` 中的 ` key `保持一致。 +### 使用 + +文档网站替换了storybook 自带的文档生成,在运行 ` yarn storybook ` 自动收集文档和demo的信息,生成对应的页面,并且自动在浏览器中打开页面,对于开发过程中的使用基本上没有太大的影响 + ### 添加文档配置 在` ./site/configs/nav.config.js ` 添加新的文档页面配置,包括标题和` path `,标题会在左侧导航展示,` path`用来生成页面路由.