diff --git a/.gitignore b/.gitignore index f22afe06b97a..2540e0990380 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,6 @@ nohup.out _site _data dist -lib +/lib elasticsearch-* config/base.yaml diff --git a/404.html b/404.html deleted file mode 100644 index 9f70f7e6a07e..000000000000 --- a/404.html +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/components/icon/index.md b/components/icon/index.md index 2dc7a75b5285..033b12f90e6a 100644 --- a/components/icon/index.md +++ b/components/icon/index.md @@ -40,7 +40,7 @@ english: Icon ### 一. 方向性图标 ```__react -import IconSet from '../../site/component/IconSet'; +import IconSet from 'site/theme/template/IconSet'; const icons1 = ['step-backward', 'step-forward', 'fast-backward', 'fast-forward', 'shrink', 'arrow-salt', 'down', 'up', 'left', 'right', 'caret-down', 'caret-up', 'caret-left', 'caret-right', 'caret-circle-right', 'caret-circle-left', 'caret-circle-o-right', 'caret-circle-o-left', 'circle-right', 'circle-left', 'circle-o-right', 'circle-o-left', 'double-right', 'double-left', 'verticle-right', 'verticle-left', 'forward', 'backward', 'rollback', 'enter', 'retweet', 'swap', 'swap-left', 'swap-right', 'arrow-right', 'arrow-up', 'arrow-down', 'arrow-left', 'play-circle', 'play-circle-o', 'circle-up', 'circle-down', 'circle-o-up', 'circle-o-down', 'caret-circle-o-up', 'caret-circle-o-down', 'caret-circle-up', 'caret-circle-down']; ReactDOM.render(, mountNode); diff --git a/components/input-number/demo/digit.md b/components/input-number/demo/digit.md index 01a9dad75277..e3cdc4b9788c 100644 --- a/components/input-number/demo/digit.md +++ b/components/input-number/demo/digit.md @@ -1,6 +1,6 @@ --- -- order: 3 -- title: 小数 +order: 3 +title: 小数 --- 和原生的数字输入框一样,value 的精度由 step 的小数位数决定。 diff --git a/components/locale-provider/demo/all.md b/components/locale-provider/demo/all.md index 4253a821073d..22bb9730907b 100644 --- a/components/locale-provider/demo/all.md +++ b/components/locale-provider/demo/all.md @@ -9,7 +9,6 @@ title: 所有组件 import { LocaleProvider, Pagination, DatePicker, TimePicker, Calendar, Popconfirm, Table, Modal, Button, Select, Transfer, Radio } from 'antd'; import enUS from 'antd/lib/locale-provider/en_US'; -import ruRU from 'antd/lib/locale-provider/ru_RU'; const Option = Select.Option; const RangePicker = DatePicker.RangePicker; @@ -110,7 +109,6 @@ const App = React.createClass({ Change locale of components: English - русский язык 中文 diff --git a/docs/spec/colors.md b/docs/spec/colors.md index 9a9e0f84ab3a..03d74f5d3678 100644 --- a/docs/spec/colors.md +++ b/docs/spec/colors.md @@ -157,6 +157,7 @@ ReactDOM.render(, mountNode); `````__react const Values = require('values.js'); const CopyToClipboard = require('react-copy-to-clipboard'); +const antd = require('antd'); const Button = antd.Button; const InputNumber = antd.InputNumber; const Slider = antd.Slider; diff --git a/docs/spec/layout/demo/aside-collapse.md b/docs/spec/layout/demo/aside-collapse.md index c5ac3500be3e..d61761200e66 100644 --- a/docs/spec/layout/demo/aside-collapse.md +++ b/docs/spec/layout/demo/aside-collapse.md @@ -7,7 +7,7 @@ title: 可收起展开的侧边导航 ````jsx import { Menu, Breadcrumb, Icon } from 'antd'; -import BrowserDemo from 'site/component/BrowserDemo'; +import BrowserDemo from 'site/theme/template/BrowserDemo'; const SubMenu = Menu.SubMenu; const AsideCollapse = React.createClass({ diff --git a/docs/spec/layout/demo/aside.md b/docs/spec/layout/demo/aside.md index f6ad090b5b4a..f1d00763b745 100644 --- a/docs/spec/layout/demo/aside.md +++ b/docs/spec/layout/demo/aside.md @@ -9,7 +9,7 @@ title: 侧边导航 ````jsx import { Menu, Breadcrumb, Icon } from 'antd'; -import BrowserDemo from 'site/component/BrowserDemo'; +import BrowserDemo from 'site/theme/template/BrowserDemo'; const SubMenu = Menu.SubMenu; ReactDOM.render( diff --git a/docs/spec/layout/demo/ceiling.md b/docs/spec/layout/demo/ceiling.md index 97822900dd5e..2b15ce835c84 100644 --- a/docs/spec/layout/demo/ceiling.md +++ b/docs/spec/layout/demo/ceiling.md @@ -9,7 +9,7 @@ title: 吊顶规范 ````jsx import { Menu, Breadcrumb } from 'antd'; -import BrowserDemo from 'site/component/BrowserDemo'; +import BrowserDemo from 'site/theme/template/BrowserDemo'; ReactDOM.render( diff --git a/docs/spec/layout/demo/top-aside.md b/docs/spec/layout/demo/top-aside.md index 6fe494f1c38a..4295dbaeadf5 100644 --- a/docs/spec/layout/demo/top-aside.md +++ b/docs/spec/layout/demo/top-aside.md @@ -7,7 +7,7 @@ title: 顶部导航 + 侧边栏 ````jsx import { Menu, Breadcrumb, Icon } from 'antd'; -import BrowserDemo from 'site/component/BrowserDemo'; +import BrowserDemo from 'site/theme/template/BrowserDemo'; const SubMenu = Menu.SubMenu; ReactDOM.render( diff --git a/docs/spec/layout/demo/top.md b/docs/spec/layout/demo/top.md index f7e3ead9fd85..c9d942c01243 100644 --- a/docs/spec/layout/demo/top.md +++ b/docs/spec/layout/demo/top.md @@ -11,7 +11,7 @@ title: 顶部导航 ````jsx import { Menu, Breadcrumb } from 'antd'; -import BrowserDemo from 'site/component/BrowserDemo'; +import BrowserDemo from 'site/theme/template/BrowserDemo'; ReactDOM.render( diff --git a/docs/spec/motion.md b/docs/spec/motion.md index 84abb4760639..82690ac8d7f7 100644 --- a/docs/spec/motion.md +++ b/docs/spec/motion.md @@ -11,6 +11,7 @@ english: Motion `````__react const cssAnimation = require('css-animation'); +const antd = require('antd'); const Select = antd.Select; const Option = Select.Option; const OptGroup = Select.OptGroup; diff --git a/package.json b/package.json index b176f260f57d..57e0beea1dc3 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,10 @@ "babel-eslint": "^6.0.2", "babel-jest": "^12.0.2", "babel-plugin-antd": "^0.4.0", + "bisheng": "^0.5.0", + "bisheng-plugin-description": "^0.1.1", + "bisheng-plugin-react": "^0.1.0", + "bisheng-plugin-toc": "0.2.0", "dom-scroll-into-view": "^1.1.0", "enquire.js": "^2.1.1", "es6-shim": "^0.35.0", @@ -104,6 +108,7 @@ "react": "^15.0.0", "react-addons-test-utils": "^15.0.0", "react-copy-to-clipboard": "^4.0.1", + "react-document-title": "^2.0.1", "react-dom": "^15.0.0", "react-github-button": "^0.1.1", "react-intl": "^2.0.1", @@ -118,7 +123,7 @@ "dist": "antd-tools run dist", "compile": "antd-tools run compile", "clean": "antd-tools run clean", - "start": "antd-tools run start", + "start": "bisheng start -c ./site/bisheng.config.js", "site": "antd-tools run site", "pre-deploy": "cp CNAME _site && rsync -R components/*/demo/*.json _site", "deploy": "antd-tools run update-self && antd-tools run deploy", diff --git a/site/component/ImagePreview/index.jsx b/site/bisheng-plugin-antd/lib/ImagePreview.jsx similarity index 100% rename from site/component/ImagePreview/index.jsx rename to site/bisheng-plugin-antd/lib/ImagePreview.jsx diff --git a/site/component/VideoPlayer/index.jsx b/site/bisheng-plugin-antd/lib/VideoPlayer.jsx similarity index 100% rename from site/component/VideoPlayer/index.jsx rename to site/bisheng-plugin-antd/lib/VideoPlayer.jsx diff --git a/site/bisheng-plugin-antd/lib/browser.js b/site/bisheng-plugin-antd/lib/browser.js new file mode 100644 index 000000000000..13236dd59763 --- /dev/null +++ b/site/bisheng-plugin-antd/lib/browser.js @@ -0,0 +1,50 @@ +const React = require('react'); +const Link = require('react-router').Link; +const toReactComponent = require('jsonml-to-react-component'); +const JsonML = require('jsonml.js/lib/utils'); +const VideoPlayer = require('./VideoPlayer'); +const ImagePreview = require('./ImagePreview'); + +function isHeading(node) { + return /h[1-6]/i.test(JsonML.getTagName(node)); +} + +module.exports = () => { + return { + converters: [ + [(node) => JsonML.isElement(node) && isHeading(node), (node, index) => { + const children = JsonML.getChildren(node); + return React.createElement(JsonML.getTagName(node), { + key: index, + id: children, + ...JsonML.getAttributes(node), + }, [ + {children.map((child) => toReactComponent(child))}, + #, + ]); + }], + [(node) => JsonML.isElement(node) && JsonML.getTagName(node) === 'video', (node, index) => + , + ], + [(node) => JsonML.isElement(node) && JsonML.getTagName(node) === 'a' && !( + JsonML.getAttributes(node).class || + (JsonML.getAttributes(node).href && + JsonML.getAttributes(node).href.indexOf('http') === 0) || + /^#/.test(JsonML.getAttributes(node).href) + ), (node, index) => { + return {toReactComponent(JsonML.getChildren(node)[0])}; + }], + [(node) => { + return JsonML.isElement(node) && + JsonML.getTagName(node) === 'p' && + JsonML.getTagName(JsonML.getChildren(node)[0]) === 'img' && + /preview-img/gi.test(JsonML.getAttributes(JsonML.getChildren(node)[0]).class); + }, (node, index) => { + const imgs = JsonML.getChildren(node) + .filter((img) => JsonML.isElement(img) && Object.keys(JsonML.getAttributes(img)).length > 0) + .map((img) => JsonML.getAttributes(img)); + return ; + }], + ], + }; +}; diff --git a/site/bisheng-plugin-antd/lib/node.js b/site/bisheng-plugin-antd/lib/node.js new file mode 100644 index 000000000000..4b11bf253c83 --- /dev/null +++ b/site/bisheng-plugin-antd/lib/node.js @@ -0,0 +1,11 @@ +const path = require('path'); +const processDoc = require('./process-doc'); +const processDemo = require('./process-demo'); + +module.exports = (markdownData) => { + const isDemo = path.dirname(markdownData.meta.filename).endsWith('/demo'); + if (isDemo) { + return processDemo(markdownData); + } + return processDoc(markdownData); +}; diff --git a/site/bisheng-plugin-antd/lib/process-demo.js b/site/bisheng-plugin-antd/lib/process-demo.js new file mode 100644 index 000000000000..4a6c4e14c2e9 --- /dev/null +++ b/site/bisheng-plugin-antd/lib/process-demo.js @@ -0,0 +1,90 @@ +const fs = require('fs'); +const path = require('path'); +const JsonML = require('jsonml.js/lib/utils'); +const pkgPath = path.join(process.cwd(), 'package.json'); +const pkgName = require(pkgPath).name; + +const nunjucks = require('nunjucks'); +nunjucks.configure({ autoescape: false }); + +const babel = require('babel-core'); +const babelrc = { + presets: ['es2015', 'react'].map((m) => { + return require.resolve(`babel-preset-${m}`); + }), +}; + +const tmpl = fs.readFileSync(path.join(__dirname, 'template.html')).toString(); + +function isStyleTag(node) { + return node && JsonML.getTagName(node) === 'style'; +} + +function getCode(node) { + return JsonML.getChildren( + JsonML.getChildren(node)[0] + )[0]; +} + +module.exports = (markdownData) => { + const meta = markdownData.meta; + meta.id = meta.filename.replace(/\.md$/, '').replace(/\//g, '-'); + + const contentChildren = JsonML.getChildren(markdownData.content); + const chineseIntroStart = contentChildren.findIndex((node) => { + return JsonML.getTagName(node) === 'h2' && + JsonML.getChildren(node)[0] === 'zh-CN'; + }); + const englishIntroStart = contentChildren.findIndex((node) => { + return JsonML.getTagName(node) === 'h2' && + JsonML.getChildren(node)[0] === 'en-US'; + }); + const codeIndex = contentChildren.findIndex((node) => { + return JsonML.getTagName(node) === 'pre' && + JsonML.getAttributes(node).lang === 'jsx'; + }); + if (chineseIntroStart > -1 /* equal to englishIntroStart > -1 */) { + markdownData.content = { + 'zh-CN': contentChildren.slice(chineseIntroStart + 1, englishIntroStart), + 'en-US': contentChildren.slice(englishIntroStart + 1, codeIndex), + }; + } else { + markdownData.content = contentChildren.slice(0, codeIndex); + } + + markdownData.highlightedCode = contentChildren[codeIndex].slice(0, 2); + const preview = [ + 'pre', { lang: '__react' }, + ]; + const componentsPath = path.join(process.cwd(), 'components'); + preview.push([ + 'code', + getCode(contentChildren[codeIndex]) + .replace(`${pkgName}/lib`, componentsPath), + ]); + markdownData.preview = preview; + + const styleNode = contentChildren.find((node) => { + return isStyleTag(node) || + (JsonML.getTagName(node) === 'pre' && JsonML.getAttributes(node).lang === 'css'); + }); + if (isStyleTag(styleNode)) { + markdownData.style = JsonML.getChildren(styleNode)[0]; + } else if (styleNode) { + markdownData.style = getCode(styleNode); + markdownData.highlightedStyle = JsonML.getAttributes(styleNode).highlighted; + } + + if (meta.iframe) { + const html = nunjucks.renderString(tmpl, { + id: meta.id, + style: markdownData.style, + script: babel.transform(getCode(markdownData.preview), babelrc).code, + }); + const fileName = `demo-${Math.random()}.html`; + fs.writeFile(path.join(process.cwd(), '_site', fileName), html); + markdownData.src = path.join('/', fileName); + } + + return markdownData; +}; diff --git a/site/bisheng-plugin-antd/lib/process-doc.js b/site/bisheng-plugin-antd/lib/process-doc.js new file mode 100644 index 000000000000..a4358c1e420b --- /dev/null +++ b/site/bisheng-plugin-antd/lib/process-doc.js @@ -0,0 +1,19 @@ +const JsonML = require('jsonml.js/lib/utils'); + +module.exports = (markdownData) => { + const contentChildren = JsonML.getChildren(markdownData.content); + const apiStartIndex = contentChildren.findIndex((node) => { + return JsonML.getTagName(node) === 'h2' && + JsonML.getChildren(node)[0] === 'API'; + }); + + if (apiStartIndex > -1) { + const content = contentChildren.slice(0, apiStartIndex); + markdownData.content = ['section'].concat(content); + + const api = contentChildren.slice(apiStartIndex); + markdownData.api = ['section'].concat(api); + } + + return markdownData; +}; diff --git a/site/bisheng-plugin-antd/lib/template.html b/site/bisheng-plugin-antd/lib/template.html new file mode 100644 index 000000000000..10ffe5729997 --- /dev/null +++ b/site/bisheng-plugin-antd/lib/template.html @@ -0,0 +1,34 @@ + + + + Demo + + + + +
+ + + + + diff --git a/site/bisheng.config.js b/site/bisheng.config.js new file mode 100644 index 000000000000..37b41a589a89 --- /dev/null +++ b/site/bisheng.config.js @@ -0,0 +1,34 @@ +const path = require('path'); + +module.exports = { + source: [ + './components', + './docs', + 'CHANGELOG.md', // TODO: fix it in bisheng + ], + theme: './site/theme', + htmlTemplate: './site/theme/static/template.html', + plugins: [ + 'bisheng-plugin-description', + 'bisheng-plugin-toc?maxDepth=2', + 'bisheng-plugin-react?lang=__react', + './site/bisheng-plugin-antd', + ], + webpackConfig(config) { + config.resolve.alias = { + antd: process.cwd(), + site: path.join(process.cwd(), 'site'), + }; + + config.babel.plugins.push([ + require.resolve('babel-plugin-antd'), + { + style: true, + libraryName: 'antd', + libDir: 'components', + }, + ]); + + return config; + }, +}; diff --git a/site/common/lib.js b/site/common/lib.js deleted file mode 100644 index a2d09a1faa2e..000000000000 --- a/site/common/lib.js +++ /dev/null @@ -1,18 +0,0 @@ -import './styles/highlight.less'; -import './styles/common.less'; -import './styles/markdown.less'; -import './styles/toc.less'; -import './styles/font.less'; -import './styles/resource.less'; -import './styles/clearfix.less'; -import './styles/demo.less'; -import './styles/page-nav.less'; -import './styles/footer.less'; -import './styles/preview-img.less'; -import './styles/mock-browser.less'; -import './styles/colors.less'; -import './styles/motion.less'; -import './styles/responsive.less'; -import './styles/new-version-info-modal.less'; - -import 'es6-shim'; diff --git a/site/common/styles/clearfix.less b/site/common/styles/clearfix.less deleted file mode 100644 index d044d0a4c393..000000000000 --- a/site/common/styles/clearfix.less +++ /dev/null @@ -1,14 +0,0 @@ -.clearfix { - zoom: 1; - &:before, - &:after { - content: " "; - display: table; - } - &:after { - clear: both; - visibility: hidden; - font-size: 0; - height: 0; - } -} \ No newline at end of file diff --git a/site/component/App.jsx b/site/component/App.jsx deleted file mode 100644 index f78a4b96d80a..000000000000 --- a/site/component/App.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import Header from './Header'; -import Footer from './Footer'; - -export default function App(props) { - return ( -
-
- {props.children} -
-
- ); -} diff --git a/site/component/Article/index.jsx b/site/component/Article/index.jsx deleted file mode 100644 index ff07921b0e91..000000000000 --- a/site/component/Article/index.jsx +++ /dev/null @@ -1,90 +0,0 @@ -import React, { Children, cloneElement } from 'react'; -import { Link } from 'react-router'; -import * as utils from '../utils'; -import { getTagName, getChildren } from 'jsonml.js/lib/utils'; -import { Timeline } from 'antd'; - -export default class Article extends React.Component { - componentDidMount() { - this.componentDidUpdate(); - } - componentDidUpdate() { - const { title, chinese, english } = this.props.content.meta; - utils.setTitle(`${title || chinese || english} - Ant Design`); - const links = Array.apply(null, document.querySelectorAll('.outside-link.internal')); - if (links.length === 0) { - return; - } - const checkImgUrl = 'http://alipay-rmsdeploy-dev-image.oss-cn-hangzhou-zmf.aliyuncs.com/rmsportal/JdVaTbZzPxEldUi.png'; - utils.ping(checkImgUrl, status => { - if (status === 'responded') { - links.forEach(link => (link.style.display = 'block')); - } - }); - } - getArticle(article) { - const { content } = this.props; - const { meta } = content; - if (!meta.timeline) { - return article; - } - const timelineItems = []; - let temp = []; - Children.forEach(article.props.children, (child, i) => { - if (child.type === 'h2' && temp.length > 0) { - timelineItems.push({temp}); - temp = []; - } - temp.push(child); - }); - return cloneElement(article, { - children: {timelineItems}, - }); - } - render() { - const { content, location } = this.props; - const jumper = content.description.filter((node) => { - return getTagName(node) === 'h2'; - }).map((node) => { - return ( -
  • - - {utils.jsonmlToComponent(location.pathname, getChildren(node)[0])} - -
  • - ); - }); - - const { meta, intro, description } = content; - - return ( -
    -

    - {meta.title || meta.english} {meta.subtitle || meta.chinese} - { - !meta.subtitle ? null : - {meta.subtitle} - } -

    - { - !intro ? null : - utils.jsonmlToComponent( - location.pathname, - ['section', { className: 'markdown' }].concat(intro) - ) - } - { - (jumper.length > 0 && meta.toc !== false) ? -
      {jumper}
    : - null - } - { - this.getArticle(utils.jsonmlToComponent( - location.pathname, - ['section', { className: 'markdown' }].concat(description) - )) - } -
    - ); - } -} diff --git a/site/component/ComponentDoc/index.jsx b/site/component/ComponentDoc/index.jsx deleted file mode 100644 index 683ce2d895c2..000000000000 --- a/site/component/ComponentDoc/index.jsx +++ /dev/null @@ -1,131 +0,0 @@ -import React from 'react'; -import { Link } from 'react-router'; -import classNames from 'classnames'; -import { Row, Col, Icon, Affix } from 'antd'; -import Demo from '../Demo'; -import * as utils from '../utils'; -import demosList from '../../../_data/demos-list'; - -export default class ComponentDoc extends React.Component { - static contextTypes = { - intl: React.PropTypes.object, - } - - constructor(props) { - super(props); - - this.state = { - expandAll: false, - }; - } - - componentDidMount() { - this.componentDidUpdate(); - } - componentDidUpdate() { - const { title, subtitle, chinese, english } = this.props.doc.meta; - utils.setTitle(`${subtitle || chinese || ''} ${title || english} - Ant Design`); - } - - handleExpandToggle = () => { - this.setState({ - expandAll: !this.state.expandAll, - }); - } - - render() { - const { doc, location } = this.props; - const scrollTo = location.query.scrollTo; - const { description, meta } = doc; - const locale = this.context.intl.locale; - const demos = (demosList[meta.fileName.replace(`.${locale}`, '')] || []) - .filter((demoData) => !demoData.meta.hidden); - const expand = this.state.expandAll; - - const isSingleCol = meta.cols === 1; - const leftChildren = []; - const rightChildren = []; - demos.sort((a, b) => { - return parseInt(a.meta.order, 10) - parseInt(b.meta.order, 10); - }).forEach((demoData, index) => { - if (index % 2 === 0 || isSingleCol) { - leftChildren.push( - - ); - } else { - rightChildren.push( - - ); - } - }); - const expandTriggerClass = classNames({ - 'code-box-expand-trigger': true, - 'code-box-expand-trigger-active': expand, - }); - - const jumper = demos.map((demo) => { - const title = demo.meta.title; - const localizeTitle = typeof title === 'object' ? - title[locale] : title; - return ( -
  • - - {localizeTitle} - -
  • - ); - }); - - return ( -
    - -
      - {jumper} -
    -
    -
    -

    {meta.title || meta.english} {meta.subtitle || meta.chinese}

    - { - utils.jsonmlToComponent( - location.pathname, - ['section', { className: 'markdown' }] - .concat(description) - ) - } -

    - 代码演示 - -

    -
    - - - {leftChildren} - - { - isSingleCol ? null : - {rightChildren} - } - - { - utils.jsonmlToComponent( - location.pathname, - ['section', { - className: 'markdown api-container', - }].concat(doc.api || []) - ) - } -
    - ); - } -} diff --git a/site/component/utils.js b/site/component/utils.js deleted file mode 100644 index 23886e767fc5..000000000000 --- a/site/component/utils.js +++ /dev/null @@ -1,80 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { Link } from 'react-router'; -import { getTagName, getAttributes, getChildren, isElement } from 'jsonml.js/lib/utils'; -import toReactComponent from 'jsonml-to-react-component'; -import VideoPlayer from './VideoPlayer'; -import ImagePreview from './ImagePreview'; - -function isHeading(type) { - return /h[1-6]/i.test(type); -} - -export function jsonmlToComponent(pathname, jsonml) { - return toReactComponent(jsonml, [ - [(node) => React.isValidElement(node), (node, index) => { - return React.cloneElement(node, { key: index }); - }], - [(node) => typeof node === 'function', (node, index) => { - return React.cloneElement(node(React, ReactDOM), { key: index }); - }], - [(node) => isHeading(getTagName(node)), (node, index) => { - const children = getChildren(node); - return React.createElement(getTagName(node), { - key: index, - id: children, - ...getAttributes(node), - }, [ - {children.map((child) => toReactComponent(child))}, - #, - ]); - }], - [(node) => getTagName(node) === 'pre' && getAttributes(node).highlighted, (node, index) => { - return React.createElement('pre', { key: index, lang: getAttributes(node).lang }, React.createElement( - 'code', - { dangerouslySetInnerHTML: { __html: getChildren(getChildren(node)[0])[0] } } - )); - }], - [(node) => getTagName(node) === 'video', (node, index) => - , - ], - [(node) => isElement(node) && getTagName(node) === 'a' && !( - getAttributes(node).class || - (getAttributes(node).href && - getAttributes(node).href.indexOf('http') === 0) - ), (node, index) => { - return {toReactComponent(getChildren(node)[0])}; - }], - [(node) => { - return isElement(node) && - getTagName(node) === 'p' && - getTagName(getChildren(node)[0]) === 'img' && - /preview-img/gi.test(getAttributes(getChildren(node)[0]).class); - }, (node, index) => { - const imgs = getChildren(node) - .filter((img) => isElement(img) && Object.keys(getAttributes(img)).length > 0) - .map((img) => getAttributes(img)); - return ; - }], - ]); -} - -export function setTitle(title) { - document.title = title; -} - -export function ping(url, callback) { - const img = new Image(); - let done; - const finish = (status) => { - if (!done) { - done = true; - img.src = ''; - callback(status); - } - }; - img.onload = () => finish('responded'); - img.onerror = () => finish('error'); - img.src = url; - setTimeout(() => finish('timeout'), 1500); -} diff --git a/site/entry/index.jsx b/site/entry/index.jsx deleted file mode 100644 index cf53a5209ae0..000000000000 --- a/site/entry/index.jsx +++ /dev/null @@ -1,131 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { addLocaleData, IntlProvider } from 'react-intl'; -import { Router, Route, IndexRoute, Redirect, useRouterHistory } from 'react-router'; -import antd from '../../index.js'; -import * as utils from './utils'; -import '../common/lib'; -import App from '../component/App'; -import Home from '../component/Home/index'; -import NotFound from '../component/NotFound/index'; -import practice from '../../_data/practice'; -import pattern from '../../_data/pattern'; -import reactComponents from '../../_data/react-components'; -import spec from '../../_data/spec'; -import resource from '../../_data/resource'; -import config from '../website.config'; -import enLocale from './en-US.js'; -import cnLocale from './zh-CN.js'; -import { createHashHistory } from 'history'; - -// useRouterHistory creates a composable higher-order function -const appHistory = useRouterHistory(createHashHistory)({ queryKey: false }); - -// TODO: pack dependencies with atool build -// Expose React, ReactDOM -window.react = React; -window['react-dom'] = ReactDOM; -window.antd = antd; - -const ReactComponents = utils.generateContainer(reactComponents); -const Practice = utils.generateContainer(practice); -const Pattern = utils.generateContainer(pattern); -const Spec = utils.generateContainer(spec); -const Resource = utils.generateContainer(resource); -const redirects = Object.keys(config.redirects).map((from, index) => { - return ; -}); - -// Enable Google Analytics -if (!location.port) { - /* eslint-disable */ - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - ga('create', 'UA-72788897-1', 'auto'); - ga('send', 'pageview'); - - appHistory.listen((loc) => { - ga('send', 'pageview', loc.pathname + loc.search); - }); - /* eslint-enable */ -} - -// Polyfill -const areIntlLocalesSupported = require('intl-locales-supported'); -const localesMyAppSupports = ['zh-CN', 'en-US']; - -if (global.Intl) { - // Determine if the built-in `Intl` has the locale data we need. - if (!areIntlLocalesSupported(localesMyAppSupports)) { - // `Intl` exists, but it doesn't have the data we need, so load the - // polyfill and patch the constructors we need with the polyfill's. - /* eslint-disable global-require */ - const IntlPolyfill = require('intl'); - /* eslint-enable global-require */ - Intl.NumberFormat = IntlPolyfill.NumberFormat; - Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat; - } -} else { - // No `Intl`, so use and load the polyfill. - /* eslint-disable global-require */ - global.Intl = require('intl'); - /* eslint-enable global-require */ -} - - -const isZhCN = (typeof localStorage !== 'undefined' && localStorage.getItem('locale') !== 'en-US'); - // (typeof localStorage !== 'undefined' && localStorage.getItem('locale') === 'zh-CN') || - // (navigator.language === 'zh-CN'); -const appLocale = isZhCN ? cnLocale : enLocale; -addLocaleData(appLocale.data); - -ReactDOM.render( - - - - - - {utils.generateIndex(reactComponents)} - - - {redirects} - - {utils.generateIndex(reactComponents)} - - - - {utils.generateIndex(practice)} - - - - {utils.generateIndex(pattern)} - - - - {utils.generateIndex(spec)} - - - - {utils.generateIndex(resource)} - - - - - - - , document.getElementById('react-content') -); diff --git a/site/entry/utils.js b/site/entry/utils.js deleted file mode 100644 index 808bb42d1331..000000000000 --- a/site/entry/utils.js +++ /dev/null @@ -1,120 +0,0 @@ -/* eslint-disable react/prefer-stateless-function, react/no-multi-comp */ -import React from 'react'; -import { IndexRedirect } from 'react-router'; -import MainContent from '../component/MainContent'; -import Article from '../component/Article'; -import ComponentDoc from '../component/ComponentDoc'; -import demosList from '../../_data/demos-list'; -import { redirects } from '../website.config'; - -if (module.hot) { - module.hot.accept('../../_data/demos-list', () => {}); -} - -function fileNameToPath(fileName) { - const snippets = fileName - .replace(/(\/index)?((\.zh-CN)|(\.en-US))?\.md$/i, '').split('/'); - return snippets[snippets.length - 1]; -} - -function getMenuItems(data, locale) { - const menuMeta = Object.keys(data) - .map((key) => data[key]) - .map((file) => { - if (file.localized) { - return file[locale].meta; - } - return file.meta; - }); - - const menuItems = {}; - menuMeta.sort((a, b) => { - return parseInt(a.order, 10) - parseInt(b.order, 10); - }).forEach((meta) => { - const category = meta.category || 'topLevel'; - if (!menuItems[category]) { - menuItems[category] = {}; - } - - const type = meta.type || 'topLevel'; - if (!menuItems[category][type]) { - menuItems[category][type] = []; - } - - menuItems[category][type].push(meta); - }); - - return menuItems; -} - -export function generateContainer(data) { - return class containerWrapper extends React.Component { - static contextTypes = { - intl: React.PropTypes.object, - } - - render() { - const locale = this.context.intl.locale; - const menuItems = getMenuItems(data, locale); - return ( - - ); - } - }; -} - -export function generateIndex(data) { - const menuItems = getMenuItems(data, 'zh-CN'); // 以中文版配置为准 - const firstChild = menuItems.topLevel.topLevel.filter((item) => { - return !item.disabled; - })[0]; - return ( - - ); -} - -const pathToFile = {}; -Object.keys(redirects).forEach((key) => pathToFile[redirects[key]] = key); -pathToFile['docs/react/changelog'] = './CHANGELOG'; // TODO - -function getDoc(data, props) { - const trimedPathname = props.location.pathname.replace(/^\//, ''); - const processedPathname = pathToFile[trimedPathname] || trimedPathname; - const doc = data[`${processedPathname}.md`] || - data[`${processedPathname}/index.md`]; - return doc; -} - -export function getChildrenWrapper(data) { - return class childrenWrapper extends React.Component { - static contextTypes = { - intl: React.PropTypes.object, - } - - render() { - const props = this.props; - const trimedPathname = props.location.pathname.replace(/^\//, ''); - const processedPathname = pathToFile[trimedPathname] || trimedPathname; - const rawDoc = data[`${processedPathname}.md`] || - data[`${processedPathname}/index.md`]; - - const locale = this.context.intl.locale; - const doc = rawDoc.localized ? rawDoc[locale] : rawDoc; - - const hasDemos = demosList[doc.meta.fileName.replace(`.${locale}`, '')]; - return !hasDemos ? -
    : - ; - } - }; -} - -export function getEnterHandler(data) { - return function handleEnter(nextState, replace) { - const doc = getDoc(data, nextState); - if (!doc) { - replace('/404'); - } - }; -} diff --git a/site/entry/en-US.js b/site/theme/en-US.js similarity index 100% rename from site/entry/en-US.js rename to site/theme/en-US.js diff --git a/site/website.config.js b/site/theme/index.js similarity index 51% rename from site/website.config.js rename to site/theme/index.js index e79c5cd65771..ca840762a4ab 100644 --- a/site/website.config.js +++ b/site/theme/index.js @@ -1,3 +1,5 @@ +const contentTmpl = './template/Content/index'; + export default { categoryOrder: { 组件: 0, @@ -12,13 +14,20 @@ export default { Navigation: 3, Other: 4, }, - redirects: { - CHANGELOG: 'docs/react/changelog', - }, docVersions: { '0.9.x': 'http://09x.ant.design/', '0.10.x': 'http://010x.ant.design/', '0.11.x': 'http://011x.ant.design/', '0.12.x': 'http://012x.ant.design/', }, + routes: { + '/': './template/Home/index', + '/docs/practice/:children': contentTmpl, + '/docs/pattern/:children': contentTmpl, + '/docs/react/:children': contentTmpl, + '/CHANGELOG': contentTmpl, + '/components/:children': contentTmpl, + '/docs/spec/:children': contentTmpl, + '/docs/resource/:children': contentTmpl, + }, }; diff --git a/site/common/styles/colors.less b/site/theme/static/colors.less similarity index 100% rename from site/common/styles/colors.less rename to site/theme/static/colors.less diff --git a/site/common/styles/common.less b/site/theme/static/common.less similarity index 99% rename from site/common/styles/common.less rename to site/theme/static/common.less index cadd93bcc611..f190ba788568 100644 --- a/site/common/styles/common.less +++ b/site/theme/static/common.less @@ -34,7 +34,7 @@ a { position: relative; } -.main-container { +div.main-container { padding: 0 6% 120px 4%; margin-left: -1px; background: #fff; diff --git a/site/common/styles/demo.less b/site/theme/static/demo.less similarity index 99% rename from site/common/styles/demo.less rename to site/theme/static/demo.less index 83324681a5d3..5701252bfbbf 100644 --- a/site/common/styles/demo.less +++ b/site/theme/static/demo.less @@ -39,7 +39,7 @@ background: #fff; } -.code-box.code-box-target { +.code-box:target { border: 1px solid rgba(45, 183, 245, 0.7); box-shadow: 0 0 4px rgba(45, 183, 245, 0.5); } @@ -80,7 +80,7 @@ } .code-box-meta h4, -.code-box-meta p { +section.code-box-meta p { margin: 0; width: 93%; } @@ -172,8 +172,10 @@ } .code-box .highlight { - padding: 5px; - + pre { + margin: 0; + padding: 0; + } &:not(:first-child) { border-top: 1px dashed #e9e9e9; } diff --git a/site/common/styles/font.less b/site/theme/static/font.less similarity index 100% rename from site/common/styles/font.less rename to site/theme/static/font.less diff --git a/site/common/styles/footer.less b/site/theme/static/footer.less similarity index 100% rename from site/common/styles/footer.less rename to site/theme/static/footer.less diff --git a/site/component/Header/index.less b/site/theme/static/header.less similarity index 83% rename from site/component/Header/index.less rename to site/theme/static/header.less index cb3944d5444f..2e0eb1ba7b75 100644 --- a/site/component/Header/index.less +++ b/site/theme/static/header.less @@ -90,22 +90,22 @@ .component-select { &.ant-select-dropdown { - border: 0; - border-radius: 0; - box-shadow: 0 0 8px rgba(0,0,0,0.25); - font-size: 14px; + border: 0; + border-radius: 0; + box-shadow: 0 0 8px rgba(0,0,0,0.25); + font-size: 14px; } .ant-select-dropdown-menu { - max-height: 200px; + max-height: 200px; } .ant-select-dropdown-menu-item { - border-radius: 0 !important; + border-radius: 0 !important; } .ant-component-decs { - font-size: 12px; - position: absolute; - top: 9px; - color: #aaa; - right: 16px; + font-size: 12px; + position: absolute; + top: 9px; + color: #aaa; + right: 16px; } } diff --git a/site/common/styles/highlight.less b/site/theme/static/highlight.less similarity index 100% rename from site/common/styles/highlight.less rename to site/theme/static/highlight.less diff --git a/site/component/Home/index.less b/site/theme/static/home.less similarity index 100% rename from site/component/Home/index.less rename to site/theme/static/home.less diff --git a/site/common/styles/markdown.less b/site/theme/static/markdown.less similarity index 99% rename from site/common/styles/markdown.less rename to site/theme/static/markdown.less index 7d59a034588c..2209bc32a96c 100644 --- a/site/common/styles/markdown.less +++ b/site/theme/static/markdown.less @@ -99,7 +99,6 @@ .markdown pre code { border: none; - padding: 1em 2em; background: #f7f7f7; margin: 0; font-size: 13px; diff --git a/site/common/styles/mock-browser.less b/site/theme/static/mock-browser.less similarity index 100% rename from site/common/styles/mock-browser.less rename to site/theme/static/mock-browser.less diff --git a/site/common/styles/motion.less b/site/theme/static/motion.less similarity index 100% rename from site/common/styles/motion.less rename to site/theme/static/motion.less diff --git a/site/common/styles/new-version-info-modal.less b/site/theme/static/new-version-info-modal.less similarity index 100% rename from site/common/styles/new-version-info-modal.less rename to site/theme/static/new-version-info-modal.less diff --git a/site/component/NotFound/index.less b/site/theme/static/not-found.less similarity index 100% rename from site/component/NotFound/index.less rename to site/theme/static/not-found.less diff --git a/site/common/styles/page-nav.less b/site/theme/static/page-nav.less similarity index 100% rename from site/common/styles/page-nav.less rename to site/theme/static/page-nav.less diff --git a/site/common/styles/preview-img.less b/site/theme/static/preview-img.less similarity index 100% rename from site/common/styles/preview-img.less rename to site/theme/static/preview-img.less diff --git a/site/common/styles/resource.less b/site/theme/static/resource.less similarity index 100% rename from site/common/styles/resource.less rename to site/theme/static/resource.less diff --git a/site/common/styles/responsive.less b/site/theme/static/responsive.less similarity index 100% rename from site/common/styles/responsive.less rename to site/theme/static/responsive.less diff --git a/site/theme/static/style.js b/site/theme/static/style.js new file mode 100644 index 000000000000..e2120de2b762 --- /dev/null +++ b/site/theme/static/style.js @@ -0,0 +1,18 @@ +import './common.less'; +import './header.less'; +import './footer.less'; +import './home.less'; +import './page-nav.less'; +import './markdown.less'; +import './resource.less'; +import './responsive.less'; +import './preview-img.less'; +import './toc.less'; +import './not-found.less'; +import './font.less'; +import './highlight.less'; +import './demo.less'; +import './colors.less'; +import './mock-browser.less'; +import './new-version-info-modal.less'; +import './motion.less'; diff --git a/index.html b/site/theme/static/template.html similarity index 89% rename from index.html rename to site/theme/static/template.html index ae27b48d6cca..72bd7fa4829f 100644 --- a/index.html +++ b/site/theme/static/template.html @@ -6,7 +6,7 @@ Ant Design - 一个 UI 设计语言 - + @@ -30,6 +30,7 @@ return '' + letter + ''; }).join(''); - + + diff --git a/site/common/styles/toc.less b/site/theme/static/toc.less similarity index 100% rename from site/common/styles/toc.less rename to site/theme/static/toc.less diff --git a/site/component/BrowserDemo/index.jsx b/site/theme/template/BrowserDemo.jsx similarity index 100% rename from site/component/BrowserDemo/index.jsx rename to site/theme/template/BrowserDemo.jsx diff --git a/site/theme/template/Content/Article.jsx b/site/theme/template/Content/Article.jsx new file mode 100644 index 000000000000..bf3056a1ab92 --- /dev/null +++ b/site/theme/template/Content/Article.jsx @@ -0,0 +1,77 @@ +import React, { Children, cloneElement } from 'react'; +import DocumentTitle from 'react-document-title'; +import { getChildren } from 'jsonml.js/lib/utils'; +import { Timeline } from 'antd'; +import * as utils from '../utils'; + +export default class Article extends React.Component { + componentDidMount() { + this.componentDidUpdate(); + } + componentDidUpdate() { + const links = Array.apply(null, document.querySelectorAll('.outside-link.internal')); + if (links.length === 0) { + return; + } + const checkImgUrl = 'http://alipay-rmsdeploy-dev-image.oss-cn-hangzhou-zmf.aliyuncs.com/rmsportal/JdVaTbZzPxEldUi.png'; + utils.ping(checkImgUrl, status => { + if (status === 'responded') { + links.forEach(link => (link.style.display = 'block')); + } + }); + } + getArticle(article) { + const { content } = this.props; + const { meta } = content; + if (!meta.timeline) { + return article; + } + const timelineItems = []; + let temp = []; + Children.forEach(article.props.children, (child, i) => { + if (child.type === 'h2' && temp.length > 0) { + timelineItems.push({temp}); + temp = []; + } + temp.push(child); + }); + return cloneElement(article, { + children: {timelineItems}, + }); + } + render() { + const props = this.props; + const content = props.content; + + const { meta, description } = content; + const { title, subtitle, chinese, english } = meta; + return ( + +
    +

    + {title || english} + { + (!subtitle && !chinese) ? null : + {subtitle || chinese} + } +

    + { + !description ? null : + props.utils.toReactComponent( + ['section', { className: 'markdown' }].concat(getChildren(description)) + ) + } + { + !content.toc ? null : +
    {props.utils.toReactComponent(content.toc)}
    + } + { + this.getArticle(props.utils.toReactComponent( + ['section', { className: 'markdown' }].concat(getChildren(content.content)) + )) + } +
    +
    + ); + } +} diff --git a/site/theme/template/Content/ComponentDoc.jsx b/site/theme/template/Content/ComponentDoc.jsx new file mode 100644 index 000000000000..6b93c65f8078 --- /dev/null +++ b/site/theme/template/Content/ComponentDoc.jsx @@ -0,0 +1,120 @@ +import React from 'react'; +import DocumentTitle from 'react-document-title'; +import classNames from 'classnames'; +import { Row, Col, Icon, Affix } from 'antd'; +import { getChildren } from 'jsonml.js/lib/utils'; +import Demo from './Demo'; + +export default class ComponentDoc extends React.Component { + static contextTypes = { + intl: React.PropTypes.object, + } + + constructor(props) { + super(props); + + this.state = { + expandAll: false, + }; + } + + handleExpandToggle = () => { + this.setState({ + expandAll: !this.state.expandAll, + }); + } + + render() { + const props = this.props; + const { doc, location } = props; + const { content, meta } = doc; + const locale = this.context.intl.locale; + const demos = Object.keys(props.demos).map((key) => props.demos[key]) + .filter((demoData) => !demoData.meta.hidden); + const expand = this.state.expandAll; + + const isSingleCol = meta.cols === 1; + const leftChildren = []; + const rightChildren = []; + demos.sort((a, b) => a.meta.order - b.meta.order) + .forEach((demoData, index) => { + if (index % 2 === 0 || isSingleCol) { + leftChildren.push( + + ); + } else { + rightChildren.push( + + ); + } + }); + const expandTriggerClass = classNames({ + 'code-box-expand-trigger': true, + 'code-box-expand-trigger-active': expand, + }); + + const jumper = demos.map((demo) => { + const title = demo.meta.title; + const localizeTitle = title[locale] || title; + return ( +
  • + + {localizeTitle} + +
  • + ); + }); + + const { title, subtitle, chinese, english } = meta; + return ( + +
    + +
      + {jumper} +
    +
    +
    +

    {meta.title || meta.english} {meta.subtitle || meta.chinese}

    + { + props.utils.toReactComponent( + ['section', { className: 'markdown' }] + .concat(getChildren(content)) + ) + } +

    + 代码演示 + +

    +
    + + + {leftChildren} + + { + isSingleCol ? null : + {rightChildren} + } + + { + props.utils.toReactComponent( + ['section', { + className: 'markdown api-container', + }].concat(getChildren(doc.api || ['placeholder'])) + ) + } +
    +
    + ); + } +} diff --git a/site/component/Demo/index.jsx b/site/theme/template/Content/Demo.jsx similarity index 63% rename from site/component/Demo/index.jsx rename to site/theme/template/Content/Demo.jsx index f4d240266575..a5cd27c92530 100644 --- a/site/component/Demo/index.jsx +++ b/site/theme/template/Content/Demo.jsx @@ -1,9 +1,6 @@ import React from 'react'; -import { Link } from 'react-router'; +import ReactDOM from 'react-dom'; import classNames from 'classnames'; -import * as utils from '../utils'; - -const isLocal = location.port; export default class Demo extends React.Component { static contextTypes = { @@ -31,27 +28,40 @@ export default class Demo extends React.Component { } render() { - const { id, className, meta, intro, preview, style, src, - highlightedCode, highlightedStyle, pathname } = this.props; + const props = this.props; + const { + meta, + src, + content, + preview, + highlightedCode, + style, + highlightedStyle, + } = props; + const codeExpand = this.state.codeExpand; const codeBoxClass = classNames({ 'code-box': true, - [className]: className, expand: codeExpand, }); const locale = this.context.intl.locale; - const localizeIntro = intro[locale] || intro; - const introChildren = utils.jsonmlToComponent(pathname, ['div'].concat(localizeIntro)); - const localizedTitle = typeof meta.title === 'object' ? - meta.title[locale] : meta.title; + const localizedTitle = meta.title[locale] || meta.title; + const localizeIntro = content[locale] || content; + const introChildren = props.utils + .toReactComponent(['div'].concat(localizeIntro)); + + const highlightClass = classNames({ + 'highlight-wrapper': true, + 'highlight-wrapper-expand': codeExpand, + }); return ( -
    +
    { meta.iframe ? -