diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 83f8ac1e6..000000000
--- a/.eslintrc
+++ /dev/null
@@ -1,82 +0,0 @@
-{
- "root": true,
- "env": {
- "browser": true,
- "node": true,
- "jasmine": true,
- "jest": true,
- "es6": true
- },
- "parser": "@typescript-eslint/parser",
- "parserOptions": {
- "parser": "babel-eslint"
- },
- "extends": ["plugin:vue/vue3-recommended", "prettier"],
- "plugins": ["markdown", "jest", "@typescript-eslint", "eslint-plugin-no-explicit-type-exports"],
- "overrides": [
- {
- "files": ["**/demo/*.md"],
- "processor": "markdown/markdown",
- "rules": {
- "no-console": "off"
- }
- },
- {
- "files": ["*.ts", "*.tsx"],
- "extends": [
- "@vue/typescript/recommended",
- "@vue/prettier",
- "@vue/prettier/@typescript-eslint"
- ],
- "parserOptions": {
- "project": "./tsconfig.json"
- },
- "rules": {
- "no-explicit-type-exports/no-explicit-type-exports": 2,
- "@typescript-eslint/no-explicit-any": 0,
- "@typescript-eslint/ban-types": 0,
- "@typescript-eslint/consistent-type-imports": 1,
- "@typescript-eslint/explicit-module-boundary-types": 0,
- "@typescript-eslint/no-empty-function": 0,
- "@typescript-eslint/no-non-null-assertion": 0,
- "@typescript-eslint/no-unused-vars": [
- "error",
- { "vars": "all", "args": "after-used", "ignoreRestSiblings": true }
- ],
- "@typescript-eslint/ban-ts-comment": 0
- }
- }
- ],
- "rules": {
- "comma-dangle": [2, "always-multiline"],
- "no-var": "error",
- "no-console": [2, { "allow": ["warn", "error"] }],
- "object-shorthand": 2,
- "no-unused-vars": [2, { "ignoreRestSiblings": true, "argsIgnorePattern": "^h$" }],
- "no-undef": 2,
- "camelcase": "off",
- "no-extra-boolean-cast": "off",
- "semi": ["error", "always"],
- "vue/require-explicit-emits": "off",
- "vue/require-prop-types": "off",
- "vue/require-default-prop": "off",
- "vue/no-reserved-keys": "off",
- "vue/comment-directive": "off",
- "vue/prop-name-casing": "off",
- "vue/one-component-per-file": "off",
- "vue/custom-event-name-casing": "off",
- "vue/max-attributes-per-line": [
- 2,
- {
- "singleline": 20,
- "multiline": {
- "max": 1,
- "allowFirstLine": false
- }
- }
- ]
- },
- "globals": {
- "h": true
- }
-}
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 000000000..3c7e1c9d8
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,78 @@
+module.exports = {
+ root: true,
+ env: {
+ browser: true,
+ node: true,
+ jasmine: true,
+ jest: true,
+ es6: true,
+ },
+ parser: '@typescript-eslint/parser',
+ parserOptions: {
+ parser: 'babel-eslint',
+ },
+ extends: ['plugin:vue/vue3-recommended', 'prettier'],
+ plugins: ['markdown', 'jest', '@typescript-eslint', 'eslint-plugin-no-explicit-type-exports'],
+ overrides: [
+ {
+ files: ['**/demo/*.md'],
+ processor: 'markdown/markdown',
+ rules: {
+ 'no-console': 'off',
+ },
+ },
+ {
+ files: ['*.ts', '*.tsx'],
+ extends: ['@vue/typescript/recommended', '@vue/prettier', '@vue/prettier/@typescript-eslint'],
+ parserOptions: {
+ project: './tsconfig.json',
+ },
+ rules: {
+ 'no-explicit-type-exports/no-explicit-type-exports': 2,
+ '@typescript-eslint/no-explicit-any': 0,
+ '@typescript-eslint/ban-types': 0,
+ '@typescript-eslint/consistent-type-imports': 1,
+ '@typescript-eslint/explicit-module-boundary-types': 0,
+ '@typescript-eslint/no-empty-function': 0,
+ '@typescript-eslint/no-non-null-assertion': 0,
+ '@typescript-eslint/no-unused-vars': [
+ 'error',
+ { vars: 'all', args: 'after-used', ignoreRestSiblings: true },
+ ],
+ '@typescript-eslint/ban-ts-comment': 0,
+ },
+ },
+ ],
+ rules: {
+ 'comma-dangle': [2, 'always-multiline'],
+ 'no-var': 'error',
+ 'no-console': [2, { allow: ['warn', 'error'] }],
+ 'object-shorthand': 2,
+ 'no-unused-vars': [2, { ignoreRestSiblings: true, argsIgnorePattern: '^h$' }],
+ 'no-undef': 2,
+ camelcase: 'off',
+ 'no-extra-boolean-cast': 'off',
+ semi: ['error', 'always'],
+ 'vue/require-explicit-emits': 'off',
+ 'vue/require-prop-types': 'off',
+ 'vue/require-default-prop': 'off',
+ 'vue/no-reserved-keys': 'off',
+ 'vue/comment-directive': 'off',
+ 'vue/prop-name-casing': 'off',
+ 'vue/one-component-per-file': 'off',
+ 'vue/custom-event-name-casing': 'off',
+ 'vue/max-attributes-per-line': [
+ 2,
+ {
+ singleline: 20,
+ multiline: {
+ max: 1,
+ allowFirstLine: false,
+ },
+ },
+ ],
+ },
+ globals: {
+ h: true,
+ },
+};
diff --git a/package.json b/package.json
index e057dc5e8..d85f27273 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,8 @@
"vetur"
],
"scripts": {
- "dev": "vite",
+ "debugger": "vite",
+ "dev": "vite serve site",
"test": "cross-env NODE_ENV=test WORKFLOW=true jest --config .jest.js",
"test:dev": "cross-env NODE_ENV=test jest --config .jest.js",
"compile": "node antd-tools/cli/run.js compile",
diff --git a/plugin/md/utils/tsToJs.ts b/plugin/md/utils/tsToJs.ts
index b901939ed..b0ee312a3 100644
--- a/plugin/md/utils/tsToJs.ts
+++ b/plugin/md/utils/tsToJs.ts
@@ -1,8 +1,10 @@
import { transformSync } from '@babel/core';
import { CLIEngine } from 'eslint';
+import path from 'path';
const engine = new CLIEngine({
fix: true,
useEslintrc: false,
+ baseConfig: require(path.join(process.cwd(), '.eslintrc.js')),
});
const tsToJs = (content: string): string => {
if (!content) {
diff --git a/site/404.html b/site/404.html
new file mode 100644
index 000000000..2687f6e43
--- /dev/null
+++ b/site/404.html
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+ Ant Design Vue
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/index.html b/site/index.html
new file mode 100644
index 000000000..58265e687
--- /dev/null
+++ b/site/index.html
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+ Ant Design Vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/public/docsearch.min.js b/site/public/docsearch.min.js
new file mode 100644
index 000000000..efb45b8f6
--- /dev/null
+++ b/site/public/docsearch.min.js
@@ -0,0 +1,2 @@
+/*! docsearch 2.6.3 | © Algolia | github.com/algolia/docsearch */
+(function webpackUniversalModuleDefinition(root,factory){if(typeof exports==="object"&&typeof module==="object")module.exports=factory();else if(typeof define==="function"&&define.amd)define([],factory);else if(typeof exports==="object")exports["docsearch"]=factory();else root["docsearch"]=factory()})(typeof self!=="undefined"?self:this,function(){return function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{configurable:false,enumerable:true,get:getter})}};__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module["default"]}:function getModuleExports(){return module};__webpack_require__.d(getter,"a",getter);return getter};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p="";return __webpack_require__(__webpack_require__.s=22)}([function(module,exports,__webpack_require__){"use strict";var DOM=__webpack_require__(1);function escapeRegExp(str){return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}module.exports={isArray:null,isFunction:null,isObject:null,bind:null,each:null,map:null,mixin:null,isMsie:function(agentString){if(agentString===undefined){agentString=navigator.userAgent}if(/(msie|trident)/i.test(agentString)){var match=agentString.match(/(msie |rv:)(\d+(.\d+)?)/i);if(match){return match[2]}}return false},escapeRegExChars:function(str){return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isNumber:function(obj){return typeof obj==="number"},toStr:function toStr(s){return s===undefined||s===null?"":s+""},cloneDeep:function cloneDeep(obj){var clone=this.mixin({},obj);var self=this;this.each(clone,function(value,key){if(value){if(self.isArray(value)){clone[key]=[].concat(value)}else if(self.isObject(value)){clone[key]=self.cloneDeep(value)}}});return clone},error:function(msg){throw new Error(msg)},every:function(obj,test){var result=true;if(!obj){return result}this.each(obj,function(val,key){if(result){result=test.call(null,val,key,obj)&&result}});return!!result},any:function(obj,test){var found=false;if(!obj){return found}this.each(obj,function(val,key){if(test.call(null,val,key,obj)){found=true;return false}});return found},getUniqueId:function(){var counter=0;return function(){return counter++}}(),templatify:function templatify(obj){if(this.isFunction(obj)){return obj}var $template=DOM.element(obj);if($template.prop("tagName")==="SCRIPT"){return function template(){return $template.text()}}return function template(){return String(obj)}},defer:function(fn){setTimeout(fn,0)},noop:function(){},formatPrefix:function(prefix,noPrefix){return noPrefix?"":prefix+"-"},className:function(prefix,clazz,skipDot){return(skipDot?"":".")+prefix+clazz},escapeHighlightedString:function(str,highlightPreTag,highlightPostTag){highlightPreTag=highlightPreTag||"";var pre=document.createElement("div");pre.appendChild(document.createTextNode(highlightPreTag));highlightPostTag=highlightPostTag||" ";var post=document.createElement("div");post.appendChild(document.createTextNode(highlightPostTag));var div=document.createElement("div");div.appendChild(document.createTextNode(str));return div.innerHTML.replace(RegExp(escapeRegExp(pre.innerHTML),"g"),highlightPreTag).replace(RegExp(escapeRegExp(post.innerHTML),"g"),highlightPostTag)}}},function(module,exports,__webpack_require__){"use strict";module.exports={element:null}},function(module,exports){var hasOwn=Object.prototype.hasOwnProperty;var toString=Object.prototype.toString;module.exports=function forEach(obj,fn,ctx){if(toString.call(fn)!=="[object Function]"){throw new TypeError("iterator must be a function")}var l=obj.length;if(l===+l){for(var i=0;i was loaded but did not call our provided callback"),JSONPScriptError:createCustomError("JSONPScriptError","
diff --git a/site/src/SymbolKey.ts b/site/src/SymbolKey.ts
new file mode 100644
index 000000000..9d14bbefa
--- /dev/null
+++ b/site/src/SymbolKey.ts
@@ -0,0 +1 @@
+export const GLOBAL_CONFIG = Symbol('globalConfig');
diff --git a/site/src/assets/ant-design-vue.svg b/site/src/assets/ant-design-vue.svg
new file mode 100644
index 000000000..4fe899edc
--- /dev/null
+++ b/site/src/assets/ant-design-vue.svg
@@ -0,0 +1,11 @@
+
+
+
+ Ant Design Vue Copy 5
+ Created with Sketch.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/site/src/assets/logo.png b/site/src/assets/logo.png
new file mode 100644
index 000000000..f6305e2f8
Binary files /dev/null and b/site/src/assets/logo.png differ
diff --git a/site/src/assets/logo.svg b/site/src/assets/logo.svg
new file mode 100644
index 000000000..dbfcee7e5
--- /dev/null
+++ b/site/src/assets/logo.svg
@@ -0,0 +1,29 @@
+
+
+
+ Vue
+ Created with Sketch.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/site/src/components/DemoBox.vue b/site/src/components/DemoBox.vue
new file mode 100644
index 000000000..ed77a8b52
--- /dev/null
+++ b/site/src/components/DemoBox.vue
@@ -0,0 +1,214 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/site/src/components/SimpleLayout.vue b/site/src/components/SimpleLayout.vue
new file mode 100644
index 000000000..bfb461552
--- /dev/null
+++ b/site/src/components/SimpleLayout.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/site/src/components/api.vue b/site/src/components/api.vue
new file mode 100644
index 000000000..468e73e5b
--- /dev/null
+++ b/site/src/components/api.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
diff --git a/site/src/components/demoContainer.vue b/site/src/components/demoContainer.vue
new file mode 100644
index 000000000..11605a3eb
--- /dev/null
+++ b/site/src/components/demoContainer.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ codeStr }}
+
+
+
+
+
+
diff --git a/site/src/components/demoSort.jsx b/site/src/components/demoSort.jsx
new file mode 100644
index 000000000..149cec1d9
--- /dev/null
+++ b/site/src/components/demoSort.jsx
@@ -0,0 +1,47 @@
+import { defineComponent, inject } from 'vue';
+import { GLOBAL_CONFIG } from '@/SymbolKey';
+
+export default defineComponent({
+ props: {
+ cols: {
+ type: [Number, String],
+ default: 2,
+ },
+ },
+ setup() {
+ return {
+ demoContext: inject('demoContext', {}),
+ globalConfig: inject(GLOBAL_CONFIG),
+ };
+ },
+ render() {
+ const { cols, globalConfig, $slots } = this;
+ // 手机访问强制开启单行 demo 模式
+ const isSingleCol = cols === 1 || globalConfig.isMobile.value;
+ const leftChildren = [];
+ const rightChildren = [];
+ const children = $slots.default?.() || [];
+ children.forEach((demo, index) => {
+ if (index % 2 === 0 || isSingleCol) {
+ leftChildren.push(demo);
+ } else {
+ rightChildren.push(demo);
+ }
+ });
+ return (
+
+
+ {leftChildren}
+
+ {isSingleCol ? null : (
+
+ {rightChildren}
+
+ )}
+
+ );
+ },
+});
diff --git a/site/src/components/header.jsx b/site/src/components/header.jsx
new file mode 100644
index 000000000..b88899875
--- /dev/null
+++ b/site/src/components/header.jsx
@@ -0,0 +1,204 @@
+import { isZhCN } from '../utils/util';
+import packageInfo from '../../package.json';
+import logo from '../assets/logo.svg';
+import antDesignVue from '../assets/ant-design-vue.svg';
+import { SearchOutlined } from '@ant-design/icons-vue';
+
+export default {
+ props: {
+ name: String,
+ searchData: Array,
+ },
+ data() {
+ return {
+ value: null,
+ };
+ },
+ mounted() {
+ this.initDocSearch(this.$i18n.locale);
+ },
+ methods: {
+ handleClose(key) {
+ localStorage.removeItem('jobs-notification-key');
+ localStorage.setItem('jobs-notification-key', key);
+ },
+ initDocSearch(locale) {
+ window.docsearch({
+ apiKey: '92003c1d1d07beef165b08446f4224a3',
+ indexName: 'antdv',
+ inputSelector: '#search-box input',
+ algoliaOptions: { facetFilters: [isZhCN(locale) ? 'cn' : 'en'] },
+ transformData(hits) {
+ hits.forEach(hit => {
+ hit.url = hit.url.replace('www.antdv.com', window.location.host);
+ hit.url = hit.url.replace('https:', window.location.protocol);
+ });
+ return hits;
+ },
+ debug: false, // Set debug to true if you want to inspect the dropdown
+ });
+ },
+ handleClick() {
+ const name = this.name;
+ const path = this.$route.path;
+ const newName = isZhCN(name) ? name.replace(/-cn\/?$/, '') : `${name}-cn`;
+ this.$router.push({
+ path: path.replace(name, newName),
+ });
+ this.$i18n.locale = isZhCN(name) ? 'en-US' : 'zh-CN';
+ },
+ onSelect(val) {
+ this.$router.push(val);
+ this.value = val;
+ },
+ },
+ render() {
+ const name = this.name;
+ const isCN = isZhCN(name);
+ const path = this.$route.path;
+ const selectedKeys = path === '/jobs/list-cn' ? ['jobs'] : ['components'];
+ return (
+
+ );
+ },
+};
diff --git a/site/src/components/rice/CarbonAds.vue b/site/src/components/rice/CarbonAds.vue
new file mode 100644
index 000000000..14e3e0eb8
--- /dev/null
+++ b/site/src/components/rice/CarbonAds.vue
@@ -0,0 +1,124 @@
+
+
+
+
+
diff --git a/site/src/components/rice/GoogleAds.vue b/site/src/components/rice/GoogleAds.vue
new file mode 100644
index 000000000..4f4dfa138
--- /dev/null
+++ b/site/src/components/rice/GoogleAds.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
diff --git a/site/src/components/rice/GoogleAdsMin.vue b/site/src/components/rice/GoogleAdsMin.vue
new file mode 100644
index 000000000..c2507b015
--- /dev/null
+++ b/site/src/components/rice/GoogleAdsMin.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
diff --git a/site/src/components/rice/GoogleAdsTop.vue b/site/src/components/rice/GoogleAdsTop.vue
new file mode 100644
index 000000000..17f705d85
--- /dev/null
+++ b/site/src/components/rice/GoogleAdsTop.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
diff --git a/site/src/components/rice/WWAds.vue b/site/src/components/rice/WWAds.vue
new file mode 100644
index 000000000..c4f1f8254
--- /dev/null
+++ b/site/src/components/rice/WWAds.vue
@@ -0,0 +1,27 @@
+
+
+
+
diff --git a/site/src/components/rice/geektime.vue b/site/src/components/rice/geektime.vue
new file mode 100644
index 000000000..bf98d7e24
--- /dev/null
+++ b/site/src/components/rice/geektime.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
diff --git a/site/src/components/rice/geektime_ads.vue b/site/src/components/rice/geektime_ads.vue
new file mode 100644
index 000000000..e23e4b8d2
--- /dev/null
+++ b/site/src/components/rice/geektime_ads.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
diff --git a/site/src/components/rice/right_bottom_rice.vue b/site/src/components/rice/right_bottom_rice.vue
new file mode 100644
index 000000000..61965eade
--- /dev/null
+++ b/site/src/components/rice/right_bottom_rice.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/src/components/rice/sponsors.vue b/site/src/components/rice/sponsors.vue
new file mode 100644
index 000000000..bf9372645
--- /dev/null
+++ b/site/src/components/rice/sponsors.vue
@@ -0,0 +1,71 @@
+
+
+
+
+ 如果您有品牌推广、活动推广、招聘推广、社区合作等需求,欢迎联系我们,成为赞助商。
+
+ 您的广告将出现在 And Design Vue 文档所有子页面及 GitHub Readme 等页面。
+
+ 咨询邮箱:
+ antdv@foxmail.com
+
+
+
+
+
diff --git a/site/src/components/rice/top_rice.vue b/site/src/components/rice/top_rice.vue
new file mode 100644
index 000000000..fb4b2091a
--- /dev/null
+++ b/site/src/components/rice/top_rice.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/src/demo.js b/site/src/demo.js
new file mode 100644
index 000000000..ff3209fa1
--- /dev/null
+++ b/site/src/demo.js
@@ -0,0 +1,398 @@
+export default {
+ avatar: {
+ category: 'Components',
+ subtitle: '头像',
+ type: 'Data Display',
+ title: 'Avatar',
+ },
+ badge: {
+ category: 'Components',
+ subtitle: '徽标数',
+ type: 'Data Display',
+ title: 'Badge',
+ },
+ comment: {
+ category: 'Components',
+ subtitle: '评论',
+ type: 'Data Display',
+ title: 'Comment',
+ },
+ configProvider: {
+ category: 'Components',
+ subtitle: '全局化配置',
+ type: 'Other',
+ title: 'ConfigProvider',
+ },
+ empty: {
+ category: 'Components',
+ subtitle: '空状态',
+ type: 'Data Display',
+ title: 'Empty',
+ },
+ breadcrumb: {
+ category: 'Components',
+ subtitle: '面包屑',
+ type: 'Navigation',
+ title: 'Breadcrumb',
+ },
+ button: {
+ category: 'Components',
+ subtitle: '按钮',
+ type: 'General',
+ title: 'Button',
+ },
+ card: {
+ category: 'Components',
+ subtitle: '卡片',
+ type: 'Data Display',
+ title: 'Card',
+ cols: 1,
+ },
+ checkbox: {
+ category: 'Components',
+ subtitle: '多选框',
+ type: 'Data Entry',
+ title: 'Checkbox',
+ },
+ grid: {
+ category: 'Components',
+ subtitle: '栅格',
+ type: 'Layout',
+ title: 'Grid',
+ cols: 1,
+ },
+ icon: {
+ category: 'Components',
+ subtitle: '图标',
+ type: 'General',
+ title: 'Icon',
+ },
+ input: {
+ category: 'Components',
+ subtitle: '输入框',
+ type: 'Data Entry',
+ title: 'Input',
+ },
+ mentions: {
+ category: 'Components',
+ subtitle: '提及',
+ type: 'Data Entry',
+ title: 'Mentions',
+ },
+ select: {
+ category: 'Components',
+ subtitle: '选择器',
+ type: 'Data Entry',
+ title: 'Select',
+ },
+ menu: {
+ category: 'Components',
+ subtitle: '导航菜单',
+ type: 'Navigation',
+ title: 'Menu',
+ cols: 1,
+ },
+ pagination: {
+ category: 'Components',
+ subtitle: '分页',
+ type: 'Navigation',
+ title: 'Pagination',
+ cols: 1,
+ },
+ pageHeader: {
+ category: 'Components',
+ subtitle: '页头',
+ type: 'Navigation',
+ title: 'PageHeader',
+ cols: 1,
+ },
+ popconfirm: {
+ category: 'Components',
+ subtitle: '气泡确认框',
+ type: 'Feedback',
+ title: 'Popconfirm',
+ },
+ popover: {
+ category: 'Components',
+ subtitle: '气泡卡片',
+ type: 'Data Display',
+ title: 'Popover',
+ },
+ radio: {
+ category: 'Components',
+ subtitle: '单选框',
+ type: 'Data Entry',
+ title: 'Radio',
+ },
+ rate: {
+ category: 'Components',
+ subtitle: '评分',
+ type: 'Data Entry',
+ title: 'Rate',
+ cols: 1,
+ },
+ tabs: {
+ category: 'Components',
+ subtitle: '标签页',
+ type: 'Data Display',
+ title: 'Tabs',
+ cols: 1,
+ },
+ tag: {
+ category: 'Components',
+ subtitle: '标签',
+ type: 'Data Display',
+ title: 'Tag',
+ },
+ tooltip: {
+ category: 'Components',
+ subtitle: '文字提示',
+ type: 'Data Display',
+ title: 'Tooltip',
+ },
+ dropdown: {
+ category: 'Components',
+ subtitle: '下拉菜单',
+ type: 'Navigation',
+ title: 'Dropdown',
+ },
+ divider: {
+ category: 'Components',
+ subtitle: '分割线',
+ type: 'Other',
+ title: 'Divider',
+ },
+ collapse: {
+ category: 'Components',
+ subtitle: '折叠面板',
+ type: 'Data Display',
+ title: 'Collapse',
+ cols: 1,
+ },
+ notification: {
+ category: 'Components',
+ subtitle: '通知提醒框',
+ type: 'Feedback',
+ title: 'Notification',
+ },
+ message: {
+ category: 'Components',
+ subtitle: '全局提示',
+ type: 'Feedback',
+ title: 'Message',
+ },
+ spin: {
+ category: 'Components',
+ subtitle: '加载中',
+ type: 'Feedback',
+ title: 'Spin',
+ },
+ result: {
+ category: 'Components',
+ subtitle: '结果',
+ type: 'Feedback',
+ title: 'Result',
+ },
+ switch: {
+ category: 'Components',
+ subtitle: '开关',
+ type: 'Data Entry',
+ title: 'Switch',
+ },
+ autoComplete: {
+ category: 'Components',
+ subtitle: '自动完成',
+ type: 'Data Entry',
+ title: 'AutoComplete',
+ cols: 2,
+ },
+ affix: {
+ category: 'Components',
+ subtitle: '固钉',
+ type: 'Navigation',
+ title: 'Affix',
+ },
+ cascader: {
+ category: 'Components',
+ subtitle: '级联选择',
+ type: 'Data Entry',
+ title: 'Cascader',
+ },
+ backTop: {
+ category: 'Components',
+ subtitle: '回到顶部',
+ type: 'Other',
+ title: 'BackTop',
+ },
+ modal: {
+ category: 'Components',
+ subtitle: '对话框',
+ type: 'Feedback',
+ title: 'Modal',
+ },
+ alert: {
+ category: 'Components',
+ subtitle: '警告提示',
+ type: 'Feedback',
+ title: 'Alert',
+ },
+ timePicker: {
+ category: 'Components',
+ subtitle: '时间选择框',
+ type: 'Data Entry',
+ title: 'TimePicker',
+ },
+ steps: {
+ category: 'Components',
+ subtitle: '步骤条',
+ type: 'Navigation',
+ title: 'Steps',
+ cols: 1,
+ },
+ calendar: {
+ category: 'Components',
+ subtitle: '日历',
+ type: 'Data Display',
+ title: 'Calendar',
+ cols: 1,
+ },
+ datePicker: {
+ category: 'Components',
+ subtitle: '日期选择框',
+ type: 'Data Entry',
+ title: 'DatePicker',
+ },
+ slider: {
+ category: 'Components',
+ subtitle: '滑动输入条',
+ type: 'Data Entry',
+ title: 'Slider',
+ },
+ progress: {
+ category: 'Components',
+ subtitle: '进度条',
+ type: 'Feedback',
+ title: 'Progress',
+ },
+ timeline: {
+ category: 'Components',
+ subtitle: '时间轴',
+ type: 'Data Display',
+ title: 'Timeline',
+ },
+ table: {
+ category: 'Components',
+ subtitle: '表格',
+ type: 'Data Display',
+ title: 'Table',
+ cols: 1,
+ },
+ inputNumber: {
+ category: 'Components',
+ subtitle: '数字输入框',
+ type: 'Data Entry',
+ title: 'InputNumber',
+ },
+ transfer: {
+ category: 'Components',
+ subtitle: '穿梭框',
+ type: 'Data Entry',
+ title: 'Transfer',
+ cols: '1',
+ },
+ upload: {
+ category: 'Components',
+ subtitle: '上传',
+ type: 'Data Entry',
+ title: 'Upload',
+ },
+ carousel: {
+ category: 'Components',
+ type: 'Data Display',
+ title: 'Carousel',
+ subtitle: '走马灯',
+ },
+ tree: {
+ category: 'Components',
+ subtitle: '树形控件',
+ type: 'Data Display',
+ title: 'Tree',
+ },
+ treeSelect: {
+ category: 'Components',
+ subtitle: '树选择',
+ type: 'Data Entry',
+ title: 'TreeSelect',
+ },
+ layout: {
+ category: 'Components',
+ subtitle: '布局',
+ type: 'Layout',
+ title: 'Layout',
+ cols: 1,
+ },
+ form: {
+ category: 'Components',
+ subtitle: '表单',
+ type: 'Data Entry',
+ title: 'Form',
+ cols: 1,
+ },
+ anchor: {
+ category: 'Components',
+ subtitle: '锚点',
+ type: 'Other',
+ title: 'Anchor',
+ cols: 2,
+ },
+ image: {
+ category: 'Components',
+ subtitle: '图片',
+ type: 'Data Display',
+ title: 'Image',
+ cols: 2,
+ },
+ list: {
+ category: 'Components',
+ subtitle: '列表',
+ type: 'Data Display',
+ title: 'List',
+ cols: 1,
+ },
+ drawer: {
+ category: 'Components',
+ type: 'Feedback',
+ title: 'Drawer',
+ subtitle: '抽屉',
+ },
+ skeleton: {
+ category: 'Components',
+ type: 'Feedback',
+ title: 'Skeleton',
+ subtitle: '骨架屏',
+ },
+ statistic: {
+ category: 'Components',
+ subtitle: '统计数值',
+ type: 'Data Display',
+ title: 'Statistic',
+ },
+ descriptions: {
+ category: 'Components',
+ subtitle: '描述列表',
+ type: 'Data Display',
+ title: 'Descriptions',
+ },
+ space: {
+ category: 'Components',
+ subtitle: '间距',
+ type: 'Layout',
+ title: 'Space',
+ },
+ // colorPicker: {
+ // category: 'Components',
+ // subtitle: '取色器',
+ // type: 'Data Entry',
+ // title: 'ColorPicker (Beta)',
+ // },
+};
diff --git a/site/src/directives/clipboard/clipboard-action.js b/site/src/directives/clipboard/clipboard-action.js
new file mode 100644
index 000000000..679e1529d
--- /dev/null
+++ b/site/src/directives/clipboard/clipboard-action.js
@@ -0,0 +1,208 @@
+import select from './select';
+
+/**
+ * Inner class which performs selection from either `text` or `target`
+ * properties and then executes copy or cut operations.
+ */
+class ClipboardAction {
+ /**
+ * @param {Object} options
+ */
+ constructor(options) {
+ this.resolveOptions(options);
+ this.initSelection();
+ }
+
+ /**
+ * Defines base properties passed from constructor.
+ * @param {Object} options
+ */
+ resolveOptions(options = {}) {
+ this.action = options.action;
+ this.container = options.container;
+ this.emitter = options.emitter;
+ this.target = options.target;
+ this.text = options.text;
+ this.trigger = options.trigger;
+
+ this.selectedText = '';
+ }
+
+ /**
+ * Decides which selection strategy is going to be applied based
+ * on the existence of `text` and `target` properties.
+ */
+ initSelection() {
+ if (this.text) {
+ this.selectFake();
+ } else if (this.target) {
+ this.selectTarget();
+ }
+ }
+
+ /**
+ * Creates a fake textarea element, sets its value from `text` property,
+ * and makes a selection on it.
+ */
+ selectFake() {
+ const isRTL = document.documentElement.getAttribute('dir') == 'rtl';
+
+ this.removeFake();
+
+ this.fakeHandlerCallback = () => this.removeFake();
+ this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;
+
+ this.fakeElem = document.createElement('textarea');
+ // Prevent zooming on iOS
+ this.fakeElem.style.fontSize = '12pt';
+ // Reset box model
+ this.fakeElem.style.border = '0';
+ this.fakeElem.style.padding = '0';
+ this.fakeElem.style.margin = '0';
+ // Move element out of screen horizontally
+ this.fakeElem.style.position = 'absolute';
+ this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';
+ // Move element to the same position vertically
+ let yPosition = window.pageYOffset || document.documentElement.scrollTop;
+ this.fakeElem.style.top = `${yPosition}px`;
+
+ this.fakeElem.setAttribute('readonly', '');
+ this.fakeElem.value = this.text;
+
+ this.container.appendChild(this.fakeElem);
+
+ this.selectedText = select(this.fakeElem);
+ this.copyText();
+ }
+
+ /**
+ * Only removes the fake element after another click event, that way
+ * a user can hit `Ctrl+C` to copy because selection still exists.
+ */
+ removeFake() {
+ if (this.fakeHandler) {
+ this.container.removeEventListener('click', this.fakeHandlerCallback);
+ this.fakeHandler = null;
+ this.fakeHandlerCallback = null;
+ }
+
+ if (this.fakeElem) {
+ this.container.removeChild(this.fakeElem);
+ this.fakeElem = null;
+ }
+ }
+
+ /**
+ * Selects the content from element passed on `target` property.
+ */
+ selectTarget() {
+ this.selectedText = select(this.target);
+ this.copyText();
+ }
+
+ /**
+ * Executes the copy operation based on the current selection.
+ */
+ copyText() {
+ let succeeded;
+
+ try {
+ succeeded = document.execCommand(this.action);
+ } catch (err) {
+ succeeded = false;
+ }
+
+ this.handleResult(succeeded);
+ }
+
+ /**
+ * Fires an event based on the copy operation result.
+ * @param {Boolean} succeeded
+ */
+ handleResult(succeeded) {
+ this.emitter.emit(succeeded ? 'success' : 'error', {
+ action: this.action,
+ text: this.selectedText,
+ trigger: this.trigger,
+ clearSelection: this.clearSelection.bind(this),
+ });
+ }
+
+ /**
+ * Moves focus away from `target` and back to the trigger, removes current selection.
+ */
+ clearSelection() {
+ if (this.trigger) {
+ this.trigger.focus();
+ }
+ document.activeElement.blur();
+ window.getSelection().removeAllRanges();
+ }
+
+ /**
+ * Sets the `action` to be performed which can be either 'copy' or 'cut'.
+ * @param {String} action
+ */
+ set action(action = 'copy') {
+ this._action = action;
+
+ if (this._action !== 'copy' && this._action !== 'cut') {
+ throw new Error('Invalid "action" value, use either "copy" or "cut"');
+ }
+ }
+
+ /**
+ * Gets the `action` property.
+ * @return {String}
+ */
+ get action() {
+ return this._action;
+ }
+
+ /**
+ * Sets the `target` property using an element
+ * that will be have its content copied.
+ * @param {Element} target
+ */
+ set target(target) {
+ if (target !== undefined) {
+ if (target && typeof target === 'object' && target.nodeType === 1) {
+ if (this.action === 'copy' && target.hasAttribute('disabled')) {
+ throw new Error(
+ 'Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute',
+ );
+ }
+
+ if (
+ this.action === 'cut' &&
+ (target.hasAttribute('readonly') || target.hasAttribute('disabled'))
+ ) {
+ throw new Error(
+ 'Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes',
+ );
+ }
+
+ this._target = target;
+ } else {
+ throw new Error('Invalid "target" value, use a valid Element');
+ }
+ }
+ }
+
+ /**
+ * Gets the `target` property.
+ * @return {String|HTMLElement}
+ */
+ get target() {
+ return this._target;
+ }
+
+ /**
+ * Destroy lifecycle.
+ */
+ destroy() {
+ this.removeFake();
+ }
+}
+
+export default ClipboardAction;
diff --git a/site/src/directives/clipboard/clipboard.js b/site/src/directives/clipboard/clipboard.js
new file mode 100644
index 000000000..ceff0df5d
--- /dev/null
+++ b/site/src/directives/clipboard/clipboard.js
@@ -0,0 +1,134 @@
+import ClipboardAction from './clipboard-action';
+import Emitter from './tiny-emitter';
+import listen from './listen';
+
+/**
+ * Base class which takes one or more elements, adds event listeners to them,
+ * and instantiates a new `ClipboardAction` on each click.
+ */
+class Clipboard extends Emitter {
+ /**
+ * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
+ * @param {Object} options
+ */
+ constructor(trigger, options) {
+ super();
+
+ this.resolveOptions(options);
+ this.listenClick(trigger);
+ }
+
+ /**
+ * Defines if attributes would be resolved using internal setter functions
+ * or custom functions that were passed in the constructor.
+ * @param {Object} options
+ */
+ resolveOptions(options = {}) {
+ this.action = typeof options.action === 'function' ? options.action : this.defaultAction;
+ this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;
+ this.text = typeof options.text === 'function' ? options.text : this.defaultText;
+ this.container = typeof options.container === 'object' ? options.container : document.body;
+ }
+
+ /**
+ * Adds a click event listener to the passed trigger.
+ * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
+ */
+ listenClick(trigger) {
+ this.listener = listen(trigger, 'click', e => this.onClick(e));
+ }
+
+ /**
+ * Defines a new `ClipboardAction` on each click event.
+ * @param {Event} e
+ */
+ onClick(e) {
+ const trigger = e.delegateTarget || e.currentTarget;
+
+ if (this.clipboardAction) {
+ this.clipboardAction = null;
+ }
+
+ this.clipboardAction = new ClipboardAction({
+ action: this.action(trigger),
+ target: this.target(trigger),
+ text: this.text(trigger),
+ container: this.container,
+ trigger: trigger,
+ emitter: this,
+ });
+ }
+
+ /**
+ * Default `action` lookup function.
+ * @param {Element} trigger
+ */
+ defaultAction(trigger) {
+ return getAttributeValue('action', trigger);
+ }
+
+ /**
+ * Default `target` lookup function.
+ * @param {Element} trigger
+ */
+ defaultTarget(trigger) {
+ const selector = getAttributeValue('target', trigger);
+
+ if (selector) {
+ return document.querySelector(selector);
+ }
+ }
+
+ /**
+ * Returns the support of the given action, or all actions if no action is
+ * given.
+ * @param {String} [action]
+ */
+ static isSupported(action = ['copy', 'cut']) {
+ const actions = typeof action === 'string' ? [action] : action;
+ let support = !!document.queryCommandSupported;
+
+ actions.forEach(action => {
+ support = support && !!document.queryCommandSupported(action);
+ });
+
+ return support;
+ }
+
+ /**
+ * Default `text` lookup function.
+ * @param {Element} trigger
+ */
+ defaultText(trigger) {
+ return getAttributeValue('text', trigger);
+ }
+
+ /**
+ * Destroy lifecycle.
+ */
+ destroy() {
+ this.listener.destroy();
+
+ if (this.clipboardAction) {
+ this.clipboardAction.destroy();
+ this.clipboardAction = null;
+ }
+ }
+}
+
+/**
+ * Helper function to retrieve attribute value.
+ * @param {String} suffix
+ * @param {Element} element
+ */
+function getAttributeValue(suffix, element) {
+ const attribute = `data-clipboard-${suffix}`;
+
+ if (!element.hasAttribute(attribute)) {
+ return;
+ }
+
+ return element.getAttribute(attribute);
+}
+
+export default Clipboard;
diff --git a/site/src/directives/clipboard/closest.js b/site/src/directives/clipboard/closest.js
new file mode 100644
index 000000000..d99a0c9fb
--- /dev/null
+++ b/site/src/directives/clipboard/closest.js
@@ -0,0 +1,33 @@
+var DOCUMENT_NODE_TYPE = 9;
+
+/**
+ * A polyfill for Element.matches()
+ */
+if (typeof Element !== 'undefined' && !Element.prototype.matches) {
+ var proto = Element.prototype;
+
+ proto.matches =
+ proto.matchesSelector ||
+ proto.mozMatchesSelector ||
+ proto.msMatchesSelector ||
+ proto.oMatchesSelector ||
+ proto.webkitMatchesSelector;
+}
+
+/**
+ * Finds the closest parent that matches a selector.
+ *
+ * @param {Element} element
+ * @param {String} selector
+ * @return {Function}
+ */
+function closest(element, selector) {
+ while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {
+ if (typeof element.matches === 'function' && element.matches(selector)) {
+ return element;
+ }
+ element = element.parentNode;
+ }
+}
+
+export default closest;
diff --git a/site/src/directives/clipboard/delegate.js b/site/src/directives/clipboard/delegate.js
new file mode 100644
index 000000000..b332695ff
--- /dev/null
+++ b/site/src/directives/clipboard/delegate.js
@@ -0,0 +1,78 @@
+import closest from './closest';
+
+/**
+ * Delegates event to a selector.
+ *
+ * @param {Element} element
+ * @param {String} selector
+ * @param {String} type
+ * @param {Function} callback
+ * @param {Boolean} useCapture
+ * @return {Object}
+ */
+function _delegate(element, selector, type, callback, useCapture) {
+ var listenerFn = listener.apply(this, arguments);
+
+ element.addEventListener(type, listenerFn, useCapture);
+
+ return {
+ destroy: function () {
+ element.removeEventListener(type, listenerFn, useCapture);
+ },
+ };
+}
+
+/**
+ * Delegates event to a selector.
+ *
+ * @param {Element|String|Array} [elements]
+ * @param {String} selector
+ * @param {String} type
+ * @param {Function} callback
+ * @param {Boolean} useCapture
+ * @return {Object}
+ */
+function delegate(elements, selector, type, callback, useCapture) {
+ // Handle the regular Element usage
+ if (typeof elements.addEventListener === 'function') {
+ return _delegate.apply(null, arguments);
+ }
+
+ // Handle Element-less usage, it defaults to global delegation
+ if (typeof type === 'function') {
+ // Use `document` as the first parameter, then apply arguments
+ // This is a short way to .unshift `arguments` without running into deoptimizations
+ return _delegate.bind(null, document).apply(null, arguments);
+ }
+
+ // Handle Selector-based usage
+ if (typeof elements === 'string') {
+ elements = document.querySelectorAll(elements);
+ }
+
+ // Handle Array-like based usage
+ return Array.prototype.map.call(elements, function (element) {
+ return _delegate(element, selector, type, callback, useCapture);
+ });
+}
+
+/**
+ * Finds closest match and invokes callback.
+ *
+ * @param {Element} element
+ * @param {String} selector
+ * @param {String} type
+ * @param {Function} callback
+ * @return {Function}
+ */
+function listener(element, selector, type, callback) {
+ return function (e) {
+ e.delegateTarget = closest(e.target, selector);
+
+ if (e.delegateTarget) {
+ callback.call(element, e);
+ }
+ };
+}
+
+export default delegate;
diff --git a/site/src/directives/clipboard/index.js b/site/src/directives/clipboard/index.js
new file mode 100644
index 000000000..848bb6fda
--- /dev/null
+++ b/site/src/directives/clipboard/index.js
@@ -0,0 +1,92 @@
+import Clipboard from './clipboard'; // FIXME: workaround for browserify
+
+const VueClipboardConfig = {
+ autoSetContainer: false,
+ appendToBody: true, // This fixes IE, see #50
+};
+
+const VueClipboard = {
+ install(app) {
+ // Vue.prototype.$clipboardConfig = VueClipboardConfig;
+ // Vue.prototype.$copyText = function(text, container) {
+ // return new Promise(function(resolve, reject) {
+ // const fakeElement = document.createElement('button');
+ // const clipboard = new Clipboard(fakeElement, {
+ // text() {
+ // return text;
+ // },
+ // action() {
+ // return 'copy';
+ // },
+ // container: typeof container === 'object' ? container : document.body,
+ // });
+ // clipboard.on('success', function(e) {
+ // clipboard.destroy();
+ // resolve(e);
+ // });
+ // clipboard.on('error', function(e) {
+ // clipboard.destroy();
+ // reject(e);
+ // });
+ // if (VueClipboardConfig.appendToBody) document.body.appendChild(fakeElement);
+ // fakeElement.click();
+ // if (VueClipboardConfig.appendToBody) document.body.removeChild(fakeElement);
+ // });
+ // };
+
+ app.directive('clipboard', {
+ mounted(el, binding) {
+ if (binding.arg === 'success') {
+ el._vClipboard_success = binding.value;
+ } else if (binding.arg === 'error') {
+ el._vClipboard_error = binding.value;
+ } else {
+ const clipboard = new Clipboard(el, {
+ text() {
+ return binding.value;
+ },
+ action() {
+ return binding.arg === 'cut' ? 'cut' : 'copy';
+ },
+ container: VueClipboardConfig.autoSetContainer ? el : undefined,
+ });
+ clipboard.on('success', function (e) {
+ const callback = el._vClipboard_success;
+ callback && callback(e);
+ });
+ clipboard.on('error', function (e) {
+ const callback = el._vClipboard_error;
+ callback && callback(e);
+ });
+ el._vClipboard = clipboard;
+ }
+ },
+ updated(el, binding) {
+ if (binding.arg === 'success') {
+ el._vClipboard_success = binding.value;
+ } else if (binding.arg === 'error') {
+ el._vClipboard_error = binding.value;
+ } else {
+ el._vClipboard.text = function () {
+ return binding.value;
+ };
+ el._vClipboard.action = function () {
+ return binding.arg === 'cut' ? 'cut' : 'copy';
+ };
+ }
+ },
+ unmounted(el, binding) {
+ if (binding.arg === 'success') {
+ delete el._vClipboard_success;
+ } else if (binding.arg === 'error') {
+ delete el._vClipboard_error;
+ } else {
+ el._vClipboard.destroy();
+ delete el._vClipboard;
+ }
+ },
+ });
+ },
+};
+
+export default VueClipboard;
diff --git a/site/src/directives/clipboard/is.js b/site/src/directives/clipboard/is.js
new file mode 100644
index 000000000..d1509a4ba
--- /dev/null
+++ b/site/src/directives/clipboard/is.js
@@ -0,0 +1,55 @@
+/**
+ * Check if argument is a HTML element.
+ *
+ * @param {Object} value
+ * @return {Boolean}
+ */
+export const node = function (value) {
+ return value !== undefined && value instanceof HTMLElement && value.nodeType === 1;
+};
+
+/**
+ * Check if argument is a list of HTML elements.
+ *
+ * @param {Object} value
+ * @return {Boolean}
+ */
+export const nodeList = function (value) {
+ var type = Object.prototype.toString.call(value);
+
+ return (
+ value !== undefined &&
+ (type === '[object NodeList]' || type === '[object HTMLCollection]') &&
+ 'length' in value &&
+ (value.length === 0 || node(value[0]))
+ );
+};
+
+/**
+ * Check if argument is a string.
+ *
+ * @param {Object} value
+ * @return {Boolean}
+ */
+export const string = function (value) {
+ return typeof value === 'string' || value instanceof String;
+};
+
+/**
+ * Check if argument is a function.
+ *
+ * @param {Object} value
+ * @return {Boolean}
+ */
+export const fn = function (value) {
+ var type = Object.prototype.toString.call(value);
+
+ return type === '[object Function]';
+};
+
+export default {
+ node,
+ nodeList,
+ string,
+ fn,
+};
diff --git a/site/src/directives/clipboard/listen.js b/site/src/directives/clipboard/listen.js
new file mode 100644
index 000000000..ac9376ab4
--- /dev/null
+++ b/site/src/directives/clipboard/listen.js
@@ -0,0 +1,94 @@
+import is from './is';
+import delegate from './delegate';
+
+/**
+ * Validates all params and calls the right
+ * listener function based on its target type.
+ *
+ * @param {String|HTMLElement|HTMLCollection|NodeList} target
+ * @param {String} type
+ * @param {Function} callback
+ * @return {Object}
+ */
+function listen(target, type, callback) {
+ if (!target && !type && !callback) {
+ throw new Error('Missing required arguments');
+ }
+
+ if (!is.string(type)) {
+ throw new TypeError('Second argument must be a String');
+ }
+
+ if (!is.fn(callback)) {
+ throw new TypeError('Third argument must be a Function');
+ }
+
+ if (is.node(target)) {
+ return listenNode(target, type, callback);
+ } else if (is.nodeList(target)) {
+ return listenNodeList(target, type, callback);
+ } else if (is.string(target)) {
+ return listenSelector(target, type, callback);
+ } else {
+ throw new TypeError(
+ 'First argument must be a String, HTMLElement, HTMLCollection, or NodeList',
+ );
+ }
+}
+
+/**
+ * Adds an event listener to a HTML element
+ * and returns a remove listener function.
+ *
+ * @param {HTMLElement} node
+ * @param {String} type
+ * @param {Function} callback
+ * @return {Object}
+ */
+function listenNode(node, type, callback) {
+ node.addEventListener(type, callback);
+
+ return {
+ destroy: function () {
+ node.removeEventListener(type, callback);
+ },
+ };
+}
+
+/**
+ * Add an event listener to a list of HTML elements
+ * and returns a remove listener function.
+ *
+ * @param {NodeList|HTMLCollection} nodeList
+ * @param {String} type
+ * @param {Function} callback
+ * @return {Object}
+ */
+function listenNodeList(nodeList, type, callback) {
+ Array.prototype.forEach.call(nodeList, function (node) {
+ node.addEventListener(type, callback);
+ });
+
+ return {
+ destroy: function () {
+ Array.prototype.forEach.call(nodeList, function (node) {
+ node.removeEventListener(type, callback);
+ });
+ },
+ };
+}
+
+/**
+ * Add an event listener to a selector
+ * and returns a remove listener function.
+ *
+ * @param {String} selector
+ * @param {String} type
+ * @param {Function} callback
+ * @return {Object}
+ */
+function listenSelector(selector, type, callback) {
+ return delegate(document.body, selector, type, callback);
+}
+
+export default listen;
diff --git a/site/src/directives/clipboard/select.js b/site/src/directives/clipboard/select.js
new file mode 100644
index 000000000..5eb335d7b
--- /dev/null
+++ b/site/src/directives/clipboard/select.js
@@ -0,0 +1,40 @@
+function select(element) {
+ var selectedText;
+
+ if (element.nodeName === 'SELECT') {
+ element.focus();
+
+ selectedText = element.value;
+ } else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
+ var isReadOnly = element.hasAttribute('readonly');
+
+ if (!isReadOnly) {
+ element.setAttribute('readonly', '');
+ }
+
+ element.select();
+ element.setSelectionRange(0, element.value.length);
+
+ if (!isReadOnly) {
+ element.removeAttribute('readonly');
+ }
+
+ selectedText = element.value;
+ } else {
+ if (element.hasAttribute('contenteditable')) {
+ element.focus();
+ }
+
+ var selection = window.getSelection();
+ var range = document.createRange();
+
+ range.selectNodeContents(element);
+ selection.removeAllRanges();
+ selection.addRange(range);
+
+ selectedText = selection.toString();
+ }
+
+ return selectedText;
+}
+export default select;
diff --git a/site/src/directives/clipboard/tiny-emitter.js b/site/src/directives/clipboard/tiny-emitter.js
new file mode 100644
index 000000000..2b54fca90
--- /dev/null
+++ b/site/src/directives/clipboard/tiny-emitter.js
@@ -0,0 +1,63 @@
+function E() {
+ // Keep this empty so it's easier to inherit from
+ // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
+}
+
+E.prototype = {
+ on: function (name, callback, ctx) {
+ var e = this.e || (this.e = {});
+
+ (e[name] || (e[name] = [])).push({
+ fn: callback,
+ ctx: ctx,
+ });
+
+ return this;
+ },
+
+ once: function (name, callback, ctx) {
+ var self = this;
+ function listener() {
+ self.off(name, listener);
+ callback.apply(ctx, arguments);
+ }
+
+ listener._ = callback;
+ return this.on(name, listener, ctx);
+ },
+
+ emit: function (name) {
+ var data = [].slice.call(arguments, 1);
+ var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
+ var i = 0;
+ var len = evtArr.length;
+
+ for (i; i < len; i++) {
+ evtArr[i].fn.apply(evtArr[i].ctx, data);
+ }
+
+ return this;
+ },
+
+ off: function (name, callback) {
+ var e = this.e || (this.e = {});
+ var evts = e[name];
+ var liveEvents = [];
+
+ if (evts && callback) {
+ for (var i = 0, len = evts.length; i < len; i++) {
+ if (evts[i].fn !== callback && evts[i].fn._ !== callback) liveEvents.push(evts[i]);
+ }
+ }
+
+ // Remove event from queue to prevent memory leak
+ // Suggested by https://github.com/lazd
+ // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
+
+ liveEvents.length ? (e[name] = liveEvents) : delete e[name];
+
+ return this;
+ },
+};
+
+export default E;
diff --git a/site/src/hooks/useMediaQuery.ts b/site/src/hooks/useMediaQuery.ts
new file mode 100644
index 000000000..3417f366d
--- /dev/null
+++ b/site/src/hooks/useMediaQuery.ts
@@ -0,0 +1,76 @@
+import { ref } from 'vue';
+
+export const MediaQueryEnum = {
+ xs: {
+ maxWidth: 575,
+ matchMedia: '(max-width: 575px)',
+ },
+ sm: {
+ minWidth: 576,
+ maxWidth: 767,
+ matchMedia: '(min-width: 576px) and (max-width: 767px)',
+ },
+ md: {
+ minWidth: 768,
+ maxWidth: 991,
+ matchMedia: '(min-width: 768px) and (max-width: 991px)',
+ },
+ lg: {
+ minWidth: 992,
+ maxWidth: 1199,
+ matchMedia: '(min-width: 992px) and (max-width: 1199px)',
+ },
+ xl: {
+ minWidth: 1200,
+ maxWidth: 1599,
+ matchMedia: '(min-width: 1200px) and (max-width: 1599px)',
+ },
+ xxl: {
+ minWidth: 1600,
+ matchMedia: '(min-width: 1600px)',
+ },
+};
+
+export type MediaQueryKey = keyof typeof MediaQueryEnum;
+
+/**
+ * loop query screen className
+ * Array.find will throw a error
+ * `Rendered more hooks than during the previous render.`
+ * So should use Array.forEach
+ */
+export const getScreenClassName = () => {
+ let className: MediaQueryKey = 'md';
+ // support ssr
+ if (typeof window === 'undefined') {
+ return className;
+ }
+ const mediaQueryKey = (Object.keys(MediaQueryEnum) as MediaQueryKey[]).find(key => {
+ const { matchMedia } = MediaQueryEnum[key];
+ if (window.matchMedia(matchMedia).matches) {
+ return true;
+ }
+ return false;
+ });
+ className = (mediaQueryKey as unknown) as MediaQueryKey;
+ return className;
+};
+
+const useMedia = () => {
+ const colSpan = ref(getScreenClassName());
+ (Object.keys(MediaQueryEnum) as MediaQueryKey[]).forEach(key => {
+ const { matchMedia } = MediaQueryEnum[key];
+ const mediaQuery = window.matchMedia(matchMedia);
+ if (mediaQuery.matches) {
+ colSpan.value = key;
+ }
+ mediaQuery.onchange = e => {
+ if (e.matches) {
+ colSpan.value = key;
+ }
+ };
+ });
+ return colSpan;
+};
+
+export default useMedia;
diff --git a/site/src/hooks/useMenus.ts b/site/src/hooks/useMenus.ts
new file mode 100644
index 000000000..cfe940257
--- /dev/null
+++ b/site/src/hooks/useMenus.ts
@@ -0,0 +1,86 @@
+import { groupBy, sortBy } from 'lodash-es';
+import { computed, ComputedRef, inject } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { GLOBAL_CONFIG } from '../SymbolKey';
+const typeOrder: any = {
+ 组件总览: { order: -1, en: 'Overview' },
+ 通用: { order: 0, en: 'General' },
+ 布局: { order: 1, en: 'Layout' },
+ 导航: { order: 2, en: 'Navigation' },
+ 数据录入: { order: 3, en: 'Data Entry' },
+ 数据展示: { order: 4, en: 'Data Display' },
+ 反馈: { order: 5, en: 'Feedback' },
+ 其他: { order: 6, en: 'Other' },
+ 废弃: { order: 7, en: 'Deprecated' },
+};
+const useMenus = (): {
+ menus: ComputedRef;
+ dataSource: ComputedRef;
+ currentMenuIndex: ComputedRef;
+ activeMenuItem: ComputedRef;
+} => {
+ const route = useRoute();
+ const router = useRouter();
+ const routes = router.getRoutes();
+ const globalConfig = inject(GLOBAL_CONFIG);
+ const menus = computed(() => {
+ const path = route.path;
+ const category = path.split('/')[1];
+ const pattern = /^\/iframe/;
+ const isZhCN = globalConfig.isZhCN.value;
+ const ms = routes
+ .filter(r => {
+ const inCategory =
+ r.meta &&
+ r.meta.category &&
+ r.meta.category.toLowerCase() === category &&
+ !pattern.test(r.path);
+ if (inCategory && category === 'docs') {
+ if (isZhCN) {
+ return r.path.indexOf('-cn') >= 0;
+ } else {
+ return r.path.indexOf('-cn') === -1;
+ }
+ } else {
+ return inCategory;
+ }
+ })
+ .map(r => ({ ...r.meta, path: r.path.split(':lang')[0].replace('-cn', '') }));
+ if (category === 'docs') {
+ ms.push({
+ enTitle: 'Change Log',
+ title: '更新日志',
+ category: 'docs',
+ target: '_blank',
+ path: globalConfig.isZhCN.value
+ ? 'https://github.com/vueComponent/ant-design-vue/blob/next/CHANGELOG.zh-CN.md'
+ : 'https://github.com/vueComponent/ant-design-vue/blob/next/CHANGELOG.en-US.md',
+ } as any);
+ }
+ return ms;
+ });
+ const activeMenuItem = computed(() => {
+ return route.path.split('-cn')[0];
+ });
+ const currentMenuIndex = computed(() => {
+ return menus.value.findIndex(m => m.path === activeMenuItem.value);
+ });
+ const dataSource = computed(() => {
+ const group = groupBy(menus.value, (m: any) => m.type || m.category);
+ const keys: string[] = Object.keys(group);
+ const newMenus = keys
+ .map(key => {
+ return {
+ title: key,
+ order: typeOrder[key] && typeOrder[key].order,
+ enTitle: typeOrder[key] && typeOrder[key].en,
+ children: sortBy(group[key], 'title'),
+ };
+ })
+ .sort((a, b) => a.order - b.order);
+ return keys.length === 1 ? menus.value : newMenus;
+ });
+ return { menus, dataSource, activeMenuItem, currentMenuIndex };
+};
+
+export default useMenus;
diff --git a/site/src/i18n.js b/site/src/i18n.js
new file mode 100644
index 000000000..be4afa414
--- /dev/null
+++ b/site/src/i18n.js
@@ -0,0 +1,16 @@
+import { createI18n } from 'vue-i18n';
+import enUS from './locale/en-US';
+import zhCN from './locale/zh-CN';
+import { isZhCN } from './utils/util';
+
+const i18n = createI18n({
+ legacy: true,
+ locale: isZhCN(location.pathname) ? 'zh-CN' : 'en-US',
+ fallbackLocale: 'en-US',
+ messages: {
+ 'zh-CN': zhCN,
+ 'en-US': enUS,
+ },
+});
+
+export default i18n;
diff --git a/site/src/index.less b/site/src/index.less
new file mode 100644
index 000000000..042998d3d
--- /dev/null
+++ b/site/src/index.less
@@ -0,0 +1,159 @@
+@import './theme/static/index.less';
+
+.drawer {
+ position: fixed;
+ z-index: 9999;
+ -webkit-transition: width 0s ease 0.3s, height 0s ease 0.3s,
+ -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: width 0s ease 0.3s, height 0s ease 0.3s,
+ -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: width 0s ease 0.3s, height 0s ease 0.3s,
+ transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: width 0s ease 0.3s, height 0s ease 0.3s,
+ transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+}
+
+.drawer > * {
+ -webkit-transition: opacity 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ -webkit-box-shadow 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: opacity 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ -webkit-box-shadow 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ opacity 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ box-shadow 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ opacity 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ box-shadow 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ -webkit-box-shadow 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+}
+
+.drawer.drawer-open {
+ -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+}
+
+.drawer .drawer-mask {
+ background: #000;
+ opacity: 0;
+ width: 100%;
+ height: 0;
+ position: absolute;
+ top: 0;
+ left: 0;
+ -webkit-transition: opacity 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86), height 0s ease 0.3s;
+ transition: opacity 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86), height 0s ease 0.3s;
+}
+
+.drawer-content-wrapper {
+ position: absolute;
+ background: #fff;
+}
+
+.drawer-content {
+ overflow: auto;
+ z-index: 1;
+ position: relative;
+}
+
+.drawer-handle {
+ position: absolute;
+ top: 72px;
+ width: 41px;
+ height: 40px;
+ cursor: pointer;
+ z-index: 0;
+ text-align: center;
+ line-height: 40px;
+ font-size: 16px;
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ background: #fff;
+}
+
+.drawer-handle-icon {
+ width: 14px;
+ height: 2px;
+ background: #333;
+ position: relative;
+ -webkit-transition: background 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: background 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+}
+
+.drawer-handle-icon:after,
+.drawer-handle-icon:before {
+ content: '';
+ display: block;
+ position: absolute;
+ background: #333;
+ width: 100%;
+ height: 2px;
+ -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+ transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
+ -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
+}
+
+.drawer-handle-icon:before {
+ top: -5px;
+}
+
+.drawer-handle-icon:after {
+ top: 5px;
+}
+
+.drawer-left,
+.drawer-right {
+ width: 0;
+ height: 100%;
+}
+
+.drawer-left .drawer-content,
+.drawer-left .drawer-content-wrapper,
+.drawer-right .drawer-content,
+.drawer-right .drawer-content-wrapper {
+ height: 100%;
+}
+
+.drawer-left.drawer-open,
+.drawer-right.drawer-open {
+ width: 100%;
+}
+
+.drawer-left.drawer-open.no-mask,
+.drawer-right.drawer-open.no-mask {
+ width: 0;
+}
+
+.drawer-left {
+ top: 0;
+ left: 0;
+}
+.drawer-left .ant-drawer-body {
+ padding: 0;
+}
+
+.drawer-left .drawer-handle {
+ right: -40px;
+ border-radius: 0 4px 4px 0;
+}
+
+.drawer-left .drawer-handle,
+.drawer-left.drawer-open .drawer-content-wrapper {
+ -webkit-box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
+ box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
+}
diff --git a/site/src/layouts/BaseLayout.vue b/site/src/layouts/BaseLayout.vue
new file mode 100644
index 000000000..63b304900
--- /dev/null
+++ b/site/src/layouts/BaseLayout.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/site/src/layouts/Demo.vue b/site/src/layouts/Demo.vue
new file mode 100644
index 000000000..363813bd4
--- /dev/null
+++ b/site/src/layouts/Demo.vue
@@ -0,0 +1,49 @@
+
+
+
+
+ {{ frontmatter.title }}
+ {{ frontmatter.subtitle }}
+
+
+
+
+ {{ $t('app.component.examples') }}
+
+
+
+
+
+
+
+
diff --git a/site/src/layouts/Footer.vue b/site/src/layouts/Footer.vue
new file mode 100644
index 000000000..19b550921
--- /dev/null
+++ b/site/src/layouts/Footer.vue
@@ -0,0 +1,126 @@
+
+
+
+
diff --git a/site/src/layouts/Iframe.vue b/site/src/layouts/Iframe.vue
new file mode 100644
index 000000000..dd02e385c
--- /dev/null
+++ b/site/src/layouts/Iframe.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
diff --git a/site/src/layouts/Menu.vue b/site/src/layouts/Menu.vue
new file mode 100644
index 000000000..d596bcd05
--- /dev/null
+++ b/site/src/layouts/Menu.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
diff --git a/site/src/layouts/PrevAndNext.vue b/site/src/layouts/PrevAndNext.vue
new file mode 100644
index 000000000..3e5792f36
--- /dev/null
+++ b/site/src/layouts/PrevAndNext.vue
@@ -0,0 +1,68 @@
+
+
+
+
+ {{ prev.title }}
+ {{ prev.subtitle }}
+
+
+ {{ next.title }}
+ {{ next.subtitle }}
+
+
+
+
+
+
diff --git a/site/src/layouts/ThemeIcon.vue b/site/src/layouts/ThemeIcon.vue
new file mode 100644
index 000000000..615f3480e
--- /dev/null
+++ b/site/src/layouts/ThemeIcon.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/src/layouts/UserLayout.less b/site/src/layouts/UserLayout.less
new file mode 100755
index 000000000..93be8a9fb
--- /dev/null
+++ b/site/src/layouts/UserLayout.less
@@ -0,0 +1,71 @@
+@import '../../components/style/themes/default.less';
+
+.container {
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+ overflow: auto;
+ background: @layout-body-background;
+}
+
+.lang {
+ width: 100%;
+ height: 40px;
+ line-height: 44px;
+ text-align: right;
+ :global(.ant-dropdown-trigger) {
+ margin-right: 24px;
+ }
+}
+
+.content {
+ flex: 1;
+ padding: 32px 0;
+}
+
+@media (min-width: @screen-md-min) {
+ .container {
+ background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
+ background-repeat: no-repeat;
+ background-position: center 110px;
+ background-size: 100%;
+ }
+
+ .content {
+ padding: 32px 0 24px 0;
+ }
+}
+
+.top {
+ text-align: center;
+}
+
+.header {
+ height: 44px;
+ line-height: 44px;
+ a {
+ text-decoration: none;
+ }
+}
+
+.logo {
+ height: 44px;
+ margin-right: 16px;
+ vertical-align: top;
+}
+
+.title {
+ position: relative;
+ top: 2px;
+ color: @heading-color;
+ font-weight: 600;
+ font-size: 33px;
+ font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
+}
+
+.desc {
+ margin-top: 12px;
+ margin-bottom: 40px;
+ color: @text-color-secondary;
+ font-size: @font-size-base;
+}
diff --git a/site/src/layouts/UserLayout.vue b/site/src/layouts/UserLayout.vue
new file mode 100644
index 000000000..cf4dbd89d
--- /dev/null
+++ b/site/src/layouts/UserLayout.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
diff --git a/site/src/layouts/header/Ecosystem.vue b/site/src/layouts/header/Ecosystem.vue
new file mode 100644
index 000000000..887111d2d
--- /dev/null
+++ b/site/src/layouts/header/Ecosystem.vue
@@ -0,0 +1,70 @@
+
+
+
+
+ Pro For Vue2(Free)
+
+
+ Pro For Vue3(VIP)
+
+
+
+ {{ isZhCN ? '设计资源' : 'Design Resources' }}
+
+
+
+
+ VS Code Extension
+
+
+
+ Awesome
+
+
+
+ {{ isZhCN ? '微信' : 'WeChat' }}
+
+
+
+
+
+
+ QQ 1群(217490093)
+
+
+ QQ 2群(809774695)
+
+
+
+
+
+
diff --git a/site/src/layouts/header/Github.less b/site/src/layouts/header/Github.less
new file mode 100644
index 000000000..764409070
--- /dev/null
+++ b/site/src/layouts/header/Github.less
@@ -0,0 +1,133 @@
+.github-btn {
+ font: bold 11px/14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ height: 20px;
+ overflow: hidden;
+}
+.gh-btn,
+.gh-count,
+.gh-ico {
+ float: left;
+}
+.gh-btn,
+.gh-count {
+ padding: 2px 5px 2px 4px;
+ color: #333;
+ text-decoration: none;
+ white-space: nowrap;
+ cursor: pointer;
+ border-radius: 3px;
+}
+.gh-btn {
+ background-color: #eee;
+ background-image: -webkit-gradient(
+ linear,
+ left top,
+ left bottom,
+ color-stop(0, #fcfcfc),
+ color-stop(100%, #eee)
+ );
+ background-image: -webkit-linear-gradient(top, #fcfcfc 0, #eee 100%);
+ background-image: -moz-linear-gradient(top, #fcfcfc 0, #eee 100%);
+ background-image: -ms-linear-gradient(top, #fcfcfc 0, #eee 100%);
+ background-image: -o-linear-gradient(top, #fcfcfc 0, #eee 100%);
+ background-image: linear-gradient(to bottom, #fcfcfc 0, #eee 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fcfcfc', endColorstr='#eeeeee', GradientType=0);
+ background-repeat: no-repeat;
+ border: 1px solid #d5d5d5;
+}
+.gh-btn:hover,
+.gh-btn:focus {
+ text-decoration: none;
+ background-color: #ddd;
+ background-image: -webkit-gradient(
+ linear,
+ left top,
+ left bottom,
+ color-stop(0, #eee),
+ color-stop(100%, #ddd)
+ );
+ background-image: -webkit-linear-gradient(top, #eee 0, #ddd 100%);
+ background-image: -moz-linear-gradient(top, #eee 0, #ddd 100%);
+ background-image: -ms-linear-gradient(top, #eee 0, #ddd 100%);
+ background-image: -o-linear-gradient(top, #eee 0, #ddd 100%);
+ background-image: linear-gradient(to bottom, #eee 0, #ddd 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#dddddd', GradientType=0);
+ border-color: #ccc;
+}
+.gh-btn:active {
+ background-image: none;
+ background-color: #dcdcdc;
+ border-color: #b5b5b5;
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
+}
+.gh-ico {
+ width: 14px;
+ height: 14px;
+ margin-right: 4px;
+ background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjQwcHgiIGhlaWdodD0iNDBweCIgdmlld0JveD0iMTIgMTIgNDAgNDAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMTIgMTIgNDAgNDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGZpbGw9IiMzMzMzMzMiIGQ9Ik0zMiAxMy40Yy0xMC41IDAtMTkgOC41LTE5IDE5YzAgOC40IDUuNSAxNS41IDEzIDE4YzEgMC4yIDEuMy0wLjQgMS4zLTAuOWMwLTAuNSAwLTEuNyAwLTMuMiBjLTUuMyAxLjEtNi40LTIuNi02LjQtMi42QzIwIDQxLjYgMTguOCA0MSAxOC44IDQxYy0xLjctMS4yIDAuMS0xLjEgMC4xLTEuMWMxLjkgMC4xIDIuOSAyIDIuOSAyYzEuNyAyLjkgNC41IDIuMSA1LjUgMS42IGMwLjItMS4yIDAuNy0yLjEgMS4yLTIuNmMtNC4yLTAuNS04LjctMi4xLTguNy05LjRjMC0yLjEgMC43LTMuNyAyLTUuMWMtMC4yLTAuNS0wLjgtMi40IDAuMi01YzAgMCAxLjYtMC41IDUuMiAyIGMxLjUtMC40IDMuMS0wLjcgNC44LTAuN2MxLjYgMCAzLjMgMC4yIDQuNyAwLjdjMy42LTIuNCA1LjItMiA1LjItMmMxIDIuNiAwLjQgNC42IDAuMiA1YzEuMiAxLjMgMiAzIDIgNS4xYzAgNy4zLTQuNSA4LjktOC43IDkuNCBjMC43IDAuNiAxLjMgMS43IDEuMyAzLjVjMCAyLjYgMCA0LjYgMCA1LjJjMCAwLjUgMC40IDEuMSAxLjMgMC45YzcuNS0yLjYgMTMtOS43IDEzLTE4LjFDNTEgMjEuOSA0Mi41IDEzLjQgMzIgMTMuNHoiLz48L3N2Zz4=');
+ background-size: 100% 100%;
+ background-repeat: no-repeat;
+}
+.gh-count {
+ position: relative;
+ display: none; /* hidden to start */
+ margin-left: 4px;
+ background-color: #fafafa;
+ border: 1px solid #d4d4d4;
+}
+.gh-count:hover,
+.gh-count:focus {
+ color: #4183c4;
+}
+.gh-count:before,
+.gh-count:after {
+ content: '';
+ position: absolute;
+ display: inline-block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+.gh-count:before {
+ top: 50%;
+ left: -3px;
+ margin-top: -4px;
+ border-width: 4px 4px 4px 0;
+ border-right-color: #fafafa;
+}
+.gh-count:after {
+ top: 50%;
+ left: -4px;
+ z-index: -1;
+ margin-top: -5px;
+ border-width: 5px 5px 5px 0;
+ border-right-color: #d4d4d4;
+}
+.github-btn-large {
+ height: 30px;
+}
+.github-btn-large .gh-btn,
+.github-btn-large .gh-count {
+ padding: 3px 10px 3px 8px;
+ font-size: 16px;
+ line-height: 22px;
+ border-radius: 4px;
+}
+.github-btn-large .gh-ico {
+ width: 20px;
+ height: 20px;
+}
+.github-btn-large .gh-count {
+ margin-left: 6px;
+}
+.github-btn-large .gh-count:before {
+ left: -5px;
+ margin-top: -6px;
+ border-width: 6px 6px 6px 0;
+}
+.github-btn-large .gh-count:after {
+ left: -6px;
+ margin-top: -7px;
+ border-width: 7px 7px 7px 0;
+}
diff --git a/site/src/layouts/header/Github.vue b/site/src/layouts/header/Github.vue
new file mode 100644
index 000000000..266744850
--- /dev/null
+++ b/site/src/layouts/header/Github.vue
@@ -0,0 +1,59 @@
+
+
+
+
+ Star
+
+
+
+
diff --git a/site/src/layouts/header/Logo.vue b/site/src/layouts/header/Logo.vue
new file mode 100644
index 000000000..47d48453c
--- /dev/null
+++ b/site/src/layouts/header/Logo.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
diff --git a/site/src/layouts/header/Menu.vue b/site/src/layouts/header/Menu.vue
new file mode 100644
index 000000000..e490aced1
--- /dev/null
+++ b/site/src/layouts/header/Menu.vue
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+ {{ antdVersion }}
+ 1.x
+
+
+
+
+
+
+
+
diff --git a/site/src/layouts/header/More.vue b/site/src/layouts/header/More.vue
new file mode 100644
index 000000000..b6112f543
--- /dev/null
+++ b/site/src/layouts/header/More.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
diff --git a/site/src/layouts/header/Navigation.vue b/site/src/layouts/header/Navigation.vue
new file mode 100644
index 000000000..f72536496
--- /dev/null
+++ b/site/src/layouts/header/Navigation.vue
@@ -0,0 +1,177 @@
+
+
+
+
+
diff --git a/site/src/layouts/header/SearchBox.less b/site/src/layouts/header/SearchBox.less
new file mode 100644
index 000000000..c24fca1ea
--- /dev/null
+++ b/site/src/layouts/header/SearchBox.less
@@ -0,0 +1,136 @@
+@import '../../theme/static/theme.less';
+
+@search-icon-color: #ced4d9;
+
+#search-box {
+ position: relative;
+ display: flex;
+ flex: auto !important;
+ align-items: center;
+ height: 22px;
+ margin: 0 auto 0 0 !important;
+ padding-left: 16px;
+ line-height: 22px;
+ white-space: nowrap;
+ border-left: 1px solid @site-border-color-split;
+ transition: width 0.5s;
+
+ .ant-row-rtl & {
+ margin: 0 0 0 auto !important;
+ padding-right: 16px;
+ padding-left: 0;
+ border-right: 1px solid @site-border-color-split;
+ border-left: none;
+ }
+
+ > * {
+ flex: auto;
+ }
+
+ .anticon {
+ position: absolute;
+ top: 50%;
+ z-index: 1;
+ flex: none;
+ color: @search-icon-color;
+ transform: translateY(-50%);
+ pointer-events: none;
+ }
+
+ input {
+ width: 100%;
+ max-width: 200px;
+ padding-left: 20px;
+ font-size: 14px;
+ background: transparent;
+ border: 0;
+ box-shadow: none;
+
+ .ant-row-rtl & {
+ padding-right: 20px;
+ padding-left: 11px;
+ }
+
+ &::placeholder {
+ color: #a3b1bf;
+ }
+ }
+
+ // ================ Narrow ================
+ &.narrow-mode {
+ flex: none !important;
+ width: 30px;
+
+ &:hover {
+ .anticon {
+ color: #a3b1bf;
+ }
+ }
+
+ .anticon {
+ right: 0;
+ left: auto;
+
+ .ant-row-rtl & {
+ right: auto;
+ left: 0;
+ }
+ }
+
+ input {
+ max-width: none;
+ padding-right: 20px;
+ padding-left: 11px;
+ cursor: pointer;
+
+ .ant-row-rtl & {
+ padding-right: 11px;
+ padding-left: 20px;
+ }
+ }
+
+ &.focused {
+ width: 500px;
+
+ .anticon {
+ color: @search-icon-color;
+ }
+
+ input {
+ cursor: text;
+ }
+ }
+ }
+}
+
+.component-select {
+ &.ant-select-dropdown {
+ font-size: 14px;
+ border: 0;
+ border-radius: 0;
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.25);
+ }
+ .ant-select-dropdown-menu {
+ max-height: 200px;
+ }
+ .ant-select-dropdown-menu-item {
+ border-radius: 0 !important;
+ }
+ .ant-component-decs {
+ position: absolute;
+ right: 16px;
+ color: #aaa;
+ font-size: 12px;
+
+ .ant-row-rtl & {
+ right: auto;
+ left: 16px;
+ }
+ }
+}
+
+@media only screen and (max-width: @mobile-max-width) {
+ #search-box {
+ display: none;
+ }
+}
diff --git a/site/src/layouts/header/SearchBox.vue b/site/src/layouts/header/SearchBox.vue
new file mode 100644
index 000000000..959de4601
--- /dev/null
+++ b/site/src/layouts/header/SearchBox.vue
@@ -0,0 +1,37 @@
+
+
+
+
diff --git a/site/src/layouts/header/index.less b/site/src/layouts/header/index.less
new file mode 100644
index 000000000..84fd5d0fd
--- /dev/null
+++ b/site/src/layouts/header/index.less
@@ -0,0 +1,83 @@
+@import '../../theme/static/theme.less';
+@import './SearchBox.less';
+
+@header-height: 64px;
+@menu-item-border: 2px;
+@mobile-max-width: 767.99px;
+
+#header {
+ position: relative;
+ z-index: 10;
+ max-width: 100%;
+ background: @component-background;
+ box-shadow: 0 2px 8px rgba(240, 241, 242, 65);
+
+ .menu-row {
+ display: flex;
+ align-items: center;
+ margin: 0;
+
+ > * {
+ flex: none;
+ margin: 0 16px 0 0;
+
+ &:last-child {
+ margin-right: 40px;
+ }
+ }
+ }
+
+ // Adjust github button style
+
+ .ant-row-rtl {
+ .menu-row {
+ > * {
+ &:last-child {
+ margin-right: 16px;
+ margin-left: 40px;
+ }
+ }
+ }
+ }
+
+ // Buttons
+ .header-button {
+ color: @text-color;
+ border-color: @border-color-base;
+ }
+}
+
+@media only screen and (max-width: @mobile-max-width) {
+ #header {
+ text-align: center;
+ }
+}
+
+// Popover menu is only used for mobile
+.popover-menu {
+ width: 300px;
+
+ .ant-popover-inner-content {
+ padding: 0;
+
+ #nav {
+ .ant-menu-item,
+ .ant-menu-submenu {
+ text-align: left;
+ }
+
+ .ant-menu-item-group-title {
+ padding-left: 24px;
+ }
+
+ .ant-menu-item-group-list {
+ padding: 0 16px;
+ }
+
+ .ant-menu-item,
+ a {
+ color: #333;
+ }
+ }
+ }
+}
diff --git a/site/src/layouts/header/index.vue b/site/src/layouts/header/index.vue
new file mode 100644
index 000000000..38014acfc
--- /dev/null
+++ b/site/src/layouts/header/index.vue
@@ -0,0 +1,156 @@
+
+
+
+
+
+
diff --git a/site/src/layouts/index.vue b/site/src/layouts/index.vue
new file mode 100644
index 000000000..286eda6b6
--- /dev/null
+++ b/site/src/layouts/index.vue
@@ -0,0 +1,214 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/src/locale/en-US.js b/site/src/locale/en-US.js
new file mode 100644
index 000000000..3be3499c2
--- /dev/null
+++ b/site/src/locale/en-US.js
@@ -0,0 +1,29 @@
+import login from '../views/user/login/locales/en-US';
+import register from '../views/user/register/locales/en-US';
+import registerResult from '../views/user/register-result/locales/en-US';
+import theme from '../theme/en-US';
+export default {
+ ...theme,
+ 'navBar.lang': 'Languages',
+ 'layout.user.link.help': 'Help',
+ 'layout.user.link.privacy': 'Privacy',
+ 'layout.user.link.terms': 'Terms',
+ 'app.preview.down.block': 'Download this page to your local project',
+ 'app.welcome.link.fetch-blocks': 'Get all block',
+ 'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development',
+ 'app.docs.components.icon.search.placeholder': 'Search icon here, click icon to copy code',
+ 'app.docs.components.icon.pick-theme': 'Select the Icon Theme',
+ 'app.docs.components.icon.outlined': 'Outlined',
+ 'app.docs.components.icon.filled': 'Filled',
+ 'app.docs.components.icon.two-tone': 'Two Tone',
+ 'app.docs.components.icon.category.direction': 'Directional Icons',
+ 'app.docs.components.icon.category.suggestion': 'Suggested Icons',
+ 'app.docs.components.icon.category.editor': 'Editor Icons',
+ 'app.docs.components.icon.category.data': 'Data Icons',
+ 'app.docs.components.icon.category.other': 'Application Icons',
+ 'app.docs.components.icon.category.logo': 'Brand and Logos',
+ 'app.docs.components.icon.pic-searcher.intro': 'AI Search by image is online, welcome to use! 🎉',
+ ...login,
+ ...register,
+ ...registerResult,
+};
diff --git a/site/src/locale/zh-CN.js b/site/src/locale/zh-CN.js
new file mode 100644
index 000000000..6b06beccc
--- /dev/null
+++ b/site/src/locale/zh-CN.js
@@ -0,0 +1,34 @@
+import login from '../views/user/login/locales/zh-CN';
+import register from '../views/user/register/locales/zh-CN';
+import registerResult from '../views/user/register-result/locales/zh-CN';
+import theme from '../theme/zh-CN';
+
+export default {
+ ...theme,
+ 'navBar.lang': '语言',
+ 'layout.user.link.help': '帮助',
+ 'layout.user.link.privacy': '隐私',
+ 'layout.user.link.terms': '条款',
+ 'app.preview.down.block': '下载此页面到本地项目',
+ 'app.welcome.link.fetch-blocks': '获取全部区块',
+ 'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
+ 'app.docs.components.icon.search.placeholder': '在此搜索图标,点击图标可复制代码',
+ 'app.docs.components.icon.pick-theme': '选择图标主题风格',
+ 'app.docs.components.icon.outlined': '线框风格',
+ 'app.docs.components.icon.filled': '实底风格',
+ 'app.docs.components.icon.two-tone': '双色风格',
+ 'app.docs.components.icon.category.direction': '方向性图标',
+ 'app.docs.components.icon.category.suggestion': '提示建议性图标',
+ 'app.docs.components.icon.category.editor': '编辑类图标',
+ 'app.docs.components.icon.category.data': '数据类图标',
+ 'app.docs.components.icon.category.other': '网站通用图标',
+ 'app.docs.components.icon.category.logo': '品牌和标识',
+ 'app.docs.components.icon.pic-searcher.intro': 'AI 截图搜索上线了,快来体验吧!🎉',
+ 'app.docs.components.icon.pic-searcher.title': '上传图片搜索图标',
+ 'app.docs.components.icon.pic-searcher.upload-text': '点击/拖拽/粘贴上传图片',
+ 'app.docs.components.icon.pic-searcher.upload-hint':
+ '我们会通过上传的图片进行匹配,得到最相似的图标',
+ ...login,
+ ...register,
+ ...registerResult,
+};
diff --git a/site/src/main.js b/site/src/main.js
new file mode 100644
index 000000000..823a020dc
--- /dev/null
+++ b/site/src/main.js
@@ -0,0 +1,57 @@
+import 'ant-design-vue/dist/antd.less';
+import 'docsearch.js/dist/cdn/docsearch.css';
+import './index.less';
+import 'nprogress/nprogress.css';
+import { createApp, Transition, TransitionGroup } from 'vue';
+import i18n from './i18n';
+import NProgress from 'nprogress';
+import router from './router';
+import Antd from 'ant-design-vue';
+import demoBox from './components/DemoBox.vue';
+import demoContainer from './components/demoContainer.vue';
+import demoSort from './components/demoSort.jsx';
+import store from './store/index.js';
+import clipboard from './directives/clipboard';
+import App from './App.vue';
+
+const app = createApp(App);
+
+app.use(Antd);
+app.use(clipboard);
+app.component('transition', Transition);
+app.component('transition-group', TransitionGroup);
+app.component('demo-box', demoBox);
+app.component('demo-container', demoContainer);
+app.component('demo-sort', demoSort);
+app.component('VNodes', function (_, { attrs: { value } }) {
+ return value;
+});
+
+// app.component('tempVar', {
+// functional: true,
+// render: (h, ctx) => {
+// return ctx.scopedSlots && ctx.scopedSlots.default && ctx.scopedSlots.default(ctx.props);
+// },
+// });
+
+router.beforeEach((to, from, next) => {
+ if (to.path !== from.path) {
+ NProgress.start();
+ }
+ next();
+});
+
+router.afterEach((to, from) => {
+ if (to.path !== from.path) {
+ NProgress.done();
+ document.documentElement.scrollTop = 0;
+ }
+});
+
+app.use(store);
+app.use(router);
+app.use(i18n);
+
+app.config.globalProperties.$i18n = i18n;
+
+app.mount('#app');
diff --git a/site/src/mock/user.js b/site/src/mock/user.js
new file mode 100644
index 000000000..345cf155d
--- /dev/null
+++ b/site/src/mock/user.js
@@ -0,0 +1,52 @@
+// 代码中会兼容本地 service mock 以及部署站点的静态数据
+module.exports = {
+ // 支持值为 Object 和 Array
+ 'GET /api/currentUser': {
+ name: 'Serati Ma',
+ avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
+ userid: '00000001',
+ email: 'antdesign@alipay.com',
+ signature: '海纳百川,有容乃大',
+ title: '交互专家',
+ group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
+ },
+ // GET POST 可省略
+ 'GET /api/users': [
+ {
+ key: '1',
+ name: 'John Brown',
+ age: 32,
+ address: 'New York No. 1 Lake Park',
+ },
+ {
+ key: '2',
+ name: 'Jim Green',
+ age: 42,
+ address: 'London No. 1 Lake Park',
+ },
+ {
+ key: '3',
+ name: 'Joe Black',
+ age: 32,
+ address: 'Sidney No. 1 Lake Park',
+ },
+ ],
+ 'POST /api/login/account': (req, res) => {
+ const { password, userName, type } = req.body;
+ if (password === 'antdv' && userName === 'admin') {
+ res.send({ status: 'ok', type });
+ return;
+ }
+ if (password === 'antdv' && userName === 'user') {
+ res.send({ status: 'ok', type });
+ return;
+ }
+ res.send({ status: 'error', type });
+ },
+ 'GET /api/login/captcha': (req, res) => {
+ return res.json('captcha-xxx');
+ },
+ 'POST /api/register': (req, res) => {
+ res.send({ status: 'ok' });
+ },
+};
diff --git a/site/src/router/index.js b/site/src/router/index.js
new file mode 100644
index 000000000..b17bdb533
--- /dev/null
+++ b/site/src/router/index.js
@@ -0,0 +1,155 @@
+import Layout from '../layouts/index.vue';
+import Iframe from '../layouts/Iframe.vue';
+// import Iframe from '../components/iframe.jsx';
+import demoRoutes from './demoRoutes';
+// import otherRoutes from './otherRoutes';
+import { createRouter, createWebHistory } from 'vue-router';
+const routes = [
+ // ...otherRoutes,
+ {
+ path: '/components',
+ component: Layout,
+ children: [
+ {
+ path: 'overview:lang(.*)',
+ component: () => import('../views/ComponentOverview.vue'),
+ },
+ ...demoRoutes,
+ ],
+ },
+ {
+ path: '/iframe',
+ component: Iframe,
+ children: [
+ {
+ path: 'layout:lang(.*)',
+ meta: {
+ category: 'Components',
+ subtitle: '布局',
+ type: '布局',
+ cols: 1,
+ title: 'Layout',
+ cover: 'https://gw.alipayobjects.com/zos/alicdn/hzEndUVEx/Layout.svg',
+ },
+ props: route => {
+ const hash = route.hash.replace('#', '');
+ return { iframeName: hash };
+ },
+ component: () => import('../docs/layout/demo/index.vue'),
+ },
+ ],
+ },
+ {
+ path: '/docs',
+ component: Layout,
+ // props: route => {
+ // const name = route.path.split('/docs/vue/')[1].split('/')[0];
+ // return { name, showApi: true };
+ // },
+ children: [
+ {
+ path: 'vue/introduce-cn',
+ meta: { enTitle: 'Ant Design of Vue', title: 'Ant Design of Vue', category: 'docs' },
+ component: () => import('../vueDocs/introduce.zh-CN.md'),
+ },
+ {
+ path: 'vue/introduce',
+ meta: { enTitle: 'Ant Design of Vue', title: 'Ant Design of Vue', category: 'docs' },
+ component: () => import('../vueDocs/introduce.en-US.md'),
+ },
+ {
+ path: 'vue/getting-started-cn',
+ meta: { enTitle: 'Getting Started', title: '快速上手', category: 'docs' },
+ component: () => import('../vueDocs/getting-started.zh-CN.md'),
+ },
+ {
+ path: 'vue/getting-started',
+ meta: { enTitle: 'Getting Started', title: '快速上手', category: 'docs' },
+ component: () => import('../vueDocs/getting-started.en-US.md'),
+ },
+ {
+ path: 'vue/customize-theme-cn',
+ meta: { enTitle: 'Customize Theme', title: '定制主题', category: 'docs' },
+ component: () => import('../vueDocs/customize-theme.zh-CN.md'),
+ },
+ {
+ path: 'vue/customize-theme',
+ meta: { enTitle: 'Customize Theme', title: '定制主题', category: 'docs' },
+ component: () => import('../vueDocs/customize-theme.en-US.md'),
+ },
+ {
+ path: 'vue/migration-v2-cn',
+ meta: { enTitle: 'V1 to V2', title: '从 v1 到 v2', category: 'docs' },
+ component: () => import('../vueDocs/migration-v2.zh-CN.md'),
+ },
+ {
+ path: 'vue/migration-v2',
+ meta: { enTitle: 'V1 to V2', title: '从 v1 到 v2', category: 'docs' },
+ component: () => import('../vueDocs/migration-v2.en-US.md'),
+ },
+ {
+ path: 'vue/replace-date-cn',
+ meta: { enTitle: 'Custom Date Library', title: '自定义时间库', category: 'docs' },
+ component: () => import('../vueDocs/replace-date.zh-CN.md'),
+ },
+ {
+ path: 'vue/replace-date',
+ meta: { enTitle: 'Custom Date Library', title: '自定义时间库', category: 'docs' },
+ component: () => import('../vueDocs/replace-date.en-US.md'),
+ },
+ {
+ path: 'vue/i18n-cn',
+ meta: { enTitle: 'Internationalization', title: '国际化', category: 'docs' },
+ component: () => import('../vueDocs/i18n.zh-CN.md'),
+ },
+ {
+ path: 'vue/i18n',
+ meta: { enTitle: 'Internationalization', title: '国际化', category: 'docs' },
+ component: () => import('../vueDocs/i18n.en-US.md'),
+ },
+ {
+ path: 'vue/faq-cn',
+ meta: { enTitle: 'FAQ', title: '常见问题', category: 'docs' },
+ component: () => import('../vueDocs/faq.zh-CN.md'),
+ },
+ {
+ path: 'vue/faq',
+ meta: { enTitle: 'FAQ', title: '常见问题', category: 'docs' },
+ component: () => import('../vueDocs/faq.en-US.md'),
+ },
+ {
+ path: 'vue/download-cn',
+ meta: { enTitle: 'Download Design Resources', title: '下载设计资源', category: 'docs' },
+ component: () => import('../vueDocs/download.zh-CN.md'),
+ },
+ {
+ path: 'vue/download',
+ meta: { enTitle: 'Download Design Resources', title: '下载设计资源', category: 'docs' },
+ component: () => import('../vueDocs/download.en-US.md'),
+ },
+ {
+ path: 'vue/sponsor-cn',
+ meta: { enTitle: 'Sponsor', title: '支持我们', category: 'docs' },
+ component: () => import('../vueDocs/sponsor.zh-CN.md'),
+ },
+ {
+ path: 'vue/sponsor',
+ meta: { enTitle: 'Sponsor', title: '支持我们', category: 'docs' },
+ component: () => import('../vueDocs/sponsor.en-US.md'),
+ },
+ { path: '', redirect: '/docs/vue/introduce/' },
+ ],
+ },
+ { path: '/:lang(.*)', redirect: '/components/overview/' },
+];
+
+export default createRouter({
+ history: createWebHistory(),
+ fallback: false,
+ routes,
+ scrollBehavior: to => {
+ if (to.hash) {
+ return { el: to.hash, top: 80, behavior: 'auto' };
+ }
+ },
+});
diff --git a/site/src/router/otherRoutes.js b/site/src/router/otherRoutes.js
new file mode 100644
index 000000000..e1967a832
--- /dev/null
+++ b/site/src/router/otherRoutes.js
@@ -0,0 +1,56 @@
+import UserLayout from '../layouts/UserLayout';
+import BaseLayout from '../layouts/BaseLayout';
+export default [
+ {
+ path: '/vip',
+
+ component: () => import('../views/vip.vue'),
+ },
+ {
+ path: '/jobs',
+ component: BaseLayout,
+ props: () => {
+ return { name: 'list-cn' };
+ },
+ children: [
+ {
+ path: '/jobs',
+ redirect: '/jobs/list-cn',
+ },
+ {
+ path: '/jobs/list-cn',
+ name: 'list-cn',
+ component: () => import(/* webpackChunkName: "jobs" */ '../views/jobs.vue'),
+ },
+ ],
+ },
+ {
+ path: '/user',
+ component: UserLayout,
+ children: [
+ {
+ path: '/user',
+ redirect: '/user/login',
+ },
+ {
+ path: '/user/login',
+ name: 'login',
+ component: () =>
+ import(
+ /* webpackChunkName: "user" */
+ '../views/user/login'
+ ),
+ },
+ {
+ path: '/user/register',
+ name: 'register',
+ component: () => import(/* webpackChunkName: "user" */ '../views/user/register'),
+ },
+ {
+ path: '/user/register-result',
+ name: 'register.result',
+ component: () => import(/* webpackChunkName: "user" */ '../views/user/register-result'),
+ },
+ ],
+ },
+];
diff --git a/site/src/services/login.js b/site/src/services/login.js
new file mode 100644
index 000000000..306b2b47e
--- /dev/null
+++ b/site/src/services/login.js
@@ -0,0 +1,12 @@
+import request from '../utils/request';
+
+export async function fakeAccountLogin(params) {
+ return request('/api/login/account', {
+ method: 'POST',
+ data: params,
+ });
+}
+
+export async function getFakeCaptcha(mobile) {
+ return request(`/api/login/captcha?mobile=${mobile}`);
+}
diff --git a/site/src/services/user.js b/site/src/services/user.js
new file mode 100644
index 000000000..777b17795
--- /dev/null
+++ b/site/src/services/user.js
@@ -0,0 +1,13 @@
+import request from '../utils/request';
+
+export async function query() {
+ return request('/api/users');
+}
+
+export async function queryCurrent() {
+ return request('/api/currentUser');
+}
+
+export async function queryNotices() {
+ return request('/api/notices');
+}
diff --git a/site/src/store/index.js b/site/src/store/index.js
new file mode 100644
index 000000000..8c871e683
--- /dev/null
+++ b/site/src/store/index.js
@@ -0,0 +1,60 @@
+import { createStore } from 'vuex';
+import user from './user';
+import userAndLogin from '../views/user/login/model';
+import userAndregister from '../views/user/register/model';
+
+const createLoadingPlugin = ({ namespace: NAMESPACE = 'loading' } = {}) => {
+ const SHOW = '@@ANTDV_LOADING/SHOW';
+ const HIDE = '@@ANTDV_LOADING/HIDE';
+ return store => {
+ if (store.state[NAMESPACE]) {
+ throw new Error(`createLoadingPlugin: ${NAMESPACE} exited in current store`);
+ }
+ store.registerModule(NAMESPACE, {
+ namespaced: true,
+ state: {
+ global: false,
+ models: {},
+ effects: {},
+ },
+ mutations: {
+ [SHOW]: (state, { payload: { actionType } }) => {
+ state.global = true;
+ const _namespace = actionType.split('/')[0];
+ state.models = { ...state.models, [_namespace]: true };
+ state.effects = { ...state.effects, [actionType]: true };
+ },
+ [HIDE]: (state, { payload: { actionType } }) => {
+ state.global = false;
+ const _namespace = actionType.split('/')[0];
+ state.models = { ...state.models, [_namespace]: false };
+ state.effects = { ...state.effects, [actionType]: false };
+ },
+ },
+ });
+ store.subscribeAction({
+ before: action => {
+ store.commit(
+ { type: `${NAMESPACE}/${SHOW}`, payload: { actionType: action.type } },
+ { root: true },
+ );
+ },
+ after: action => {
+ store.commit(
+ { type: `${NAMESPACE}/${HIDE}`, payload: { actionType: action.type } },
+ { root: true },
+ );
+ },
+ });
+ };
+};
+
+export default createStore({
+ state: {},
+ plugins: [createLoadingPlugin()],
+ modules: {
+ userAndLogin,
+ userAndregister,
+ user,
+ },
+});
diff --git a/site/src/store/user.js b/site/src/store/user.js
new file mode 100644
index 000000000..4cc416dae
--- /dev/null
+++ b/site/src/store/user.js
@@ -0,0 +1,41 @@
+/* eslint-disable no-unused-vars */
+import { queryCurrent, query as queryUsers } from '../services/user';
+
+export default {
+ namespaced: true,
+ state: {
+ currentUser: {},
+ },
+ actions: {
+ async login({ commit }, payload) {
+ const response = await queryUsers(payload);
+ commit({
+ type: 'save',
+ payload: response,
+ });
+ },
+ async fetchCurrent({ commit }, payload) {
+ const response = await queryCurrent(payload);
+ commit({
+ type: 'saveCurrentUser',
+ payload: response,
+ });
+ },
+ },
+ mutations: {
+ saveCurrentUser(state, { payload }) {
+ Object.assign(state, {
+ currentUser: payload || {},
+ });
+ },
+ changeNotifyCount(state, { payload }) {
+ Object.assign(state, {
+ currentUser: {
+ ...state.currentUser,
+ notifyCount: payload.totalCount,
+ unreadCount: payload.unreadCount,
+ },
+ });
+ },
+ },
+};
diff --git a/site/src/theme/en-US.js b/site/src/theme/en-US.js
new file mode 100644
index 000000000..ae545cfb9
--- /dev/null
+++ b/site/src/theme/en-US.js
@@ -0,0 +1,149 @@
+export default {
+ 'app.theme.switch.default': 'Default theme',
+ 'app.theme.switch.dark': 'Dark theme',
+ 'app.theme.switch.compact': 'Compact theme',
+ 'app.header.search': 'Search...',
+ 'app.header.menu.documentation': 'Docs',
+ 'app.header.menu.components': 'Components',
+ 'app.header.menu.spec': 'Design',
+ 'app.header.menu.resource': 'Resources',
+ 'app.header.menu.more': 'More',
+ 'app.header.menu.mobile': 'Mobile',
+ 'app.header.menu.pro.v4': 'Ant Design Pro',
+ 'app.header.menu.charts': 'Ant Design Charts',
+ 'app.header.menu.ecosystem': 'Ecosystem',
+ 'app.header.menu.store': 'Store',
+ 'app.header.lang': '中文',
+ 'app.content.edit-page': 'Edit this page on GitHub!',
+ 'app.content.edit-demo': 'Edit this demo on GitHub!',
+ 'app.content.contributors': 'contributors',
+ 'app.component.examples': 'Examples',
+ 'app.component.examples.expand': 'Expand all code',
+ 'app.component.examples.collapse': 'Collapse all code',
+ 'app.component.examples.visible': 'Expand debug examples',
+ 'app.component.examples.hide': 'Collapse debug examples',
+ 'app.demo.debug': "Debug only, won't display at online",
+ 'app.demo.type.js': 'Switch to TypeScript',
+ 'app.demo.type.ts': 'Switch to JavaScript',
+ 'app.demo.copy': 'Copy code',
+ 'app.demo.copied': 'Copied!',
+ 'app.demo.code.show': 'Show code',
+ 'app.demo.code.hide': 'Hide code',
+ 'app.demo.codepen': 'Open in CodePen',
+ 'app.demo.codesandbox': 'Open in CodeSandbox',
+ 'app.demo.stackblitz': 'Open in Stackblitz',
+ 'app.demo.riddle': 'Open in Riddle',
+ 'app.home.introduce':
+ 'A design system for enterprise-level products. Create an efficient and enjoyable work experience.',
+ 'app.home.recommend': 'Recommended',
+ 'app.home.popularize': 'Popular',
+ 'app.home.design-and-framework': 'Design language and framework',
+ 'app.home.design-values': 'Design values',
+ 'app.home.design-values-description':
+ 'This is Ant Design\'s internal standard for evaluating design quality. Based on the assumption that "everyone is pursuing happiness at work", we have added the two values of "Meaningfulness" and "Growth" on the basis of "Certainty" and "Naturalness" to guide each designer towards better judgment and decision-making.',
+ 'app.home.certainty': 'Certainty',
+ 'app.home.meaningful': 'Meaningfulness',
+ 'app.home.growth': 'Growth',
+ 'app.home.natural': 'Naturalness',
+ 'app.home.design-guide': 'Guidelines',
+ 'app.home.components': 'Components',
+ 'app.home.detail': 'More details',
+ 'app.home.global-style': 'Global style',
+ 'app.home.design-patterns': 'Design patterns',
+ 'app.home.more': 'Learn More',
+ 'app.home.play-video': 'Play video',
+ 'app.home.qr': '4.0 is out',
+ 'app.home.qr.desc': '4.0 is out',
+ 'app.home.getting-started': 'Getting Started',
+ 'app.home.design-language': 'Design Language',
+ 'app.home.product-antv-slogan': 'A new way to do data visualization',
+ 'app.home.product-pro-slogan': 'Out-of-the-box UI solution for enterprise applications',
+ 'app.home.product-mobile-slogan': 'Mobile UI components with Ant Design',
+ 'app.home.product-hitu': 'HiTu',
+ 'app.home.product-hitu-slogan': 'A new generation of graphical solutions',
+ 'app.home.product-kitchen-slogan': 'A Sketch plugin to enhance designers',
+ 'app.home.product-icons-slogan': 'A set of premium icons',
+ 'app.home.view-more': 'More',
+ 'app.footer.repo': 'GitHub Repository',
+ 'app.footer.awesome': 'Awesome Ant Design',
+ 'app.footer.course': 'Ant Design Practical Tutorial',
+ 'app.footer.chinamirror': 'China Mirror 🇨🇳',
+ 'app.footer.primary-color-changing': 'Changing primary color...',
+ 'app.footer.primary-color-changed': 'Changed primary color successfully!',
+ 'app.footer.scaffold': 'Scaffold',
+ 'app.footer.kitchen': 'Sketch Toolkit',
+ 'app.footer.landing': 'Landing Templates',
+ 'app.footer.scaffolds': 'Scaffold Market',
+ 'app.footer.dev-tools': 'Developer Tools',
+ 'app.footer.umi': 'React Application Framework',
+ 'app.footer.dumi': 'Component doc generator',
+ 'app.footer.remax': 'Mini Program Framework',
+ 'app.footer.hooks': 'React Hooks Library',
+ 'app.footer.resources': 'Resources',
+ 'app.footer.data-vis': 'Data Visualization',
+ 'app.footer.eggjs': 'Enterprise Node Framework',
+ 'app.footer.motion': 'Motion Solution',
+ 'app.footer.design-resources': 'Design Resources',
+ 'app.footer.antd-library': 'Axure library',
+ 'app.footer.antux': 'Sitemap Template',
+ 'app.footer.community': 'Community',
+ 'app.footer.help': 'Help',
+ 'app.footer.change-log': 'Change Log',
+ 'app.footer.faq': 'FAQ',
+ 'app.footer.feedback': 'Feedback',
+ 'app.footer.stackoverflow': 'StackOverflow',
+ 'app.footer.segmentfault': 'SegmentFault',
+ 'app.footer.discussions': 'Discussions',
+ 'app.footer.bug-report': 'Bug Report',
+ 'app.footer.issues': 'Issues',
+ 'app.footer.version': 'Version: ',
+ 'app.footer.author': 'Created by XTech',
+ 'app.footer.work_with_us': 'Work with Us',
+ 'app.footer.more-product': 'More Products',
+ 'app.footer.company': 'XTech',
+ 'app.footer.ant-design': 'UI Design Language',
+ 'app.footer.yuque': 'YuQue',
+ 'app.footer.yuque.slogan': 'Write your document as a team',
+ 'app.footer.fengdie': 'FengDie',
+ 'app.footer.fengdie.slogan': 'Console Applications Builder',
+ 'app.footer.antv.slogan': 'Data Visualization',
+ 'app.footer.egg.slogan': 'Enterprise Node.js Framework',
+ 'app.footer.zhihu': 'Ant Design Blog',
+ 'app.footer.zhihu.xtech': 'Experience Cloud Blog',
+ 'app.footer.seeconf': 'Experience Tech Conference',
+ 'app.footer.xtech': 'Ant Financial Experience Tech',
+ 'app.footer.xtech.slogan': 'Experience The Beauty',
+ 'app.publish.title': 'antd@3.0.0 has been released! 🎉 🎉 🎉',
+ 'app.publish.greeting': 'Hello, ',
+ 'app.publish.intro': ' has been released, so please upgrade. ',
+ 'app.publish.old-version-guide': 'If you need documentation for an older version, please visit ',
+ 'app.publish.old-version-tips': ', or switch the version via the dropdown in the header nav bar.',
+ 'app.docs.color.pick-primary': 'Pick your primary color',
+ 'app.docs.color.pick-background': 'Pick your background color',
+ 'app.docs.components.icon.search.placeholder': 'Search icons here, click icon to copy code',
+ 'app.docs.components.icon.pick-theme': 'Select the Icon Theme',
+ 'app.docs.components.icon.outlined': 'Outlined',
+ 'app.docs.components.icon.filled': 'Filled',
+ 'app.docs.components.icon.two-tone': 'Two Tone',
+ 'app.docs.components.icon.category.direction': 'Directional Icons',
+ 'app.docs.components.icon.category.suggestion': 'Suggested Icons',
+ 'app.docs.components.icon.category.editor': 'Editor Icons',
+ 'app.docs.components.icon.category.data': 'Data Icons',
+ 'app.docs.components.icon.category.other': 'Application Icons',
+ 'app.docs.components.icon.category.logo': 'Brand and Logos',
+ 'app.docs.components.icon.pic-searcher.intro':
+ 'AI Search by image is online, you are welcome to use it! 🎉',
+ 'app.docs.components.icon.pic-searcher.title': 'Search by image',
+ 'app.docs.components.icon.pic-searcher.upload-text':
+ 'Click, drag, or paste file to this area to upload',
+ 'app.docs.components.icon.pic-searcher.upload-hint':
+ 'We will find the best matching icon based on the image provided',
+ 'app.docs.components.icon.pic-searcher.server-error':
+ 'Predict service is temporarily unavailable',
+ 'app.docs.components.icon.pic-searcher.matching': 'Matching...',
+ 'app.docs.components.icon.pic-searcher.modelloading': 'Model is loading...',
+ 'app.docs.components.icon.pic-searcher.result-tip': 'Matched the following icons for you:',
+ 'app.docs.components.icon.pic-searcher.th-icon': 'Icon',
+ 'app.docs.components.icon.pic-searcher.th-score': 'Probability',
+ 'app.components.overview.search': 'Search in components',
+};
diff --git a/site/src/theme/static/colors.less b/site/src/theme/static/colors.less
new file mode 100644
index 000000000..76960e704
--- /dev/null
+++ b/site/src/theme/static/colors.less
@@ -0,0 +1,242 @@
+.make-palette(@color, @index: 1) when (@index <= 10) {
+ .palette-@{color}-@{index} {
+ @background: '@{color}-@{index}';
+
+ background: @@background;
+ }
+ .make-palette(@color, (@index + 1)); // next iteration
+}
+
+@gray-1: #fff;
+@gray-2: #fafafa;
+@gray-3: #f5f5f5;
+@gray-4: #f0f0f0;
+@gray-5: #d9d9d9;
+@gray-6: #bfbfbf;
+@gray-7: #8c8c8c;
+@gray-8: #595959;
+@gray-9: #434343;
+@gray-10: #262626;
+@gray-11: #1f1f1f;
+@gray-12: #141414;
+@gray-13: #000;
+@border-color: rgba(229, 231, 235, 100);
+
+.color-palettes {
+ margin: 0 1%;
+ &-dark {
+ margin: 0;
+ padding: 0 28px;
+ background-color: #141414;
+ .color-title {
+ color: fade(@white, 85);
+ }
+ .color-description {
+ color: fade(@white, 45);
+ }
+ .color-palette {
+ margin: 45px 3.5% 45px 0;
+ &:nth-of-type(3n) {
+ margin-right: 0;
+ }
+ .main-color-item {
+ margin-right: 0;
+ &:hover {
+ margin-right: -8px;
+ }
+ }
+ }
+ }
+}
+.color-palette {
+ display: inline-block;
+ width: 31%;
+ margin: 45px 1%;
+ &-pick {
+ margin: 0 0 20px;
+ font-size: 20px;
+ text-align: center;
+ }
+ &-picker {
+ margin: 24px 0;
+ &-value {
+ position: relative;
+ top: -3px;
+ margin-left: 16px;
+ font-size: 14px;
+ font-family: Consolas, sans-serif;
+ .ant-row-rtl & {
+ margin-right: 16px;
+ margin-left: 0;
+ }
+ }
+ &-validation {
+ position: relative;
+ top: -3px;
+ margin-left: 16px;
+ color: @error-color;
+ font-size: 13px;
+ .ant-row-rtl & {
+ margin-right: 16px;
+ margin-left: 0;
+ }
+ &-dark {
+ margin-left: 0;
+ }
+ }
+ }
+}
+
+.main-color {
+ .make-palette(blue);
+ .make-palette(purple);
+ .make-palette(cyan);
+ .make-palette(green);
+ .make-palette(magenta);
+ .make-palette(red);
+ .make-palette(volcano);
+ .make-palette(orange);
+ .make-palette(gold);
+ .make-palette(yellow);
+ .make-palette(lime);
+ .make-palette(geekblue);
+ .make-palette(gray);
+
+ .palette-gray-11 {
+ background: @gray-11;
+ }
+ .palette-gray-12 {
+ background: @gray-12;
+ }
+ .palette-gray-13 {
+ background: @gray-13;
+ }
+
+ text-align: left;
+
+ &-item {
+ position: relative;
+ height: 44px;
+ margin-right: 4px;
+ padding: 0 12px;
+ font-size: 14px;
+ font-family: Consolas, sans-serif;
+ line-height: 44px;
+ cursor: pointer;
+ transition: all 0.2s;
+ &:first-child {
+ border-radius: 4px 4px 0 0;
+ }
+ &:last-child {
+ border-radius: 0 0 4px 4px;
+ }
+ &:hover {
+ margin-right: -8px;
+ border-radius: 0 4px 4px 0;
+ }
+ }
+ &-item &-text {
+ float: left;
+ transition: all 0.3s;
+ }
+ &-item &-value {
+ position: relative;
+ left: 3px;
+ float: right;
+ transform: scale(0.85);
+ transform-origin: 100% 50%;
+ opacity: 0;
+ transition: all 0.3s;
+ }
+}
+
+.color-title {
+ margin: 0 0 24px;
+ color: #5c6b77;
+ font-weight: 500;
+ font-size: 22px;
+ text-align: center;
+ text-transform: capitalize;
+}
+
+.color-description {
+ display: block;
+ color: #777;
+ font-weight: lighter;
+ font-size: 14px;
+}
+
+.main-color:hover {
+ .main-color-value {
+ left: 0;
+ opacity: 0.7;
+ }
+}
+
+.color-palette-horizontal {
+ width: 100%;
+ &-dark {
+ height: 303px;
+ padding: 32px 28px;
+ background-color: #141414;
+ .color-palette-picker {
+ margin-bottom: 0;
+ }
+ .color-palette-pick {
+ color: fade(@white, 65);
+ text-align: left;
+ &-hex {
+ color: fade(@white, 65);
+ }
+ .ant-row-rtl & {
+ direction: rtl;
+ text-align: right;
+ }
+ }
+ }
+
+ .main-color {
+ display: flex;
+
+ &-item {
+ position: relative;
+ flex: 1;
+ height: 86px;
+ margin-right: 0;
+ padding: 37px 0 0;
+ line-height: normal;
+ text-align: center;
+ border-radius: 0;
+
+ .main-color-text {
+ float: none;
+ }
+
+ &:hover {
+ height: 96px;
+ margin-top: -10px;
+ border-radius: 4px 4px 0 0;
+ }
+ }
+
+ &-value {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ text-align: center;
+ transform-origin: unset;
+ }
+
+ &:hover {
+ .main-color-item {
+ padding-top: 8px;
+ }
+
+ .main-color-value {
+ bottom: 8px;
+ opacity: 0.7;
+ }
+ }
+ }
+}
diff --git a/site/src/theme/static/common.less b/site/src/theme/static/common.less
new file mode 100644
index 000000000..0265e2d04
--- /dev/null
+++ b/site/src/theme/static/common.less
@@ -0,0 +1,263 @@
+html {
+ &.rtl {
+ direction: rtl;
+ }
+}
+
+body {
+ overflow-x: hidden;
+ color: @site-text-color;
+ font-size: @font-size-base;
+ font-family: @font-family;
+ background: @body-background;
+ transition: background 1s cubic-bezier(0.075, 0.82, 0.165, 1);
+}
+
+a {
+ transition: color 0.3s ease;
+}
+
+.main-wrapper {
+ position: relative;
+ padding: 40px 0 0;
+ background: @component-background;
+}
+
+.main-container {
+ position: relative;
+ min-height: 500px;
+ padding: 0 170px 32px 64px;
+ background: @component-background;
+
+ .ant-row-rtl & {
+ padding: 0 64px 144px 170px;
+ }
+}
+
+.main-menu {
+ z-index: 1;
+
+ &-inner {
+ height: 100%;
+ max-height: 100vh;
+ overflow: hidden;
+ }
+
+ &:hover &-inner {
+ overflow-y: auto;
+ }
+
+ > div,
+ > div > div {
+ height: 100%;
+ }
+}
+
+.aside-container {
+ min-height: 100%;
+ padding-bottom: 48px;
+ font-family: Avenir, @font-family, sans-serif;
+
+ &.ant-menu-inline {
+ .ant-menu-submenu-title h4,
+ > .ant-menu-item,
+ .ant-menu-item a {
+ overflow: hidden;
+ font-size: 14px;
+ text-overflow: ellipsis;
+ }
+
+ > .ant-menu-item-group > .ant-menu-item-group-title {
+ margin-top: 16px;
+ margin-bottom: 16px;
+ font-size: 13px;
+
+ &::after {
+ position: relative;
+ top: 12px;
+ display: block;
+ width: calc(100% - 20px);
+ height: 1px;
+ background: @border-color-split;
+ content: '';
+ }
+ }
+
+ > .ant-menu-item,
+ > .ant-menu-submenu > .ant-menu-submenu-title,
+ > .ant-menu-item-group > .ant-menu-item-group-title,
+ > .ant-menu-item-group > .ant-menu-item-group-list > .ant-menu-item,
+ &.ant-menu-inline > .ant-menu-item-group > .ant-menu-item-group-list > .ant-menu-item {
+ padding-left: 40px !important;
+
+ .ant-row-rtl & {
+ padding-right: 40px !important;
+ padding-left: 16px !important;
+ }
+ }
+
+ // Nest Category > Type > Article
+ &.ant-menu-inline {
+ .ant-menu-item-group-title {
+ padding-left: 60px;
+
+ .ant-row-rtl & {
+ padding-right: 60px;
+ padding-left: 16px;
+ }
+ }
+
+ .ant-menu-item-group-list > .ant-menu-item {
+ padding-left: 80px !important;
+
+ .ant-row-rtl & {
+ padding-right: 80px !important;
+ padding-left: 16px !important;
+ }
+ }
+ }
+
+ .ant-menu-item-group:first-child {
+ .ant-menu-item-group-title {
+ margin-top: 0;
+ }
+ }
+ }
+
+ a[disabled] {
+ color: #ccc;
+ }
+
+ .menu-item-link-outside {
+ position: relative;
+ .anticon {
+ position: absolute;
+ top: 16px;
+ right: -10px;
+ color: @primary-color;
+ font-size: 12px;
+ opacity: 0;
+ transition: all 0.3s;
+ }
+ &:hover .anticon {
+ opacity: 1;
+ }
+ }
+}
+
+.aside-container .chinese {
+ margin-left: 6px;
+ font-weight: normal;
+ font-size: 12px;
+ opacity: 0.67;
+}
+
+.outside-link {
+ display: inline-block;
+}
+
+.outside-link-icon {
+ margin-left: 5px;
+ color: #aaa;
+ font-size: 12px;
+ .ant-row-rtl & {
+ margin-right: 5px;
+ margin-left: 0;
+ transform: rotate(180deg);
+ }
+}
+
+// reset menu text color
+.menu-site {
+ .ant-menu-item > a {
+ color: @site-text-color;
+ }
+
+ .ant-menu-item-selected > a,
+ .ant-menu-item > a:hover {
+ color: @primary-color;
+ }
+}
+
+#app {
+ height: 100%;
+ transition: transform 0.3s @ease-in-out-circ;
+}
+
+.drawer-content {
+ padding: 40px 0;
+ &-wrapper {
+ background-color: @component-background;
+ }
+}
+
+.drawer {
+ z-index: 1029;
+}
+
+#_hj_feedback_container {
+ [class$='icon_emotion_path1']::before {
+ color: @primary-color !important;
+ }
+}
+
+.fixed-widgets {
+ position: fixed;
+ right: 32px;
+ bottom: 102px;
+ z-index: 2147483640;
+ display: flex;
+ flex-direction: column;
+ cursor: pointer;
+ &-tooltip {
+ .ant-tooltip-inner {
+ min-width: 100px;
+ }
+ }
+ & > div {
+ display: block;
+ }
+ &-active {
+ color: @primary-color;
+ }
+ & &-avatar {
+ color: #000;
+ background-color: #fff;
+ box-shadow: @shadow-2;
+ transition: color 0.3s;
+ &:hover {
+ color: @primary-color;
+ }
+ }
+}
+
+// keep transition consistent to make mode toggle animation be more smooth, include:
+// nav bar background
+// nav search border
+// nav menu background
+// wrapper content
+// sidemenu background
+// sidemenu active background
+// content background
+// affix toc
+#header,
+#header #search-box,
+#header #nav.ant-menu,
+.main-wrapper,
+.main-wrapper > .ant-row > .main-menu .main-menu-inner > .ant-menu,
+.main-wrapper
+ > .ant-row
+ > .main-menu
+ .main-menu-inner
+ > .ant-menu.aside-container.ant-menu-inline
+ > .ant-menu-item-group
+ > .ant-menu-item-group-title::after,
+.main-wrapper .main-container,
+#demo-toc.toc {
+ transition: all 0.3s @ease-in-out-circ;
+}
+
+// remove margin-bottom from dark theme (why need margin-bottom in dark theme?)
+#header > .ant-row > .ant-col h1 {
+ margin-bottom: 0;
+}
diff --git a/site/src/theme/static/contributors.less b/site/src/theme/static/contributors.less
new file mode 100644
index 000000000..004199b56
--- /dev/null
+++ b/site/src/theme/static/contributors.less
@@ -0,0 +1,11 @@
+.contributors-list {
+ display: flex;
+ flex-wrap: wrap;
+ margin-top: 120px !important;
+
+ a,
+ .ant-avatar + .ant-avatar {
+ margin-right: 8px;
+ margin-bottom: 8px;
+ }
+}
diff --git a/site/src/theme/static/dark.less b/site/src/theme/static/dark.less
new file mode 100644
index 000000000..3b560c695
--- /dev/null
+++ b/site/src/theme/static/dark.less
@@ -0,0 +1,369 @@
+[data-theme='dark'] {
+ /* Change autocomplete styles in WebKit */
+ input:-webkit-autofill,
+ input:-webkit-autofill:hover,
+ input:-webkit-autofill:focus,
+ textarea:-webkit-autofill,
+ textarea:-webkit-autofill:hover,
+ textarea:-webkit-autofill:focus,
+ select:-webkit-autofill,
+ select:-webkit-autofill:hover,
+ select:-webkit-autofill:focus {
+ border: 1px solid @border-color-base;
+ -webkit-text-fill-color: @text-color;
+ box-shadow: none;
+ transition: background-color 5000s ease-in-out 0s;
+ }
+
+ #header {
+ box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.65);
+ }
+
+ #search-box input {
+ &::placeholder {
+ color: fade(@white, 30%);
+ }
+ }
+
+ .toc-affix {
+ .ant-affix {
+ background: @component-background;
+ }
+ }
+
+ :not(pre) > code[class*='language-'],
+ pre[class*='language-'] {
+ color: rgba(255, 255, 255, 0.65);
+ background: #262626;
+ }
+
+ .code-box {
+ border: 1px solid @border-color-split;
+ .markdown {
+ pre {
+ margin: 0.5em 0;
+ padding: 6px 12px;
+ }
+ pre code {
+ margin: 0;
+ background: #262626;
+ }
+ }
+ &-debug {
+ border-color: @purple-3;
+ }
+
+ &-expand-trigger {
+ position: relative;
+ margin-left: 12px;
+ color: #fff;
+ font-size: 20px;
+ cursor: pointer;
+ opacity: 0.45;
+ transition: all 0.3s;
+ &:hover {
+ opacity: 0.65;
+ }
+ }
+
+ &-demo {
+ border-bottom: 1px solid @border-color-split;
+ }
+
+ &-actions > &-code-action {
+ color: @site-text-color-secondary;
+
+ &:hover {
+ color: @site-text-color;
+ }
+ }
+ }
+
+ ul.anticons-list {
+ li {
+ color: #acacac;
+ &.TwoTone:hover {
+ background-color: #15395b;
+ }
+ &:hover {
+ .anticon {
+ color: #fff;
+ }
+ }
+ }
+ }
+
+ a[disabled] {
+ color: rgba(255, 255, 255, 0.25);
+ }
+
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
+ color: @heading-color;
+ }
+
+ .markdown {
+ code,
+ pre,
+ pre code {
+ background: #262626;
+ }
+ }
+
+ .markdown.api-container {
+ table {
+ tbody tr {
+ &:hover {
+ background: #262626;
+ }
+ }
+ }
+ }
+
+ .markdown code {
+ background: fade(@white, 8%);
+ }
+
+ .prev-next-nav {
+ > a.prev-page {
+ .footer-nav-icon-before {
+ color: fade(@white, 45%);
+ }
+
+ &:hover .footer-nav-icon-before {
+ color: @primary-color;
+ }
+ }
+
+ > .next-page {
+ .footer-nav-icon-after {
+ color: fade(@white, 45%);
+ }
+
+ &:hover .footer-nav-icon-after {
+ color: @primary-color;
+ }
+ }
+ }
+
+ .grid-demo,
+ [id^='components-grid-demo-'] {
+ .demo-row,
+ .code-box-demo .demo-row {
+ background-image: linear-gradient(
+ 90deg,
+ #1d1d1d 4.16666667%,
+ transparent 4.16666667%,
+ transparent 8.33333333%,
+ #1d1d1d 8.33333333%,
+ #1d1d1d 12.5%,
+ transparent 12.5%,
+ transparent 16.66666667%,
+ #1d1d1d 16.66666667%,
+ #1d1d1d 20.83333333%,
+ transparent 20.83333333%,
+ transparent 25%,
+ #1d1d1d 25%,
+ #1d1d1d 29.16666667%,
+ transparent 29.16666667%,
+ transparent 33.33333333%,
+ #1d1d1d 33.33333333%,
+ #1d1d1d 37.5%,
+ transparent 37.5%,
+ transparent 41.66666667%,
+ #1d1d1d 41.66666667%,
+ #1d1d1d 45.83333333%,
+ transparent 45.83333333%,
+ transparent 50%,
+ #1d1d1d 50%,
+ #1d1d1d 54.16666667%,
+ transparent 54.16666667%,
+ transparent 58.33333333%,
+ #1d1d1d 58.33333333%,
+ #1d1d1d 62.5%,
+ transparent 62.5%,
+ transparent 66.66666667%,
+ #1d1d1d 66.66666667%,
+ #1d1d1d 70.83333333%,
+ transparent 70.83333333%,
+ transparent 75%,
+ #1d1d1d 75%,
+ #1d1d1d 79.16666667%,
+ transparent 79.16666667%,
+ transparent 83.33333333%,
+ #1d1d1d 83.33333333%,
+ #1d1d1d 87.5%,
+ transparent 87.5%,
+ transparent 91.66666667%,
+ #1d1d1d 91.66666667%,
+ #1d1d1d 95.83333333%,
+ transparent 95.83333333%
+ );
+ }
+ .code-box-demo .ant-row > div:not(.gutter-row) {
+ padding: 16px 0;
+ background: #028ac8;
+ &:nth-child(2n + 1) {
+ background: fade(#0088c6, 70%);
+ }
+ }
+ .ant-row .demo-col,
+ .code-box-demo .ant-row .demo-col {
+ margin-top: 0;
+ margin-bottom: 0;
+ padding: 30px 0;
+ color: @black;
+ font-size: 18px;
+ text-align: center;
+ border: none;
+ }
+ .ant-row .demo-col-1 {
+ background: fade(#0088c6, 70%);
+ }
+ .ant-row .demo-col-3,
+ .code-box-demo .ant-row .demo-col-3 {
+ color: @site-text-color-secondary;
+ background: unset;
+ }
+ .ant-row .demo-col-5,
+ .code-box-demo .ant-row .demo-col-5 {
+ color: @site-text-color-secondary;
+ background: unset;
+ }
+ }
+
+ .markdown > table th {
+ color: fade(@white, 65%);
+ background: #1d1d1d;
+ }
+
+ .copied-code {
+ background: fade(@white, 8%);
+ }
+
+ .browser-mockup.with-url::after {
+ background-color: @component-background;
+ }
+
+ .browser-mockup {
+ border-top: 2em solid #262626;
+ }
+
+ .browser-mockup::before {
+ background-color: #fb4742;
+ box-shadow: 0 0 0 2px #fb4742, 1.5em 0 0 2px #99bc2e, 3em 0 0 2px #ffba5a;
+ }
+
+ .browser-mockup.with-tab::after {
+ border-bottom: 2em solid @component-background;
+ }
+
+ .algolia-autocomplete {
+ .ds-dropdown-menu {
+ [class^='ds-dataset-'] {
+ background: @popover-background;
+ .algolia-docsearch-suggestion {
+ background: @popover-background;
+ }
+ }
+ .ds-suggestion.ds-cursor {
+ .algolia-docsearch-suggestion:not(.suggestion-layout-simple) {
+ .algolia-docsearch-suggestion--content {
+ background-color: fade(@white, 8%);
+ }
+ }
+ }
+ }
+ .algolia-docsearch-suggestion--category-header {
+ color: rgba(255, 255, 255, 0.65);
+ border-bottom: 1px solid @border-color-split;
+ }
+ .algolia-docsearch-suggestion--subcategory-column::before {
+ background: @border-color-split;
+ }
+ .algolia-docsearch-suggestion--content {
+ &::before {
+ background: @border-color-split;
+ }
+ }
+ }
+
+ .token.comment,
+ .token.quote {
+ color: #b6b18b;
+ }
+
+ .token.property,
+ .token.variable,
+ .token.template-variable,
+ .token.tag,
+ .token.number,
+ .token.name,
+ .token.selector-id,
+ .token.selector-class,
+ .token.regexp,
+ .token.deletion {
+ color: #eb3c54;
+ }
+
+ .token.built_in,
+ .token.builtin-name,
+ .token.literal,
+ .token.type,
+ .token.params,
+ .token.meta,
+ .token.link {
+ color: #e7ce56;
+ }
+
+ .token.attribute {
+ color: #ee7c2b;
+ }
+
+ .token.string,
+ .token.symbol,
+ .token.bullet,
+ .token.addition {
+ color: @primary-color;
+ }
+
+ .token.title,
+ .token.section {
+ color: #78bb65;
+ }
+
+ .token.function,
+ .token.keyword,
+ .token.selector-tag {
+ color: #b45ea4;
+ }
+
+ .hljs {
+ display: block;
+ padding: 0.5em;
+ overflow-x: auto;
+ color: #c0c5ce;
+ background: #1c1d21;
+ }
+
+ .token.emphasis {
+ font-style: italic;
+ }
+
+ .token.strong {
+ font-weight: bold;
+ }
+
+ .components-overview {
+ &-img {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+ &-search input {
+ color: rgba(255, 255, 255, 0.65);
+ }
+ }
+}
diff --git a/site/src/theme/static/demo.less b/site/src/theme/static/demo.less
new file mode 100644
index 000000000..2e5256f70
--- /dev/null
+++ b/site/src/theme/static/demo.less
@@ -0,0 +1,359 @@
+.code-boxes-col-1-1 {
+ width: 100%;
+}
+
+.code-boxes-col-2-1 {
+ display: inline-block;
+ vertical-align: top;
+}
+
+.code-box {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+ margin: 0 0 16px;
+ border: 1px solid @site-border-color-split;
+ border-radius: @border-radius-base;
+ transition: all 0.2s;
+ .code-box-title {
+ &,
+ a {
+ color: @site-text-color;
+ background: @component-background;
+ }
+ }
+ &,
+ .code-box-demo {
+ background-color: @component-background;
+ }
+ .markdown {
+ pre {
+ margin: 0.5em 0;
+ padding: 6px 12px;
+ }
+ pre code {
+ margin: 0;
+ background: #f5f5f5;
+ }
+ }
+ &:target {
+ border: 1px solid @primary-color;
+ }
+
+ &-expand-trigger {
+ position: relative;
+ margin-left: 12px;
+ color: #3b4357;
+ font-size: 20px;
+ cursor: pointer;
+ opacity: 0.75;
+ transition: all 0.3s;
+
+ &:hover {
+ opacity: 1;
+ }
+
+ .ant-row-rtl & {
+ margin-right: 8px;
+ margin-left: 0;
+ }
+ }
+
+ &-title {
+ position: absolute;
+ top: -14px;
+ margin-left: 16px;
+ padding: 1px 8px;
+ color: #777;
+ background: @body-background;
+ border-radius: @border-radius-base @border-radius-base 0 0;
+ transition: background-color 0.4s;
+
+ .ant-row-rtl & {
+ margin-right: 16px;
+ margin-left: 0;
+ border-radius: @border-radius-base 0 0 @border-radius-base;
+ }
+
+ a,
+ a:hover {
+ color: @site-text-color;
+ font-weight: 500;
+ font-size: @font-size-base;
+ }
+ }
+
+ &-description {
+ padding: 18px 24px 12px;
+ }
+
+ a.edit-button {
+ position: absolute;
+ top: 7px;
+ right: -16px;
+ padding-right: 6px;
+ font-size: 12px;
+ text-decoration: none;
+ background: inherit;
+ transform: scale(0.9);
+
+ .anticon {
+ color: @site-text-color-secondary;
+ transition: all 0.3s;
+ &:hover {
+ color: @site-text-color;
+ }
+ }
+
+ .ant-row.ant-row-rtl & {
+ right: auto;
+ left: -22px;
+ margin-right: 0;
+ padding-right: 8px;
+ padding-left: 6px;
+ }
+ }
+
+ &-demo {
+ padding: 42px 24px 50px;
+ color: @site-text-color;
+ border-bottom: 1px solid @site-border-color-split;
+ }
+
+ iframe {
+ width: 100%;
+ border: 0;
+ }
+
+ &-meta {
+ &.markdown {
+ position: relative;
+ width: 100%;
+ font-size: @font-size-base;
+ border-radius: 0 0 @border-radius-base @border-radius-base;
+ transition: background-color 0.4s;
+ }
+
+ blockquote {
+ line-height: 1.5;
+ }
+
+ h4,
+ section& p {
+ margin: 0;
+ }
+
+ > p {
+ width: 100%;
+ margin: 0.5em 0;
+ padding-right: 25px;
+ font-size: 12px;
+ word-break: break-word;
+
+ .ant-row-rtl & {
+ padding-right: 0;
+ padding-left: 25px;
+ }
+ }
+ }
+
+ &.expand &-meta {
+ border-bottom: 1px dashed @site-border-color-split;
+ border-radius: 0;
+ }
+
+ .code-expand-icon {
+ cursor: pointer;
+ }
+
+ .code-expand-icon-show,
+ .code-expand-icon-hide {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ max-width: 100%;
+ margin: 0;
+ box-shadow: none;
+ transition: all 0.4s;
+ user-select: none;
+
+ .ant-row-rtl & {
+ right: 0;
+ left: auto;
+ }
+ }
+
+ .code-expand-icon-show {
+ opacity: 0.55;
+ pointer-events: auto;
+ &:hover {
+ opacity: 1;
+ }
+ }
+
+ .code-expand-icon.ant-tooltip-open .code-expand-icon-show {
+ opacity: 1;
+ }
+
+ .code-expand-icon-hide {
+ opacity: 0;
+ pointer-events: none;
+ }
+
+ .highlight-wrapper {
+ display: none;
+ overflow: auto;
+ border-radius: 0 0 @border-radius-base @border-radius-base;
+
+ &-expand {
+ display: block;
+ }
+ }
+
+ .highlight {
+ position: relative;
+
+ .ant-tabs-nav-list {
+ margin: 0 auto;
+ }
+
+ pre {
+ margin: 0;
+ padding: 0;
+ background: @component-background;
+ }
+ &:not(:first-child) {
+ border-top: 1px dashed @site-border-color-split;
+ }
+ }
+
+ &-actions {
+ display: flex;
+ justify-content: center;
+ padding: 12px 0;
+ border-top: 1px dashed @site-border-color-split;
+ opacity: 0.7;
+ transition: opacity 0.3s;
+
+ &:hover {
+ opacity: 1;
+ }
+ }
+
+ &-actions > &-code-action {
+ position: relative;
+ display: flex;
+ align-items: center;
+ width: 16px;
+ height: 16px;
+ margin-left: 16px;
+ color: @site-text-color-secondary;
+ cursor: pointer;
+ transition: all 0.24s;
+
+ .ant-row-rtl & {
+ margin-right: 16px;
+ margin-left: 0;
+ }
+
+ &:first-child {
+ margin-left: 0;
+
+ .ant-row-rtl & {
+ margin-right: 0;
+ }
+ }
+
+ &:hover {
+ color: @site-text-color;
+ }
+ }
+
+ &-code-copy {
+ width: 14px;
+ height: 14px;
+ font-size: 14px;
+ text-align: center;
+ background: @component-background;
+ cursor: pointer;
+ transition: transform 0.24s;
+
+ &:hover {
+ transform: scale(1.2);
+ }
+ &.anticon-check {
+ color: @green-6 !important;
+ font-weight: bold;
+ }
+ }
+
+ &-codepen {
+ width: 14px;
+ height: 14px;
+ overflow: hidden;
+ border: 0;
+ cursor: pointer;
+ }
+
+ &-riddle {
+ width: 14px;
+ height: 14px;
+ overflow: hidden;
+ border: 0;
+ cursor: pointer;
+ }
+
+ &-codesandbox {
+ width: 16px;
+ height: 16px;
+ overflow: hidden;
+ border: 0;
+ cursor: pointer;
+ &:hover {
+ opacity: 1;
+ }
+ }
+
+ .highlight-wrapper:hover &-code-copy,
+ .highlight-wrapper:hover &-codepen,
+ .highlight-wrapper:hover &-codesandbox,
+ .highlight-wrapper:hover &-riddle {
+ opacity: 1;
+ }
+
+ pre {
+ width: auto;
+ margin: 0;
+ code {
+ background: @component-background;
+ border: none;
+ }
+ }
+
+ &-debug {
+ border-color: @purple-3;
+ }
+
+ &-debug &-title a {
+ color: @purple-6;
+ }
+}
+
+.all-code-box-controls {
+ float: right;
+
+ .ant-row-rtl & {
+ float: left;
+ }
+}
+
+.ant-row-rtl {
+ #components-tooltip-demo-placement,
+ #components-popover-demo-placement,
+ #components-popconfirm-demo-placement {
+ .code-box-demo {
+ direction: ltr;
+ }
+ }
+}
diff --git a/site/src/theme/static/design-doc.less b/site/src/theme/static/design-doc.less
new file mode 100644
index 000000000..449ef832a
--- /dev/null
+++ b/site/src/theme/static/design-doc.less
@@ -0,0 +1,18 @@
+.design-inline-cards {
+ display: flex;
+ margin: 0 -20px;
+
+ > * {
+ flex: 10%;
+ margin: 0 20px;
+ }
+
+ img {
+ width: 100%;
+ max-width: 100%;
+ }
+
+ h4 {
+ margin-bottom: 0;
+ }
+}
diff --git a/site/src/theme/static/docsearch.less b/site/src/theme/static/docsearch.less
new file mode 100644
index 000000000..e5bd893e3
--- /dev/null
+++ b/site/src/theme/static/docsearch.less
@@ -0,0 +1,26 @@
+.algolia-autocomplete {
+ .ds-dropdown-menu {
+ border: none;
+ box-shadow: @box-shadow-base;
+
+ [class^='ds-dataset-'] {
+ background: @component-background;
+ border: none;
+ .algolia-docsearch-suggestion {
+ background: @component-background;
+ }
+ }
+
+ &::before {
+ display: none;
+ }
+ }
+
+ .algolia-docsearch-suggestion--title {
+ color: @site-text-color;
+ }
+
+ .algolia-docsearch-suggestion--highlight {
+ color: @primary-color;
+ }
+}
diff --git a/site/src/theme/static/footer.less b/site/src/theme/static/footer.less
new file mode 100644
index 000000000..480764ac8
--- /dev/null
+++ b/site/src/theme/static/footer.less
@@ -0,0 +1,80 @@
+@import './colors';
+
+@padding-space: 144px;
+
+#footer {
+ clear: both;
+ font-size: 14px;
+ background-color: #000;
+ position: relative;
+ z-index: 100;
+ margin-left: -1px;
+ color: rgba(255, 255, 255, 0.65);
+
+ .ant-row {
+ text-align: center;
+ .footer-center {
+ display: inline-block;
+ text-align: left;
+ > h2 {
+ font-size: 16px;
+ margin: 0 auto 24px;
+ font-weight: 500;
+ position: relative;
+
+ > .title-icon {
+ width: 27px;
+ margin-right: 16px;
+ }
+ > .anticon {
+ font-size: 16px;
+ position: absolute;
+ left: -22px;
+ top: 3px;
+ color: #aaa;
+ }
+ }
+ > div {
+ margin: 12px 0;
+ }
+ }
+ }
+ .footer-wrap {
+ position: relative;
+ padding: 86px @padding-space 93px @padding-space;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.25);
+ }
+ .bottom-bar {
+ text-align: center;
+ padding: 16px @padding-space;
+ margin: 0;
+ line-height: 32px;
+ overflow: hidden;
+ font-family: Avenir, @font-family;
+ font-size: 16px;
+ font-variant: tabular-nums;
+ a {
+ color: rgba(255, 255, 255, 0.65);
+ margin-left: 4px;
+ &:hover {
+ color: #fff;
+ }
+ }
+ .translate-button {
+ text-align: left;
+ }
+ .heart {
+ color: #f73f51;
+ font-size: 22px;
+ }
+ }
+ a {
+ color: rgba(255, 255, 255, 0.9);
+ }
+ h2 {
+ color: rgba(255, 255, 255, 1);
+ & > span {
+ color: rgba(255, 255, 255, 1);
+ }
+ }
+}
diff --git a/site/src/theme/static/header.less b/site/src/theme/static/header.less
new file mode 100644
index 000000000..4bd5aca63
--- /dev/null
+++ b/site/src/theme/static/header.less
@@ -0,0 +1,49 @@
+@import '../../../node_modules/ant-design-vue/es/style/themes/default.less';
+
+#header {
+ // ===================== Home Page =====================
+ &.home-header {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ max-width: 1280px;
+ margin-right: auto;
+ margin-left: auto;
+ background: transparent;
+ box-shadow: none;
+
+ #logo {
+ padding-right: 16px;
+ padding-left: 40px;
+
+ html.rtl & {
+ padding-right: 40px;
+ padding-left: 16px;
+ }
+ }
+
+ .ant-menu {
+ background: transparent;
+ }
+ }
+}
+
+@media (max-width: @screen-sm-min) {
+ #header.home-header {
+ .ant-row {
+ .ant-col {
+ margin: 0 auto;
+
+ a {
+ padding-right: 0;
+ padding-left: 0;
+ }
+
+ &:last-child {
+ display: none;
+ }
+ }
+ }
+ }
+}
diff --git a/site/src/theme/static/highlight.less b/site/src/theme/static/highlight.less
new file mode 100644
index 000000000..4adb492c8
--- /dev/null
+++ b/site/src/theme/static/highlight.less
@@ -0,0 +1,155 @@
+/**
+* prism.js default theme for JavaScript, CSS and HTML
+* Based on dabblet (http://dabblet.com)
+* @author Lea Verou
+*/
+
+pre code {
+ display: block;
+ padding: 16px 32px;
+ color: @site-text-color;
+ font-size: @font-size-base;
+ font-family: 'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+ line-height: 2;
+ white-space: pre;
+ background: white;
+ border: 1px solid #e9e9e9;
+ border-radius: @border-radius-base;
+}
+
+code[class*='language-'],
+pre[class*='language-'] {
+ color: black;
+ font-family: 'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+ line-height: 1.5;
+ direction: ltr;
+ white-space: pre;
+ text-align: left;
+ word-wrap: normal;
+ word-break: normal;
+ word-spacing: normal;
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+ background: none;
+}
+
+code[class*='css'] {
+ direction: ltr;
+}
+
+pre[class*='language-']::-moz-selection,
+pre[class*='language-'] ::-moz-selection,
+code[class*='language-']::-moz-selection,
+code[class*='language-'] ::-moz-selection {
+ text-shadow: none;
+ background: #b3d4fc;
+}
+
+pre[class*='language-']::selection,
+pre[class*='language-'] ::selection,
+code[class*='language-']::selection,
+code[class*='language-'] ::selection {
+ text-shadow: none;
+ background: #b3d4fc;
+}
+
+@media print {
+ code[class*='language-'],
+ pre[class*='language-'] {
+ text-shadow: none;
+ }
+}
+
+/* Code blocks */
+pre[class*='language-'] {
+ margin: 16px 0;
+ padding: 12px 20px;
+ overflow: auto;
+}
+
+:not(pre) > code[class*='language-'],
+pre[class*='language-'] {
+ background: #f5f5f5;
+}
+
+/* Inline code */
+:not(pre) > code[class*='language-'] {
+ padding: 0.1em;
+ white-space: normal;
+ border-radius: 0.3em;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+ color: slategray;
+}
+
+.token.punctuation {
+ color: #999;
+}
+
+.namespace {
+ opacity: 0.7;
+}
+
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+ color: #f81d22;
+}
+
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+ color: #0b8235;
+}
+
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+ color: #0b8235;
+}
+
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+ color: #008dff;
+}
+
+.token.function {
+ color: #f81d22;
+}
+
+.token.regex,
+.token.important,
+.token.variable {
+ color: #e90;
+}
+
+.token.important,
+.token.bold {
+ font-weight: bold;
+}
+.token.italic {
+ font-style: italic;
+}
+
+.token.entity {
+ cursor: help;
+}
diff --git a/site/src/theme/static/home.less b/site/src/theme/static/home.less
new file mode 100644
index 000000000..73f8c847f
--- /dev/null
+++ b/site/src/theme/static/home.less
@@ -0,0 +1,2 @@
+@home-bg-color: #2f54eb;
+@home-text-color: #314659;
diff --git a/site/src/theme/static/icon-pic-searcher.less b/site/src/theme/static/icon-pic-searcher.less
new file mode 100644
index 000000000..95c39c1df
--- /dev/null
+++ b/site/src/theme/static/icon-pic-searcher.less
@@ -0,0 +1,51 @@
+.icon-pic-searcher {
+ display: inline-block;
+ margin: 0 8px;
+
+ .icon-pic-btn {
+ color: @text-color-secondary;
+ cursor: pointer;
+ transition: all 0.3s;
+ &:hover {
+ color: @input-icon-hover-color;
+ }
+ }
+}
+
+.icon-pic-preview {
+ width: 66px;
+ height: 66px;
+ margin-top: 10px;
+ padding: 8px;
+ text-align: center;
+ border: 1px solid @border-color-base;
+ border-radius: 4px;
+
+ > img {
+ max-width: 50px;
+ max-height: 50px;
+ }
+}
+
+.icon-pic-search-result {
+ min-height: 50px;
+ padding: 0 10px;
+ > .result-tip {
+ padding: 10px 0;
+ color: @text-color-secondary;
+ }
+ > table {
+ width: 100%;
+ .col-icon {
+ width: 80px;
+ padding: 10px 0;
+ > .anticon {
+ font-size: 30px;
+
+ :hover {
+ color: @link-hover-color;
+ }
+ }
+ }
+ }
+}
diff --git a/site/src/theme/static/icons.less b/site/src/theme/static/icons.less
new file mode 100644
index 000000000..f5d647c70
--- /dev/null
+++ b/site/src/theme/static/icons.less
@@ -0,0 +1,88 @@
+ul.anticons-list {
+ margin: 10px 0;
+ overflow: hidden;
+ direction: ltr;
+ list-style: none;
+ li {
+ position: relative;
+ float: left;
+ width: 16.66%;
+ height: 100px;
+ margin: 3px 0;
+ padding: 10px 0 0;
+ overflow: hidden;
+ color: #555;
+ text-align: center;
+ list-style: none;
+ background-color: inherit;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: color 0.3s ease-in-out, background-color 0.3s ease-in-out;
+ .rtl & {
+ margin: 3px 0;
+ padding: 10px 0 0;
+ }
+ .anticon {
+ margin: 12px 0 8px;
+ font-size: 36px;
+ transition: transform 0.3s ease-in-out;
+ will-change: transform;
+ }
+
+ .anticon-class {
+ display: block;
+ font-family: 'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+ white-space: nowrap;
+ text-align: center;
+ transform: scale(0.83);
+ .ant-badge {
+ transition: color 0.3s ease-in-out;
+ }
+ }
+
+ &:hover {
+ color: #fff;
+ background-color: @primary-color;
+ .anticon {
+ transform: scale(1.4);
+ }
+ .ant-badge {
+ color: #fff;
+ }
+ }
+
+ &.TwoTone:hover {
+ background-color: #8ecafe;
+ }
+
+ &.copied:hover {
+ color: rgba(255, 255, 255, 0.2);
+ }
+
+ &::after {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ color: #fff;
+ line-height: 110px;
+ text-align: center;
+ opacity: 0;
+ transition: all 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28);
+ content: 'Copied!';
+ }
+
+ &.copied::after {
+ top: -10px;
+ opacity: 1;
+ }
+ }
+}
+
+.copied-code {
+ padding: 2px 4px 2px;
+ font-size: 12px;
+ background: #f5f5f5;
+ border-radius: 2px;
+}
diff --git a/site/src/theme/static/index.less b/site/src/theme/static/index.less
new file mode 100644
index 000000000..2d8af5c9b
--- /dev/null
+++ b/site/src/theme/static/index.less
@@ -0,0 +1,28 @@
+@import './reset.less';
+@import '../../../node_modules/ant-design-vue/es/style/themes/default.less';
+@import './theme';
+@import './common';
+@import './header';
+@import './footer';
+@import './home';
+@import './page-nav';
+@import './markdown';
+@import './design-doc';
+@import './preview-img';
+@import './toc';
+@import './not-found';
+@import './highlight';
+@import './demo';
+@import './colors';
+@import './icons';
+@import './icon-pic-searcher';
+@import './mock-browser';
+@import './new-version-info-modal';
+@import './motion';
+@import './responsive';
+@import './resource';
+@import './docsearch';
+@import './nprogress';
+@import './contributors';
+@import './dark.less';
+@import './rtl.less';
diff --git a/site/src/theme/static/markdown.less b/site/src/theme/static/markdown.less
new file mode 100644
index 000000000..b2d37bac0
--- /dev/null
+++ b/site/src/theme/static/markdown.less
@@ -0,0 +1,490 @@
+.markdown {
+ color: @site-text-color;
+ font-size: 14px;
+ line-height: 2;
+}
+
+.highlight {
+ line-height: 1.5;
+}
+
+.markdown img {
+ max-width: ~'calc(100% - 32px)';
+}
+
+.markdown p > img {
+ margin: 34px 0;
+ box-shadow: 0 8px 20px rgba(143, 168, 191, 0.35);
+}
+
+.markdown p > img.markdown-inline-image {
+ margin: 0;
+ box-shadow: none;
+}
+
+.markdown h1 {
+ margin-top: 8px;
+ margin-bottom: 20px;
+ color: @site-heading-color;
+ font-weight: 500;
+ font-size: 30px;
+ font-family: Avenir, @font-family, sans-serif;
+ line-height: 38px;
+
+ .subtitle {
+ margin-left: 12px;
+ }
+}
+
+.markdown h2 {
+ font-size: 24px;
+ line-height: 32px;
+}
+
+.markdown h2,
+.markdown h3,
+.markdown h4,
+.markdown h5,
+.markdown h6 {
+ clear: both;
+ margin: 1.6em 0 0.6em;
+ color: @site-heading-color;
+ font-weight: 500;
+ font-family: Avenir, @font-family, sans-serif;
+}
+
+.markdown h3 {
+ font-size: 18px;
+}
+.markdown h4 {
+ font-size: 16px;
+}
+.markdown h5 {
+ font-size: 14px;
+}
+.markdown h6 {
+ font-size: 12px;
+}
+
+.markdown hr {
+ clear: both;
+ height: 1px;
+ margin: 56px 0;
+ background: @site-border-color-split;
+ border: 0;
+}
+
+.markdown p,
+.markdown pre {
+ margin: 1em 0;
+ .ant-row-rtl & {
+ direction: rtl;
+ text-align: right;
+ }
+}
+
+.markdown ul > li {
+ margin-left: 20px;
+ padding-left: 4px;
+ list-style-type: circle;
+
+ .rtl & {
+ margin-right: 20px;
+ margin-left: 0;
+ padding-right: 4px;
+ padding-left: 0;
+ }
+ &:empty {
+ display: none;
+ }
+}
+
+.markdown ol > li {
+ margin-left: 20px;
+ padding-left: 4px;
+ list-style-type: decimal;
+
+ .ant-row-rtl & {
+ margin-right: 20px;
+ margin-left: 0;
+ padding-right: 4px;
+ padding-left: 0;
+ }
+}
+
+.markdown ul > li > p,
+.markdown ol > li > p {
+ margin: 0.2em 0;
+}
+
+.markdown code {
+ margin: 0 1px;
+ padding: 0.2em 0.4em;
+ font-size: 0.9em;
+ background: @site-markdown-code-bg;
+ border: 1px solid @border-color-split;
+ border-radius: 3px;
+}
+
+.markdown pre {
+ font-family: @code-family;
+ background: @site-markdown-code-bg;
+ border-radius: @border-radius-base;
+}
+
+.markdown pre code {
+ margin: 0;
+ padding: 0;
+ overflow: auto;
+ color: @site-text-color;
+ font-size: max(@font-size-base - 1px, 12px);
+ direction: ltr;
+ text-align: left;
+ background: #f5f5f5;
+ border: none;
+}
+
+.markdown strong,
+.markdown b {
+ font-weight: 500;
+}
+
+.markdown > table {
+ width: 100%;
+ margin: 8px 0 16px;
+ direction: ltr;
+ empty-cells: show;
+ border: 1px solid @site-border-color-split;
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+.markdown > table th {
+ color: #5c6b77;
+ font-weight: 500;
+ white-space: nowrap;
+ background: rgba(0, 0, 0, 0.02);
+}
+
+.markdown > table th,
+.markdown > table td {
+ padding: 16px 24px;
+ text-align: left;
+ border: 1px solid @site-border-color-split;
+}
+
+.markdown table td > a:not(:last-child) {
+ margin-right: 0 !important;
+ &::after {
+ position: relative !important;
+ }
+}
+
+.markdown blockquote {
+ margin: 1em 0;
+ padding-left: 0.8em;
+ color: @site-text-color-secondary;
+ font-size: 90%;
+ border-left: 4px solid @site-border-color-split;
+
+ .rtl & {
+ padding-right: 0.8em;
+ padding-left: 0;
+ border-right: 4px solid @site-border-color-split;
+ border-left: none;
+ }
+}
+
+.markdown blockquote p {
+ margin: 0;
+}
+
+.markdown .anchor {
+ margin-left: 8px;
+ opacity: 0;
+ transition: opacity 0.3s;
+
+ .rtl & {
+ margin-right: 8px;
+ margin-left: 0;
+ }
+}
+
+.markdown .waiting {
+ color: #ccc;
+ cursor: not-allowed;
+}
+
+.markdown a.edit-button {
+ display: inline-block;
+ margin-left: 8px;
+ text-decoration: none;
+
+ .rtl & {
+ margin-right: 8px;
+ margin-left: 0;
+ transform: rotateY(180deg);
+ }
+
+ .anticon {
+ display: block;
+ color: @site-text-color-secondary;
+ font-size: 16px;
+ transition: all 0.3s;
+
+ &:hover {
+ color: @site-text-color;
+ }
+ }
+}
+
+.markdown h1:hover .anchor,
+.markdown h2:hover .anchor,
+.markdown h3:hover .anchor,
+.markdown h4:hover .anchor,
+.markdown h5:hover .anchor,
+.markdown h6:hover .anchor {
+ display: inline-block;
+ opacity: 1;
+}
+
+.markdown > br,
+.markdown > p > br {
+ clear: both;
+}
+
+.markdown.api-container {
+ table {
+ display: block;
+ margin: 2em 0;
+ overflow-x: auto;
+ overflow-y: hidden;
+ font-size: max(@font-size-base - 1px, 12px);
+ font-family: @code-family;
+ line-height: @line-height-base;
+ border: 0;
+ -webkit-overflow-scrolling: touch;
+
+ th,
+ td {
+ padding: 12px;
+ border-color: @border-color-split;
+ border-width: 1px 0;
+
+ &:first-child {
+ border-left: 1px solid @border-color-split;
+ }
+
+ &:last-child {
+ border-right: 1px solid @border-color-split;
+ }
+ }
+
+ th {
+ padding-top: 14px;
+ border-width: 1px 0 2px 0;
+ }
+
+ tbody tr {
+ transition: all 0.3s;
+ &:hover {
+ background: rgba(60, 90, 100, 0.04);
+ }
+ }
+
+ td {
+ &:first-child {
+ width: 18%;
+ color: @gray-8;
+ font-weight: 600;
+ white-space: nowrap;
+ }
+ &:nth-child(2) {
+ width: 55%;
+ }
+ &:nth-child(3) {
+ width: 22%;
+ color: @magenta-7;
+ font-size: max(@font-size-base - 1px, 12px);
+ }
+ &:nth-child(4) {
+ width: 15%;
+ font-size: max(@font-size-base - 1px, 12px);
+ }
+ &:nth-child(5) {
+ width: 8%;
+ font-size: max(@font-size-base - 1px, 12px);
+ }
+ &:nth-last-child(3):first-child {
+ width: 38%;
+ }
+ &:nth-last-child(3):first-child ~ td:nth-last-child(2) {
+ width: 70%;
+ }
+ }
+ }
+
+ hr {
+ margin: 12px 0;
+ }
+}
+
+@demo-grid-color: #0092ff;
+
+.grid-demo,
+[id^='components-grid-demo-'] {
+ .demo-row,
+ .code-box-demo .demo-row {
+ margin-bottom: 8px;
+ overflow: hidden;
+ background-image: linear-gradient(
+ 90deg,
+ #f5f5f5 4.16666667%,
+ transparent 4.16666667%,
+ transparent 8.33333333%,
+ #f5f5f5 8.33333333%,
+ #f5f5f5 12.5%,
+ transparent 12.5%,
+ transparent 16.66666667%,
+ #f5f5f5 16.66666667%,
+ #f5f5f5 20.83333333%,
+ transparent 20.83333333%,
+ transparent 25%,
+ #f5f5f5 25%,
+ #f5f5f5 29.16666667%,
+ transparent 29.16666667%,
+ transparent 33.33333333%,
+ #f5f5f5 33.33333333%,
+ #f5f5f5 37.5%,
+ transparent 37.5%,
+ transparent 41.66666667%,
+ #f5f5f5 41.66666667%,
+ #f5f5f5 45.83333333%,
+ transparent 45.83333333%,
+ transparent 50%,
+ #f5f5f5 50%,
+ #f5f5f5 54.16666667%,
+ transparent 54.16666667%,
+ transparent 58.33333333%,
+ #f5f5f5 58.33333333%,
+ #f5f5f5 62.5%,
+ transparent 62.5%,
+ transparent 66.66666667%,
+ #f5f5f5 66.66666667%,
+ #f5f5f5 70.83333333%,
+ transparent 70.83333333%,
+ transparent 75%,
+ #f5f5f5 75%,
+ #f5f5f5 79.16666667%,
+ transparent 79.16666667%,
+ transparent 83.33333333%,
+ #f5f5f5 83.33333333%,
+ #f5f5f5 87.5%,
+ transparent 87.5%,
+ transparent 91.66666667%,
+ #f5f5f5 91.66666667%,
+ #f5f5f5 95.83333333%,
+ transparent 95.83333333%
+ );
+ }
+ .ant-row > div,
+ .code-box-demo .ant-row > div {
+ min-height: 30px;
+ margin-top: 8px;
+ margin-bottom: 8px;
+ color: #fff;
+ text-align: center;
+ border-radius: 0;
+ }
+ .code-box-demo .ant-row > div:not(.gutter-row) {
+ padding: 16px 0;
+ background: @demo-grid-color;
+ &:nth-child(2n + 1) {
+ background: fade(@demo-grid-color, 75%);
+ }
+ }
+ .ant-row .demo-col,
+ .code-box-demo .ant-row .demo-col {
+ margin-top: 0;
+ margin-bottom: 0;
+ padding: 30px 0;
+ color: @white;
+ font-size: 18px;
+ text-align: center;
+ border: none;
+ }
+ .ant-row .demo-col-1 {
+ background: fade(@demo-grid-color, 75%);
+ }
+ .ant-row .demo-col-2,
+ .code-box-demo .ant-row .demo-col-2 {
+ background: fade(@demo-grid-color, 50%);
+ }
+ .ant-row .demo-col-3,
+ .code-box-demo .ant-row .demo-col-3 {
+ color: #999;
+ background: rgba(255, 255, 255, 0.2);
+ }
+ .ant-row .demo-col-4,
+ .code-box-demo .ant-row .demo-col-4 {
+ background: fade(@demo-grid-color, 60%);
+ }
+ .ant-row .demo-col-5,
+ .code-box-demo .ant-row .demo-col-5 {
+ color: #999;
+ background: rgba(255, 255, 255, 0.2);
+ }
+ .code-box-demo .height-100 {
+ height: 100px;
+ line-height: 100px;
+ }
+ .code-box-demo .height-50 {
+ height: 50px;
+ line-height: 50px;
+ }
+ .code-box-demo .height-120 {
+ height: 120px;
+ line-height: 120px;
+ }
+ .code-box-demo .height-80 {
+ height: 80px;
+ line-height: 80px;
+ }
+}
+
+[id='components-grid-demo-playground'],
+[id='components-grid-demo-gutter'] {
+ > .code-box-demo .ant-row > div {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+}
+
+// For Changelog
+.markdown ul.ant-timeline {
+ line-height: 2;
+ li.ant-timeline-item {
+ margin: 0;
+ padding: 0 0 30px;
+ list-style: none;
+ .ant-timeline-item-content {
+ position: relative;
+ top: -14px;
+ padding-left: 32px;
+ font-size: 14px;
+ > h2 {
+ margin-top: 0;
+ padding-top: 4px;
+ direction: ltr;
+ span {
+ .ant-row-rtl & {
+ float: right;
+ }
+ }
+ }
+ }
+ }
+ li.ant-timeline-item:first-child {
+ margin-top: 40px;
+ }
+}
diff --git a/site/src/theme/static/mock-browser.less b/site/src/theme/static/mock-browser.less
new file mode 100644
index 000000000..a6d15f166
--- /dev/null
+++ b/site/src/theme/static/mock-browser.less
@@ -0,0 +1,53 @@
+/* Browser mockup code
+ * Contribute: https://gist.github.com/jarthod/8719db9fef8deb937f4f
+ * Live example: https://updown.io
+ */
+
+.browser-mockup {
+ position: relative;
+ border-top: 2em solid rgba(230, 230, 230, 0.7);
+ border-radius: 3px 3px 0 0;
+ box-shadow: 0 0.1em 0.5em 0 rgba(0, 0, 0, 0.28);
+}
+
+.browser-mockup::before {
+ position: absolute;
+ top: -1.25em;
+ left: 1em;
+ display: block;
+ width: 0.5em;
+ height: 0.5em;
+ background-color: #f44;
+ border-radius: 50%;
+ box-shadow: 0 0 0 2px #f44, 1.5em 0 0 2px #9b3, 3em 0 0 2px #fb5;
+ content: '';
+}
+
+.browser-mockup.with-tab::after {
+ position: absolute;
+ top: -2em;
+ left: 5.5em;
+ display: block;
+ width: 20%;
+ height: 0;
+ border-right: 0.8em solid transparent;
+ border-bottom: 2em solid white;
+ border-left: 0.8em solid transparent;
+ content: '';
+}
+
+.browser-mockup.with-url::after {
+ position: absolute;
+ top: -1.6em;
+ left: 5.5em;
+ display: block;
+ width: ~'calc(100% - 6em)';
+ height: 1.2em;
+ background-color: white;
+ border-radius: 2px;
+ content: '';
+}
+
+.browser-mockup > * {
+ display: block;
+}
diff --git a/site/src/theme/static/motion.less b/site/src/theme/static/motion.less
new file mode 100644
index 000000000..c90e1e095
--- /dev/null
+++ b/site/src/theme/static/motion.less
@@ -0,0 +1,39 @@
+.motion-container {
+ height: 190px;
+ margin: 40px 0 20px;
+ line-height: 190px;
+ text-align: center;
+}
+
+.motion-example {
+ display: inline-block !important;
+ width: 180px;
+ height: 180px;
+ color: #fff;
+ font-weight: bold;
+ font-size: 18px;
+ line-height: 180px;
+ text-align: center;
+ background: url(https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg) center/180px;
+ border-radius: 8px;
+ animation-duration: 0.5s !important;
+}
+
+.motion-select-wrapper {
+ margin-bottom: 40px;
+ text-align: center;
+}
+
+.motion-select {
+ width: 180px;
+ text-align: left;
+}
+
+.video-player {
+ position: relative;
+ max-width: 800px;
+ &-right {
+ float: right;
+ width: 616px;
+ }
+}
diff --git a/site/src/theme/static/new-version-info-modal.less b/site/src/theme/static/new-version-info-modal.less
new file mode 100644
index 000000000..6ef556e08
--- /dev/null
+++ b/site/src/theme/static/new-version-info-modal.less
@@ -0,0 +1,24 @@
+.new-version-info-modal {
+ img {
+ position: absolute;
+ top: 36px;
+ left: 34px;
+ width: 100px;
+ }
+ p {
+ margin-top: 1em;
+ }
+ .anticon {
+ display: none;
+ }
+ .ant-confirm-body {
+ .ant-confirm-title {
+ font-size: 18px;
+ }
+
+ margin-left: 120px;
+ .ant-confirm-content {
+ margin-left: 0;
+ }
+ }
+}
diff --git a/site/src/theme/static/not-found.less b/site/src/theme/static/not-found.less
new file mode 100644
index 000000000..6cb6bd98c
--- /dev/null
+++ b/site/src/theme/static/not-found.less
@@ -0,0 +1,34 @@
+#page-404 {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 100;
+ width: 100%;
+ height: 100%;
+ background-color: #fff;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: 100%;
+ background-attachment: fixed;
+
+ section {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ text-align: center;
+ transform: translate(-50%, -50%);
+ }
+
+ h1 {
+ color: @primary-color;
+ font-weight: 500;
+ font-size: 120px;
+ }
+
+ p {
+ color: @site-text-color;
+ font-size: 18px;
+ }
+}
diff --git a/site/src/theme/static/nprogress.less b/site/src/theme/static/nprogress.less
new file mode 100644
index 000000000..258bb7206
--- /dev/null
+++ b/site/src/theme/static/nprogress.less
@@ -0,0 +1,14 @@
+#nprogress {
+ .bar {
+ background: @primary-color;
+ }
+
+ .peg {
+ box-shadow: 0 0 10px @primary-color, 0 0 5px @primary-color;
+ }
+
+ .spinner-icon {
+ border-top-color: @primary-color;
+ border-left-color: @primary-color;
+ }
+}
diff --git a/site/src/theme/static/page-nav.less b/site/src/theme/static/page-nav.less
new file mode 100644
index 000000000..8706c28a6
--- /dev/null
+++ b/site/src/theme/static/page-nav.less
@@ -0,0 +1,102 @@
+.prev-next-nav {
+ width: ~'calc(100% - 128px)';
+ margin-right: 64px;
+ margin-left: 64px;
+ overflow: hidden;
+ font-size: 14px;
+ border-top: 1px solid @site-border-color-split;
+
+ > .prev-page,
+ > .next-page {
+ float: left;
+ width: 50%;
+ height: 72px;
+ line-height: 72px;
+ text-decoration: none;
+
+ .ant-row-rtl & {
+ float: right;
+
+ .footer-nav-icon-before,
+ .footer-nav-icon-after {
+ transform: rotate(180deg);
+ }
+ }
+ }
+
+ > a.prev-page {
+ .footer-nav-icon-before {
+ position: relative;
+ left: 0;
+ margin-right: 1em;
+ color: @site-text-color-secondary;
+ font-size: 12px;
+ transition: all 0.3s;
+
+ .ant-row-rtl & {
+ right: 0;
+ left: auto;
+ margin-right: 0;
+ margin-left: 1em;
+ }
+ }
+
+ .footer-nav-icon-after {
+ display: none;
+ }
+
+ &:hover .footer-nav-icon-before {
+ left: -3px;
+ color: @primary-color;
+
+ .ant-row-rtl & {
+ right: -3px;
+ left: auto;
+ }
+ }
+ }
+
+ > .next-page {
+ float: right;
+ text-align: right;
+
+ .ant-row-rtl & {
+ float: left;
+ text-align: left;
+ }
+
+ .footer-nav-icon-after {
+ position: relative;
+ right: 0;
+ margin-left: 1em;
+ color: @site-text-color-secondary;
+ font-size: 12px;
+ transition: all 0.3s;
+
+ .ant-row-rtl & {
+ right: auto;
+ left: 0;
+ margin-right: 1em;
+ margin-left: 0;
+ }
+ }
+
+ .footer-nav-icon-before {
+ display: none;
+ }
+
+ &:hover .footer-nav-icon-after {
+ right: -3px;
+ color: @primary-color;
+
+ .ant-row-rtl & {
+ right: auto;
+ left: -3px;
+ }
+ }
+ }
+
+ .chinese {
+ margin-left: 0.5em;
+ }
+}
diff --git a/site/src/theme/static/preview-img.less b/site/src/theme/static/preview-img.less
new file mode 100644
index 000000000..c7dd04b73
--- /dev/null
+++ b/site/src/theme/static/preview-img.less
@@ -0,0 +1,214 @@
+.preview-image-boxes {
+ display: flex;
+ float: right;
+ clear: both;
+ width: 496px;
+ margin: 0 0 70px 64px;
+
+ &-with-carousel {
+ width: 420px;
+
+ .preview-image-box img {
+ padding: 0;
+ }
+ }
+
+ .ant-row-rtl & {
+ float: left;
+ margin: 0 64px 70px 0;
+ }
+}
+
+.preview-image-boxes + .preview-image-boxes {
+ margin-top: -35px;
+}
+
+.preview-image-box {
+ float: left;
+ width: 100%;
+}
+
+.preview-image-box + .preview-image-box {
+ margin-left: 24px;
+
+ .ant-row-rtl & {
+ margin-right: 24px;
+ margin-left: 0;
+ }
+}
+
+.preview-image-wrapper {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+ padding: 16px;
+ text-align: center;
+ background: #f2f4f5;
+}
+
+.preview-image-wrapper.video {
+ display: block;
+ padding: 0;
+ background: 0;
+}
+
+.preview-image-wrapper video {
+ display: block;
+ width: 100%;
+ + svg {
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+}
+
+.preview-image-wrapper.good::after {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ display: block;
+ width: 100%;
+ height: 3px;
+ background: @primary-color;
+ content: '';
+}
+
+.preview-image-wrapper.bad::after {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ display: block;
+ width: 100%;
+ height: 3px;
+ background: @error-color;
+ content: '';
+}
+
+.preview-image-title {
+ margin-top: 20px;
+ color: @site-text-color;
+ font-size: 12px;
+}
+
+.preview-image-description {
+ margin-top: 2px;
+ color: @site-text-color-secondary;
+ font-size: 12px;
+ line-height: 1.5;
+}
+
+.preview-image-description hr {
+ margin: 2px 0;
+ background: none;
+ border: 0;
+}
+
+.preview-image-box img {
+ max-width: 100%;
+ padding: 12px;
+ background: @body-background;
+ border-radius: @border-radius-base;
+ cursor: pointer;
+ transition: all 0.3s;
+ &.no-padding {
+ padding: 0;
+ background: none;
+ }
+}
+
+.preview-image-boxes.preview-image-boxes-with-carousel img {
+ padding: 0;
+ box-shadow: 0 1px 0 0 #ddd, 0 3px 0 0 @body-background, 0 4px 0 0 #ddd, 0 6px 0 0 @body-background,
+ 0 7px 0 0 #ddd;
+}
+
+.preview-image-box img:hover {
+ box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.3);
+}
+
+.image-modal {
+ text-align: center;
+
+ &-container {
+ position: relative;
+ text-align: center;
+ }
+
+ .ant-carousel {
+ .slick-slider {
+ padding-bottom: 24px;
+ img {
+ display: inline;
+ max-width: 100%;
+ }
+ }
+ .slick-dots {
+ bottom: 4px;
+ li button {
+ background: #888;
+ }
+ }
+ }
+
+ .image-modal-single.slick-slider {
+ padding-bottom: 0;
+ }
+
+ .image-modal-single .slick-dots {
+ display: none !important;
+ }
+}
+
+.transition-video-player,
+.motion-video-min {
+ float: right;
+ width: 600px;
+ padding: 0 0 70px 20px;
+
+ .preview-image-wrapper {
+ padding: 0;
+ }
+
+ .ant-row-rtl & {
+ float: left;
+ }
+}
+
+.motion-video-min {
+ width: 390px;
+}
+
+.motion-principle-wrapper {
+ width: 100%;
+ max-width: 900px;
+ margin: 48px 0 24px;
+}
+
+.principle-wrapper {
+ width: 100%;
+
+ .principle {
+ display: inline-block;
+ width: 100%;
+ min-height: 180px;
+ margin-right: 12.5%;
+ margin-bottom: 24px;
+ padding: 24px;
+ font-size: 24px;
+ text-align: center;
+ border: 1px solid #e8e8e8;
+ border-radius: 4px;
+ &:last-child {
+ margin-right: 0;
+ }
+
+ h4 {
+ margin: 16px 0 8px;
+ }
+
+ p {
+ font-size: 12px;
+ line-height: 24px;
+ }
+ }
+}
diff --git a/site/src/theme/static/reset.less b/site/src/theme/static/reset.less
new file mode 100644
index 000000000..dd98cbcaa
--- /dev/null
+++ b/site/src/theme/static/reset.less
@@ -0,0 +1,46 @@
+body,
+div,
+dl,
+dt,
+dd,
+ul,
+ol,
+li,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+pre,
+code,
+form,
+fieldset,
+legend,
+input,
+textarea,
+p,
+blockquote,
+th,
+td,
+hr,
+button,
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+ margin: 0;
+ padding: 0;
+}
+
+ul,
+ol {
+ list-style: none;
+}
diff --git a/site/src/theme/static/resource.less b/site/src/theme/static/resource.less
new file mode 100644
index 000000000..c3b39f0a2
--- /dev/null
+++ b/site/src/theme/static/resource.less
@@ -0,0 +1,83 @@
+.resource-cards {
+ display: flex;
+ flex-flow: wrap;
+ width: 100%;
+}
+
+.resource-card {
+ display: flex;
+ max-width: 500px;
+ width: ~'calc(50% - 24px)';
+ min-width: 400px;
+ height: 130px;
+ border: 1px solid @border-color-base;
+ border-radius: @border-radius-base;
+ font-size: 12px;
+ color: #777;
+ margin: 24px 24px 0 0;
+ vertical-align: middle;
+ transition: all 0.3s ease;
+ position: relative;
+ overflow: hidden;
+}
+
+.resource-card:hover {
+ box-shadow: 0 3px 8px #d3ddeb;
+ border-color: transparent;
+}
+
+.resource-card:hover .resource-card-title {
+ color: @primary-color;
+}
+
+.resource-card.disabled {
+ opacity: 0.45;
+ pointer-events: none;
+}
+
+.resource-card-cover,
+.resource-card-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ width: 130px;
+}
+
+.resource-card-cover img {
+ width: 68px;
+}
+
+.resource-card-content {
+ display: flex;
+ flex-flow: column;
+ justify-content: center;
+}
+
+.resource-card-title {
+ display: block;
+ font-size: 16px;
+ color: @site-text-color;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ line-height: 1.2;
+ margin-bottom: 6px;
+}
+
+.resource-card-hot-badge {
+ background: #f50;
+ border-radius: 2px;
+ padding: 0 3px;
+ color: #fff;
+ font-size: 12px;
+ line-height: 20px;
+ margin-left: 2px;
+ vertical-align: top;
+}
+
+.resource-card-description {
+ display: block;
+ color: #697b8c;
+ padding-right: 16px;
+}
diff --git a/site/src/theme/static/responsive.less b/site/src/theme/static/responsive.less
new file mode 100644
index 000000000..bb3237386
--- /dev/null
+++ b/site/src/theme/static/responsive.less
@@ -0,0 +1,295 @@
+@import "../../../node_modules/ant-design-vue/es/style/themes/default.less";
+
+.nav-phone-icon {
+ position: absolute;
+ top: 25px;
+ right: 30px;
+ z-index: 1;
+ display: none;
+ width: 16px;
+ height: 22px;
+ cursor: pointer;
+}
+
+@media only screen and (max-width: @screen-lg) {
+ .main-container {
+ padding-right: 48px;
+ padding-left: 48px;
+
+ .toc-affix {
+ display: none;
+ }
+ }
+ .code-boxes-col-2-1,
+ .code-boxes-col-1-1 {
+ float: none;
+ width: 100%;
+ }
+}
+
+@media only screen and (max-width: 767.99px) {
+ .preview-image-boxes {
+ float: none;
+ width: 100%;
+ margin: 0 !important;
+ }
+ .preview-image-box {
+ width: 100%;
+ margin: 10px 0;
+ padding: 0;
+ }
+
+ .image-wrapper {
+ display: none;
+ }
+
+ div.version {
+ display: block;
+ margin: 29px auto 16px;
+ }
+
+ .toc {
+ display: none;
+ }
+
+ .nav-phone-icon {
+ display: block;
+ }
+
+ .main {
+ height: calc(100% - 86px);
+ }
+
+ .aside-container {
+ float: none;
+ width: auto;
+ padding-bottom: 30px;
+ border-right: 0;
+ }
+
+ .main-container,
+ .ant-row-rtl .main-container {
+ margin-right: 0;
+ margin-left: 0;
+ padding-right: 16px;
+ padding-left: 16px;
+ > .markdown > * {
+ width: 100% !important;
+ }
+ }
+
+ .main-wrapper {
+ width: 100%;
+ margin: 0;
+ border-radius: 0;
+ }
+
+ .prev-next-nav {
+ width: ~'calc(100% - 32px)';
+ margin-left: 16px;
+
+ .ant-row-rtl & {
+ margin-right: 16px;
+ margin-left: 64px;
+ }
+ }
+
+ .drawer {
+ .ant-menu-inline .ant-menu-item::after,
+ .ant-menu-vertical .ant-menu-item::after {
+ right: auto;
+ left: 0;
+ }
+ }
+
+ #_hj_feedback_container {
+ display: none;
+ }
+ /** home 区块 **/
+ .home-page-wrapper {
+ .page {
+ h2 {
+ margin: 80px auto 64px;
+ }
+ }
+ .parallax-bg {
+ display: none;
+ }
+ }
+ .banner {
+ display: block;
+ height: 632px;
+ &-bg-wrapper {
+ display: none;
+ }
+ .img-wrapper,
+ .text-wrapper {
+ display: inline-block;
+ width: 100%;
+ min-width: unset;
+ max-width: unset;
+ margin: auto;
+ text-align: center;
+ }
+ .img-wrapper {
+ position: initial;
+ margin-top: 20px;
+ text-align: center;
+ svg {
+ width: 100%;
+ max-width: 260px;
+ height: auto;
+ margin: 0 auto;
+ }
+ }
+ .text-wrapper {
+ min-height: 200px;
+ margin-top: 32px;
+ padding: 0;
+ h1 {
+ display: none;
+ }
+ p {
+ color: @home-text-color;
+ font-size: 14px;
+ line-height: 28px;
+ }
+ .banner-btns {
+ display: block;
+ min-width: 100%;
+ white-space: nowrap;
+ text-align: center;
+
+ .banner-btn {
+ padding: 0 20px;
+ font-size: 14px;
+ }
+ }
+ .banner-promote {
+ min-width: 100%;
+ margin-top: 32px;
+ .ant-divider {
+ display: none;
+ }
+ a {
+ font-size: 14px;
+ white-space: nowrap;
+ img {
+ width: 20px;
+ }
+ }
+ }
+ }
+ }
+ .page1 {
+ min-height: 1300px;
+ .ant-row {
+ margin: 24px auto 64px;
+ > div {
+ margin-bottom: 48px;
+ }
+ }
+ }
+ .page2 {
+ min-height: 840px;
+ background: @body-background;
+ &-content {
+ box-shadow: none;
+ }
+ &-components {
+ display: none;
+ }
+ &-product {
+ min-height: auto;
+ padding: 0 16px;
+ .product-block {
+ margin-bottom: 34px;
+ padding-bottom: 35px;
+ border-bottom: 1px solid @site-border-color-split;
+ &:last-child {
+ margin-bottom: 32px;
+ border-bottom: none;
+ .block-text-wrapper {
+ height: auto;
+ }
+ }
+ .block-image-wrapper {
+ height: 88px;
+ img {
+ height: 100%;
+ }
+ }
+ .block-text-wrapper {
+ padding-bottom: 0;
+ border-bottom: none;
+ h4 {
+ margin-bottom: 4px;
+ font-size: 18px;
+ line-height: 24px;
+ }
+ p {
+ margin-bottom: 8px;
+ font-size: 12px;
+ line-height: 20px;
+ }
+ a {
+ line-height: 20px;
+ }
+ .components-button-wrapper {
+ margin-top: 16px;
+ font-size: 12px;
+ a {
+ display: block;
+ }
+ }
+ a.more-mobile-react,
+ a.more-mobile-angular {
+ margin-top: 0;
+ color: @link-color;
+ }
+ a.more-mobile-react:hover,
+ a.more-mobile-angular:hover {
+ color: #40a9ff;
+ }
+ }
+ }
+ }
+ }
+ .page3 {
+ min-height: 688px;
+ background: url('https://gw.alipayobjects.com/zos/rmsportal/qICoJIqqQRMeRGhPHBBS.svg') no-repeat;
+ background-size: cover;
+ .ant-row {
+ margin: 0 8px;
+ }
+ .page3-block {
+ margin-bottom: 32px;
+ padding: 24px;
+ background: @body-background;
+ border-radius: 4px;
+ box-shadow: 0 8px 16px rgba(174, 185, 193, 0.3);
+ &:nth-child(2) {
+ .page3-img-wrapper img {
+ display: block;
+ width: 70%;
+ margin: auto;
+ }
+ }
+ p {
+ font-size: 12px;
+ }
+ .page3-img-wrapper {
+ width: 20%;
+ img {
+ width: 100%;
+ }
+ }
+ .page3-text-wrapper {
+ width: 80%;
+ max-width: initial;
+ margin: 0;
+ padding-left: 16px;
+ }
+ }
+ }
+}
diff --git a/site/src/theme/static/rtl.less b/site/src/theme/static/rtl.less
new file mode 100644
index 000000000..57c00ce70
--- /dev/null
+++ b/site/src/theme/static/rtl.less
@@ -0,0 +1,12 @@
+[data-direction='rtl'] {
+ #_hj_feedback_container {
+ > div {
+ right: auto;
+ left: 32px;
+ }
+ }
+ .fixed-widgets {
+ right: auto;
+ left: 32px;
+ }
+}
diff --git a/site/src/theme/static/theme.less b/site/src/theme/static/theme.less
new file mode 100644
index 000000000..259d872a7
--- /dev/null
+++ b/site/src/theme/static/theme.less
@@ -0,0 +1,13 @@
+@import '../../../node_modules/ant-design-vue/es/style/themes/default.less';
+@import './colors.less';
+@import './home.less';
+
+
+@input-icon-hover-color: rgba(0,0,0,.85);
+
+@site-heading-color: @heading-color;
+@site-text-color: @heading-color;
+@site-text-color-secondary: @text-color-secondary;
+@site-border-color-split: @border-color-split;
+@site-header-box-shadow: 0 2px 8px rgba(240, 241, 242, 65);
+@site-markdown-code-bg: #f2f4f5;
diff --git a/site/src/theme/static/toc.less b/site/src/theme/static/toc.less
new file mode 100644
index 000000000..c4b6d4e8d
--- /dev/null
+++ b/site/src/theme/static/toc.less
@@ -0,0 +1,98 @@
+.toc {
+ margin: 16px 0;
+ padding-left: 0;
+ font-size: 12px;
+ list-style: none;
+ border-left: 1px solid @site-border-color-split;
+
+ .ant-row-rtl & {
+ border-right: 1px solid @site-border-color-split;
+ border-left: none;
+ }
+}
+
+ul.toc > li {
+ margin-left: 0;
+ padding-left: 0;
+ line-height: 1.5;
+ list-style: none;
+ &:not(:last-child) {
+ margin-bottom: 4px;
+ }
+
+ .ant-row-rtl & {
+ margin-right: 0;
+ padding-right: 0;
+ }
+
+ &.toc-debug a {
+ color: @purple-6;
+ }
+}
+
+.toc li > ul {
+ display: none;
+ font-size: 12px;
+ text-indent: 8px;
+}
+
+.toc a {
+ display: block;
+ width: 110px;
+ margin-left: -1px;
+ padding-left: 16px;
+ overflow: hidden;
+ color: @site-text-color;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ border-left: 1px solid transparent;
+ transition: all 0.3s ease;
+
+ .ant-row-rtl & {
+ margin-right: -1px;
+ margin-left: 0;
+ padding-right: 16px;
+ padding-left: 0;
+ border-right: 1px solid transparent;
+ border-left: none;
+ }
+}
+
+.toc a:hover {
+ color: @primary-color;
+}
+
+.toc a.current {
+ color: @primary-color;
+ border-color: @primary-color;
+}
+
+.toc-affix {
+ position: absolute;
+ top: 8px;
+ right: 20px;
+ .ant-affix {
+ z-index: 9;
+ max-height: ~'calc(100vh - 16px)';
+ overflow-x: hidden;
+ overflow-y: auto;
+ background: #fff;
+ }
+ .ant-row-rtl & {
+ right: auto;
+ left: 20px;
+ }
+}
+
+.toc-affix-bottom {
+ position: absolute;
+ right: 20px;
+ bottom: 88px;
+ .ant-affix {
+ background: @body-background;
+ }
+ .ant-row-rtl & {
+ right: auto;
+ left: 20px;
+ }
+}
diff --git a/site/src/theme/template/IconDisplay/Category.jsx b/site/src/theme/template/IconDisplay/Category.jsx
new file mode 100644
index 000000000..82da3a83d
--- /dev/null
+++ b/site/src/theme/template/IconDisplay/Category.jsx
@@ -0,0 +1,51 @@
+import { message } from 'ant-design-vue';
+import { defineComponent } from 'vue';
+import CopyableIcon from './CopyableIcon.vue';
+
+const Category = defineComponent({
+ props: ['icons', 'title', 'newIcons', 'theme'],
+ data() {
+ return {
+ justCopied: null,
+ };
+ },
+ methods: {
+ onCopied(type, text) {
+ message.success(
+
+ {text}
copied 🎉
+ ,
+ );
+ this.justCopied = type;
+ setTimeout(() => {
+ this.justCopied = null;
+ }, 2000);
+ },
+ },
+ render() {
+ const { icons, title, theme, newIcons } = this.$props;
+ const items = icons.map(name => {
+ return (
+ = 0}
+ justCopied={this.justCopied}
+ onCopied={this.onCopied}
+ />
+ );
+ });
+ return (
+
+
+ {this.$t(`app.docs.components.icon.category.${title}`)}
+
+
+
+ );
+ },
+});
+
+export default Category;
diff --git a/site/src/theme/template/IconDisplay/CopyableIcon.vue b/site/src/theme/template/IconDisplay/CopyableIcon.vue
new file mode 100644
index 000000000..88d69fb60
--- /dev/null
+++ b/site/src/theme/template/IconDisplay/CopyableIcon.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+ {{ type }}
+
+
+
+
+
diff --git a/site/src/theme/template/IconDisplay/fields.js b/site/src/theme/template/IconDisplay/fields.js
new file mode 100644
index 000000000..ef1940c99
--- /dev/null
+++ b/site/src/theme/template/IconDisplay/fields.js
@@ -0,0 +1,218 @@
+import * as AntdIcons from '@ant-design/icons-vue';
+
+const all = Object.keys(AntdIcons)
+ .map(n => n.replace(/(Outlined|Filled|TwoTone)$/, ''))
+ .filter((n, i, arr) => arr.indexOf(n) === i);
+
+const direction = [
+ 'StepBackward',
+ 'StepForward',
+ 'FastBackward',
+ 'FastForward',
+ 'Shrink',
+ 'ArrowsAlt',
+ 'Down',
+ 'Up',
+ 'Left',
+ 'Right',
+ 'CaretUp',
+ 'CaretDown',
+ 'CaretLeft',
+ 'CaretRight',
+ 'UpCircle',
+ 'DownCircle',
+ 'LeftCircle',
+ 'RightCircle',
+ 'DoubleRight',
+ 'DoubleLeft',
+ 'VerticalLeft',
+ 'VerticalRight',
+ 'VerticalAlignTop',
+ 'VerticalAlignMiddle',
+ 'VerticalAlignBottom',
+ 'Forward',
+ 'Backward',
+ 'Rollback',
+ 'Enter',
+ 'Retweet',
+ 'Swap',
+ 'SwapLeft',
+ 'SwapRight',
+ 'ArrowUp',
+ 'ArrowDown',
+ 'ArrowLeft',
+ 'ArrowRight',
+ 'PlayCircle',
+ 'UpSquare',
+ 'DownSquare',
+ 'LeftSquare',
+ 'RightSquare',
+ 'Login',
+ 'Logout',
+ 'MenuFold',
+ 'MenuUnfold',
+ 'BorderBottom',
+ 'BorderHorizontal',
+ 'BorderInner',
+ 'BorderOuter',
+ 'BorderLeft',
+ 'BorderRight',
+ 'BorderTop',
+ 'BorderVerticle',
+ 'PicCenter',
+ 'PicLeft',
+ 'PicRight',
+ 'RadiusBottomleft',
+ 'RadiusBottomright',
+ 'RadiusUpleft',
+ 'RadiusUpright',
+ 'Fullscreen',
+ 'FullscreenExit',
+];
+
+const suggestion = [
+ 'Question',
+ 'QuestionCircle',
+ 'Plus',
+ 'PlusCircle',
+ 'Pause',
+ 'PauseCircle',
+ 'Minus',
+ 'MinusCircle',
+ 'PlusSquare',
+ 'MinusSquare',
+ 'Info',
+ 'InfoCircle',
+ 'Exclamation',
+ 'ExclamationCircle',
+ 'Close',
+ 'CloseCircle',
+ 'CloseSquare',
+ 'Check',
+ 'CheckCircle',
+ 'CheckSquare',
+ 'ClockCircle',
+ 'Warning',
+ 'IssuesClose',
+ 'Stop',
+];
+
+const editor = [
+ 'Edit',
+ 'Form',
+ 'Copy',
+ 'Scissor',
+ 'Delete',
+ 'Snippets',
+ 'Diff',
+ 'Highlight',
+ 'AlignCenter',
+ 'AlignLeft',
+ 'AlignRight',
+ 'BgColors',
+ 'Bold',
+ 'Italic',
+ 'Underline',
+ 'Strikethrough',
+ 'Redo',
+ 'Undo',
+ 'ZoomIn',
+ 'ZoomOut',
+ 'FontColors',
+ 'FontSize',
+ 'LineHeight',
+ 'Dash',
+ 'SmallDash',
+ 'SortAscending',
+ 'SortDescending',
+ 'Drag',
+ 'OrderedList',
+ 'UnorderedList',
+ 'RadiusSetting',
+ 'ColumnWidth',
+ 'ColumnHeight',
+];
+
+const data = [
+ 'AreaChart',
+ 'PieChart',
+ 'BarChart',
+ 'DotChart',
+ 'LineChart',
+ 'RadarChart',
+ 'HeatMap',
+ 'Fall',
+ 'Rise',
+ 'Stock',
+ 'BoxPlot',
+ 'Fund',
+ 'Sliders',
+];
+
+const logo = [
+ 'Android',
+ 'Apple',
+ 'Windows',
+ 'Ie',
+ 'Chrome',
+ 'Github',
+ 'Aliwangwang',
+ 'Dingding',
+ 'WeiboSquare',
+ 'WeiboCircle',
+ 'TaobaoCircle',
+ 'Html5',
+ 'Weibo',
+ 'Twitter',
+ 'Wechat',
+ 'Youtube',
+ 'AlipayCircle',
+ 'Taobao',
+ 'Skype',
+ 'Qq',
+ 'MediumWorkmark',
+ 'Gitlab',
+ 'Medium',
+ 'Linkedin',
+ 'GooglePlus',
+ 'Dropbox',
+ 'Facebook',
+ 'Codepen',
+ 'CodeSandbox',
+ 'CodeSandboxCircle',
+ 'Amazon',
+ 'Google',
+ 'CodepenCircle',
+ 'Alipay',
+ 'AntDesign',
+ 'AntCloud',
+ 'Aliyun',
+ 'Zhihu',
+ 'Slack',
+ 'SlackSquare',
+ 'Behance',
+ 'BehanceSquare',
+ 'Dribbble',
+ 'DribbbleSquare',
+ 'Instagram',
+ 'Yuque',
+ 'Alibaba',
+ 'Yahoo',
+ 'Reddit',
+ 'Sketch',
+];
+
+const datum = [...direction, ...suggestion, ...editor, ...data, ...logo];
+
+const other = all.filter(n => !datum.includes(n));
+
+export const categories = {
+ direction,
+ suggestion,
+ editor,
+ data,
+ logo,
+ other,
+};
+
+export default categories;
diff --git a/site/src/theme/template/IconDisplay/index.jsx b/site/src/theme/template/IconDisplay/index.jsx
new file mode 100644
index 000000000..216590f0f
--- /dev/null
+++ b/site/src/theme/template/IconDisplay/index.jsx
@@ -0,0 +1,82 @@
+import Icon, * as AntdIcons from '@ant-design/icons-vue';
+import { categories } from './fields';
+import { FilledIcon, OutlinedIcon, TwoToneIcon } from './themeIcons';
+import Category from './Category';
+import { Radio } from 'ant-design-vue';
+import { defineComponent } from 'vue';
+
+const ThemeType = {
+ Filled: 'Filled',
+ Outlined: 'Outlined',
+ TwoTone: 'TwoTone',
+};
+
+const allIcons = AntdIcons;
+
+const IconDisplay = defineComponent({
+ cagetories: categories,
+ components: {
+ ARadio: Radio,
+ ARadioGroup: Radio.Group,
+ ARadioButton: Radio.Button,
+ },
+ newIconNames: [],
+ data() {
+ return {
+ theme: ThemeType.Outlined,
+ };
+ },
+ methods: {
+ handleChangeTheme(e) {
+ this.theme = e.target.value;
+ },
+
+ renderCategories() {
+ const { theme } = this;
+
+ return Object.keys(categories)
+ .map(key => {
+ let iconList = categories[key];
+
+ return {
+ category: key,
+ icons: iconList
+ .map(iconName => iconName + theme)
+ .filter(iconName => allIcons[iconName]),
+ };
+ })
+ .filter(({ icons }) => !!icons.length)
+ .map(({ category, icons }) => (
+
+ ));
+ },
+ },
+
+ render() {
+ return (
+
+
{this.$t('app.docs.components.icon.pick-theme')}
+
+
+ {this.$t('app.docs.components.icon.outlined')}
+
+
+ {this.$t('app.docs.components.icon.filled')}
+
+
+ {this.$t('app.docs.components.icon.two-tone')}
+
+
+ {this.renderCategories()}
+
+ );
+ },
+});
+
+export default IconDisplay;
diff --git a/site/src/theme/template/IconDisplay/themeIcons.jsx b/site/src/theme/template/IconDisplay/themeIcons.jsx
new file mode 100644
index 000000000..bf1daa199
--- /dev/null
+++ b/site/src/theme/template/IconDisplay/themeIcons.jsx
@@ -0,0 +1,44 @@
+export const FilledIcon = {
+ render() {
+ const path =
+ 'M864 64H160C107 64 64 107 64 160v' +
+ '704c0 53 43 96 96 96h704c53 0 96-43 96-96V16' +
+ '0c0-53-43-96-96-96z';
+ return (
+
+
+
+ );
+ },
+};
+
+export const OutlinedIcon = {
+ render() {
+ const path =
+ 'M864 64H160C107 64 64 107 64 160v7' +
+ '04c0 53 43 96 96 96h704c53 0 96-43 96-96V160c' +
+ '0-53-43-96-96-96z m-12 800H172c-6.6 0-12-5.4-' +
+ '12-12V172c0-6.6 5.4-12 12-12h680c6.6 0 12 5.4' +
+ ' 12 12v680c0 6.6-5.4 12-12 12z';
+ return (
+
+
+
+ );
+ },
+};
+
+export const TwoToneIcon = {
+ render() {
+ const path =
+ 'M16 512c0 273.932 222.066 496 496 49' +
+ '6s496-222.068 496-496S785.932 16 512 16 16 238.' +
+ '066 16 512z m496 368V144c203.41 0 368 164.622 3' +
+ '68 368 0 203.41-164.622 368-368 368z';
+ return (
+
+
+
+ );
+ },
+};
diff --git a/site/src/theme/zh-CN.js b/site/src/theme/zh-CN.js
new file mode 100644
index 000000000..ee558be3f
--- /dev/null
+++ b/site/src/theme/zh-CN.js
@@ -0,0 +1,145 @@
+export default {
+ 'app.theme.switch.default': '默认主题',
+ 'app.theme.switch.dark': '暗黑主题',
+ 'app.theme.switch.compact': '紧凑主题',
+ 'app.header.search': '全文本搜索...',
+ 'app.header.menu.documentation': '文档',
+ 'app.header.menu.components': '组件',
+ 'app.header.menu.spec': '设计',
+ 'app.header.menu.resource': '资源',
+ 'app.header.menu.more': '更多',
+ 'app.header.menu.mobile': '移动版',
+ 'app.header.menu.pro.v4': 'Ant Design Pro',
+ 'app.header.menu.charts': 'Ant Design Charts',
+ 'app.header.menu.ecosystem': '生态',
+ 'app.header.menu.store': '商店',
+ 'app.header.lang': 'English',
+ 'app.content.edit-page': '在 GitHub 上编辑此页!',
+ 'app.content.edit-demo': '在 GitHub 上编辑此示例!',
+ 'app.content.contributors': '文档贡献者',
+ 'app.component.examples': '代码演示',
+ 'app.component.examples.expand': '展开全部代码',
+ 'app.component.examples.collapse': '收起全部代码',
+ 'app.component.examples.visible': '显示调试专用演示',
+ 'app.component.examples.hide': '隐藏调试专用演示',
+ 'app.demo.debug': '此演示仅供调试,线上不会展示',
+ 'app.demo.type.js': '切换到 TypeScript',
+ 'app.demo.type.ts': '切换到 JavaScript',
+ 'app.demo.copy': '复制代码',
+ 'app.demo.copied': '复制成功',
+ 'app.demo.code.show': '显示代码',
+ 'app.demo.code.hide': '收起代码',
+ 'app.demo.codepen': '在 CodePen 中打开',
+ 'app.demo.codesandbox': '在 CodeSandbox 中打开',
+ 'app.demo.stackblitz': '在 Stackblitz 中打开',
+ 'app.demo.riddle': '在 Riddle 中打开',
+ 'app.home.introduce': '企业级产品设计体系,创造高效愉悦的工作体验',
+ 'app.home.recommend': '精彩推荐',
+ 'app.home.popularize': '推广',
+ 'app.home.design-and-framework': '设计语言与研发框架',
+ 'app.home.design-values': '设计价值观',
+ 'app.home.design-values-description':
+ '这是 Ant Design 评价设计好坏的内在标准。基于「每个人都追求快乐工作」这一假定,我们在「确定性」和「自然」的基础上,新增「意义感」和「生长性」两个价值观,指引每个设计者做更好地判断和决策。',
+ 'app.home.certainty': '确定性',
+ 'app.home.meaningful': '意义感',
+ 'app.home.growth': '生长性',
+ 'app.home.natural': '自然',
+ 'app.home.design-guide': '设计指引',
+ 'app.home.components': '组件库',
+ 'app.home.detail': '查看详情',
+ 'app.home.global-style': '全局样式',
+ 'app.home.design-patterns': '设计模式',
+ 'app.home.more': '更多内容',
+ 'app.home.play-video': '播放视频',
+ 'app.home.qr': '4.0 正式版发布',
+ 'app.home.qr.desc': '扫描上方二维码查看',
+ 'app.home.getting-started': '开始使用',
+ 'app.home.design-language': '设计语言',
+ 'app.home.product-antv-slogan': '全新一代数据可视化解决方案',
+ 'app.home.product-pro-slogan': '开箱即用的中台前端/设计解决方案',
+ 'app.home.product-mobile-slogan': '基于 Preact / React / React Native 的 UI 组件库',
+ 'app.home.product-hitu': '海兔',
+ 'app.home.product-hitu-slogan': '全新一代图形化解决方案',
+ 'app.home.product-kitchen-slogan': '一款为设计者提升工作效率的 Sketch 工具集',
+ 'app.home.product-icons-slogan': '一整套优质的图标集',
+ 'app.home.view-more': '查看全部',
+ 'app.footer.repo': 'GitHub 仓库',
+ 'app.footer.awesome': 'Awesome Ant Design',
+ 'app.footer.course': 'Ant Design 实战教程',
+ 'app.footer.chinamirror': '国内镜像站点 🇨🇳',
+ 'app.footer.primary-color-changing': '正在修改主题色...',
+ 'app.footer.primary-color-changed': '修改主题色成功!',
+ 'app.footer.kitchen': 'Sketch 工具集',
+ 'app.footer.landing': '首页模板集',
+ 'app.footer.scaffold': '脚手架',
+ 'app.footer.scaffolds': '脚手架市场',
+ 'app.footer.dev-tools': '开发工具',
+ 'app.footer.umi': 'React 应用开发框架',
+ 'app.footer.dumi': '组件/文档研发工具',
+ 'app.footer.remax': '全新的小程序开发体验',
+ 'app.footer.hooks': 'React Hooks 库',
+ 'app.footer.resources': '相关资源',
+ 'app.footer.data-vis': '数据可视化',
+ 'app.footer.eggjs': '企业级 Node 开发框架',
+ 'app.footer.motion': '设计动效',
+ 'app.footer.antd-library': 'Axure 部件库',
+ 'app.footer.design-resources': '设计资源下载',
+ 'app.footer.antux': '页面逻辑素材',
+ 'app.footer.community': '社区',
+ 'app.footer.help': '帮助',
+ 'app.footer.change-log': '更新日志',
+ 'app.footer.faq': '常见问题',
+ 'app.footer.feedback': '反馈和建议',
+ 'app.footer.stackoverflow': 'StackOverflow',
+ 'app.footer.segmentfault': 'SegmentFault',
+ 'app.footer.discussions': '讨论区',
+ 'app.footer.bug-report': '报告 Bug',
+ 'app.footer.issues': '讨论列表',
+ 'app.footer.version': '文档版本:',
+ 'app.footer.author': '蚂蚁金服体验技术部出品 @ XTech',
+ 'app.footer.work_with_us': '加入我们',
+ 'app.footer.more-product': '更多产品',
+ 'app.footer.company': 'XTech',
+ 'app.footer.ant-design': '蚂蚁 UI 体系',
+ 'app.footer.yuque': '语雀',
+ 'app.footer.yuque.slogan': '知识创作与分享工具',
+ 'app.footer.fengdie': '云凤蝶',
+ 'app.footer.fengdie.slogan': '移动建站平台',
+ 'app.footer.antv.slogan': '数据可视化',
+ 'app.footer.egg.slogan': '企业级 Node 开发框架',
+ 'app.footer.zhihu': 'Ant Design 专栏',
+ 'app.footer.zhihu.xtech': '体验科技专栏',
+ 'app.footer.seeconf': '蚂蚁体验科技大会',
+ 'app.footer.xtech': '蚂蚁体验科技',
+ 'app.footer.xtech.slogan': '让用户体验美好',
+ 'app.publish.title': 'antd@3.0.0 发布!🎉 🎉 🎉',
+ 'app.publish.greeting': '你好,',
+ 'app.publish.intro': ' 已正式发布,欢迎升级。',
+ 'app.publish.old-version-guide': '如果您还需要使用旧版,请查阅 ',
+ 'app.publish.old-version-tips': ',也可通过页面右上角的文档版本选择框进行切换。',
+ 'app.docs.color.pick-primary': '选择你的主色',
+ 'app.docs.color.pick-background': '选择你的背景色',
+ 'app.docs.components.icon.search.placeholder': '在此搜索图标,点击图标可复制代码',
+ 'app.docs.components.icon.pick-theme': '选择图标主题风格',
+ 'app.docs.components.icon.outlined': '线框风格',
+ 'app.docs.components.icon.filled': '实底风格',
+ 'app.docs.components.icon.two-tone': '双色风格',
+ 'app.docs.components.icon.category.direction': '方向性图标',
+ 'app.docs.components.icon.category.suggestion': '提示建议性图标',
+ 'app.docs.components.icon.category.editor': '编辑类图标',
+ 'app.docs.components.icon.category.data': '数据类图标',
+ 'app.docs.components.icon.category.other': '网站通用图标',
+ 'app.docs.components.icon.category.logo': '品牌和标识',
+ 'app.docs.components.icon.pic-searcher.intro': 'AI 截图搜索上线了,快来体验吧!🎉',
+ 'app.docs.components.icon.pic-searcher.title': '上传图片搜索图标',
+ 'app.docs.components.icon.pic-searcher.upload-text': '点击/拖拽/粘贴上传图片',
+ 'app.docs.components.icon.pic-searcher.upload-hint':
+ '我们会通过上传的图片进行匹配,得到最相似的图标',
+ 'app.docs.components.icon.pic-searcher.server-error': '识别服务暂不可用',
+ 'app.docs.components.icon.pic-searcher.matching': '匹配中...',
+ 'app.docs.components.icon.pic-searcher.modelloading': '神经网络模型加载中...',
+ 'app.docs.components.icon.pic-searcher.result-tip': '为您匹配到以下图标:',
+ 'app.docs.components.icon.pic-searcher.th-icon': '图标',
+ 'app.docs.components.icon.pic-searcher.th-score': '匹配度',
+ 'app.components.overview.search': '搜索组件',
+};
diff --git a/site/src/typings.d.ts b/site/src/typings.d.ts
new file mode 100644
index 000000000..9a5389704
--- /dev/null
+++ b/site/src/typings.d.ts
@@ -0,0 +1,25 @@
+interface Window {
+ docsearch: any;
+ notBlockEnabled: any;
+}
+
+interface Header {
+ level: number;
+ title: string;
+ slug: string;
+ content: string;
+}
+interface PageData {
+ title: string;
+ description: string;
+ headers: Header[];
+ frontmatter: Record;
+}
+declare module '*.md' {
+ import type { DefineComponent } from 'vue';
+ // eslint-disable-next-line @typescript-eslint/ban-types
+ const component: DefineComponent & { readonly pageDate?: PageData };
+ export default component;
+}
+
+declare module '*.svg';
diff --git a/site/src/utils/request.js b/site/src/utils/request.js
new file mode 100644
index 000000000..ea35424b8
--- /dev/null
+++ b/site/src/utils/request.js
@@ -0,0 +1,56 @@
+/**
+ * request 网络请求工具
+ * 更详细的 api 文档: https://github.com/umijs/umi-request
+ */
+import { extend } from 'umi-request';
+import { notification } from 'ant-design-vue';
+const codeMessage = {
+ 200: '服务器成功返回请求的数据。',
+ 201: '新建或修改数据成功。',
+ 202: '一个请求已经进入后台排队(异步任务)。',
+ 204: '删除数据成功。',
+ 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
+ 401: '用户没有权限(令牌、用户名、密码错误)。',
+ 403: '用户得到授权,但是访问是被禁止的。',
+ 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
+ 406: '请求的格式不可得。',
+ 410: '请求的资源被永久删除,且不会再得到的。',
+ 422: '当创建一个对象时,发生一个验证错误。',
+ 500: '服务器发生错误,请检查服务器。',
+ 502: '网关错误。',
+ 503: '服务不可用,服务器暂时过载或维护。',
+ 504: '网关超时。',
+};
+/**
+ * 异常处理程序
+ */
+
+const errorHandler = error => {
+ const { response } = error;
+
+ if (response && response.status) {
+ const errorText = codeMessage[response.status] || response.statusText;
+ const { status, url } = response;
+ notification.error({
+ message: `请求错误 ${status}: ${url}`,
+ description: errorText,
+ });
+ } else if (!response) {
+ notification.error({
+ description: '您的网络发生异常,无法连接服务器',
+ message: '网络异常',
+ });
+ }
+
+ return response;
+};
+/**
+ * 配置request请求时的默认参数
+ */
+
+const request = extend({
+ errorHandler,
+ // 默认错误处理
+ credentials: 'include', // 默认请求是否带上cookie
+});
+export default request;
diff --git a/site/src/utils/util.js b/site/src/utils/util.js
new file mode 100644
index 000000000..ea1d5367b
--- /dev/null
+++ b/site/src/utils/util.js
@@ -0,0 +1,36 @@
+export function isZhCN(name) {
+ return /-cn\/?$/.test(name);
+}
+
+export function isLocalStorageNameSupported() {
+ const testKey = 'test';
+ const storage = window.localStorage;
+ try {
+ storage.setItem(testKey, '1');
+ storage.removeItem(testKey);
+ return true;
+ } catch (error) {
+ return false;
+ }
+}
+
+export function getLocalizedPathname(path, zhCN, query = {}, hash) {
+ const pathname = path.startsWith('/') ? path : `/${path}`;
+ let fullPath;
+ if (!zhCN) {
+ // to enUS
+ fullPath = /\/?index-cn/.test(pathname) ? '/' : pathname.replace('-cn', '');
+ } else if (pathname === '/') {
+ fullPath = '/index-cn';
+ } else if (pathname.endsWith('/')) {
+ fullPath = pathname.replace(/\/$/, '-cn/');
+ } else {
+ fullPath = `${pathname}-cn`;
+ }
+
+ if (hash) {
+ const localHash = hash[zhCN ? 'zhCN' : 'enUS'];
+ fullPath += `#${localHash}`;
+ }
+ return { path: fullPath, query };
+}
diff --git a/site/src/utils/util.ts b/site/src/utils/util.ts
new file mode 100644
index 000000000..6b1156746
--- /dev/null
+++ b/site/src/utils/util.ts
@@ -0,0 +1,47 @@
+export function isZhCN(name: string): boolean {
+ return /-cn\/?$/.test(name);
+}
+
+export function isLocalStorageNameSupported() {
+ const testKey = 'test';
+ const storage = window.localStorage;
+ try {
+ storage.setItem(testKey, '1');
+ storage.removeItem(testKey);
+ return true;
+ } catch (error) {
+ return false;
+ }
+}
+
+export function getLocalizedPathname(
+ path: string,
+ zhCN?: boolean,
+ query = {},
+ hash?: {
+ zhCN: string;
+ enUS: string;
+ },
+): {
+ path: string;
+ query: Record;
+} {
+ const pathname = path.startsWith('/') ? path : `/${path}`;
+ let fullPath;
+ if (!zhCN) {
+ // to enUS
+ fullPath = /\/?index-cn/.test(pathname) ? '/' : pathname.replace('-cn', '');
+ } else if (pathname === '/') {
+ fullPath = '/index-cn';
+ } else if (pathname.endsWith('/')) {
+ fullPath = pathname.replace(/\/$/, '-cn/');
+ } else {
+ fullPath = `${pathname}-cn`;
+ }
+
+ if (hash) {
+ const localHash = hash[zhCN ? 'zhCN' : 'enUS'];
+ fullPath += `#${localHash}`;
+ }
+ return { path: fullPath, query };
+}
diff --git a/site/src/views/ComponentOverview.less b/site/src/views/ComponentOverview.less
new file mode 100644
index 000000000..b067d56ff
--- /dev/null
+++ b/site/src/views/ComponentOverview.less
@@ -0,0 +1,45 @@
+@import '../theme/static/theme.less';
+
+.components-overview {
+ padding: 0;
+
+ &-group-title {
+ margin-bottom: 24px !important;
+ }
+
+ &-title {
+ overflow: hidden;
+ color: @text-color;
+ text-overflow: ellipsis;
+ }
+ &-img {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 152px;
+ }
+ &-card {
+ cursor: pointer;
+ transition: all @animation-duration-base @ease-in-out;
+ &:hover {
+ box-shadow: @shadow-1-down;
+ }
+ }
+}
+
+.components-overview-search {
+ width: 100%;
+ padding: 0;
+ font-size: 20px;
+ border: 0;
+ box-shadow: none;
+
+ input {
+ color: rgba(0, 0, 0, 0.85);
+ font-size: 20px;
+ }
+
+ .anticon {
+ color: #bbb;
+ }
+}
diff --git a/site/src/views/ComponentOverview.vue b/site/src/views/ComponentOverview.vue
new file mode 100644
index 000000000..c8a460ea0
--- /dev/null
+++ b/site/src/views/ComponentOverview.vue
@@ -0,0 +1,103 @@
+
+
+ {{ isZhCN ? '组件总览' : 'Overview' }}
+
+
+ ant-design-vue
+ {{
+ isZhCN
+ ? '为 Web 应用提供了丰富的基础 UI 组件,我们还将持续探索企业级应用的最佳 UI 实践。'
+ : 'provides plenty of UI components to enrich your web applications, and we will improve components experience consistently. '
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ isZhCN ? group.title : group.enTitle }}
+ {{ group.children.length }}
+
+
+
+
+
+
+
+
+
+ {{ component.title }}
+ {{ isZhCN ? component.subtitle : '' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/src/views/jobs.vue b/site/src/views/jobs.vue
new file mode 100644
index 000000000..b7d10aca8
--- /dev/null
+++ b/site/src/views/jobs.vue
@@ -0,0 +1,167 @@
+
+
+
+ 如果您正在寻找顶级的 Vue.js 开发者,请联系
+
+ 我们
+
+ !!!
+
+
+
+
+
+
+
工作地点:{{ item.location }}
+
职位描述
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/src/views/user/login/components/Login.vue b/site/src/views/user/login/components/Login.vue
new file mode 100644
index 000000000..9da8fa88d
--- /dev/null
+++ b/site/src/views/user/login/components/Login.vue
@@ -0,0 +1,96 @@
+
+
+
+
diff --git a/site/src/views/user/login/components/LoginItem.jsx b/site/src/views/user/login/components/LoginItem.jsx
new file mode 100644
index 000000000..57a9a4720
--- /dev/null
+++ b/site/src/views/user/login/components/LoginItem.jsx
@@ -0,0 +1,43 @@
+import ItemMap from './map';
+import WrapFormItem from './WrapFormItem';
+
+const LoginItem = {};
+Object.keys(ItemMap).forEach(key => {
+ const item = ItemMap[key];
+ LoginItem[key] = {
+ props: {
+ defaultValue: String,
+ name: String,
+ placeholder: String,
+ rules: {
+ type: Array,
+ default: () => item.rules,
+ },
+ getCaptchaButtonText: String,
+ getCaptchaSecondText: String,
+ getCaptcha: Function,
+ countDown: Number,
+ customprops: {
+ type: Object,
+ default: () => item.props,
+ },
+ type: {
+ type: String,
+ default: key,
+ },
+ },
+ inject: {
+ loginContext: { default: () => ({ form: {}, updateActive: () => {} }) },
+ },
+ render() {
+ const {
+ loginContext: { form, updateActive },
+ $props,
+ $listeners,
+ } = this;
+ return ;
+ },
+ };
+});
+
+export default LoginItem;
diff --git a/site/src/views/user/login/components/LoginSubmit.vue b/site/src/views/user/login/components/LoginSubmit.vue
new file mode 100644
index 000000000..1fbb4eec2
--- /dev/null
+++ b/site/src/views/user/login/components/LoginSubmit.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/site/src/views/user/login/components/LoginTab.vue b/site/src/views/user/login/components/LoginTab.vue
new file mode 100644
index 000000000..155f9158f
--- /dev/null
+++ b/site/src/views/user/login/components/LoginTab.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
diff --git a/site/src/views/user/login/components/WrapFormItem.vue b/site/src/views/user/login/components/WrapFormItem.vue
new file mode 100644
index 000000000..e2375d1a2
--- /dev/null
+++ b/site/src/views/user/login/components/WrapFormItem.vue
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+ {{ count ? `${count} ${getCaptchaSecondText}` : getCaptchaButtonText }}
+
+
+
+
+
+
+
+
+
diff --git a/site/src/views/user/login/components/index.js b/site/src/views/user/login/components/index.js
new file mode 100644
index 000000000..fec0c5208
--- /dev/null
+++ b/site/src/views/user/login/components/index.js
@@ -0,0 +1,12 @@
+import Login from './Login';
+import LoginSubmit from './LoginSubmit';
+import LoginTab from './LoginTab';
+import LoginItem from './LoginItem';
+
+Login.Tab = LoginTab;
+Login.Submit = LoginSubmit;
+Object.keys(LoginItem).forEach(item => {
+ Login[item] = LoginItem[item];
+});
+
+export default Login;
diff --git a/site/src/views/user/login/components/index.less b/site/src/views/user/login/components/index.less
new file mode 100644
index 000000000..8cc64558a
--- /dev/null
+++ b/site/src/views/user/login/components/index.less
@@ -0,0 +1,53 @@
+@import '~ant-design-vue/es/style/themes/default.less';
+
+.login {
+ :global {
+ .ant-tabs .ant-tabs-bar {
+ margin-bottom: 24px;
+ text-align: center;
+ border-bottom: 0;
+ }
+
+ .ant-form-item {
+ margin: 0 2px 24px;
+ }
+ }
+
+ .getCaptcha {
+ display: block;
+ width: 100%;
+ }
+
+ .icon {
+ margin-left: 16px;
+ color: rgba(0, 0, 0, 0.2);
+ font-size: 24px;
+ vertical-align: middle;
+ cursor: pointer;
+ transition: color 0.3s;
+
+ &:hover {
+ color: @primary-color;
+ }
+ }
+
+ .other {
+ margin-top: 24px;
+ line-height: 22px;
+ text-align: left;
+
+ .register {
+ float: right;
+ }
+ }
+
+ .prefixIcon {
+ color: @disabled-color;
+ font-size: @font-size-base;
+ }
+
+ .submit {
+ width: 100%;
+ margin-top: 24px;
+ }
+}
diff --git a/site/src/views/user/login/components/map.jsx b/site/src/views/user/login/components/map.jsx
new file mode 100644
index 000000000..86a2eb46e
--- /dev/null
+++ b/site/src/views/user/login/components/map.jsx
@@ -0,0 +1,83 @@
+import styles from './index.less';
+import { UserOutlined, MailOutlined, LockOutlined, MobileOutlined } from '@ant-design/icons-vue';
+
+export default {
+ UserName: {
+ props: {
+ size: 'large',
+ id: 'userName',
+ // eslint-disable-next-line no-unused-vars
+ prefix: h => ,
+ placeholder: 'please enter userName',
+ },
+ rules: [
+ {
+ required: true,
+ message: 'Please enter userName!',
+ },
+ ],
+ },
+ Email: {
+ props: {
+ size: 'large',
+ id: 'email',
+ // eslint-disable-next-line no-unused-vars
+ prefix: h => ,
+ placeholder: 'please enter email',
+ },
+ rules: [
+ {
+ required: true,
+ message: 'Please enter email!',
+ },
+ ],
+ },
+ Password: {
+ props: {
+ size: 'large',
+ // eslint-disable-next-line no-unused-vars
+ prefix: h => ,
+ type: 'password',
+ id: 'password',
+ placeholder: 'Please enter password!',
+ },
+ rules: [
+ {
+ required: true,
+ message: 'Please enter password!',
+ },
+ ],
+ },
+ Mobile: {
+ props: {
+ size: 'large',
+ // eslint-disable-next-line no-unused-vars
+ prefix: h => ,
+ placeholder: 'mobile number',
+ },
+ rules: [
+ {
+ required: true,
+ message: 'Please enter mobile number!',
+ },
+ {
+ pattern: /^1\d{10}$/,
+ message: 'Wrong mobile number format!',
+ },
+ ],
+ },
+ Captcha: {
+ props: {
+ size: 'large',
+ // eslint-disable-next-line no-unused-vars
+ prefix: h => ,
+ placeholder: 'captcha',
+ },
+ rules: [
+ {
+ required: true,
+ message: 'Please enter Captcha!',
+ },
+ ],
+ },
+};
diff --git a/site/src/views/user/login/index.vue b/site/src/views/user/login/index.vue
new file mode 100644
index 000000000..2fb933dff
--- /dev/null
+++ b/site/src/views/user/login/index.vue
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('userandlogin.login.login') }}
+
+
+
+ {{ $t('userandlogin.login.signup') }}
+
+
+
+
+
+
+
+
+
diff --git a/site/src/views/user/login/locales/en-US.js b/site/src/views/user/login/locales/en-US.js
new file mode 100644
index 000000000..0e63b03a9
--- /dev/null
+++ b/site/src/views/user/login/locales/en-US.js
@@ -0,0 +1,77 @@
+export default {
+ 'userandlogin.login.userName': 'userName',
+ 'userandlogin.login.password': 'password',
+ 'userandlogin.login.message-invalid-credentials': 'Invalid username or password',
+ 'userandlogin.login.message-invalid-verification-code': 'Invalid verification code',
+ 'userandlogin.login.tab-login-credentials': 'Credentials',
+ 'userandlogin.login.tab-login-mobile': 'Mobile number',
+ 'userandlogin.login.remember-me': 'Remember me',
+ 'userandlogin.login.forgot-password': 'Forgot your password?',
+ 'userandlogin.login.sign-in-with': 'Sign in with',
+ 'userandlogin.login.signup': 'Sign up',
+ 'userandlogin.login.login': 'Login',
+ 'userandlogin.register.register': 'Register',
+ 'userandlogin.register.get-verification-code': 'Get code',
+ 'userandlogin.register.sign-in': 'Already have an account?',
+ 'userandlogin.register-result.msg': 'Account:registered at {email}',
+ 'userandlogin.register-result.activation-email':
+ 'The activation email has been sent to your email address and is valid for 24 hours. Please log in to the email in time and click on the link in the email to activate the account.',
+ 'userandlogin.register-result.back-home': 'Back to home',
+ 'userandlogin.register-result.view-mailbox': 'View mailbox',
+ 'userandlogin.email.required': 'Please enter your email!',
+ 'userandlogin.email.wrong-format': 'The email address is in the wrong format!',
+ 'userandlogin.userName.required': 'Please enter your userName!',
+ 'userandlogin.password.required': 'Please enter your password!',
+ 'userandlogin.password.twice': 'The passwords entered twice do not match!',
+ 'userandlogin.strength.msg':
+ "Please enter at least 6 characters and don't use passwords that are easy to guess.",
+ 'userandlogin.strength.strong': 'Strength: strong',
+ 'userandlogin.strength.medium': 'Strength: medium',
+ 'userandlogin.strength.short': 'Strength: too short',
+ 'userandlogin.confirm-password.required': 'Please confirm your password!',
+ 'userandlogin.phone-number.required': 'Please enter your phone number!',
+ 'userandlogin.phone-number.wrong-format': 'Malformed phone number!',
+ 'userandlogin.verification-code.required': 'Please enter the verification code!',
+ 'userandlogin.title.required': 'Please enter a title',
+ 'userandlogin.date.required': 'Please select the start and end date',
+ 'userandlogin.goal.required': 'Please enter a description of the goal',
+ 'userandlogin.standard.required': 'Please enter a metric',
+ 'userandlogin.form.get-captcha': 'Get Captcha',
+ 'userandlogin.captcha.second': 'sec',
+ 'userandlogin.form.optional': ' (optional) ',
+ 'userandlogin.form.submit': 'Submit',
+ 'userandlogin.form.save': 'Save',
+ 'userandlogin.email.placeholder': 'Email',
+ 'userandlogin.password.placeholder': 'Password',
+ 'userandlogin.confirm-password.placeholder': 'Confirm password',
+ 'userandlogin.phone-number.placeholder': 'Phone number',
+ 'userandlogin.verification-code.placeholder': 'Verification code',
+ 'userandlogin.title.label': 'Title',
+ 'userandlogin.title.placeholder': 'Give the target a name',
+ 'userandlogin.date.label': 'Start and end date',
+ 'userandlogin.placeholder.start': 'Start date',
+ 'userandlogin.placeholder.end': 'End date',
+ 'userandlogin.goal.label': 'Goal description',
+ 'userandlogin.goal.placeholder': 'Please enter your work goals',
+ 'userandlogin.standard.label': 'Metrics',
+ 'userandlogin.standard.placeholder': 'Please enter a metric',
+ 'userandlogin.client.label': 'Client',
+ 'userandlogin.label.tooltip': 'Target service object',
+ 'userandlogin.client.placeholder':
+ 'Please describe your customer service, internal customers directly @ Name / job number',
+ 'userandlogin.invites.label': 'Inviting critics',
+ 'userandlogin.invites.placeholder':
+ 'Please direct @ Name / job number, you can invite up to 5 people',
+ 'userandlogin.weight.label': 'Weight',
+ 'userandlogin.weight.placeholder': 'Please enter weight',
+ 'userandlogin.public.label': 'Target disclosure',
+ 'userandlogin.label.help': 'Customers and invitees are shared by default',
+ 'userandlogin.radio.public': 'Public',
+ 'userandlogin.radio.partially-public': 'Partially public',
+ 'userandlogin.radio.private': 'Private',
+ 'userandlogin.publicUsers.placeholder': 'Open to',
+ 'userandlogin.option.A': 'Colleague A',
+ 'userandlogin.option.B': 'Colleague B',
+ 'userandlogin.option.C': 'Colleague C',
+ 'userandlogin.navBar.lang': 'Languages',
+};
diff --git a/site/src/views/user/login/locales/zh-CN.js b/site/src/views/user/login/locales/zh-CN.js
new file mode 100644
index 000000000..ca2b6676c
--- /dev/null
+++ b/site/src/views/user/login/locales/zh-CN.js
@@ -0,0 +1,74 @@
+export default {
+ 'userandlogin.login.userName': '用户名',
+ 'userandlogin.login.password': '密码',
+ 'userandlogin.login.message-invalid-credentials': '账户或密码错误',
+ 'userandlogin.login.message-invalid-verification-code': '验证码错误',
+ 'userandlogin.login.tab-login-credentials': '账户密码登录',
+ 'userandlogin.login.tab-login-mobile': '手机号登录',
+ 'userandlogin.login.remember-me': '自动登录',
+ 'userandlogin.login.forgot-password': '忘记密码',
+ 'userandlogin.login.sign-in-with': '其他登录方式',
+ 'userandlogin.login.signup': '注册账户',
+ 'userandlogin.login.login': '登录',
+ 'userandlogin.register.register': '注册',
+ 'userandlogin.register.get-verification-code': '获取验证码',
+ 'userandlogin.register.sign-in': '使用已有账户登录',
+ 'userandlogin.register-result.msg': '你的账户:{email} 注册成功',
+ 'userandlogin.register-result.activation-email':
+ '激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。',
+ 'userandlogin.register-result.back-home': '返回首页',
+ 'userandlogin.register-result.view-mailbox': '查看邮箱',
+ 'userandlogin.email.required': '请输入邮箱地址!',
+ 'userandlogin.email.wrong-format': '邮箱地址格式错误!',
+ 'userandlogin.userName.required': '请输入用户名!',
+ 'userandlogin.password.required': '请输入密码!',
+ 'userandlogin.password.twice': '两次输入的密码不匹配!',
+ 'userandlogin.strength.msg': '请至少输入 6 个字符。请不要使用容易被猜到的密码。',
+ 'userandlogin.strength.strong': '强度:强',
+ 'userandlogin.strength.medium': '强度:中',
+ 'userandlogin.strength.short': '强度:太短',
+ 'userandlogin.confirm-password.required': '请确认密码!',
+ 'userandlogin.phone-number.required': '请输入手机号!',
+ 'userandlogin.phone-number.wrong-format': '手机号格式错误!',
+ 'userandlogin.verification-code.required': '请输入验证码!',
+ 'userandlogin.title.required': '请输入标题',
+ 'userandlogin.date.required': '请选择起止日期',
+ 'userandlogin.goal.required': '请输入目标描述',
+ 'userandlogin.standard.required': '请输入衡量标准',
+ 'userandlogin.form.get-captcha': '获取验证码',
+ 'userandlogin.captcha.second': '秒',
+ 'userandlogin.form.optional': '(选填)',
+ 'userandlogin.form.submit': '提交',
+ 'userandlogin.form.save': '保存',
+ 'userandlogin.email.placeholder': '邮箱',
+ 'userandlogin.password.placeholder': '至少6位密码,区分大小写',
+ 'userandlogin.confirm-password.placeholder': '确认密码',
+ 'userandlogin.phone-number.placeholder': '手机号',
+ 'userandlogin.verification-code.placeholder': '验证码',
+ 'userandlogin.title.label': '标题',
+ 'userandlogin.title.placeholder': '给目标起个名字',
+ 'userandlogin.date.label': '起止日期',
+ 'userandlogin.placeholder.start': '开始日期',
+ 'userandlogin.placeholder.end': '结束日期',
+ 'userandlogin.goal.label': '目标描述',
+ 'userandlogin.goal.placeholder': '请输入你的阶段性工作目标',
+ 'userandlogin.standard.label': '衡量标准',
+ 'userandlogin.standard.placeholder': '请输入衡量标准',
+ 'userandlogin.client.label': '客户',
+ 'userandlogin.label.tooltip': '目标的服务对象',
+ 'userandlogin.client.placeholder': '请描述你服务的客户,内部客户直接 @姓名/工号',
+ 'userandlogin.invites.label': '邀评人',
+ 'userandlogin.invites.placeholder': '请直接 @姓名/工号,最多可邀请 5 人',
+ 'userandlogin.weight.label': '权重',
+ 'userandlogin.weight.placeholder': '请输入',
+ 'userandlogin.public.label': '目标公开',
+ 'userandlogin.label.help': '客户、邀评人默认被分享',
+ 'userandlogin.radio.public': '公开',
+ 'userandlogin.radio.partially-public': '部分公开',
+ 'userandlogin.radio.private': '不公开',
+ 'userandlogin.publicUsers.placeholder': '公开给',
+ 'userandlogin.option.A': '同事甲',
+ 'userandlogin.option.B': '同事乙',
+ 'userandlogin.option.C': '同事丙',
+ 'userandlogin.navBar.lang': '语言',
+};
diff --git a/site/src/views/user/login/model.js b/site/src/views/user/login/model.js
new file mode 100644
index 000000000..9ca999954
--- /dev/null
+++ b/site/src/views/user/login/model.js
@@ -0,0 +1,44 @@
+import router from '../../../router';
+import { parse } from 'query-string';
+import { fakeAccountLogin } from './service';
+
+export default {
+ namespaced: true,
+ state: {
+ status: undefined,
+ },
+ actions: {
+ async login({ commit }, payload) {
+ const response = await fakeAccountLogin(payload);
+ commit({
+ type: 'changeLoginStatus',
+ payload: response,
+ });
+ // Login successfully
+ if (response.status === 'ok') {
+ const urlParams = new URL(window.location.href);
+ let redirect = parse(location.search).redirect;
+ if (redirect) {
+ const redirectUrlParams = new URL(redirect);
+ if (redirectUrlParams.origin === urlParams.origin) {
+ redirect = redirect.substr(urlParams.origin.length);
+ if (redirect.match(/^\/.*#/)) {
+ redirect = redirect.substr(redirect.indexOf('#') + 1);
+ }
+ } else {
+ redirect = null;
+ }
+ }
+ router.replace({ path: redirect || '/' });
+ }
+ },
+ },
+ mutations: {
+ changeLoginStatus(state, { payload }) {
+ Object.assign(state, {
+ status: payload.status,
+ type: payload.type,
+ });
+ },
+ },
+};
diff --git a/site/src/views/user/login/service.js b/site/src/views/user/login/service.js
new file mode 100644
index 000000000..9c5e1f2a0
--- /dev/null
+++ b/site/src/views/user/login/service.js
@@ -0,0 +1,12 @@
+import request from '../../../utils/request';
+
+export async function fakeAccountLogin(params) {
+ return request('/api/login/account', {
+ method: 'POST',
+ data: params,
+ });
+}
+
+export async function getFakeCaptcha(mobile) {
+ return request(`/api/login/captcha?mobile=${mobile}`);
+}
diff --git a/site/src/views/user/login/style.less b/site/src/views/user/login/style.less
new file mode 100644
index 000000000..a16a20428
--- /dev/null
+++ b/site/src/views/user/login/style.less
@@ -0,0 +1,36 @@
+@import '~ant-design-vue/es/style/themes/default.less';
+
+.main {
+ width: 368px;
+ margin: 0 auto;
+ @media screen and (max-width: @screen-sm) {
+ width: 95%;
+ }
+
+ .icon {
+ margin-left: 16px;
+ color: rgba(0, 0, 0, 0.2);
+ font-size: 24px;
+ vertical-align: middle;
+ cursor: pointer;
+ transition: color 0.3s;
+
+ &:hover {
+ color: @primary-color;
+ }
+ }
+
+ .other {
+ margin-top: 24px;
+ line-height: 22px;
+ text-align: left;
+
+ .register {
+ float: right;
+ }
+ }
+}
+.main /deep/ .antd-pro-login-submit {
+ width: 100%;
+ margin-top: 24px;
+}
diff --git a/site/src/views/user/login/utils/utils.js b/site/src/views/user/login/utils/utils.js
new file mode 100644
index 000000000..54f470f48
--- /dev/null
+++ b/site/src/views/user/login/utils/utils.js
@@ -0,0 +1,10 @@
+import { parse } from 'qs';
+
+export function getPageQuery() {
+ return parse(window.location.href.split('?')[1]);
+}
+
+export function setAuthority(authority) {
+ const proAuthority = typeof authority === 'string' ? [authority] : authority;
+ return localStorage.setItem('antd-pro-authority', JSON.stringify(proAuthority));
+}
diff --git a/site/src/views/user/register-result/index.vue b/site/src/views/user/register-result/index.vue
new file mode 100644
index 000000000..6bfbb9c53
--- /dev/null
+++ b/site/src/views/user/register-result/index.vue
@@ -0,0 +1,59 @@
+
+
+
+ {{ $t('userandregister-result.register-result.msg', { email: account }) }}
+
+
+ {{ $t('userandregister-result.register-result.activation-email') }}
+
+
+
+
+
+ {{ $t('userandregister-result.register-result.back-home') }}
+
+
+
+
+
+
+
+
+
diff --git a/site/src/views/user/register-result/locales/en-US.js b/site/src/views/user/register-result/locales/en-US.js
new file mode 100644
index 000000000..ed75e97ba
--- /dev/null
+++ b/site/src/views/user/register-result/locales/en-US.js
@@ -0,0 +1,23 @@
+export default {
+ 'userandregister-result.login.userName': 'userName',
+ 'userandregister-result.login.password': 'password',
+ 'userandregister-result.login.message-invalid-credentials':
+ 'Invalid username or password(admin/ant.design)',
+ 'userandregister-result.login.message-invalid-verification-code': 'Invalid verification code',
+ 'userandregister-result.login.tab-login-credentials': 'Credentials',
+ 'userandregister-result.login.tab-login-mobile': 'Mobile number',
+ 'userandregister-result.login.remember-me': 'Remember me',
+ 'userandregister-result.login.forgot-password': 'Forgot your password?',
+ 'userandregister-result.login.sign-in-with': 'Sign in with',
+ 'userandregister-result.login.signup': 'Sign up',
+ 'userandregister-result.login.login': 'Login',
+ 'userandregister-result.register.register': 'Register',
+ 'userandregister-result.register.get-verification-code': 'Get code',
+ 'userandregister-result.register.sign-in': 'Already have an account?',
+ 'userandregister-result.register-result.msg': 'Account:registered at {email}',
+ 'userandregister-result.register-result.activation-email':
+ 'The activation email has been sent to your email address and is valid for 24 hours. Please log in to the email in time and click on the link in the email to activate the account.',
+ 'userandregister-result.register-result.back-home': 'Back to home',
+ 'userandregister-result.register-result.view-mailbox': 'View mailbox',
+ 'userandregister-result.navBar.lang': 'Languages',
+};
diff --git a/site/src/views/user/register-result/locales/zh-CN.js b/site/src/views/user/register-result/locales/zh-CN.js
new file mode 100644
index 000000000..eb7736301
--- /dev/null
+++ b/site/src/views/user/register-result/locales/zh-CN.js
@@ -0,0 +1,22 @@
+export default {
+ 'userandregister-result.login.userName': '用户名',
+ 'userandregister-result.login.password': '密码',
+ 'userandregister-result.login.message-invalid-credentials': '账户或密码错误(admin/ant.design)',
+ 'userandregister-result.login.message-invalid-verification-code': '验证码错误',
+ 'userandregister-result.login.tab-login-credentials': '账户密码登录',
+ 'userandregister-result.login.tab-login-mobile': '手机号登录',
+ 'userandregister-result.login.remember-me': '自动登录',
+ 'userandregister-result.login.forgot-password': '忘记密码',
+ 'userandregister-result.login.sign-in-with': '其他登录方式',
+ 'userandregister-result.login.signup': '注册账户',
+ 'userandregister-result.login.login': '登录',
+ 'userandregister-result.register.register': '注册',
+ 'userandregister-result.register.get-verification-code': '获取验证码',
+ 'userandregister-result.register.sign-in': '使用已有账户登录',
+ 'userandregister-result.register-result.msg': '你的账户:{email} 注册成功',
+ 'userandregister-result.register-result.activation-email':
+ '激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。',
+ 'userandregister-result.register-result.back-home': '返回首页',
+ 'userandregister-result.register-result.view-mailbox': '查看邮箱',
+ 'userandregister-result.navBar.lang': '语言',
+};
diff --git a/site/src/views/user/register-result/style.less b/site/src/views/user/register-result/style.less
new file mode 100644
index 000000000..09d3dca95
--- /dev/null
+++ b/site/src/views/user/register-result/style.less
@@ -0,0 +1,21 @@
+.registerResult {
+ width: 800px;
+ min-height: 400px;
+ margin: auto;
+ padding: 80px;
+ background: none;
+ /deep/ .anticon {
+ font-size: 64px;
+ }
+ .title {
+ margin-top: 32px;
+ font-size: 20px;
+ line-height: 28px;
+ }
+ .actions {
+ margin-top: 40px;
+ a + a {
+ margin-left: 8px;
+ }
+ }
+}
diff --git a/site/src/views/user/register/index.vue b/site/src/views/user/register/index.vue
new file mode 100644
index 000000000..8a175214d
--- /dev/null
+++ b/site/src/views/user/register/index.vue
@@ -0,0 +1,248 @@
+
+
+
+ {{ $t('userandregister.register.register') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('userandregister.strength.strong') }}
+
+
+ {{ $t('userandregister.strength.medium') }}
+
+
+ {{ $t('userandregister.strength.short') }}
+
+
+
+
+
+ {{ $t('userandregister.strength.msg') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('userandregister.register.register') }}
+
+
+ {{ $t('userandregister.register.sign-in') }}
+
+
+
+
+
+
+
diff --git a/site/src/views/user/register/locales/en-US.js b/site/src/views/user/register/locales/en-US.js
new file mode 100644
index 000000000..c01fa4381
--- /dev/null
+++ b/site/src/views/user/register/locales/en-US.js
@@ -0,0 +1,78 @@
+export default {
+ 'userandregister.login.userName': 'userName',
+ 'userandregister.login.password': 'password',
+ 'userandregister.login.message-invalid-credentials':
+ 'Invalid username or password(admin/ant.design)',
+ 'userandregister.login.message-invalid-verification-code': 'Invalid verification code',
+ 'userandregister.login.tab-login-credentials': 'Credentials',
+ 'userandregister.login.tab-login-mobile': 'Mobile number',
+ 'userandregister.login.remember-me': 'Remember me',
+ 'userandregister.login.forgot-password': 'Forgot your password?',
+ 'userandregister.login.sign-in-with': 'Sign in with',
+ 'userandregister.login.signup': 'Sign up',
+ 'userandregister.login.login': 'Login',
+ 'userandregister.register.register': 'Register',
+ 'userandregister.register.get-verification-code': 'Get code',
+ 'userandregister.register.sign-in': 'Already have an account?',
+ 'userandregister.register-result.msg': 'Account:registered at {email}',
+ 'userandregister.register-result.activation-email':
+ 'The activation email has been sent to your email address and is valid for 24 hours. Please log in to the email in time and click on the link in the email to activate the account.',
+ 'userandregister.register-result.back-home': 'Back to home',
+ 'userandregister.register-result.view-mailbox': 'View mailbox',
+ 'userandregister.email.required': 'Please enter your email!',
+ 'userandregister.email.wrong-format': 'The email address is in the wrong format!',
+ 'userandregister.userName.required': 'Please enter your userName!',
+ 'userandregister.password.required': 'Please enter your password!',
+ 'userandregister.password.twice': 'The passwords entered twice do not match!',
+ 'userandregister.strength.msg':
+ "Please enter at least 6 characters and don't use passwords that are easy to guess.",
+ 'userandregister.strength.strong': 'Strength: strong',
+ 'userandregister.strength.medium': 'Strength: medium',
+ 'userandregister.strength.short': 'Strength: too short',
+ 'userandregister.confirm-password.required': 'Please confirm your password!',
+ 'userandregister.phone-number.required': 'Please enter your phone number!',
+ 'userandregister.phone-number.wrong-format': 'Malformed phone number!',
+ 'userandregister.verification-code.required': 'Please enter the verification code!',
+ 'userandregister.title.required': 'Please enter a title',
+ 'userandregister.date.required': 'Please select the start and end date',
+ 'userandregister.goal.required': 'Please enter a description of the goal',
+ 'userandregister.standard.required': 'Please enter a metric',
+ 'userandregister.form.get-captcha': 'Get Captcha',
+ 'userandregister.captcha.second': 'sec',
+ 'userandregister.form.optional': ' (optional) ',
+ 'userandregister.form.submit': 'Submit',
+ 'userandregister.form.save': 'Save',
+ 'userandregister.email.placeholder': 'Email',
+ 'userandregister.password.placeholder': 'Password',
+ 'userandregister.confirm-password.placeholder': 'Confirm password',
+ 'userandregister.phone-number.placeholder': 'Phone number',
+ 'userandregister.verification-code.placeholder': 'Verification code',
+ 'userandregister.title.label': 'Title',
+ 'userandregister.title.placeholder': 'Give the target a name',
+ 'userandregister.date.label': 'Start and end date',
+ 'userandregister.placeholder.start': 'Start date',
+ 'userandregister.placeholder.end': 'End date',
+ 'userandregister.goal.label': 'Goal description',
+ 'userandregister.goal.placeholder': 'Please enter your work goals',
+ 'userandregister.standard.label': 'Metrics',
+ 'userandregister.standard.placeholder': 'Please enter a metric',
+ 'userandregister.client.label': 'Client',
+ 'userandregister.label.tooltip': 'Target service object',
+ 'userandregister.client.placeholder':
+ 'Please describe your customer service, internal customers directly @ Name / job number',
+ 'userandregister.invites.label': 'Inviting critics',
+ 'userandregister.invites.placeholder':
+ 'Please direct @ Name / job number, you can invite up to 5 people',
+ 'userandregister.weight.label': 'Weight',
+ 'userandregister.weight.placeholder': 'Please enter weight',
+ 'userandregister.public.label': 'Target disclosure',
+ 'userandregister.label.help': 'Customers and invitees are shared by default',
+ 'userandregister.radio.public': 'Public',
+ 'userandregister.radio.partially-public': 'Partially public',
+ 'userandregister.radio.private': 'Private',
+ 'userandregister.publicUsers.placeholder': 'Open to',
+ 'userandregister.option.A': 'Colleague A',
+ 'userandregister.option.B': 'Colleague B',
+ 'userandregister.option.C': 'Colleague C',
+ 'userandregister.navBar.lang': 'Languages',
+};
diff --git a/site/src/views/user/register/locales/zh-CN.js b/site/src/views/user/register/locales/zh-CN.js
new file mode 100644
index 000000000..0e0e4b0c9
--- /dev/null
+++ b/site/src/views/user/register/locales/zh-CN.js
@@ -0,0 +1,74 @@
+export default {
+ 'userandregister.login.userName': '用户名',
+ 'userandregister.login.password': '密码',
+ 'userandregister.login.message-invalid-credentials': '账户或密码错误(admin/ant.design)',
+ 'userandregister.login.message-invalid-verification-code': '验证码错误',
+ 'userandregister.login.tab-login-credentials': '账户密码登录',
+ 'userandregister.login.tab-login-mobile': '手机号登录',
+ 'userandregister.login.remember-me': '自动登录',
+ 'userandregister.login.forgot-password': '忘记密码',
+ 'userandregister.login.sign-in-with': '其他登录方式',
+ 'userandregister.login.signup': '注册账户',
+ 'userandregister.login.login': '登录',
+ 'userandregister.register.register': '注册',
+ 'userandregister.register.get-verification-code': '获取验证码',
+ 'userandregister.register.sign-in': '使用已有账户登录',
+ 'userandregister.register-result.msg': '你的账户:{email} 注册成功',
+ 'userandregister.register-result.activation-email':
+ '激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。',
+ 'userandregister.register-result.back-home': '返回首页',
+ 'userandregister.register-result.view-mailbox': '查看邮箱',
+ 'userandregister.email.required': '请输入邮箱地址!',
+ 'userandregister.email.wrong-format': '邮箱地址格式错误!',
+ 'userandregister.userName.required': '请输入用户名!',
+ 'userandregister.password.required': '请输入密码!',
+ 'userandregister.password.twice': '两次输入的密码不匹配!',
+ 'userandregister.strength.msg': '请至少输入 6 个字符。请不要使用容易被猜到的密码。',
+ 'userandregister.strength.strong': '强度:强',
+ 'userandregister.strength.medium': '强度:中',
+ 'userandregister.strength.short': '强度:太短',
+ 'userandregister.confirm-password.required': '请确认密码!',
+ 'userandregister.phone-number.required': '请输入手机号!',
+ 'userandregister.phone-number.wrong-format': '手机号格式错误!',
+ 'userandregister.verification-code.required': '请输入验证码!',
+ 'userandregister.title.required': '请输入标题',
+ 'userandregister.date.required': '请选择起止日期',
+ 'userandregister.goal.required': '请输入目标描述',
+ 'userandregister.standard.required': '请输入衡量标准',
+ 'userandregister.form.get-captcha': '获取验证码',
+ 'userandregister.captcha.second': '秒',
+ 'userandregister.form.optional': '(选填)',
+ 'userandregister.form.submit': '提交',
+ 'userandregister.form.save': '保存',
+ 'userandregister.email.placeholder': '邮箱',
+ 'userandregister.password.placeholder': '至少6位密码,区分大小写',
+ 'userandregister.confirm-password.placeholder': '确认密码',
+ 'userandregister.phone-number.placeholder': '手机号',
+ 'userandregister.verification-code.placeholder': '验证码',
+ 'userandregister.title.label': '标题',
+ 'userandregister.title.placeholder': '给目标起个名字',
+ 'userandregister.date.label': '起止日期',
+ 'userandregister.placeholder.start': '开始日期',
+ 'userandregister.placeholder.end': '结束日期',
+ 'userandregister.goal.label': '目标描述',
+ 'userandregister.goal.placeholder': '请输入你的阶段性工作目标',
+ 'userandregister.standard.label': '衡量标准',
+ 'userandregister.standard.placeholder': '请输入衡量标准',
+ 'userandregister.client.label': '客户',
+ 'userandregister.label.tooltip': '目标的服务对象',
+ 'userandregister.client.placeholder': '请描述你服务的客户,内部客户直接 @姓名/工号',
+ 'userandregister.invites.label': '邀评人',
+ 'userandregister.invites.placeholder': '请直接 @姓名/工号,最多可邀请 5 人',
+ 'userandregister.weight.label': '权重',
+ 'userandregister.weight.placeholder': '请输入',
+ 'userandregister.public.label': '目标公开',
+ 'userandregister.label.help': '客户、邀评人默认被分享',
+ 'userandregister.radio.public': '公开',
+ 'userandregister.radio.partially-public': '部分公开',
+ 'userandregister.radio.private': '不公开',
+ 'userandregister.publicUsers.placeholder': '公开给',
+ 'userandregister.option.A': '同事甲',
+ 'userandregister.option.B': '同事乙',
+ 'userandregister.option.C': '同事丙',
+ 'userandregister.navBar.lang': '语言',
+};
diff --git a/site/src/views/user/register/model.js b/site/src/views/user/register/model.js
new file mode 100644
index 000000000..b77d2b81b
--- /dev/null
+++ b/site/src/views/user/register/model.js
@@ -0,0 +1,24 @@
+import { fakeRegister } from './service';
+
+const Model = {
+ namespaced: true,
+ state: {
+ status: undefined,
+ },
+ actions: {
+ async submit({ commit }, { payload }) {
+ const response = await fakeRegister(payload);
+ commit({
+ type: 'registerHandle',
+ payload: response,
+ });
+ },
+ },
+ mutations: {
+ registerHandle(state, { payload }) {
+ state.status = payload.status;
+ },
+ },
+};
+
+export default Model;
diff --git a/site/src/views/user/register/service.js b/site/src/views/user/register/service.js
new file mode 100644
index 000000000..4a599e328
--- /dev/null
+++ b/site/src/views/user/register/service.js
@@ -0,0 +1,8 @@
+import request from '../../../utils/request';
+
+export async function fakeRegister(params) {
+ return request('/api/register', {
+ method: 'POST',
+ data: params,
+ });
+}
diff --git a/site/src/views/user/register/style.less b/site/src/views/user/register/style.less
new file mode 100644
index 000000000..ee1fd6d0f
--- /dev/null
+++ b/site/src/views/user/register/style.less
@@ -0,0 +1,52 @@
+@import '~ant-design-vue/es/style/themes/default.less';
+
+.main {
+ width: 368px;
+ margin: 0 auto;
+ /deep/ .ant-form-item {
+ margin-bottom: 24px;
+ }
+
+ /deep/ .ant-select-selection-selected-value {
+ padding-right: 0;
+ }
+ h3 {
+ margin-bottom: 20px;
+ font-size: 16px;
+ }
+
+ .getCaptcha {
+ display: block;
+ width: 100%;
+ }
+
+ .submit {
+ width: 50%;
+ }
+
+ .login {
+ float: right;
+ line-height: @btn-height-lg;
+ }
+}
+.success,
+.warning,
+.error {
+ transition: color 0.3s;
+}
+
+.success {
+ color: @success-color;
+}
+
+.warning {
+ color: @warning-color;
+}
+
+.error {
+ color: @error-color;
+}
+
+.progress-pass > .progress /deep/ .ant-progress-bg {
+ background-color: @warning-color;
+}
diff --git a/site/src/views/vip.vue b/site/src/views/vip.vue
new file mode 100644
index 000000000..659dd0e1e
--- /dev/null
+++ b/site/src/views/vip.vue
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/site/src/vueDocs/customize-theme.en-US.md b/site/src/vueDocs/customize-theme.en-US.md
new file mode 100644
index 000000000..448b1cb2c
--- /dev/null
+++ b/site/src/vueDocs/customize-theme.en-US.md
@@ -0,0 +1,200 @@
+# Customize Theme
+
+The structure and styles of ant-design-vue are exactly the same as those of Antd. You can refer to the Antd React customization mode for configuration.
+
+Ant Design allows you to customize some basic design aspects in order to meet the needs of UI diversity from business and brand, including primary color, border radius, border color, etc.
+
+
+
+## Ant Design Vue Less variables
+
+We are using [Less](http://lesscss.org/) as the development language for styling. A set of less variables are defined for each design aspect that can be customized to your needs.
+
+There are some major variables below, all less variables could be found in [Default Variables](https://github.com/vueComponent/ant-design-vue/blob/next/components/style/themes/default.less).
+
+```less
+@primary-color: #1890ff; // primary color for all components
+@link-color: #1890ff; // link color
+@success-color: #52c41a; // success state color
+@warning-color: #faad14; // warning state color
+@error-color: #f5222d; // error state color
+@font-size-base: 14px; // major text font size
+@heading-color: rgba(0, 0, 0, 0.85); // heading text color
+@text-color: rgba(0, 0, 0, 0.85); // major text color
+@text-color-secondary: rgba(0, 0, 0, 0.45); // secondary text color
+@disabled-color: rgba(0, 0, 0, 0.25); // disable state color
+@border-radius-base: 4px; // major border radius
+@border-color-base: #d9d9d9; // major border color
+@box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); // major shadow for layers
+```
+
+Please report an issue if the existing list of variables is not enough for you.
+
+## How to do it
+
+We will use [modifyVars](http://lesscss.org/usage/#using-less-in-the-browser-modify-variables) provided by less.js to override the default values of the variables. We now introduce some popular way to do it depends on different workflow.
+
+### Customize in webpack
+
+We take a typical `webpack.config.js` file as example to customize it's [less-loader](https://github.com/webpack-contrib/less-loader) options.
+
+```diff
+// webpack.config.js
+module.exports = {
+ rules: [{
+ test: /\.less$/,
+ use: [{
+ loader: 'style-loader',
+ }, {
+ loader: 'css-loader', // translates CSS into CommonJS
+ }, {
+ loader: 'less-loader', // compiles Less to CSS
++ options: {
++ lessOptions: {
++ modifyVars: {
++ 'primary-color': '#1DA57A',
++ 'link-color': '#1DA57A',
++ 'border-radius-base': '2px',
++ },
++ javascriptEnabled: true,
++ }
++ },
+ }],
+ // ...other rules
+ }],
+ // ...other config
+}
+```
+
+Note that do not exclude antd package in node_modules when using less-loader.
+
+### Customize in vue cli 2
+
+Modify the `build/utils.js` file
+
+```diff
+// build/utils.js
+- less: generateLoaders('less'),
++ less: generateLoaders('less', {
++ modifyVars: {
++ 'primary-color': '#1DA57A',
++ 'link-color': '#1DA57A',
++ 'border-radius-base': '2px',
++ },
++ javascriptEnabled: true,
++ }),
+
+```
+
+### Customize in vue cli 3
+
+Create a new file `vue.config.js` in the project directory.
+
+```js
+// vue.config.js for less-loader@6.0.0
+module.exports = {
+ css: {
+ loaderOptions: {
+ less: {
+ lessOptions: {
+ modifyVars: {
+ 'primary-color': '#1DA57A',
+ 'link-color': '#1DA57A',
+ 'border-radius-base': '2px',
+ },
+ javascriptEnabled: true,
+ },
+ },
+ },
+ },
+};
+```
+
+### Customize in less file
+
+Another approach to customize theme is creating a `less` file within variables to override `antd.less`.
+
+```css
+@import '~ant-design-vue/dist/antd.less'; // Import Ant Design Vue styles by less entry
+@import 'your-theme-file.less'; // variables to override above
+```
+
+Note: This way will load the styles of all components, regardless of your demand, which cause `style` option of `babel-plugin-import` not working.
+
+## How to avoid modifying global styles?
+
+Currently ant-design-vue is designed as a whole experience and modify global styles (eg `body` etc). If you need to integrate ant-design-vue as a part of an existing website, it's likely you want to prevent ant-design-vue to override global styles.
+
+While there's no canonical way to do it, you can take one of the following paths :
+
+### Configure webpack to load an alternale less file and scope global styles
+
+It's possible to configure webpack to load an alternate less file:
+
+```js
+new webpack.NormalModuleReplacementPlugin( /node_modules\/ant-design-vue\/lib\/style\/index\.less/, path.resolve(rootDir, 'src/myStylesReplacement.less') )
+
+#antd { @import '~ant-design-vue/lib/style/core/index.less'; @import '~ant-design-vue/lib/style/themes/default.less'; }
+```
+
+Where the src/myStylesReplacement.less file loads the same files as the index.less file, but loads them within the scope of a top-level selector : the result is that all of the "global" styles are being applied with the #antd scope.
+
+### Use a postcss processor to scope all styles
+
+See an example of usage with gulp and [postcss-prefixwrap](https://github.com/dbtedman/postcss-prefixwrap) : https://gist.github.com/sbusch/a90eafaf5a5b61c6d6172da6ff76ddaa
+
+## Not working?
+
+You must import styles as less format. A common mistake would be importing multiple copied of styles that some of them are css format to override the less styles.
+
+- If you import styles by specifying the `style` option of [babel-plugin-import](https://github.com/ant-design/babel-plugin-import), change it from `'css'` to `true`, which will import the `less` version of antd.
+- If you import styles from `'ant-design-vue/dist/antd.css'`, change it to `ant-design-vue/dist/antd.less`.
+
+## Use dark theme
+
+Method 1: Import [antd.dark.less](https://unpkg.com/browse/ant-design-vue@2.0.0/dist/antd.dark.less) in the style file:
+
+```less
+@import '~ant-design-vue/dist/antd.dark.less'; // Introduce the official dark less style entry file
+```
+
+If the project does not use Less, you can import [antd.dark.css](https://unpkg.com/browse/ant-design-vue@2.0.0/dist/antd.dark.css) in the CSS file:
+
+```css
+@import '~ant-design-vue/dist/antd.dark.css';
+```
+
+> Note that you don't need to import `ant-design-vue/dist/antd.less` or `ant-design-vue/dist/antd.css` anymore, please remove it, and remove babel-plugin-import `style` config too. You can't enable two or more theme at the same time by this method.
+
+Method 3: using [less-loader](https://github.com/webpack-contrib/less-loader) in `webpack.config.js` to introduce as needed:
+
+```diff
+const { getThemeVariables } = require('ant-design-vue/dist/theme');
+
+// webpack.config.js
+module.exports = {
+ rules: [{
+ test: /\.less$/,
+ use: [{
+ loader: 'style-loader',
+ }, {
+ loader: 'css-loader', // translates CSS into CommonJS
+ }, {
+ loader: 'less-loader', // compiles Less to CSS
++ options: {
++ lessOptions: { // If you are using less-loader@5 please spread the lessOptions to options directly
++ modifyVars: getThemeVariables({
++ dark: true, // Enable dark mode
++ }),
++ javascriptEnabled: true,
++ },
++ },
+ }],
+ }],
+};
+```
+
+## Related Articles
+
+- [How to Customize Ant Design with React & Webpack… the Missing Guide](https://medium.com/@GeoffMiller/how-to-customize-ant-design-with-react-webpack-the-missing-guide-c6430f2db10f)
+- [Theming Ant Design with Sass and Webpack](https://gist.github.com/Kruemelkatze/057f01b8e15216ae707dc7e6c9061ef7)
diff --git a/site/src/vueDocs/customize-theme.zh-CN.md b/site/src/vueDocs/customize-theme.zh-CN.md
new file mode 100644
index 000000000..de03ba7f2
--- /dev/null
+++ b/site/src/vueDocs/customize-theme.zh-CN.md
@@ -0,0 +1,177 @@
+# 定制主题
+
+ant-design-vue 的组件结构及样式和 Antd React 完全一致,你可以参考 Antd React 的定制方式进行配置。
+
+Ant Design 设计规范上支持一定程度的样式定制,以满足业务和品牌上多样化的视觉需求,包括但不限于主色、圆角、边框和部分组件的视觉定制。
+
+
+
+## Ant Design Vue 的样式变量
+
+antd 的样式使用了 [Less](http://lesscss.org/) 作为开发语言,并定义了一系列全局/组件的样式变量,你可以根据需求进行相应调整。
+
+以下是一些最常用的通用变量,所有样式变量可以在 [这里](https://github.com/vueComponent/ant-design-vue/blob/next/components/style/themes/default.less) 找到。
+
+```less
+@primary-color: #1890ff; // 全局主色
+@link-color: #1890ff; // 链接色
+@success-color: #52c41a; // 成功色
+@warning-color: #faad14; // 警告色
+@error-color: #f5222d; // 错误色
+@font-size-base: 14px; // 主字号
+@heading-color: rgba(0, 0, 0, 0.85); // 标题色
+@text-color: rgba(0, 0, 0, 0.65); // 主文本色
+@text-color-secondary: rgba(0, 0, 0, 0.45); // 次文本色
+@disabled-color: rgba(0, 0, 0, 0.25); // 失效色
+@border-radius-base: 4px; // 组件/浮层圆角
+@border-color-base: #d9d9d9; // 边框色
+@box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); // 浮层阴影
+```
+
+如果以上变量不能满足你的定制需求,可以给我们提 issue。
+
+## 定制方式
+
+我们使用 [modifyVars](http://lesscss.org/usage/#using-less-in-the-browser-modify-variables) 的方式来进行覆盖变量。下面将针对不同的场景提供一些常用的定制方式。
+
+### 在 webpack 中定制主题
+
+我们以 webpack@4 为例进行说明,以下是一个 `webpack.config.js` 的典型例子,对 [less-loader](https://github.com/webpack-contrib/less-loader) 的 options 属性进行相应配置。
+
+```diff
+// webpack.config.js
+module.exports = {
+ rules: [{
+ test: /\.less$/,
+ use: [{
+ loader: 'style-loader',
+ }, {
+ loader: 'css-loader', // translates CSS into CommonJS
+ }, {
+ loader: 'less-loader', // compiles Less to CSS
++ options: {
++ lessOptions: {
++ modifyVars: {
++ 'primary-color': '#1DA57A',
++ 'link-color': '#1DA57A',
++ 'border-radius-base': '2px',
++ },
++ javascriptEnabled: true,
++ }
++ },
+ }],
+ // ...other rules
+ }],
+ // ...other config
+}
+```
+
+注意 less-loader 的处理范围不要过滤掉 `node_modules` 下的 antd 包。
+
+### 在 vue cli 2 中定制主题
+
+修改`build/utils.js`文件
+
+```diff
+// build/utils.js
+- less: generateLoaders('less'),
++ less: generateLoaders('less', {
++ modifyVars: {
++ 'primary-color': '#1DA57A',
++ 'link-color': '#1DA57A',
++ 'border-radius-base': '2px',
++ },
++ javascriptEnabled: true,
++ }),
+
+```
+
+### 在 vue cli 3 中定制主题
+
+项目根目录下新建文件`vue.config.js`
+
+```js
+// vue.config.js for less-loader@6.0.0
+module.exports = {
+ css: {
+ loaderOptions: {
+ less: {
+ lessOptions: {
+ modifyVars: {
+ 'primary-color': '#1DA57A',
+ 'link-color': '#1DA57A',
+ 'border-radius-base': '2px',
+ },
+ javascriptEnabled: true,
+ },
+ },
+ },
+ },
+};
+```
+
+### 配置 less 变量文件
+
+另外一种方式是建立一个单独的 `less` 变量文件,引入这个文件覆盖 `antd.less` 里的变量。
+
+```css
+@import '~ant-design-vue/dist/antd.less'; // 引入官方提供的 less 样式入口文件
+@import 'your-theme-file.less'; // 用于覆盖上面定义的变量
+```
+
+注意,这种方式已经载入了所有组件的样式,不需要也无法和按需加载插件 `babel-plugin-import` 的 `style` 属性一起使用。
+
+## 没有生效?
+
+注意样式必须加载 less 格式,一个常见的问题就是引入了多份样式,less 的样式被 css 的样式覆盖了。
+
+- 如果你在使用 [babel-plugin-import](https://github.com/ant-design/babel-plugin-import) 的 `style` 配置来引入样式,需要将配置值从 `'css'` 改为 `true`,这样会引入 less 文件。
+- 如果你是通过 `'ant-design-vue/dist/antd.css'` 引入样式的,改为 `ant-design-vue/dist/antd.less`。
+
+## 使用暗黑主题
+
+方式一:在样式文件全量引入 [antd.dark.less](https://unpkg.com/browse/ant-design-vue@2.0.0/dist/antd.dark.less)。
+```less
+@import '~ant-design-vue/dist/antd.dark.less'; // 引入官方提供的暗色 less 样式入口文件
+```
+
+如果项目不使用 Less,可在 CSS 文件中全量引入 [antd.dark.css](https://unpkg.com/browse/ant-design-vue@2.0.0/dist/antd.dark.css)。
+
+```css
+@import '~ant-design-vue/dist/antd.dark.css';
+```
+
+> 注意这种方式下你不需要再引入 `ant-design-vue/dist/antd.less` 或 `ant-design-vue/dist/antd.css` 了,可以安全移除掉。也不需要开启 babel-plugin-import 的 `style` 配置。通过此方式不能同时配置两种及以上主题。
+
+方式二:是用在 `webpack.config.js` 使用 [less-loader](https://github.com/webpack-contrib/less-loader) 按需引入:
+
+```diff
+const { getThemeVariables } = require('ant-design-vue/dist/theme');
+
+// webpack.config.js
+module.exports = {
+ rules: [{
+ test: /\.less$/,
+ use: [{
+ loader: 'style-loader',
+ }, {
+ loader: 'css-loader', // translates CSS into CommonJS
+ }, {
+ loader: 'less-loader', // compiles Less to CSS
++ options: {
++ lessOptions: { // 如果使用less-loader@5,请移除 lessOptions 这一级直接配置选项。
++ modifyVars: getThemeVariables({
++ dark: true, // 开启暗黑模式
++ }),
++ javascriptEnabled: true,
++ },
++ },
+ }],
+ }],
+};
+```
+
+## 社区教程 for Antd React
+
+- [How to Customize Ant Design with React & Webpack… the Missing Guide](https://medium.com/@GeoffMiller/how-to-customize-ant-design-with-react-webpack-the-missing-guide-c6430f2db10f)
+- [Theming Ant Design with Sass and Webpack](https://gist.github.com/Kruemelkatze/057f01b8e15216ae707dc7e6c9061ef7)
diff --git a/site/src/vueDocs/download.en-US.md b/site/src/vueDocs/download.en-US.md
new file mode 100644
index 000000000..045cb9a88
--- /dev/null
+++ b/site/src/vueDocs/download.en-US.md
@@ -0,0 +1,89 @@
+Please find below some of the design resources and tools about Ant Design Vue that we consider valuable. More of this is still being collected.
+
+> The following design resources are developed and maintained by the [Ant Design](https://ant.design).
+
+
diff --git a/site/src/vueDocs/download.zh-CN.md b/site/src/vueDocs/download.zh-CN.md
new file mode 100644
index 000000000..5847b7548
--- /dev/null
+++ b/site/src/vueDocs/download.zh-CN.md
@@ -0,0 +1,77 @@
+这里提供 Ant Design Vue 相关设计资源和设计工具的下载,更多设计资源正在整理和完善中。
+
+> 以下设计资源由[Ant Design](https://ant.design)官方开发并维护
+
+
diff --git a/site/src/vueDocs/faq.en-US.md b/site/src/vueDocs/faq.en-US.md
new file mode 100644
index 000000000..64be463ba
--- /dev/null
+++ b/site/src/vueDocs/faq.en-US.md
@@ -0,0 +1,55 @@
+# FAQ
+
+Here are the frequently asked questions about Ant Design Vue that you should look up before you ask in community or create new issue.
+
+### Are you going to provide Sass/Stylus(etc...) style file?
+
+No, actually, you can convert Less to Sass/Stylus(etc...) with tools (which you can Google).
+
+### How to use DatePicker with dayjs
+
+We also provide another implementation, which we provide with antd-dayjs-webpack-plugin, replacing dayjs with Day.js directly without changing a line of existing code. More info can be found at [antd-dayjs-webpack-plugin](https://github.com/ant-design/antd-dayjs-webpack-plugin).
+
+### Internationalization does not take effect?
+
+The language pack provided by the component does not affect date formatting. You need to import the dayjs language pack and apply it. Refer to the `ConfigProvider` component.
+
+### `Select Dropdown DatePicker TimePicker Popover Popconfirm` disappear when I click another popup component inside it, How to resolve it?
+
+Use `` to render component inside Popover. (Or other getXxxxContainer props)
+
+### `Select Dropdown DatePicker TimePicker Popover Popconfirm` scroll with the page?
+
+Use `` to render component inside the scroll area. (Or other getXxxxContainer props).
+
+### How to modify the default theme of Ant Design Vue?
+
+See [Customize Theme](/docs/vue/customize-theme/).
+
+### It doesn't work when I change `defaultValue`,`defaultOpenKeys`, `initialValue` dynamically.
+
+The `defaultXxxx` (like `defaultValue`) of `Input`/`Select`(etc...) only works in first render. This feature is referenced from [React](https://facebook.github.io/react/docs/forms.html#controlled-components).
+
+### I set the `value` of `Input`/`Select`(etc...), and then, it cannot be changed by user's action.
+
+Try `defaultValue` or `change` or `v-model` to change `value`.
+
+### ant-design-vue override my global styles!
+
+Yes, ant-design-vue is designed to develop a complete background application, we override some global styles for styling convenience, and it can't be removed now. More info trace https://github.com/ant-design/ant-design/issues/4331 .
+
+Or, follow the instructions in [How to avoid modifying global styles?](docs/react/customize-theme#How-to-avoid-modifying-global-styles-?)
+
+### `ant-design-vue` makes only poor user experience on mobile.
+
+`ant-design-vue` is not designed for mobile.
+
+### When I set `mode` to DatePicker/RangePicker, I cannot select year or month anymore?
+
+In a real world development, you may need a YearPicker, MonthRangePicker or WeekRangePicker. You are trying to add `mode` to DatePicker/RangePicker expected to implement those pickers. However, the DatePicker/RangePicker cannot be selected and the panels won't close now.
+
+That is because ` ` do not equal to `YearPicker`, ` ` do not equal to `MonthRangePicker` either. The `mode` property was added to support [showing time picker panel in DatePicker](https://github.com/ant-design/ant-design/issues/5190), which simply control the displayed panel and won't change the original date picking behavior of `DatePicker/RangePicker` (for instance you still need to click date cell to finish selection in a DatePicker, whatever the `mode` is).
+
+##### Solution
+
+The following articles are the implementation articles of the react version, the ideas are the same. In [one article](https://juejin.im/post/5cf65c366fb9a07eca6968f9) or [another article](https://www.cnblogs.com/zyl-Tara/p/10197177.html) approach, using methods `mode` and `panelChange` to encapsulate a component such as `YearPicker`. We plan to add more date related components directly in ant-design-vue@2.0 to support these needs.
diff --git a/site/src/vueDocs/faq.zh-CN.md b/site/src/vueDocs/faq.zh-CN.md
new file mode 100644
index 000000000..db3bd9f72
--- /dev/null
+++ b/site/src/vueDocs/faq.zh-CN.md
@@ -0,0 +1,49 @@
+# 常见问题
+
+以下整理了一些 Ant Design Vue 社区常见的问题和官方答复,在提问之前建议找找有没有类似的问题。
+
+### 你们会提供 Sass/Stylus 等格式的样式文件吗?
+
+不。事实上你可以使用工具(请自行 Google)将 Less 转换成 Sass/Stylus 等。
+
+### 国际化没有生效?
+
+组件提供的语言包并不对日期格式化起作用,你需要额外导入 dayjs 语言包,并应用,参考 `ConfigProvider` 组件。
+
+### 当我点击 `Select Dropdown DatePicker TimePicker Popover Popconfirm` 内的另一个 popup 组件时它会消失,如何解决?
+
+使用 `` 来在 Popover 中渲染组件,或者使用其他的 getXxxxContainer 参数。
+
+### `Select Dropdown DatePicker TimePicker Popover Popconfirm` 会跟随滚动条上下移动?
+
+使用 `` 来将组件渲染到滚动区域内,或者使用其他的 getXxxxContainer 参数。
+
+### 如何修改 Ant Design Vue 的默认主题?
+
+参考[主题定制](/docs/vue/customize-theme/)。
+
+### 当我动态改变 `defaultValue`,`defaultOpenKeys`, `initialValue`等`defaultXxxx`的时候它并没有生效。
+
+`Input`/`Select` 等的 `defaultXxxx`(例如 `defaultValue`)只有在组件第一次渲染的时候有效,此特性参考自[React](https://reactjs.org/docs/forms.html#controlled-components)。切记:第一次、第一次、第一次....
+
+### 当我设置了 `Input`/`Select` 等的 `value` 时它就无法修改了。
+
+尝试使用 `defaultValue` 或 `change` 或 `v-model` 来改变 `value`。
+
+### ant-design-vue 覆盖了我的全局样式!
+
+是的,ant-design-vue 在设计的时候就是用来开发一个完整的应用的,为了方便,我们覆盖了一些全局样式,现在还不能移除,想要了解更多请追踪这个 issue:https://github.com/ant-design/ant-design/issues/4331 ,或者参考这个教程 [How to avoid modifying global styles?](docs/react/customize-theme#How-to-avoid-modifying-global-styles-?)
+
+### `ant-design-vue` 在移动端体验不佳。
+
+`ant-design-vue` 并非针对移动端设计。
+
+### 当我指定了 DatePicker/RangePicker 的 `mode` 属性后,点击后无法选择年份/月份?
+
+在业务开发中,你可能有年份选择,月份范围选择,周范围选择等需求,此时你给现有组件增加了 `mode` 属性,却发现无法进行点击选择行为,面板也不会关闭。
+
+这是因为 ` ` 不等于 `YearPicker`,` ` 不等于 `MonthRangePicker`。 `mode` 属性是在 antd 3.0 时,为了控制面板展现状态而添加的属性,以支持[展示时间面板](https://github.com/ant-design/ant-design/issues/5190)等需求而添加的。`mode` 只会简单的改变当前显示的面板,不会修改默认的交互行为(比如 DatePicker 依然是点击日才会完成选择并关闭面板)。
+
+##### 解决办法
+
+以下文章均是 react 版本的实现文章,思路一致。参照 [这篇文章](https://juejin.im/post/5cf65c366fb9a07eca6968f9) 或者 [这篇文章](https://www.cnblogs.com/zyl-Tara/p/10197177.html) 里的做法,利用 `mode` 和 `panelChange` 等方法去封装一个 `YearPicker` 等组件。我们计划在 ant-design-vue@2.0 中直接添加更多相关日期组件来支持这些需求。
diff --git a/site/src/vueDocs/getting-started.en-US.md b/site/src/vueDocs/getting-started.en-US.md
new file mode 100644
index 000000000..3623da503
--- /dev/null
+++ b/site/src/vueDocs/getting-started.en-US.md
@@ -0,0 +1,116 @@
+# Getting Started
+
+Ant Design Vue is dedicated to providing a **good development experience** for programmers. Make sure that you had installed [Node.js](https://nodejs.org/)(> v8.9) correctly.
+
+> Before delving into Ant Design Vue, a good knowledge base of [Vue](https://cn.vuejs.org/) and [JavaScript ES2015](http://babeljs.io/docs/learn-es2015/) is needed.
+
+## Playground
+
+The following CodeSandbox demo is the simplest use case, and it's also a good habit to fork this demo to provide a re-producible demo while reporting a bug.
+
+- [](https://codesandbox.io/s/agitated-franklin-1w72v)
+
+## Import ant-design-vue
+
+### 1. Installation
+
+[vue-cli](https://github.com/vuejs/vue-cli)
+
+```bash
+$ npm install -g @vue/cli
+# OR
+$ yarn global add @vue/cli
+```
+
+### 2. Create a New Project
+
+A new project can be created using CLI tools.
+
+```bash
+$ vue create antd-demo
+```
+
+And, setup your vue project configuration.
+
+### 3. Use antd's Components
+
+```bash
+$ npm i --save ant-design-vue@next
+```
+
+**Fully import**
+
+```jsx
+import { createApp } from 'vue';
+import Antd from 'ant-design-vue';
+import App from './App';
+import 'ant-design-vue/dist/antd.css';
+
+const app = createApp();
+app.config.productionTip = false;
+
+app.use(Antd);
+```
+
+The above imports Antd entirely. Note that CSS file needs to be imported separately.
+
+**Only import the components you need**
+
+```jsx
+import { createApp } from 'vue';
+import { Button, message } from 'ant-design-vue';
+import App from './App';
+
+const app = createApp(App);
+app.config.productionTip = false;
+
+/* Automatically register components under Button, such as Button.Group */
+app.use(Button).mount('#app');
+
+app.config.globalProperties.$message = message;
+```
+
+> All the components in antd are listed in the sidebar.
+
+### 4. Component list
+
+[Component list](https://github.com/vueComponent/ant-design-vue/blob/next/components/components.ts)
+
+## Compatibility
+
+Ant Design Vue 2.x supports all the modern browsers. If you want to use IE9+, you can use Ant Design Vue 1.x & Vue 2.x.
+
+You need to provide [es5-shim](https://github.com/es-shims/es5-shim) and [es6-shim](https://github.com/paulmillr/es6-shim) and other polyfills for IE browsers.
+
+If you are using babel, we strongly recommend using [babel-polyfill](https://babeljs.io/docs/usage/polyfill/) and [babel-plugin-transform-runtime](https://babeljs.io/docs/plugins/transform-runtime/) instead of those two shims.
+
+> Please avoid using both the babel and shim methods at the same time.
+
+## Import on Demand
+
+we can import individual components on demand:
+
+```jsx
+import Button from 'ant-design-vue/lib/button';
+import 'ant-design-vue/lib/button/style'; // or ant-design-vue/lib/button/style/css for css format file
+```
+
+We strongly recommend using [babel-plugin-import](https://github.com/ant-design/babel-plugin-import), which can convert the following code to the 'ant-design-vue/lib/xxx' way:
+
+```jsx
+import { Button } from 'ant-design-vue';
+```
+
+And this plugin can load styles too, read [usage](https://github.com/ant-design/babel-plugin-import#usage) for more details.
+
+> FYI, babel-plugin-import's `style` option will importing some global reset styles, don't use it if you don't need those styles. You can import styles manually via `import 'ant-design-vue/dist/antd.css'` and override the global reset styles.
+
+If you use Vite, you can use [vite-plugin-components](https://github.com/antfu/vite-plugin-components) to load on demand.
+
+## Customization
+
+- [Customize Theme](/docs/vue/customize-theme)
+
+## Tips
+
+- You can use any `npm` modules.
diff --git a/site/src/vueDocs/getting-started.zh-CN.md b/site/src/vueDocs/getting-started.zh-CN.md
new file mode 100644
index 000000000..21b9e740a
--- /dev/null
+++ b/site/src/vueDocs/getting-started.zh-CN.md
@@ -0,0 +1,117 @@
+# 快速上手
+
+Ant Design Vue 致力于提供给程序员**愉悦**的开发体验。
+
+> 在开始之前,推荐先学习 [Vue](https://cn.vuejs.org/) 和 [ES2015](http://babeljs.io/docs/learn-es2015/),并正确安装和配置了 [Node.js](https://nodejs.org/) v8.9 或以上。官方指南假设你已了解关于 HTML、CSS 和 JavaScript 的中级知识,并且已经完全掌握了 Vue 的正确开发方式。如果你刚开始学习前端或者 Vue,将 UI 框架作为你的第一步可能不是最好的主意。
+
+## 在线演示
+
+最简单的使用方式参照以下 CodeSandbox 演示,也推荐 Fork 本例来进行 `Bug Report`。
+
+- [](https://codesandbox.io/s/agitated-franklin-1w72v)
+
+## 引入 ant-design-vue
+
+### 1. 安装脚手架工具
+
+[vue-cli](https://github.com/vuejs/vue-cli)
+
+```bash
+$ npm install -g @vue/cli
+# OR
+$ yarn global add @vue/cli
+```
+
+### 2. 创建一个项目
+
+使用命令行进行初始化。
+
+```bash
+$ vue create antd-demo
+```
+
+并配置项目。
+
+若安装缓慢报错,可尝试用 `cnpm` 或别的镜像源自行安装:`rm -rf node_modules && cnpm install`。
+
+### 3. 使用组件
+
+```bash
+$ npm i --save ant-design-vue@next
+```
+
+**完整引入**
+
+```jsx
+import { createApp } from 'vue';
+import Antd from 'ant-design-vue';
+import App from './App';
+import 'ant-design-vue/dist/antd.css';
+
+const app = createApp();
+app.config.productionTip = false;
+
+app.use(Antd);
+```
+
+以上代码便完成了 Antd 的引入。需要注意的是,样式文件需要单独引入。
+
+**局部导入组件**
+
+```jsx
+import { createApp } from 'vue';
+import { Button, message } from 'ant-design-vue';
+import App from './App';
+
+const app = createApp(App);
+app.config.productionTip = false;
+
+/* 会自动注册 Button 下的子组件, 例如 Button.Group */
+app.use(Button).mount('#app');
+
+app.config.globalProperties.$message = message;
+```
+
+> 你可以在左侧菜单选用更多组件。
+
+## 兼容性
+
+Ant Design Vue 2.x 支持所有的现代浏览器。
+
+如果需要支持 IE9+,你可以使用 Ant Design Vue 1.x & Vue 2.x。
+
+对于 IE 系列浏览器,需要提供 [es5-shim](https://github.com/es-shims/es5-shim) 和 [es6-shim](https://github.com/paulmillr/es6-shim) 等 Polyfills 的支持。
+
+如果你使用了 babel,强烈推荐使用 [babel-polyfill](https://babeljs.io/docs/usage/polyfill/) 和 [babel-plugin-transform-runtime](https://babeljs.io/docs/plugins/transform-runtime/) 来替代以上两个 shim。
+
+> 避免同时使用 babel 和 shim 两种兼容方法,以规避 [#6512](https://github.com/ant-design/ant-design/issues/6512) 中所遇问题
+
+## 按需加载
+
+如果你仅需要加载使用的组件,可以通过以下的写法来按需加载组件。
+
+```jsx
+import Button from 'ant-design-vue/lib/button';
+import 'ant-design-vue/lib/button/style'; // 或者 ant-design-vue/lib/button/style/css 加载 css 文件
+```
+
+如果你使用了 babel,那么可以使用 [babel-plugin-import](https://github.com/ant-design/babel-plugin-import) 来进行按需加载,加入这个插件后。你可以仍然这么写:
+
+```jsx
+import { Button } from 'ant-design-vue';
+```
+
+插件会帮你转换成 `ant-design-vue/lib/xxx` 的写法。另外此插件配合 [style](https://github.com/ant-design/babel-plugin-import#usage) 属性可以做到模块样式的按需自动加载。
+
+> 注意,babel-plugin-import 的 `style` 属性除了引入对应组件的样式,也会引入一些必要的全局样式。如果你不需要它们,建议不要使用此属性。你可以 `import 'ant-design-vue/dist/antd.css` 手动引入,并覆盖全局样式。
+
+如果你使用的 Vite,你可以使用 [vite-plugin-components](https://github.com/antfu/vite-plugin-components) 来进行按需加载
+
+## 配置主题和字体
+
+- [改变主题](/docs/vue/customize-theme-cn)
+
+## 小贴士
+
+- 你可以享用 `npm` 生态圈里的所有模块。
+- 虽然 Vue 官方推荐模板编写组件,不过对于复杂组件,[jsx](https://github.com/vueComponent/jsx)未必不是一个更好的选择。
diff --git a/site/src/vueDocs/i18n.en-US.md b/site/src/vueDocs/i18n.en-US.md
new file mode 100644
index 000000000..859ba1bbc
--- /dev/null
+++ b/site/src/vueDocs/i18n.en-US.md
@@ -0,0 +1,70 @@
+# Internationalization
+
+The default language is English as of yet. If you want to use other languages, you can follow the instructions below.
+
+## ConfigProvider
+
+ant-design-vue provides a Vue Component [ConfigProvider](/components/config-provider) for configuring ant-design-vue locale text globally.
+
+```html
+
+
+
+
+
+
+
+```
+
+Note: `fr_FR` is the filename, follow below.
+
+Supported languages:
+
+| Language | Filename |
+| --------------------- | -------- |
+| Arabic | ar_EG |
+| Bulgarian | bg_BG |
+| Catalan | ca_ES |
+| Czech | cs_CZ |
+| German | de_DE |
+| Greek | el_GR |
+| English (Global) | en_GB |
+| English | en_US |
+| Spanish | es_ES |
+| Estonian | et_EE |
+| Persian | fa_IR |
+| Finnish | fi_FI |
+| French (Belgium) | fr_BE |
+| French (France) | fr_FR |
+| Italian | it_IT |
+| Icelandic | is_IS |
+| Japanese | ja_JP |
+| Korean | ko_KR |
+| Norwegian | nb_NO |
+| Dutch (Belgium) | nl_BE |
+| Dutch | nl_NL |
+| Polish | pl_PL |
+| Portuguese (Brazil) | pt_BR |
+| Portuguese | pt_PT |
+| Slovak | sk_SK |
+| Serbian | sr_RS |
+| Slovenian | sl_SI |
+| Swedish | sv_SE |
+| Thai | th_TH |
+| Turkish | tr_TR |
+| Russian | ru_RU |
+| Ukrainian | uk_UA |
+| Vietnamese | vi_VN |
+| Chinese (Simplified) | zh_CN |
+| Chinese (Traditional) | zh_TW |
+
+See usage at [ConfigProvider](/components/config-provider).
diff --git a/site/src/vueDocs/i18n.zh-CN.md b/site/src/vueDocs/i18n.zh-CN.md
new file mode 100644
index 000000000..aac3329c3
--- /dev/null
+++ b/site/src/vueDocs/i18n.zh-CN.md
@@ -0,0 +1,70 @@
+# 国际化
+
+`ant-design-vue` 目前的默认文案是英文,如果需要使用其他语言,可以参考下面的方案。
+
+## ConfigProvider
+
+ant-design-vue 提供了一个 Vue 组件 [ConfigProvider](/components/config-provider-cn) 用于全局配置国际化文案。
+
+```html
+
+
+
+
+
+
+
+```
+
+注意:`zh_CN` 是文件名,以下表格也遵循同样的规则。
+
+目前支持以下语言:
+
+| 语言 | 文件名 |
+| ---------------- | ------ |
+| 阿拉伯 | ar_EG |
+| 保加利亚语 | bg_BG |
+| 加泰罗尼亚语 | ca_ES |
+| 捷克语 | cs_CZ |
+| 德语 | de_DE |
+| 希腊语 | el_GR |
+| 英语 | en_GB |
+| 英语(美式) | en_US |
+| 西班牙语 | es_ES |
+| 爱沙尼亚语 | et_EE |
+| 波斯语 | fa_IR |
+| 芬兰语 | fi_FI |
+| 法语(比利时) | fr_BE |
+| 法语 | fr_FR |
+| 冰岛语 | is_IS |
+| 意大利语 | it_IT |
+| 日语 | ja_JP |
+| 韩语/朝鲜语 | ko_KR |
+| 挪威 | nb_NO |
+| 荷兰语(比利时) | nl_BE |
+| 荷兰语 | nl_NL |
+| 波兰语 | pl_PL |
+| 葡萄牙语(巴西) | pt_BR |
+| 葡萄牙语 | pt_PT |
+| 斯洛伐克语 | sk_SK |
+| 塞尔维亚 | sr_RS |
+| 斯洛文尼亚 | sl_SI |
+| 瑞典语 | sv_SE |
+| 泰语 | th_TH |
+| 土耳其语 | tr_TR |
+| 俄罗斯语 | ru_RU |
+| 乌克兰语 | uk_UA |
+| 越南语 | vi_VN |
+| 简体中文 | zh_CN |
+| 繁体中文 | zh_TW |
+
+具体的使用方法请参考 [ConfigProvider 文档](/components/config-provider-cn)。
diff --git a/site/src/vueDocs/introduce.en-US.md b/site/src/vueDocs/introduce.en-US.md
new file mode 100644
index 000000000..f524055d2
--- /dev/null
+++ b/site/src/vueDocs/introduce.en-US.md
@@ -0,0 +1,157 @@
+# Ant Design of Vue
+
+Following the Ant Design specification, we developed a Vue UI library `antd` that contains a set of high quality components and demos for building rich, interactive user interfaces.
+
+
+
+
+
+
+
+
+
+
+## Features
+
+- An enterprise-class UI design language for web applications.
+- A set of high-quality Vue components out of the box.
+- Shared [Ant Design of React](https://ant.design/docs/spec/introduce) design resources.
+
+## Environment Support
+
+- Modern browsers. You can use [1.x](https://1x.antdv.com/) for IE9+.
+- Server-side Rendering
+- [Electron](https://electronjs.org/)
+
+| [ ](http://godban.github.io/browsers-support-badges/) Edge | [ ](http://godban.github.io/browsers-support-badges/) Firefox | [ ](http://godban.github.io/browsers-support-badges/) Chrome | [ ](http://godban.github.io/browsers-support-badges/) Safari | [ ](http://godban.github.io/browsers-support-badges/) Opera | [ ](http://godban.github.io/browsers-support-badges/) Electron |
+| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+|Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
+
+## Version
+
+- Stable: [](https://www.npmjs.org/package/ant-design-vue)
+
+You can subscribe to this feed for new version notifications: https://github.com/vueComponent/ant-design-vue/releases.atom
+
+## Installation
+
+### Using npm or yarn
+
+**We recommend using npm or yarn to install**,it not only makes development easier,but also allow you to take advantage of the rich ecosystem of Javascript packages and tooling.
+
+```bash
+$ npm install ant-design-vue@next --save
+```
+
+```bash
+$ yarn add ant-design-vue@next
+```
+
+If you are in a bad network environment,you can try other registries and tools like [cnpm](https://github.com/cnpm/cnpm).
+
+### Import in Browser
+
+Add `script` and `link` tags in your browser and use the global variable `antd`.
+
+We provide `antd.js` `antd.css` and `antd.min.js` `antd.min.css` under `ant-design-vue/dist` in antd's npm package. You can also download these files directly from [](https://www.jsdelivr.com/package/npm/ant-design-vue) or [unpkg](https://unpkg.com/ant-design-vue/dist/).
+
+> **We strongly discourage loading the entire files** this will add bloat to your application and make it more difficult to receive bugfixes and updates. Antd is intended to be used in conjunction with a build tool, such as [webpack](https://webpack.github.io/), which will make it easy to import only the parts of antd that you are using.
+
+> Note: you should import [dayjs](https://day.js.org/) before using antd.js.
+
+## Usage
+
+```jsx
+import { DatePicker } from "ant-design-vue";
+app.use(DatePicker);
+```
+
+And import stylesheets manually:
+
+```jsx
+import "ant-design-vue/dist/antd.css"; // or 'ant-design-vue/dist/antd.less'
+```
+
+### Use modularized antd
+
+- Use [babel-plugin-import](https://github.com/ant-design/babel-plugin-import) (Recommended)
+
+ ```jsx
+ // .babelrc or babel-loader option
+ {
+ "plugins": [
+ ["import", { "libraryName": "ant-design-vue", "libraryDirectory": "es", "style": "css" }] // `style: true` for less
+ ]
+ }
+ ```
+
+ > Note: Don't set `libraryDirectory` if you are using webpack 1.
+
+ This allows you to import components from antd without having to manually import the corresponding stylesheet. The antd babel plugin will automatically import stylesheets.
+
+ ```jsx
+ // import js and css modularly, parsed by babel-plugin-import
+ import { DatePicker } from "ant-design-vue";
+ ```
+
+- Manually import
+
+ ```jsx
+ import DatePicker from "ant-design-vue/lib/date-picker"; // for js
+ import "ant-design-vue/lib/date-picker/style/css"; // for css
+ // import 'ant-design-vue/lib/date-picker/style'; // that will import less
+ ```
+
+- For Vite
+ ```js
+ // vite.config.js
+ import ViteComponents, { AntDesignVueResolver } from 'vite-plugin-components';
+
+ export default {
+ plugins: [
+ /* ... */
+ ViteComponents({
+ customComponentResolvers: [AntDesignVueResolver()],
+ }),
+ ],
+ };
+```
+
+## Links
+
+- [Home Page](https://www.antdv.com/)
+- [Ant Design Of React](https://ant.design/)
+- [Components](https://www.antdv.com/docs/vue/introduce)
+- [Change Log](/docs/vue/changelog)
+- [CodeSandbox template](https://codesandbox.io/s/agitated-franklin-1w72v) for bug reports
+- [Customize Theme](/docs/vue/customize-theme)
+- [FAQ](/docs/vue/faq)
+- [Support US](/docs/vue/sponsor)
+- [Awesome Ant Design](https://github.com/vueComponent/ant-design-vue-awesome)
+
+## Contributing
+
+If you'd like to help us improve antd, just create a [Pull Request](https://github.com/vueComponent/ant-design-vue/pulls). Feel free to report bugs and issues [here](https://vuecomponent.github.io/issue-helper/).
+
+> If you're new to posting issues, we ask that you read [_How To Ask Questions The Smart Way_](http://www.catb.org/~esr/faqs/smart-questions.html) and [How to Ask a Question in Open Source Community](https://github.com/seajs/seajs/issues/545) and [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) prior to posting. Well written bug reports help us help you!
+
+## About ant-design-vue
+
+As we all know, Ant Design, as a design language, has gone through many years of iteration and accumulation. Its UI design ideas have become a set of de facto standards and are sought after and loved by many front-end developers and enterprises, and it is also a magic weapon in the hands of React developers. I hope that ant-design-vue will allow Vue developers to enjoy the excellent design of Ant Design.
+
+The ant-design-vue is the Vue implementation of Ant Design. The style of the component is kept in sync with Ant Design. The html structure and css style of the component are also consistent. The style 0 modification is really achieved, and the component API is kept as consistent as possible.
+
+Ant Design Vue is committed to providing programmers with a ** pleasant ** development experience.
+
+## THANK YOU
+
+[Ant Design Team](https://github.com/ant-design/ant-design/blob/master/AUTHORS.txt)
diff --git a/site/src/vueDocs/introduce.zh-CN.md b/site/src/vueDocs/introduce.zh-CN.md
new file mode 100644
index 000000000..d94558700
--- /dev/null
+++ b/site/src/vueDocs/introduce.zh-CN.md
@@ -0,0 +1,160 @@
+
+# Ant Design of Vue
+
+这里是 Ant Design 的 Vue 实现,开发和服务于企业级后台产品。
+
+
+
+
+
+
+
+
+
+
+## 特性
+
+- 提炼自企业级中后台产品的交互语言和视觉风格。
+- 开箱即用的高质量 Vue 组件。
+- 共享[Ant Design of React](http://ant-design.gitee.io/docs/spec/introduce-cn)设计工具体系。
+
+## 支持环境
+
+- 现代浏览器, 如果需要支持 IE9,你可以选择使用 [1.x 版本](https://1x.antdv.com/)。
+- 支持服务端渲染。
+- [Electron](https://electronjs.org/)
+
+| [ ](http://godban.github.io/browsers-support-badges/) IE / Edge | [ ](http://godban.github.io/browsers-support-badges/) Firefox | [ ](http://godban.github.io/browsers-support-badges/) Chrome | [ ](http://godban.github.io/browsers-support-badges/) Safari | [ ](http://godban.github.io/browsers-support-badges/) Opera | [ ](http://godban.github.io/browsers-support-badges/) Electron |
+| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
+
+## 版本
+
+- 稳定版:[](https://www.npmjs.org/package/ant-design-vue)
+
+你可以订阅:https://github.com/vueComponent/ant-design-vue/releases.atom 来获得稳定版发布的通知。
+
+## 安装
+
+### 使用 npm 或 yarn 安装
+
+**我们推荐使用 npm 或 yarn 的方式进行开发**,不仅可在开发环境轻松调试,也可放心地在生产环境打包部署使用,享受整个生态圈和工具链带来的诸多好处。
+
+```bash
+$ npm install ant-design-vue@next --save
+```
+
+```bash
+$ yarn add ant-design-vue@next
+```
+
+如果你的网络环境不佳,推荐使用 [cnpm](https://github.com/cnpm/cnpm)。
+
+### 浏览器引入
+
+在浏览器中使用 `script` 和 `link` 标签直接引入文件,并使用全局变量 `antd`。
+
+我们在 npm 发布包内的 `ant-design-vue/dist` 目录下提供了 `antd.js` `antd.css` 以及 `antd.min.js` `antd.min.css`。你也可以通过 [](https://www.jsdelivr.com/package/npm/ant-design-vue) 或 [UNPKG](https://unpkg.com/ant-design-vue/dist/) 进行下载。
+
+> **强烈不推荐使用已构建文件**,这样无法按需加载,而且难以获得底层依赖模块的 bug 快速修复支持。
+
+> 注意:引入 antd.js 前你需要自行引入 [dayjs](https://day.js.org/)。
+
+## 示例
+
+```jsx
+import { DatePicker } from "ant-design-vue";
+app.use(DatePicker);
+```
+
+引入样式:
+
+```jsx
+import "ant-design-vue/dist/antd.css"; // or 'ant-design-vue/dist/antd.less'
+```
+
+### 按需加载
+
+下面两种方式都可以只加载用到的组件。
+
+- 使用 [babel-plugin-import](https://github.com/ant-design/babel-plugin-import)(推荐)。
+
+ ```jsx
+ // .babelrc or babel-loader option
+ {
+ "plugins": [
+ ["import", { "libraryName": "ant-design-vue", "libraryDirectory": "es", "style": "css" }] // `style: true` 会加载 less 文件
+ ]
+ }
+ ```
+
+ > 注意:webpack 1 无需设置 `libraryDirectory`。
+
+ 然后只需从 ant-design-vue 引入模块即可,无需单独引入样式。等同于下面手动引入的方式。
+
+ ```jsx
+ // babel-plugin-import 会帮助你加载 JS 和 CSS
+ import { DatePicker } from "ant-design-vue";
+ ```
+
+- 手动引入
+
+ ```jsx
+ import DatePicker from "ant-design-vue/lib/date-picker"; // 加载 JS
+ import "ant-design-vue/lib/date-picker/style/css"; // 加载 CSS
+ // import 'ant-design-vue/lib/date-picker/style'; // 加载 LESS
+ ```
+
+- Vite 按需
+ ```js
+ // vite.config.js
+ import ViteComponents, { AntDesignVueResolver } from 'vite-plugin-components';
+
+ export default {
+ plugins: [
+ /* ... */
+ ViteComponents({
+ customComponentResolvers: [AntDesignVueResolver()],
+ }),
+ ],
+ };
+ ```
+
+## 链接
+
+- [首页](https://www.antdv.com/)
+- [Ant Design Of React](https://ant.design/)
+- [组件库](https://www.antdv.com/docs/vue/introduce-cn)
+- [更新日志](/docs/vue/changelog-cn)
+- [CodeSandbox 模板](https://codesandbox.io/s/agitated-franklin-1w72v) for bug reports
+- [定制主题](/docs/vue/customize-theme-cn)
+- [常见问题](/docs/vue/faq-cn)
+- [支持我们](/docs/vue/sponsor-cn)
+- [Awesome Ant Design](https://github.com/vueComponent/ant-design-vue-awesome)
+
+## 如何贡献
+
+如果你希望参与贡献,欢迎 [Pull Request](https://github.com/vueComponent/ant-design-vue/pulls),或给我们 [报告 Bug](https://vuecomponent.github.io/issue-helper/)([国内镜像](http://ant-design-vue.gitee.io/issue-helper/))。
+
+> 强烈推荐阅读 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)、[《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545) 和 [《如何有效地报告 Bug》](http://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html)、[《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393),更好的问题更容易获得帮助。
+
+## 关于 ant-design-vue
+
+众所周知,Ant Design 作为一门设计语言面世,经历过多年的迭代和积累,它对 UI 的设计思想已经成为一套事实标准,受到众多前端开发者及企业的追捧和喜爱,也是 React 开发者手中的神兵利器。希望 ant-design-vue 能够让 Vue 开发者也享受到 Ant Design 的优秀设计。
+
+ant-design-vue 是 Ant Design 的 Vue 实现,组件的风格与 Ant Design 保持同步,组件的 html 结构和 css 样式也保持一致,真正做到了样式 0 修改,组件 API 也尽量保持了一致。
+
+Ant Design Vue 致力于提供给程序员**愉悦**的开发体验。
+
+## 特别感谢
+
+[Ant Design Team](https://github.com/ant-design/ant-design/blob/master/AUTHORS.txt)
diff --git a/site/src/vueDocs/migration-v2.en-US.md b/site/src/vueDocs/migration-v2.en-US.md
new file mode 100644
index 000000000..726afcdce
--- /dev/null
+++ b/site/src/vueDocs/migration-v2.en-US.md
@@ -0,0 +1,100 @@
+This document will help you upgrade from ant-design-vue `1.x` version to ant-design-vue `2.x` version.
+
+## Upgrade preparation
+
+1. Please upgrade to the latest version of 1.x first, and remove/modify related APIs according to the console warning message.
+2. Upgrade the project to Vue 3.0 and above.
+
+## 2.0 What are the incompatible changes
+
+### Design specification adjustment
+
+- Adjust the row height from `1.5`(`21px`) to `1.5715`(`22px`).
+- Basic round corner adjustment, changed from `4px` to `2px`.
+- The color brightness of the dividing line is reduced, from `#E8E8E8` to `#F0F0F0`.
+- The default background color of Table is changed from transparent to white.
+
+### Compatibility adjustment
+
+- The minimum supported version of Vue is Vue 3.0.
+
+#### Adjusted API
+
+- Removed LocaleProvider, please use `ConfigProvider` instead.
+- Removed the afterClose property of Tag.
+- Merged FormModel and Form, see the Form refactoring part below for details.
+- `tabIndex`, `maxLength`, `readOnly`, `autoComplete`, `autoFocus` are changed to all lowercase.
+- In order to use the slot more friendly in template syntax, all related to xxxRender, renderXxxx are changed to single parameter, involving `itemRender`, `renderItem`, `customRender`, `dropdownRender`, `dateCellRender`, `dateFullCellRender`, `monthCellRender`, `monthFullCellRender`, `renderTabBar`.
+- All the places where scopedSlots are configured are changed to slots.
+- `{ on, props, attrs, ... }` configuration is flattened, such as `{ props: {type:'xxx'}, on: {click: this.handleClick}}` changed to `{ type: 'xxx', onClick: this.handleClick }`, related fields: `okButtonProps`, `cancelButtonProps`.
+- Change xxx.sync to v-model:xxx
+- v-model is changed to v-model:xxx, which specifically involves components:
+
+ - The components changed from v-model to v-model:checked are: CheckableTag, Checkbox, Switch
+ - The components changed from v-model to v-model:value are: Radio, Mentions, CheckboxGroup, Rate, DatePicker、Select
+ - The components changed from v-model to v-model:visible are: Tag, Popconfirm, Popove, Tooltip, Moda, Dropdown
+ - The components changed from v-model to v-model:activeKey are: Collaps, Tabs
+ - The components changed from v-model to v-model:current are: Steps
+ - The components changed from v-model to v-model:selectedKeys are: Menu
+
+#### Icon Upgrade
+
+In `ant-design-vue@1.2.0`, we introduced the svg icon ([Why use the svg icon?](https://github.com/ant-design/ant-design/issues/10353)). The icon API that uses string naming cannot be loaded on demand, so the svg icon file is fully introduced, which greatly increases the size of the packaged product. In 2.0, we adjusted the icon usage API to support tree shaking, reducing the default package size by approximately 150 KB (Gzipped).
+
+The old way of using Icon will be obsolete:
+
+```html
+
+```
+
+In 2.0, an on-demand introduction method will be adopted:
+
+```html
+
+
+
+
+
+
+
+```
+
+#### Component refactoring
+
+In 1.x, we provide two form components, Form and FormModel. The original Form component uses v-decorator for data binding. In Vue2, we use context to force update components. However, in Vue3, due to the introduction of patchFlag, etc. Optimization method, forced refresh will destroy the performance advantage brought by patchFlag. So in version 2.0, we merged Form and FormModel, retained the use of FormModel, enriched related functions, and renamed it to Form.
+
+Involving changes:
+
+- Added `scrollToFirstError`, `name`, `validateTrigger` properties for Form, added `finish`, `finishFailed` events, and added `scrollToField` method.
+- Form.Item adds `validateFirst`, `validateTrigger`, and discards the `prop` attribute, and replaces it with `name`.
+- The nested field path uses an array. In the past version, we used. To represent the nested path (such as user.name to represent {user: {name:''} }). However, in some back-end systems, the variable name will also carry .. This causes users to need additional codes for conversion. Therefore, in the new version, nested paths are represented by arrays to avoid wrong handling behaviors (such as ['user','name']).
+- validateFields no longer supports callback. validateFields will return a Promise object, so you can perform corresponding error handling through async/await or then/catch. It is no longer necessary to determine whether errors is empty:
+
+```js
+// v1
+validateFields((err, value) => {
+ if (!err) {
+ // Do something with value
+ }
+});
+```
+
+Change to
+
+```js
+// v2
+validateFields().then(values => {
+ // Do something with value
+});
+```
+
+## Encounter problems
+
+V2 has made a lot of detailed improvements and refactorings. We have collected all known incompatible changes and related effects as much as possible, but there may still be some scenarios that we have not considered. If you encounter problems during the upgrade process, please go to [GitHub issues](https://vuecomponent.github.io/issue-helper/) for feedback. We will respond as soon as possible and improve this document accordingly.
diff --git a/site/src/vueDocs/migration-v2.zh-CN.md b/site/src/vueDocs/migration-v2.zh-CN.md
new file mode 100644
index 000000000..efa7af57f
--- /dev/null
+++ b/site/src/vueDocs/migration-v2.zh-CN.md
@@ -0,0 +1,102 @@
+本文档将帮助你从 ant-design-vue `1.x` 版本升级到 ant-design-vue `2.x` 版本。
+
+## 升级准备
+
+1. 请先升级到 1.x 的最新版本,按照控制台 warning 信息移除/修改相关的 API。
+2. 升级项目 Vue 3.0 以上。
+
+## 2.0 有哪些不兼容的变化
+
+### 设计规范调整
+
+- 行高从 `1.5`(`21px`) 调整为 `1.5715`(`22px`)。
+- 基础圆角调整,由`4px` 改为 `2px`。
+- 分割线颜色明度降低,由 `#E8E8E8` 改为 `#F0F0F0`。
+- Table 默认背景颜色从透明修改为白色。
+
+### 兼容性调整
+
+- 浏览器不再兼容 IE 11及以下版本。
+- Vue 最低支持版本为 Vue 3.0。
+
+#### 调整的 API
+
+- 移除了 LocaleProvider,请使用 `ConfigProvider` 替代。
+- 移除了 Tag 的 afterClose 属性。
+- 合并了 FormModel、Form,详见下方的 Form 重构部分。
+- `tabIndex`、`maxLength`、`readOnly`、`autoComplete`、`autoFocus` 更改为全小写。
+- 为了在 template 语法中更友好的使用插槽,所有涉及到 xxxRender, renderXxxx 的均改成单参数,涉及到 `itemRender`、`renderItem`、`customRender`、`dropdownRender`、`dateCellRender`、`dateFullCellRender`、`monthCellRender`、`monthFullCellRender`、`renderTabBar`。
+- 所有配置 scopedSlots 的地方统一改成 slots。
+- `{ on, props, attrs, ... }` 配置进行扁平化处理,如 `{ props: {type: 'xxx'}, on: {click: this.handleClick}}` 改成 `{ type: 'xxx', onClick: this.handleClick }`, 涉及相关字段:`okButtonProps`、`cancelButtonProps`。
+- xxx.sync 改成 v-model:xxx
+- v-model 更改成 v-model:xxx,具体涉及组件:
+
+ - v-model 改成 v-model:checked 的组件有: CheckableTag、Checkbox、Switch
+ - v-model 改成 v-model:value 的组件有: Radio、Mentions、CheckboxGroup、Rate、DatePicker、Select
+ - v-model 改成 v-model:visible 的组件有: Tag、Popconfirm、Popove、Tooltip、Moda、Dropdown
+ - v-model 改成 v-model:activeKey 的组件有: Collaps、Tabs
+ - v-model 改成 v-model:current 的组件有: Steps
+ - v-model 改成 v-model:selectedKeys 的组件有: Menu
+
+#### 图标升级
+
+在 `ant-design-vue@1.2.0` 中,我们引入了 svg 图标([为何使用 svg 图标?](https://github.com/ant-design/ant-design/issues/10353))。使用了字符串命名的图标 API 无法做到按需加载,因而全量引入了 svg 图标文件,这大大增加了打包产物的尺寸。在 2.0 中,我们调整了图标的使用 API 从而支持 tree shaking,减少默认包体积约 150 KB(Gzipped)。
+
+旧版 Icon 使用方式将被废弃:
+
+```html
+
+
+```
+
+2.0 中会采用按需引入的方式:
+
+```html
+
+
+
+
+
+
+
+```
+
+#### 组件重构
+
+在 1.x 中我们提供了 Form、FormModel 两个表单组件,原有的 Form 组件使用 v-decorator 进行数据绑定,在 Vue2 中我们通过上下文进行强制更新组件,但是在 Vue3 中,由于引入 patchFlag 等优化方式,强制刷新会破坏 patchFlag 带来的性能优势。所以在 2.0 版本中我们将 Form、FormModel 进行合并,保留了 FormModel 的使用方式,丰富了相关功能,并改名成 Form。
+
+涉及改动:
+
+- Form 新增 `scrollToFirstError`,`name`,`validateTrigger` 属性,新增 `finish`、`finishFailed` 事件,新增 `scrollToField` 方法。
+- Form.Item 新增 `validateFirst`, `validateTrigger`, 废弃 `prop` 属性,使用 `name` 替换。
+- 嵌套字段路径使用数组,过去版本我们通过 . 代表嵌套路径(诸如 user.name 来代表 { user: { name: '' } })。然而在一些后台系统中,变量名中也会带上 .。这造成用户需要额外的代码进行转化,因而新版中,嵌套路径通过数组来表示以避免错误的处理行为(如 ['user', 'name'])。
+- validateFields 不再支持 callback。validateFields 会返回 Promise 对象,因而你可以通过 async/await 或者 then/catch 来执行对应的错误处理。不再需要判断 errors 是否为空:
+
+```js
+// v1
+validateFields((err, value) => {
+ if (!err) {
+ // Do something with value
+ }
+});
+```
+
+改成
+
+```js
+// v2
+validateFields().then(values => {
+ // Do something with value
+});
+```
+
+## 遇到问题
+
+v2 做了非常多的细节改进和重构,我们尽可能收集了已知的所有不兼容变化和相关影响,但是有可能还是有一些场景我们没有考虑到。如果你在升级过程中遇到了问题,请到 [GitHub issues](https://vuecomponent.github.io/issue-helper/) 进行反馈。我们会尽快响应和相应改进这篇文档。
diff --git a/site/src/vueDocs/replace-date.en-US.md b/site/src/vueDocs/replace-date.en-US.md
new file mode 100644
index 000000000..1fb855415
--- /dev/null
+++ b/site/src/vueDocs/replace-date.en-US.md
@@ -0,0 +1,27 @@
+# custom date library
+
+Starting from the V3 version, the momentjs library is replaced by dayjs by default. If you need to use the momentjs or date-fns date library, you can replace it as follows:
+
+
+### 替换 DatePicker
+```js
+// moment or date-fns
+import DatePicker from 'ant-design-vue/es/date-picker/moment';
+import TimePicker from 'ant-design-vue/es/time-picker/moment';
+import Calendar from 'ant-design-vue/es/calendar/moment';
+// import DatePicker from 'ant-design-vue/es/date-picker/date-fns';
+// import TimePicker from 'ant-design-vue/es/time-picker/date-fns';
+// import Calendar from 'ant-design-vue/es/calendar/date-fns';
+import { createApp } from 'vue';
+import App from './App.vue';
+import antd from 'ant-design-vue';
+const app = createApp(App);
+app
+ .use(DatePicker)
+ .use(TimePicker)
+ .use(Calendar)
+ .use(antd)
+ .mount('#app');
+```
+
+> Note: If you need to register the ant-design-vue component library globally, then `use(DatePicker)` `use(TimePicker)` `use(Calendar)` must be executed before `use(antd)`, otherwise the default cannot be overridden dayjs version.
\ No newline at end of file
diff --git a/site/src/vueDocs/replace-date.zh-CN.md b/site/src/vueDocs/replace-date.zh-CN.md
new file mode 100644
index 000000000..489ec0709
--- /dev/null
+++ b/site/src/vueDocs/replace-date.zh-CN.md
@@ -0,0 +1,27 @@
+# 自定义时间库
+
+在 V3 版本开始,默认使用 dayjs 替换了 momentjs 库,如果你需要使用 momentjs 或者 date-fns 日期库,你可以通过如下方式替换:
+
+
+### 替换 DatePicker
+```js
+// moment 或者 date-fns
+import DatePicker from 'ant-design-vue/es/date-picker/moment';
+import TimePicker from 'ant-design-vue/es/time-picker/moment';
+import Calendar from 'ant-design-vue/es/calendar/moment';
+// import DatePicker from 'ant-design-vue/es/date-picker/date-fns';
+// import TimePicker from 'ant-design-vue/es/time-picker/date-fns';
+// import Calendar from 'ant-design-vue/es/calendar/date-fns';
+import { createApp } from 'vue';
+import App from './App.vue';
+import antd from 'ant-design-vue';
+const app = createApp(App);
+app
+ .use(DatePicker)
+ .use(TimePicker)
+ .use(Calendar)
+ .use(antd)
+ .mount('#app');
+```
+
+> 注意: 如果你需要全局注册 ant-design-vue 组件库,那么 `use(DatePicker)` `use(TimePicker)` `use(Calendar)` 必须在 `use(antd)` 之前执行,否则无法覆盖默认的 dayjs 版本。
\ No newline at end of file
diff --git a/site/src/vueDocs/sponsor.en-US.md b/site/src/vueDocs/sponsor.en-US.md
new file mode 100644
index 000000000..5e90c1089
--- /dev/null
+++ b/site/src/vueDocs/sponsor.en-US.md
@@ -0,0 +1,71 @@
+# Sponsor Ant Design Vue Development
+
+ant-design-vue is an MIT licensed open source project and completely free to use. However, in order to continue the healthy and sustainable development of the project, we expect to receive corresponding financial support. You can support ant-design-vue development via the following methods:
+
+## One-time Donations
+
+We accept donations through these channels:
+
+
+
+## Recurring Pledges
+
+Recurring pledges come with exclusive perks, e.g. having your name listed in the Vue GitHub repository, or have your company logo placed on this website.
+
+
+
+## Corporate sponsorship
+
+You can consult me by email [antdv@foxmail.com](antdv@foxmail.com).
+
+## Current sponsors
+
+### Sponsors
+
+Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/ant-design-vue#sponsor)]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+### Backers
+
+Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/ant-design-vue#backer)]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+If you run a business and are using Ant Design Vue in a revenue-generating product, it makes business sense to sponsor Ant Design Vue development: it ensures the project that your product relies on stays healthy and actively maintained. It can also help your exposure in the Vue community and makes it easier to attract Vue developers.
+
+If you are an individual user and have enjoyed the productivity of using Ant Design Vue, consider donating as a sign of appreciation - like buying me coffee once in a while :)
diff --git a/site/src/vueDocs/sponsor.zh-CN.md b/site/src/vueDocs/sponsor.zh-CN.md
new file mode 100644
index 000000000..205d1d8fe
--- /dev/null
+++ b/site/src/vueDocs/sponsor.zh-CN.md
@@ -0,0 +1,75 @@
+# 赞助 Ant Design Vue 的研发
+
+ant-design-vue 是采用 MIT 许可的开源项目,使用完全免费。 但为了项目能够健康持续的发展下去,我们期望获得相应的资金支持。 你可以通过下列的方法来赞助我们的开发。
+
+## 一次性赞助
+
+你可以通过以下任意一种方式赞助:
+
+
+
+## 购买贴纸
+
+你也可以通过 购买我们官方授权的贴纸 的方式来支持 Ant Design Vue - 每售出一张贴纸,我们将获得 2 元的捐赠。
+
+## 周期性赞助
+
+周期性赞助可以获得额外的回报,比如你的名字会出现在 Vue 的 GitHub 仓库中,再比如你的公司 logo 会出现在我们的官网上。
+
+
+
+## 企业赞助
+
+您可以通过邮件咨询[antdv@foxmail.com](antdv@foxmail.com)。
+
+## 当前的赞助商
+
+### Sponsors
+
+Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/ant-design-vue#sponsor)]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+### Backers
+
+Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/ant-design-vue#backer)]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+如果你是企业经营者并且将 Ant Design Vue 用在商业产品中,那么赞助 Ant Design Vue 有商业上的益处:可以让你的产品所依赖的框架保持健康并得到积极的维护,也能帮助你在 Vue 社区里获得更高的曝光度,从而更容易地吸引到 Vue 开发者。
+
+如果你是个人开发者并且享受 Ant Design Vue 带来的高开发效率,可以用捐助来表示你的谢意 —— 就好像偶尔给我买杯咖啡 :)
diff --git a/site/src/vueDocs/use-with-vue-cli.en-US.md b/site/src/vueDocs/use-with-vue-cli.en-US.md
new file mode 100644
index 000000000..9ce64d62a
--- /dev/null
+++ b/site/src/vueDocs/use-with-vue-cli.en-US.md
@@ -0,0 +1,167 @@
+# Use in vue-cli 3
+
+[vue-cli](https://github.com/vuejs/vue-cli) is one of the best Vue application development tools. We are going to use `antd` within it and modify the webpack config for some customized needs.
+
+## Install and Initialization
+
+We need to install `vue-cli` first, you may need install [yarn](https://github.com/yarnpkg/yarn/) too.
+
+```bash
+$ npm install -g @vue/cli
+# OR
+$ yarn global add @vue/cli
+```
+
+Create a new project named `antd-demo`.
+
+```bash
+$ vue create antd-demo
+```
+
+And, setup your vue project configuration.
+
+The tool will create and initialize environment and dependencies automatically, please try config your proxy setting or use another npm registry if any network errors happen during it.
+
+Then we go inside `antd-demo` and start it.
+
+```bash
+$ cd antd-demo
+$ npm run serve
+```
+
+Open the browser at http://localhost:8080/. It renders a header saying "Welcome to Your Vue.js App" on the page.
+
+## Import antd
+
+Below is the default directory structure.
+
+```null
+├── README.md
+├── babel.config
+├── package.json
+├── public
+│ ├── favicon.ico
+│ └── index.html
+├── src
+│ ├── assets
+│ │ └── logo.png
+│ ├── components
+│ │ └── HelloWorld.vue
+│ ├── App.vue
+│ └── main.js
+└── yarn.lock
+```
+
+Now we install `ant-design-vue` from yarn or npm.
+
+```bash
+$ yarn add ant-design-vue
+```
+
+Modify `src/main.js`, import Button component from `antd`.
+
+```jsx
+import Vue from 'vue';
+import Button from 'ant-design-vue/lib/button';
+import 'ant-design-vue/dist/antd.css';
+import App from './App';
+
+const app = createApp(App);
+
+/* Automatically register components under Button, such as Button.Group */
+app.use(Button).mount('#app');
+```
+
+Modify `src/App.vue`。
+
+```jsx
+
+
+
+
Button>
+
+
+
+...
+```
+
+Ok, you should now see a blue primary button displayed on the page. Next you can choose any components of `antd` to develop your application. Visit other workflows of `vue-cli` at its [User Guide ](https://github.com/vuejs/vue-cli/blob/master/README.md).
+
+## Advanced Guides
+
+We are successfully running antd components now but in the real world, there are still lots of problems about antd-demo. For instance, we actually import all styles of components in the project which may be a network performance issue.
+
+Now we need to customize the default webpack config.
+
+### Use babel-plugin-import
+
+[babel-plugin-import](https://github.com/ant-design/babel-plugin-import) is a babel plugin for importing components on demand ([How does it work?](/docs/vue/getting-started/#Import-on-Demand)).
+
+```bash
+$ yarn add babel-plugin-import --dev
+```
+
+#### if you use vue-cli 2
+
+Modify `.babelrc`.
+
+```diff
+ {
+ "presets": [
+ ["env", {
+ "modules": false,
+ "targets": {
+ "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
+ }
+ }],
+ "stage-2"
+ ],
+- "plugins": ["transform-vue-jsx", "transform-runtime"]
++ "plugins": [
++ "transform-vue-jsx",
++ "transform-runtime",
++ ["import", { "libraryName": "ant-design-vue", "libraryDirectory": "es", "style": "css" }]
++ ]
+ }
+```
+
+#### if you use vue-cli 3
+
+Modify `babel.config.js`
+
+```diff
+ module.exports = {
+ presets: ["@vue/app"],
++ plugins: [
++ [
++ "import",
++ { libraryName: "ant-design-vue", libraryDirectory: "es", style: true }
++ ]
++ ]
+};
+```
+
+Remove the `import 'ant-design-vue/dist/antd.css';` statement added before because `babel-plugin-import` will import styles and import components like below:
+
+```diff
+ // src/main.js
+ import Vue from 'vue'
+- import Button from 'ant-design-vue/lib/button';
++ import { Button } from 'ant-design-vue';
+- import 'ant-design-vue/dist/antd.css'
+ import App from './App'
+
+ Vue.component(Button.name, Button)
+
+ Vue.config.productionTip = false
+
+ new Vue({
+ render: h => h(App)
+ }).$mount('#app')
+```
+
+Then reboot with `npm run dev` and visit the demo page, you should not find any [warning messages](https://zos.alipayobjects.com/rmsportal/vgcHJRVZFmPjAawwVoXK.png) in the console, which prove that the `import on demand` config is working now. You will find more info about it in [this guide](/docs/vue/getting-started/#Import-on-Demand).
+
+### Customize Theme
+
+According to the [Customize Theme documentation](/docs/vue/customize-theme), to customize the theme, we need to modify `less` variables with tools such as [less-loader](https://github.com/webpack/less-loader).
diff --git a/site/src/vueDocs/use-with-vue-cli.zh-CN.md b/site/src/vueDocs/use-with-vue-cli.zh-CN.md
new file mode 100644
index 000000000..6b4362fc4
--- /dev/null
+++ b/site/src/vueDocs/use-with-vue-cli.zh-CN.md
@@ -0,0 +1,167 @@
+# 在 vue-cli 3 中使用
+
+[vue-cli](https://github.com/vuejs/vue-cli) 是业界最优秀的 Vue 应用开发工具之一,本文会尝试在 vue-cli 创建的工程中使用 antd 组件,并自定义 webpack 的配置以满足各类工程化需求。
+
+## 安装和初始化
+
+我们需要在命令行中安装 vue-cli 工具,你可能还需要安装 [yarn](https://github.com/yarnpkg/yarn/)。
+
+```bash
+$ npm install -g @vue/cli
+# OR
+$ yarn global add @vue/cli
+```
+
+然后新建一个项目。
+
+```bash
+$ vue create antd-demo
+```
+
+并配置项目。
+
+工具会自动初始化一个脚手架并安装 Vue 项目的各种必要依赖,如果在过程中出现网络问题,请尝试配置代理或使用其他 npm registry。
+
+然后我们进入项目并启动。
+
+```bash
+$ cd antd-demo
+$ npm run serve
+```
+
+此时浏览器会访问 http://localhost:8080/ ,看到 `Welcome to Your Vue.js App` 的界面就算成功了。
+
+## 引入 antd
+
+这是 vue-cli 生成的默认目录结构。
+
+```null
+├── README.md
+├── babel.config
+├── package.json
+├── public
+│ ├── favicon.ico
+│ └── index.html
+├── src
+│ ├── assets
+│ │ └── logo.png
+│ ├── components
+│ │ └── HelloWorld.vue
+│ ├── App.vue
+│ └── main.js
+└── yarn.lock
+```
+
+现在从 yarn 或 npm 安装并引入 ant-design-vue。
+
+```bash
+$ yarn add ant-design-vue
+```
+
+修改 `src/main.js`,引入 antd 的按钮组件以及全部样式文件。
+
+```jsx
+import Vue from 'vue';
+import Button from 'ant-design-vue/lib/button';
+import 'ant-design-vue/dist/antd.css';
+import App from './App';
+
+const app = createApp(App);
+app.config.productionTip = false;
+
+/* 会自动注册 Button 下的子组件, 例如 Button.Group */
+app.use(Button).mount('#app');
+```
+
+修改 `src/App.vue`的 template 内容。
+
+```jsx
+
+
+
+
Button>
+
+
+...
+```
+
+好了,现在你应该能看到页面上已经有了 antd 的蓝色按钮组件,接下来就可以继续选用其他组件开发应用了。其他开发流程你可以参考 vue-cli 的[官方文档](https://github.com/vuejs/vue-cli/blob/master/README.md)。
+
+## 高级配置
+
+我们现在已经把组件成功运行起来了,但是在实际开发过程中还有很多问题,例如上面的例子实际上加载了全部的 antd 组件的样式(对前端性能是个隐患)。
+
+此时我们需要对 vue-cli 的默认配置进行自定义。
+
+### 使用 babel-plugin-import
+
+[babel-plugin-import](https://github.com/ant-design/babel-plugin-import) 是一个用于按需加载组件代码和样式的 babel 插件([原理](/docs/vue/getting-started-cn/#按需加载))。
+
+```bash
+$ yarn add babel-plugin-import --dev
+```
+
+#### 使用 vue-cli 2 的小伙伴
+
+修改`.babelrc`文件,配置 babel-plugin-import
+
+```diff
+ {
+ "presets": [
+ ["env", {
+ "modules": false,
+ "targets": {
+ "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
+ }
+ }],
+ "stage-2"
+ ],
+- "plugins": ["transform-vue-jsx", "transform-runtime"]
++ "plugins": [
++ "transform-vue-jsx",
++ "transform-runtime",
++ ["import", { "libraryName": "ant-design-vue", "libraryDirectory": "es", "style": "css" }]
++ ]
+ }
+```
+
+#### 使用 vue-cli 3 的小伙伴
+
+修改`babel.config.js`文件,配置 babel-plugin-import
+
+```diff
+ module.exports = {
+ presets: ["@vue/app"],
++ plugins: [
++ [
++ "import",
++ { libraryName: "ant-design-vue", libraryDirectory: "es", style: true }
++ ]
++ ]
+};
+```
+
+然后移除前面在 `src/main.js` 里全量添加的 `import 'ant-design-vue/dist/antd.css';` 样式代码,并且按下面的格式引入模块。
+
+```diff
+ // src/main.js
+ import Vue from 'vue'
+- import Button from 'ant-design-vue/lib/button';
++ import { Button } from 'ant-design-vue';
+- import 'ant-design-vue/dist/antd.css'
+ import App from './App'
+
+ Vue.component(Button.name, Button)
+
+ Vue.config.productionTip = false
+
+ new Vue({
+ render: h => h(App)
+ }).$mount("#app");
+```
+
+最后重启 `npm run serve` 访问页面,antd 组件的 js 和 css 代码都会按需加载,你在控制台也不会看到这样的[警告信息](https://zos.alipayobjects.com/rmsportal/vgcHJRVZFmPjAawwVoXK.png)。关于按需加载的原理和其他方式可以阅读[这里](/docs/vue/getting-started-cn/#按需加载)。
+
+### 自定义主题
+
+按照 [配置主题](/docs/vue/customize-theme-cn) 的要求,自定义主题需要用到 less 变量覆盖功能。
diff --git a/site/typings/index.d.ts b/site/typings/index.d.ts
new file mode 100644
index 000000000..799016fa7
--- /dev/null
+++ b/site/typings/index.d.ts
@@ -0,0 +1,8 @@
+declare module '*.json' {
+ const value: any;
+ export const version: string;
+ export default value;
+}
+
+export * from '../plugin/docs';
+export * from '../plugin/md';
diff --git a/site/typings/shared.d.ts b/site/typings/shared.d.ts
new file mode 100644
index 000000000..c8da68bd5
--- /dev/null
+++ b/site/typings/shared.d.ts
@@ -0,0 +1,42 @@
+// types shared between server and client
+
+export interface LocaleConfig {
+ lang: string;
+ title?: string;
+ description?: string;
+ head?: HeadConfig[];
+ label?: string;
+ selectText?: string;
+}
+
+export interface SiteData {
+ base: string;
+ lang: string;
+ title: string;
+ description: string;
+ head: HeadConfig[];
+ themeConfig: ThemeConfig;
+ locales: Record;
+}
+
+export type HeadConfig =
+ | [string, Record]
+ | [string, Record, string];
+
+export interface PageData {
+ relativePath: string;
+ title: string;
+ description: string;
+ headers: Header[];
+ frontmatter: Record;
+ lastUpdated: number;
+ content?: string;
+ html?: string;
+}
+
+export interface Header {
+ level: number;
+ title: string;
+ slug: string;
+ content: string;
+}
diff --git a/vite.config.ts b/vite.config.ts
index 3e8e4b53e..0637a8b7e 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -10,7 +10,6 @@ import vueJsx from '@vitejs/plugin-vue-jsx';
export default {
resolve: {
alias: {
- // moment: 'moment/dist/moment.js',
vue: 'vue/dist/vue.esm-bundler.js',
'ant-design-vue': path.resolve(__dirname, './components'),
},
@@ -34,7 +33,7 @@ export default {
'lodash-es',
'vue',
'vue-router',
- 'moment',
+ 'dayjs',
'async-validator',
],
},