commit e320f43c2d0a7ca425ad3b8d49d24d8efe4ff545 Author: qingwei.li Date: Wed Jul 27 14:15:02 2016 +0800 Initial commit diff --git a/.babelrc b/.babelrc new file mode 100644 index 000000000..1e2392943 --- /dev/null +++ b/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["es2015"], + "comments": false +} diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..9385523f1 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +popper.js +fecha.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..75310fc75 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,9 @@ +{ + "plugins": ['vue'], + "extends": 'elemefe', + "parserOptions": { + "ecmaFeatures": { + "experimentalObjectRestSpread": true + } + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..f823b3750 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +node_modules +.DS_Store +npm-debug.log +lerna-debug.log +npm-debug.log.* +lerna-debug.log.* +lib +.idea +examples/element-ui +fe.element/element-ui diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..3a8e31723 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,65 @@ +## 更新日志 + +### 0.2.0 + +`2016-07-29` + +- 增加 grid 相关组件 +- form-item 增加 required 属性 + +*非兼容性更新* + +- progress 的 percentage 属性值范围从 0~1 改变为 1~100. + +### 0.1.9 + +`2016-07-12` + +- 增加 menu 组件 + +*非兼容性更新* + +- 使用了新的图标集,原有的某些图标可能不存在了 + +### 0.1.8 + +`2016-07-07` + +- dropdown 和 tooltip 首次出现闪动问题 + +### 0.1.7 + +`2016-07-06` + +- dropdown 增加触发下拉菜单方式的参数 +- 修正若干组件的样式问题和浏览器兼容问题 +- 修复 popper.js 产生的过多 scroll 事件绑定 +- 在 radio-group 里增加 radio-button 类型 +- 为 select 添加 singleCancelable 选项 + +### 0.1.6 + +`2016-06-30` +- dropdown-menu 改为动态创建 +- 修复 textarea 缺失 model 参数的 bug +- 修复 pagination 和 input-group 在 safari 下的样式问题 + +### 0.1.5 + +`2016-06-29` +- 为 MessageBox 和 Notification 注册全局方法 +- button 的 loading 状态时改为不触发 hover 和点击等操作 +- input组件 增加 maxlength 和 minlength 属性支持 + +### 0.1.4 + +`2016-06-29` + +- 增加 Tabs、Form、MessageBox、Alert、Notification、Slider 组件 +- dropdown menu 现在是直接插入到 body 节点下,并且修正了自适应宽度的 bug +- dropdown item 点击后关闭 dropdown menu +- radio 绑定值增加 Number 类型支持 +- 增加 radio-group 组件 +- 增加 checkbox-group 组件 +- Select 组件 API 更新,现在的用法和原生 select 标签更加相似 + diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..0b7bd180e --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +.PHONY: dist +default: help + +# build all theme +build-theme: build-theme-default + +build-theme-default: + packages/theme-default/node_modules/.bin/gulp build --gulpfile packages/theme-default/gulpfile.js + cp -rf packages/theme-default/lib lib/theme-default + +install: + npm i --registry=http://registry.npm.taobao.org --ignore-scripts --loglevel=error + ./node_modules/.bin/lerna bootstrap + +dev: install + node bin/iconInit.js + npm run dev + +new: + node bin/new.js $(filter-out $@,$(MAKECMDGOALS)) + +dist: + npm run dist + make build-theme + +dist-all: + node bin/build-all.js + make build-theme + +deploy: install + @npm run deploy + @rm -rf fe.element/element-ui + @cp -r examples/element-ui fe.element + +gh-docs: + @npm run gh-docs + +pub: + ./node_modules/.bin/kp $(filter-out $@,$(MAKECMDGOALS)) + +pub-all: dist-all + ./node_modules/.bin/lerna publish + +help: + @echo " \033[35mmake\033[0m \033[1m命令使用说明\033[0m" + @echo " \033[35mmake install\033[0m\t\033[0m\t\033[0m\t\033[0m\t--- 安装依赖" + @echo " \033[35mmake new [中文名]\033[0m\t--- 创建新组件 package. 例如 'make new button 按钮'" + @echo " \033[35mmake dev\033[0m\t\033[0m\t\033[0m\t\033[0m\t--- 开发模式" + @echo " \033[35mmake dist\033[0m\t\033[0m\t\033[0m\t\033[0m\t--- 编译项目,生成目标文件" + @echo " \033[35mmake dist-all\033[0m\t\033[0m\t\033[0m\t--- 分别编译每个组件项目" + @echo " \033[35mmake deploy\033[0m\t\033[0m\t\033[0m\t\033[0m\t--- 部署 demo" + @echo " \033[35mmake pub\033[0m\t\033[0m\t\033[0m\t\033[0m\t--- 发布到 npm 上" + @echo " \033[35mmake pub-all\033[0m\t\033[0m\t\033[0m\t\033[0m\t--- 发布各组件到 npm 上" diff --git a/README.md b/README.md new file mode 100644 index 000000000..3821ec685 --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +# Element +> UI Elements for admin page. + +## Demo/Docs +https://element-component.github.io/element + +## Usages +```shell +npm i element-ui -S +``` + +## Quick Start +use [babel-plugin-component](https://github.com/QingWei-Li/babel-plugin-component) + +``` javascript +import Vue from 'vue' +import Element from 'element-ui' + +Vue.use(Element) + +// or +import { + Select, + Button + // ... +} from 'element-ui' + +Vue.component(Select.name, ElSelect) +Vue.component(Button.name, Button) +``` + +(roughly) to + +``` javascript +import Vue from 'vue' +import Element from 'element-ui' +import 'element-ui/lib/theme-default/index.css'; + +Vue.use(Element) + +// or +import Select from 'element-ui/lib/select'; +import Select from 'element-ui/lib/theme-default/select.css'; +import Button from 'element-ui/lib/button'; +import Button from 'element-ui/lib/theme-default/button.css'; + +Vue.component(Select.name, ElSelect) +Vue.component(Button.name, Button) +``` + +## babel-plugin-component +.babelrc +```json +{ + "plugins": ["xxx", ["component", [ + { + "libraryName": "element-ui", + "styleLibraryName": "theme-default" + } + ]]] +} +``` + +## LICENSE +MIT + diff --git a/bin/build-all.js b/bin/build-all.js new file mode 100644 index 000000000..8c52544ef --- /dev/null +++ b/bin/build-all.js @@ -0,0 +1,26 @@ +'use strict'; + +const components = require('../components.json'); +const execSync = require('child_process').execSync; +const existsSync = require('fs').existsSync; +const path = require('path'); + +let componentPaths = []; + +delete components.index; +delete components.font; + +Object.keys(components).forEach(key => { + const filePath = path.join(__dirname, `../packages/${key}/cooking.conf.js`); + + if (existsSync(filePath)) { + componentPaths.push(`packages/${key}/cooking.conf.js`) + } +}); + +const paths = componentPaths.join(','); +const cli = `cooking build -c ${paths} -p`; + +execSync(cli, { + stdio: 'inherit' +}); diff --git a/bin/build-entry.js b/bin/build-entry.js new file mode 100644 index 000000000..3a7b47d5d --- /dev/null +++ b/bin/build-entry.js @@ -0,0 +1,70 @@ +var Components = require('../components.json') +var fs = require('fs') +var render = require('json-templater/string') +var uppercamelcase = require('uppercamelcase') +var path = require('path') + +var OUTPUT_PATH = path.join(__dirname, '../src/index.js') +var IMPORT_TEMPLATE = `import {{name}} from '../packages/{{package}}/index.js';` +var ISNTALL_COMPONENT_TEMPLATE = ` Vue.component({{name}}.name, {{name}});` +var MAIN_TEMPLATE = `{{include}} + +const install = function(Vue) { +{{install}} + + Vue.use(Loading); + + Vue.prototype.$msgbox = MessageBox; + Vue.prototype.$alert = MessageBox.alert; + Vue.prototype.$confirm = MessageBox.confirm; + Vue.prototype.$prompt = MessageBox.prompt; + Vue.prototype.$notify = Notification; +}; + +// auto install +if (typeof window !== 'undefined' && window.Vue) { + install(window.Vue); +}; + +module.exports = { + install, +{{list}} +}; +` + +delete Components.font + +var ComponentNames = Object.keys(Components) + +var includeComponentTemplate = [] +var installTemplate = [] +var listTemplate = [] + +ComponentNames.forEach(name => { + var componentName = uppercamelcase(name) + + includeComponentTemplate.push(render(IMPORT_TEMPLATE, { + name: componentName, + package: name + })) + + + if (['Loading', 'MessageBox', 'Notification'].indexOf(componentName) === -1) { + installTemplate.push(render(ISNTALL_COMPONENT_TEMPLATE, { + name: componentName, + component: name + })) + } + + listTemplate.push(` ${componentName}`) +}) + +var template = render(MAIN_TEMPLATE, { + include: includeComponentTemplate.join('\n'), + install: installTemplate.join('\n'), + list: listTemplate.join(',\n') +}) + +fs.writeFileSync(OUTPUT_PATH, template) +console.log('[build entry] DONE:', OUTPUT_PATH) + diff --git a/bin/iconInit.js b/bin/iconInit.js new file mode 100644 index 000000000..5f5df7db3 --- /dev/null +++ b/bin/iconInit.js @@ -0,0 +1,20 @@ +'use strict'; + +var postcss = require('postcss'); +var fs = require('fs'); +var path = require('path'); +var fontFile = fs.readFileSync(path.resolve(__dirname, '../packages/theme-default/src/icon.css'), 'utf8'); +var nodes = postcss.parse(fontFile).nodes; +var classList = []; + +nodes.forEach((node) => { + var selector = node.selector || ''; + var reg = new RegExp(/\.el-icon-([^:]+):before/); + var arr = selector.match(reg); + + if (arr && arr[1]) { + classList.push(arr[1]); + } +}) + +fs.writeFile(path.resolve(__dirname, '../examples/icon.json'), JSON.stringify(classList)); diff --git a/bin/new.js b/bin/new.js new file mode 100644 index 000000000..253eaa5f5 --- /dev/null +++ b/bin/new.js @@ -0,0 +1,133 @@ +'use strict'; + +console.log(); +process.on('exit', () => { + console.log(); +}); + +if (!process.argv[2]) { + console.error('[组件名]必填.'); + process.exit(1); +} + +const path = require('path'); +const fileSave = require('file-save'); +const uppercamelcase = require('uppercamelcase'); +const componentname = process.argv[2]; +const chineseName = process.argv[3] || componentname; +const ComponentName = uppercamelcase(componentname); +const PackagePath = path.resolve(__dirname, '../packages', componentname); +const Files = [ + { + filename: 'index.js', + content: `const ${ComponentName} = require('./src/main'); + +${ComponentName}.install = function(Vue) { + Vue.component(${ComponentName}.name, ${ComponentName}); +}; + +module.exports = ${ComponentName};` + }, + { + filename: 'cooking.conf.js', + content: `var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'El${ComponentName}', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve();` + }, + { + filename: 'package.json', + content: `{ + "name": "el-${componentname}", + "version": "0.0.0", + "description": "A ${componentname} component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/${componentname}", + "author": "elemefe", + "license": "MIT", + "dependencies": {} +}` + }, + { + filename: 'src/main.vue', + content: ` + +` + }, + { + filename: path.join('../../examples/docs/', `${componentname}.md`), + content:`## ${chineseName}` + } +] + +// 添加到 components.json +const componentsFile = require('../components.json'); +if (componentsFile[componentname]) { + console.error(`${componentname} 已存在.`); + process.exit(1); +} +componentsFile[componentname] = [`./packages/${componentname}/index.js`]; +fileSave(path.join(__dirname, '../components.json')) + .write(JSON.stringify(componentsFile, null, ' '), 'utf8') + .end('\n') + +// 创建 package +Files.forEach(file => { + fileSave(path.join(PackagePath, file.filename)) + .write(file.content, 'utf8') + .end('\n') +}) + +// 添加到 nav.config.json +const navConfigFile = require('../examples/nav.config.json'); +navConfigFile[navConfigFile.length - 1].list.push({ + path: `/${componentname}`, + name: `${chineseName} (${componentname})`, + title: componentname === chineseName ? + componentname : + `${componentname} ${chineseName}` +}); + +fileSave(path.join(__dirname, '../examples/nav.config.json')) + .write(JSON.stringify(navConfigFile, null, ' '), 'utf8') + .end('\n') + +console.log('DONE!'); diff --git a/components.json b/components.json new file mode 100644 index 000000000..90a32fd66 --- /dev/null +++ b/components.json @@ -0,0 +1,155 @@ +{ + "group": [ + "./packages/group/index.js" + ], + "select-dropdown": [ + "./packages/select-dropdown/index.js" + ], + "pagination": [ + "./packages/pagination/index.js" + ], + "dialog": [ + "./packages/dialog/index.js" + ], + "cascader": [ + "./packages/cascader/index.js" + ], + "autocomplete": [ + "./packages/autocomplete/index.js" + ], + "dropdown": [ + "./packages/dropdown/index.js" + ], + "dropdown-item": [ + "./packages/dropdown-item/index.js" + ], + "menu": [ + "./packages/menu/index.js" + ], + "submenu": [ + "./packages/submenu/index.js" + ], + "menu-item": [ + "./packages/menu-item/index.js" + ], + "input": [ + "./packages/input/index.js" + ], + "input-number": [ + "./packages/input-number/index.js" + ], + "input-group": [ + "./packages/input-group/index.js" + ], + "radio": [ + "./packages/radio/index.js" + ], + "radio-group": [ + "./packages/radio-group/index.js" + ], + "radio-button": [ + "./packages/radio-button/index.js" + ], + "checkbox": [ + "./packages/checkbox/index.js" + ], + "checkbox-group": [ + "./packages/checkbox-group/index.js" + ], + "switch": [ + "./packages/switch/index.js" + ], + "select": [ + "./packages/select/index.js" + ], + "option": [ + "./packages/option/index.js" + ], + "option-group": [ + "./packages/option-group/index.js" + ], + "button": [ + "./packages/button/index.js" + ], + "button-group": [ + "./packages/button-group/index.js" + ], + "table": [ + "./packages/table/index.js" + ], + "table-column": [ + "./packages/table-column/index.js" + ], + "date-picker": [ + "./packages/date-picker/index.js" + ], + "time-select": [ + "./packages/time-select/index.js" + ], + "time-picker": [ + "./packages/time-picker/index.js" + ], + "popover": [ + "./packages/popover/index.js" + ], + "tooltip": [ + "./packages/tooltip/index.js" + ], + "message-box": [ + "./packages/message-box/index.js" + ], + "breadcrumb": [ + "./packages/breadcrumb/index.js" + ], + "breadcrumb-item": [ + "./packages/breadcrumb-item/index.js" + ], + "form": [ + "./packages/form/index.js" + ], + "form-item": [ + "./packages/form-item/index.js" + ], + "tabs": [ + "./packages/tabs/index.js" + ], + "tab-pane": [ + "./packages/tab-pane/index.js" + ], + "tag": [ + "./packages/tag/index.js" + ], + "tree": [ + "./packages/tree/index.js" + ], + "alert": [ + "./packages/alert/index.js" + ], + "notification": [ + "./packages/notification/index.js" + ], + "slider": [ + "./packages/slider/index.js" + ], + "loading": [ + "./packages/loading/index.js" + ], + "icon": [ + "./packages/icon/index.js" + ], + "row": [ + "./packages/row/index.js" + ], + "col": [ + "./packages/col/index.js" + ], + "upload": [ + "./packages/upload/index.js" + ], + "progress": [ + "./packages/progress/index.js" + ], + "spinner": [ + "./packages/spinner/index.js" + ] +} diff --git a/examples/app.vue b/examples/app.vue new file mode 100644 index 000000000..9983abafc --- /dev/null +++ b/examples/app.vue @@ -0,0 +1,386 @@ + + + + + diff --git a/examples/assets/images/guide1.png b/examples/assets/images/guide1.png new file mode 100644 index 000000000..f799c23d3 Binary files /dev/null and b/examples/assets/images/guide1.png differ diff --git a/examples/assets/images/guide2.png b/examples/assets/images/guide2.png new file mode 100644 index 000000000..84f815ed8 Binary files /dev/null and b/examples/assets/images/guide2.png differ diff --git a/examples/assets/images/guide3.png b/examples/assets/images/guide3.png new file mode 100644 index 000000000..cb1cdaa08 Binary files /dev/null and b/examples/assets/images/guide3.png differ diff --git a/examples/assets/images/guide4.png b/examples/assets/images/guide4.png new file mode 100644 index 000000000..cc404ecc8 Binary files /dev/null and b/examples/assets/images/guide4.png differ diff --git a/examples/assets/styles/common.css b/examples/assets/styles/common.css new file mode 100644 index 000000000..de970e0b8 --- /dev/null +++ b/examples/assets/styles/common.css @@ -0,0 +1,88 @@ +.app__content { + min-width: 740px; + + &.no-toc { + min-width: 660px; + + .app__main { + max-width: 906px; + margin: 36px 42px; + } + } + + .app__main { + margin: 36px 242px 36px 42px; + } +} + +.app__main.commonpage { + p { + color: #333; + font-size: 14px; + line-height: 22px; + margin-bottom: 0; + } + + small { + color: #ccc; + font-size: 12px; + font-weight: normal; + padding-left: 20px; + } + + ul { + list-style: none; + padding-left: 0; + margin: 0; + } + + li { + color: #333; + font-size: 14px; + line-height: 22px; + + &::before { + content: "-"; + margin: 0 4px; + } + } + + hr { + border-color: #e4e4e4; + border-style: solid; + margin: 36px 0; + border-top: 0; + } + + a { + color: #2270c1; + } + + code { + background-color: #f8f8f8; + padding: 4px; + color: #666; + } + + .hljs { + max-height: 100%; + min-height: 100%; + background-color: #f8f8f8; + border: none; + color: #666; + padding: 20px 36px; + margin: 16px 0; + + &::before { + content: none; + } + + .hljs-comment, .hljs-quote { + color: #aaa; + } + + .hljs__button { + display: none; + } + } +} \ No newline at end of file diff --git a/examples/components/lightbox.vue b/examples/components/lightbox.vue new file mode 100644 index 000000000..5fd423af4 --- /dev/null +++ b/examples/components/lightbox.vue @@ -0,0 +1,55 @@ + + + + + \ No newline at end of file diff --git a/examples/components/table-filter.vue b/examples/components/table-filter.vue new file mode 100644 index 000000000..bb37a7cd4 --- /dev/null +++ b/examples/components/table-filter.vue @@ -0,0 +1,30 @@ + + + + + \ No newline at end of file diff --git a/examples/components/toc-item.vue b/examples/components/toc-item.vue new file mode 100644 index 000000000..971513ab2 --- /dev/null +++ b/examples/components/toc-item.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/examples/components/toc.vue b/examples/components/toc.vue new file mode 100644 index 000000000..f7ef3c675 --- /dev/null +++ b/examples/components/toc.vue @@ -0,0 +1,203 @@ + + + + + diff --git a/examples/data/cascader.json b/examples/data/cascader.json new file mode 100644 index 000000000..fe758cdc1 --- /dev/null +++ b/examples/data/cascader.json @@ -0,0 +1,29 @@ +[ + { + "label": "普通业务", + "value": "普通业务", + "children": [ + { + "label": "业务1", + "value": "业务1", + "children": [ + { + "label": "业务5", + "value": "业务5", + "children": [] + } + ] + }, + { + "label": "业务2", + "value": "业务2", + "children": [] + } + ] + }, + { + "label": "会员业务", + "value": "会员业务", + "children": [] + } +] diff --git a/examples/data/select-disabled.json b/examples/data/select-disabled.json new file mode 100644 index 000000000..d977035bb --- /dev/null +++ b/examples/data/select-disabled.json @@ -0,0 +1,19 @@ +[ + { + "label": "选项1", + "value": "结果1" + }, + { + "label": "选项2", + "value": "结果2", + "disabled": true + }, + { + "label": "选项3", + "value": "结果3" + }, + { + "label": "选项4", + "value": "结果4" + } +] diff --git a/examples/data/select-group.json b/examples/data/select-group.json new file mode 100644 index 000000000..c30ebb732 --- /dev/null +++ b/examples/data/select-group.json @@ -0,0 +1,54 @@ +{ + "gourp1": [ + { + "label": "选项1", + "value": "结果1" + }, + { + "label": "选项2", + "value": "结果2" + }, + { + "label": "选项3", + "value": "结果3" + }, + { + "label": "选项4", + "value": "结果4" + }, + { + "label": "选项5", + "value": "结果5" + }, + { + "label": "选项6", + "value": "结果6" + } + ], + "gourp2": [ + { + "label": "选项7", + "value": "结果7" + }, + { + "label": "选项8", + "value": "结果8" + }, + { + "label": "选项9", + "value": "结果9" + }, + { + "label": "选项10", + "value": "结果10" + }, + { + "label": "选项11", + "value": "结果11" + }, + { + "label": "选项12", + "value": "结果12" + } + ] +} \ No newline at end of file diff --git a/examples/data/select-normal.json b/examples/data/select-normal.json new file mode 100644 index 000000000..2310060da --- /dev/null +++ b/examples/data/select-normal.json @@ -0,0 +1,32 @@ +[ + { + "label": "选项1", + "value": "结果1", + "remark": "remark1" + }, + { + "label": "选项2", + "value": "结果2", + "remark": "remark2" + }, + { + "label": "选项3", + "value": "结果3", + "remark": "remark3" + }, + { + "label": "选项4", + "value": "结果4", + "remark": "remark4" + }, + { + "label": "选项5", + "value": "结果5", + "remark": "remark5" + }, + { + "label": "选项6", + "value": "结果6", + "remark": "remark6" + } +] diff --git a/examples/docs/alert.md b/examples/docs/alert.md new file mode 100644 index 000000000..5304eaf13 --- /dev/null +++ b/examples/docs/alert.md @@ -0,0 +1,111 @@ + + +## 基本用法 + +
+ + + + +
+ +```html + + + + +``` + +## 自定义关闭按钮 + +
+ +
+ +```html + +``` + +## 带有 icon + +
+ + + + +
+ +```html + + + + +``` + +## 带有辅助性文字介绍 + +
+ +
+ +```html + + +``` + +## 带有 icon 和辅助性文字介绍 + +
+ + + + +
+ +```html + + + + + + + + + + + +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| title | 标题, 必选参数 | string | | | +| type | 主题 | string | 'success', 'warning', 'info', 'error' | 'info' | +| description | 说明文字 | string | | | +| closable | 是否可关闭 | boolean | | true | +| closeText | 关闭按钮自定义文本 | string | | | +| showIcon | 是否显示图标 | boolean | | false | +| onClose | 关闭时的回调函数 | function | | | diff --git a/examples/docs/autocomplete.md b/examples/docs/autocomplete.md new file mode 100644 index 000000000..c0ca13132 --- /dev/null +++ b/examples/docs/autocomplete.md @@ -0,0 +1,419 @@ + + + +## 基础使用 + +
+ +
+ +```html + + +``` + +## 通过键盘控制下拉的显示 + +
+ +
+ +```html + + +``` + +## 自定义模板 + +
+ +
+ +```html + + + +``` + +## 服务端数据 + +
+ +
+ +```html + + +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|------------- |---------------- |---------------- |---------------------- |-------- | +| placeholder | 输入框占位文本 | string | | | +| disabled | 禁用 | boolean | true, false | false | +| suggestions | 建议列表 | array,object | | | +| value | 输入绑定值 | string | | | +| showOnUpDown | 是否通过键盘上下键控制建议列表 | boolean | | | +| partial | 建议列表的自定义模板 | object | | | diff --git a/examples/docs/breadcrumb.md b/examples/docs/breadcrumb.md new file mode 100644 index 000000000..8f8ae8c5c --- /dev/null +++ b/examples/docs/breadcrumb.md @@ -0,0 +1,25 @@ +## 基础使用 + +
+ + Home + Page 1 + Page 1-2 + Page 1-2-1 + +
+ + +```html + + Home + Page 1 + Page 1-2 + Page 1-2-1 + +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| separator | 分隔符 | string | | 反斜杠'/' | diff --git a/examples/docs/button.md b/examples/docs/button.md new file mode 100644 index 000000000..3e806c636 --- /dev/null +++ b/examples/docs/button.md @@ -0,0 +1,159 @@ + + + +## 基础用法 + +
+ Default + primary + success + warning + danger + info + disabled + text button +
+ +```html +Default +primary +success +warning +danger +info +disabled +text button +``` + +## Plain Button + +
+ Default + success + warning + danger + disabled +
+ +```html +Default +success +warning +danger +disabled +``` + +## 尺寸 + +
+ large + Default + small + mini +
+ +```html +large +Default +small +mini +``` + +## Loading + +
+ Button +
+

点击后变成 loading 状态

+
+ Button + Button +
+ +```html +Button + +Button +Button +``` + +## 图标按钮 + +
+ + Search + Upload +
+ +```html + +Search +Upload +``` + +## 按钮组 + +
+ + Button + Button + Button + +
+ + + + + +
+ +```html + + Button + Button + Button + +
+ + + + + +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| size | 尺寸 | string | large,small,mini | | +| type | 类型 | string | primary,success,warning,danger,info,text | | +| plain | 是否朴素按钮 | Boolean | true,false | false | +| disabled | 禁用 | boolean | true, false | false | +| icon | 图标,已有的图标库中的图标名 | string | | | diff --git a/examples/docs/checkbox.md b/examples/docs/checkbox.md new file mode 100644 index 000000000..d5e53e9dc --- /dev/null +++ b/examples/docs/checkbox.md @@ -0,0 +1,110 @@ + + + + + +## 基础用法 + +### 单个勾选框,逻辑值 + +
+ {{checked | json}} +
+ +```html +{{checked | json}} +``` + +### 多个勾选框,绑定到同一个数组 + +
+ + + + + +
+ +

{{checkList | json}}

+ +```html + + + +``` + +### 绑定 value: + +
+ + {{name}} +
+ +```html + + +``` + +```js +vm.a = 'Jonny'; +vm.b = 'Lara'; +// 当选中时 +vm.name === vm.a +// 当没有选中时 +vm.name === vm.b +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| model | 绑定值 | string\|string[]\|boolean | | | +| value | 真实值 | string | | | +| label | 显示值,不填则显示 value | string | | | +| disabled | 禁用 | boolean | true, false | false | diff --git a/examples/docs/date-picker.md b/examples/docs/date-picker.md new file mode 100644 index 000000000..06097e21a --- /dev/null +++ b/examples/docs/date-picker.md @@ -0,0 +1,266 @@ + + +## 日期点 + +### 日 + +以『日』为基本单位 + + + + +```html + + +``` + +### 周 + +以『周』为基本单位 + + + + +```html + + +``` + +### 月 + +以『月』为基本单位 + + + + +```html + + +``` + +### 年 + +以『年』为基本单位 + + + + +```html + + +``` + +### 含快捷选项 + +左侧区域可配置快捷选项,例如『今天』、『昨天』等 + + + + +```html + + + + +``` + +## 日期范围 + +### 日期范围 + +在一个选择器中选择 + + + + +```html + + +``` + +### 含快捷选项 + +左侧区域可配置快捷选项,例如『最近一周』、『最近一个月』等 + + + + +```html + + + + +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| value | 绑定值,需双向绑定 | String | | | +| readonly | 只读 | Boolean | | false | +| placeholder | 占位内容 | String | | | +| type | 显示类型 | String | year, month, date, datetime, week | date | +| format | 时间日期格式化 | String | 年 `yyyy`, 月 `MM`, 日 `dd`, 小时 `HH`, 分 `mm`, 秒 `ss` | 'yyyy-MM-dd' | +| shortcuts | 快捷选项列表,配置信息查看下表 | Object[] | | | + +### shortcuts +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| text | 标题 | String | | | +| onClick | 选中后会调用函数,参数是 vm,设置值通过触发 'pick' 事件。例如 vm.$emit('pick', new Date()) | Function | | | diff --git a/examples/docs/datetime-picker.md b/examples/docs/datetime-picker.md new file mode 100644 index 000000000..6e03bb2b4 --- /dev/null +++ b/examples/docs/datetime-picker.md @@ -0,0 +1,438 @@ + + +## 日期和时间点 + +### 日期和时间点的选择(一) + +在两个选择器中选择日期与时间。 + +
+ + + + +
+ +```html + + + + +``` + +### 日期和时间点的选择(二) + +在一个选择器中选择日期与时间。 + +
+ + +
+ +```html + + +``` + +### 含快捷选项(一) + +左侧区域可配置快捷选项,例如『今天』、『昨天』等。 + +
+ + + + +
+ +```html + + + + + + +``` + +### 含快捷选项(二) + +左侧区域可配置快捷选项,例如『今天』、『昨天』等。 + +
+ + +
+ +```html + + + + +``` + +## 日期和时间范围 + +### 日期和时间范围的选择(一) + +在两个选择器中选择。 + +
+ + + + +
+ +```html + + + + +``` + +### 日期和时间范围的选择(二) + +在一个选择器中选择。 + +
+ + +
+ +```html + + +``` + +### 含快捷选项 + +左侧区域可配置快捷选项,例如『最近一周』、『最近一个月』等。 + +
+ + +
+ +```html + + + + +``` + +### 日期范围和时间范围选择 + +在两个选择器中分别选择日期范围和时间范围。 + +
+ + + + +
+ +```html + + + + +``` + +## 环比时间 +选择两个时间作为限制条件,以得到统计量在这两个时间段的环比数据。 + +### 周环比 + +
+ + + + +
+ +```html + + + + +``` + +### 月环比 + +
+ + + + +
+ +```html + + + + +``` + +### 年环比 + +
+ + + + +
+ +```html + + + + +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| value | 绑定值,需双向绑定 | String | | | +| format | 时间日期格式化 | String | 年 `yyyy`, 月 `MM`, 日 `dd`, 小时 `HH`, 分 `mm`, 秒 `ss` | 'yyyy-MM-dd' | +| type | 显示类型 | String | year, month, date, datetime, week | date | +| readonly | 只读 | Boolean | | false | +| placeholder | 占位内容 | String | | | +| shortcuts | 快捷选项列表,配置信息查看下表 | Object[] | | | + + +### shortcuts +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| text | 标题 | String | | | +| onClick | 选中后会调用函数,参数是 vm,设置值通过触发 'pick' 事件。例如 vm.$emit('pick', new Date()) | Function | | | + + diff --git a/examples/docs/development.md b/examples/docs/development.md new file mode 100644 index 000000000..1a42ac120 --- /dev/null +++ b/examples/docs/development.md @@ -0,0 +1 @@ +待补充 \ No newline at end of file diff --git a/examples/docs/dialog.md b/examples/docs/dialog.md new file mode 100644 index 000000000..4fd9b7138 --- /dev/null +++ b/examples/docs/dialog.md @@ -0,0 +1,228 @@ + + + + +## 基本用法 + +点击打开 Dialog + +
+ + 这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息 + + 取 消 + 确 定 + + +
+ +```html +点击打开 Dialog + + + 这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息 + + 取 消 + 确 定 + + +``` + +## 设置尺寸 + +点击打开小尺寸 Dialog + +
+ + 这是一段内容 + + 取 消 + 确 定 + + +
+ +```html +点击打开小尺寸 Dialog + + + 这是一段内容 + + 取 消 + 确 定 + + +``` + +点击打开全屏幕 Dialog + +
+ + + +
+ +```html +点击打开全屏幕 Dialog + + + + +``` + +## 设置能否通过点击modal或按下esc关闭dialog + +打开 Dialog,点击 modal 或按下 esc 关闭无法将其关闭 + +
+ + 这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息 + +
+ +```html +打开 Dialog,点击 modal 或按下 esc 关闭无法将其关闭 + + + 这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息,这是一段信息 + +``` + +## 自定义内容 + +打开嵌套表格的 Dialog + +
+ + + + + + + +
+ +打开嵌套表单的 Dialog + +
+ + + + + + + + + + + + + + 取 消 + 确 定 + + +
+ +```html +打开嵌套表格的 Dialog + + + + + + + + + +打开嵌套表单的 Dialog + + + + + + + + + + + + + + + 取 消 + 确 定 + + +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| title | dialog 的标题 | string | | | +| size | dialog 的大小 | string | 'tiny', 'small', 'large', 'full' | 'small' | +| customClass | dialog 的自定义类名 | string | | | +| closeOnClickModal | 是否可以通过点击 modal 关闭 dialog | boolean | | true | +| closeOnPressEscape | 是否可以通过按下 esc 关闭 dialog | boolean | | true | + +## Slot +| name | 说明 | +|------|--------| +| - | dialog 的内容 | +| footer | dialog 按钮操作区的内容 | diff --git a/examples/docs/dropdown.md b/examples/docs/dropdown.md new file mode 100644 index 000000000..b3af23598 --- /dev/null +++ b/examples/docs/dropdown.md @@ -0,0 +1,163 @@ + + + + +## 基础用法 + +

通过与 button 一致的 type 属性来指定菜单按钮类型。

+ +
+ + 选项一 + 选项二 + 选项三 + 选项四 哈哈哈哈 + +
+ +```html + + 选项一 + 选项二 + 选项三 + 选项四 + +``` + +## 不带独立按钮的下拉菜单 + +可以通过将`icon-separate`属性设为`false`来呈现不带独立按钮的下拉菜单。 + + + 选项一 + 选项二 + 选项三 + 选项四 + + +```html + + 选项一 + 选项二 + 选项三 + 选项四 + +``` + +## 通过点击触发下拉 + +
+ + 选项一 + 选项二 + 选项三 + 选项四 哈哈哈哈 + + + 选项一 + 选项二 + 选项三 + 选项四 + +
+ +```html + + 选项一 + 选项二 + 选项三 + 选项四 + + + 选项一 + 选项二 + 选项三 + 选项四 + +``` + +## 文字类型下拉菜单 + +
+ + 选项一 + 选项二 + 选项三 + 选项四 + +
+ +```html + + 选项一 + 选项二 + 选项三 +
  • 选项四 + +``` + +## 尺寸 + +
    + + 选项一 + 选项二 + 选项三 +
  • 选项四 + + 选项一 + 选项二 + 选项三 + 选项四 + + 选项一 + 选项二 + 选项三 + 选项四 + + 选项一 + 选项二 + 选项三 + 选项四 + + + +```html + + 选项一 + 选项二 + 选项三 + 选项四 + +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|------------- |---------------- |---------------- |---------------------- |-------- | +| text | 菜单按钮文本 | string | | | +| type | 菜单按钮类型,同 button | string | | | +| trigger | 触发下拉菜单的方式 | string | hover,click | hover | +| icon-separate | 独立的下拉菜单按钮 | boolean | true, false | false | +| size | 菜单按钮尺寸,同 button | string | large, small, mini | | +| menu-align | 菜单水平对齐方向 | string | start, end | end | diff --git a/examples/docs/form.md b/examples/docs/form.md new file mode 100644 index 000000000..7cf6690ef --- /dev/null +++ b/examples/docs/form.md @@ -0,0 +1,949 @@ + + + + +## 基础使用 + +Form 组件是一个具有校验和提交功能的表单,包含复选框、单选框、输入框、下拉选择框等元素。 + +
    + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + 立即创建 + 取消 + + +
    + +```html + + +``` + +## Inline Form + +
    + + + + + + + + + + 查询 + + +
    + +```html + + +``` + +## Stacked Form + +
    + + + + + + + + + + + + + + + + + + 立即创建 + 取消 + + +
    + +```html + + +``` + +## 标签左对齐 + +
    + + + + + + + + + + + + + + + 查询 + + +
    + +```html + + +``` + +## 无标签 + +
    + + + + + + + + + 登录 + + + +
    + +```html + + +``` + +## 表单验证 + +
    + + + + + + + +
    -
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + 提交 + 重置 + +
    +
    + +```html + + +``` + +## 自定义校验规则 + +
    + + + + + + + + + + + + 提交 + 重置 + + +
    + +```html + + +``` + +## 动态增减表单项 + +
    + + + + + + 删除 + + + 提交 + 新增域名 + + +
    + +```html + + +``` + +## el-form API +### 组件属性和事件 + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| model | 表单数据对象 | object | | | +| rules | 表单验证规则 | object | | | +| type | 表单类型 | string | stacked, inline, horizontal | horizontal | +| label-align | 表单域标签的水平对齐位置 | string | right,left | right | +| label-width | 表单域标签的宽度,所有的 form-item 都会继承 form 组件的 labelWidth 的值 | string | | | +| label-suffix | 表单域标签的后缀 | string | | | + +### el-form 实例属性和方法 + +| 方法名 | 说明 | 类型 | 默认值 | +|---------- |-------------- |---------- |-------- | +| validate(cb) | 对整个表单进行校验的方法, 校验结束后会调用传入的回调方法, cb(valid), valid 参数是校验 bool 值结果 | function | | +| validateField(prop, cb) | 对部分表单字段进行校验的方法 | | | +| resetForm | 对整个表单进行重置,将所有字段值重置为空并移除校验结果 | | | + +## el-form-item API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| prop | 表单域model字段 | string | 传入父级 v-form 的 model 中的所有属性 | | +| label | 标签文本 | string | | | +| label-width | 表单域标签的的宽度,例如 '50px' | string | | | +| required | 是否必填,如不设置,则会根据校验规则自动生成 | bolean | | false | diff --git a/examples/docs/guideline.md b/examples/docs/guideline.md new file mode 100644 index 000000000..f28fa723a --- /dev/null +++ b/examples/docs/guideline.md @@ -0,0 +1,123 @@ +
    +

    选择合适的导航

    +

    选择合适的导航可以让用户在产品的使用过程中非常流畅,相反若是不合适就会引起用户操作不适(方向不明确),以下是「侧栏导航」和「顶部导航」的区别。

    + +

    侧栏导航:

    +

    可将导航栏固定在左侧,提高导航可见性,方便页面之间切换;顶部可放置常用工具,如搜索条、帮助按钮、通知按钮等。适用于中后台的管理型、工具型网站。

    + + + + + + + + + + + + + + + + +
    一级类目 + + 一级类目 + + 一级类目 +

    适用于结构简单的网站;只有一级页面时,不需要使用面包屑。

    +
    二级类目 + + 二级类目 + + 二级类目 +

    侧栏中最多可显示两级导航;当使用二级导航时,我们建议搭配使用面包屑,方便用户定位自己的位置和快速返回。

    +
    三级类目 + + 三级类目 + + 三级类目 +

    适用于较复杂的工具型后台,左侧栏为一级导航,中间栏可显示其对应的二级导航,也可放置其他的工具型选项。

    +
    + +

    顶部导航:

    +

    顺应了从上至下的正常浏览顺序,方便浏览信息;顶部宽度限制了导航的数量和文本长度。

    + + + + + + + + +
    一级类目 + + 一级类目 + + 一级类目 +

    适用于导航较少,页面篇幅较长的网站;

    +
    +
    + + + + \ No newline at end of file diff --git a/examples/docs/home.md b/examples/docs/home.md new file mode 100644 index 000000000..f6a32dde2 --- /dev/null +++ b/examples/docs/home.md @@ -0,0 +1,11 @@ +# 组件说明文档 + + \ No newline at end of file diff --git a/examples/docs/icon.md b/examples/docs/icon.md new file mode 100644 index 000000000..a93d4be7e --- /dev/null +++ b/examples/docs/icon.md @@ -0,0 +1,82 @@ + + +## icon-font 图标 + +我们为你提供了一套常用的图标集合。 + +## 使用方法 + +直接通过`el-icon-iconName`的类名来使用即可。例如: + +
    + +```html + +``` + +也可以在按钮组件中使用图标: + +
    搜索
    + +```html +搜索 +``` + +## 图标集合 + +
      +
    • + + + {{'el-icon-' + name}} + +
    • +
    diff --git a/examples/docs/input-number.md b/examples/docs/input-number.md new file mode 100644 index 000000000..75b1a94a1 --- /dev/null +++ b/examples/docs/input-number.md @@ -0,0 +1,71 @@ + + + +## 基础使用 + +

    当我们需要标准的数字值时可以用到这个组件,它为你提供了数值输入提供了范围控制和递增递减的步数控制。

    + +
    + +
    + +```html + +``` + +## 禁用状态 + +
    + +
    + +```html + +``` + +## 步数 + +
    + +
    + +```html + +``` + +## 尺寸 + +
    + + + +
    + +```html + + + +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|----------|-------------- |----------|-------------------------------- |-------- | +| model | 绑定值 | number | | | +| step | 步数 | number | | 1 | +| size | 尺寸 | string | large, small | | +| disabled | 是否禁用 | boolean | true, false | false | diff --git a/examples/docs/input.md b/examples/docs/input.md new file mode 100644 index 000000000..b869f76e6 --- /dev/null +++ b/examples/docs/input.md @@ -0,0 +1,269 @@ + + + + +## 基本用法 + +
    + + + + +
    + +```html + + + + +``` + +## 禁用状态 + +
    + + +
    + +```html + + +``` + + + +## Input 图标 + +
    + + +
    + +```html + + +``` + +## Input Group + +前置和后置元素可以是任何东西, 通过使用`.el-input-group__label`可以声明附加元素是一个标签从而获得合适的样式。 + +### 后置元素 + +
    + + + + .com + +
    + +```html + + + + .com + +``` + +### 前置元素 +
    + + 按钮 + + + +
    + +```html + + 按钮 + + + +``` + +### 前置和后置元素 +
    + + +
  • 选项一
  • +
  • 选项二
  • +
  • 选项三
  • +
  • 选项四
  • +
    + + 搜索 + + + +```html + + +
  • 选项一
  • +
  • 选项二
  • +
  • 选项三
  • +
  • 选项四
  • +
    + + 搜索 +
    +``` + +## 尺寸 +### large + +
    + + +
    + +### normal + +
    + + +
    + +### small + +
    + + +
    + +### mini + +
    + + +
    + +```html + + + + + + + + +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|------------- |---------------- |---------------- |---------------------- |-------- | +| type | 同原生的 input 的 type 属性,如果为textarea则显示为extarea | string | | | +| name | 同原生的 input 的 name 属性 | string | | | +| model | 绑定值 | string, number | | | +| maxlength | 最大输入长度 | number | | | +| minlength | 最小输入长度 | number | | | +| placeholder | 输入框占位文本 | string | | | +| disabled | 禁用 | boolean | true, false | false | +| readonly | 禁用 | boolean | true, false | false | +| size | 输入框尺寸 | string | large, small, mini | | +| icon | 输入框尾部图标 | string | | | +| number | 指定model值为number类型 | boolean | | false | + diff --git a/examples/docs/loading.md b/examples/docs/loading.md new file mode 100644 index 000000000..5950c7465 --- /dev/null +++ b/examples/docs/loading.md @@ -0,0 +1,130 @@ + + + + +## 基本用法 + +打开 / 关闭 loading + +
    +

    点击上面的按钮,本区域显示 loading 遮罩

    +
    + +```html +打开 / 关闭 loading + +
    +

    点击上面的按钮,本区域显示 loading 遮罩

    +
    +``` + +## 修饰符 + +loading 遮罩默认插入至绑定了 `v-loading` 指令的元素上。通过添加 `body` 修饰符,可以使遮罩插入至 body 上 + +打开 / 关闭 loading + +
    +

    点击上面的按钮,本区域显示 loading 遮罩

    +
    + +```html +打开 / 关闭 loading + +
    +

    点击上面的按钮,本区域显示 loading 遮罩

    +
    +``` + +当需要全屏遮罩时,可使用 `fullscreen` 修饰符(此时遮罩会插入至 body 上) + +打开全屏 loading + +```html + + + +``` \ No newline at end of file diff --git a/examples/docs/menu.md b/examples/docs/menu.md new file mode 100644 index 000000000..937d5286f --- /dev/null +++ b/examples/docs/menu.md @@ -0,0 +1,312 @@ + + + + +## 基础用法 + +
    + +
    + +```html + +``` + +
    + +
    + +```html + +``` + +## 侧边栏导航 + +
    + +
    + +```html + +``` + +
    + +
    + +```html + +``` + +## menu API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| mode | 模式 | string | horizontal,vertical | horizontal | +| theme | 主题色 | string | light,dark | light | +| activeKey | 当前激活菜单的 key | string | | | +| openedKeys | 当前打开的submenu的 key 数组 | Array | | | +| uniqueOpend | 是否只保持一个子菜单的展开 | boolean | true, false | false | +| router | 是否使用 vue-router 的模式,启用该模式会在 select 事件触发时执行 this.$route.$router.go(key) 进行路由跳转 | boolean | true, false | false | +| select | 菜单激活回调 | function(key, keyPath) | | | +| open | SubMenu 展开的回调 | function(key, keyPath) | | | +| close | SubMenu 收起的回调 | function(key, keyPath) | | | + +## submenu API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| key | 唯一标志 | string | | | + +## menu-item API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| key | 唯一标志 | string | | | diff --git a/examples/docs/message-box.md b/examples/docs/message-box.md new file mode 100644 index 000000000..bc3ed37e3 --- /dev/null +++ b/examples/docs/message-box.md @@ -0,0 +1,242 @@ + + +## 基本用法 +打开 Alert + +```html + + + +``` + +## 返回 Promise + +打开 alert + +```html + + + +``` + +打开 confirm + +```html + + + +``` +打开 prompt + +```html + + + +``` + +打开 Message Box + +```html + + + +``` + +## 更多配置项 + +打开 Message Box + +```html + + + +``` + +## 全局方法 + +element 为 Vue.prototype 添加了如下全局方法:$msgbox, $alert, $confirm 和 $prompt。因此在 vue instance 中可以采用本页面中的方式调用 `MessageBox`。 + +## 单独引用 + +单独引入 `MessageBox`: + +```javascript +import { MessageBox } from 'element-ui'; +``` + +对应于上述四个全局方法的调用方法依次为:MessageBox, MessageBox.alert, MessageBox.confirm 和 MessageBox.prompt。 + +## API + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| title | 标题 | string | | | +| message | 消息内容 | string | | | +| type | 消息类型 | string | 'success', 'info', 'warning', 'error' | | +| showCancelButton | 是否显示取消按钮 | boolean | | false(以 confirm 和 prompt 方式调用时为 true) | +| showConfirmButton | 是否显示确定按钮 | boolean | | true | +| cancelButtonText | 取消按钮的文本内容 | string | | '取消' | +| confirmButtonText | 确定按钮的文本内容 | string | | '确定' | +| cancelButtonClass | 取消按钮的自定义类名 | string | | | +| confirmButtonClass | 确定按钮的自定义类名 | string | | | +| showInput | 是否显示输入框 | boolean | | false(以 prompt 方式调用时为 true)| +| inputValue | 输入框的值 | string | | | +| inputPlaceholder | 输入框的占位符 | string | | | +| inputPattern | 输入框的校验表达式 | regexp | | | +| inputValidator | 输入框的校验函数。若返回一个字符串, 则返回结果会被赋值给 inputErrorMessage | function | | | +| inputErrorMessage | 校验未通过时的提示文本 | string | | '输入的数据不合法!' | diff --git a/examples/docs/notification.md b/examples/docs/notification.md new file mode 100644 index 000000000..665d9de2e --- /dev/null +++ b/examples/docs/notification.md @@ -0,0 +1,230 @@ + + + + +## 基本用法 + +
    + 点击展示 Notification +
    + +```html + + + +``` + +## 带有 icon + +
    + 成功 + 警告 + 消息 + 错误 +
    + +```html + + + +``` + +## 不会自动关闭 +
    + 不会自动关闭的 Notification +
    + +```html + + + +``` + +## 回调函数 +
    + 带有回调函数的 Notification +
    + +```html + + + +``` + +## 全局方法 + +element 为 Vue.prototype 添加了全局方法 $notify。因此在 vue instance 中可以采用本页面中的方式调用 `Notification`。 + +## 单独引用 + +单独引入 `Notification`: + +```javascript +import { Notification } from 'element-ui'; +``` + +此时调用方法为 `Notification(options)`。 + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| title | 标题 | string | | | +| message | 说明文字 | string | | | +| type | 主题 | string | 'success', 'warning', 'info', 'error' | | +| duration | 显示时间, 毫秒。设为 0 则不会自动关闭 | number | | 4500 | +| onClose | 关闭时的回调函数 | function | | | diff --git a/examples/docs/pagination.md b/examples/docs/pagination.md new file mode 100644 index 000000000..58344f70e --- /dev/null +++ b/examples/docs/pagination.md @@ -0,0 +1,90 @@ +## 基础用法 +适用广泛的基础用法。 + + + + +```html + + +``` + +## 小型分页 +在空间有限的情况下,可以使用简单的小型分页。 + + + + +```html + + +``` + +## 复杂分页 +能够承载复杂交互的分页,显示更多页码。 + + + + +```html + + +``` + +## 附加功能 +在复杂分页的基础上,根据场景需要,可以添加其他功能模块。 + + + + + +```html + + +``` + + + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|--------------------|----------------------------------------------------------|-------------------|-------------|--------| +| small | 小型分页样式 | Boolean | | false | +| page-size | 每页个数 | Number | | 10 | +| total | 总个数 | Number | | 0 | +| current-page | 当前页数 | Number | | 0| +| layout | 组件布局,子组件名用逗号分隔。| String | `prev`, `pager`, `next`, `jumper`, `slot`, `->`, `total` | 'prev, pager, next, jumper, slot, ->, total' | +| page-sizes | 切换每页显示个数的子组件值 | Number[] | | [10, 20, 30, 40, 50, 100] | +| size-change | pageSize 改变时会触发的事件 | Function | | | +| current-change | currentPage 改变时会触发的事件 | Function | | | + diff --git a/examples/docs/popover.md b/examples/docs/popover.md new file mode 100644 index 000000000..b9973a8d4 --- /dev/null +++ b/examples/docs/popover.md @@ -0,0 +1,273 @@ + + + + +## 基础用法 +可选择提示出现的位置。 + +
    + + + + + + + + + + hover 激活 + click 激活 + + +
    + + +```html + + + + + + + + + +hover 激活 +click 激活 +focus 激活 +``` + +## 嵌套信息 +可以嵌套表格等多种信息。 + +
    + + + + + + + + + click 激活 +
    + +```html + + + + + + + + +click 激活 +``` + + +## 嵌套操作 +常用于确认操作中,比 Dialog 更加轻量。 + +
    + +

    这是一段内容这是一段内容确定删除吗?

    +
    + 取消 + 确定 +
    +
    + + 删除 +
    + +```html + +

    这是一段内容这是一段内容确定删除吗?

    +
    + 取消 + 确定 +
    +
    + +删除 +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|--------------------|----------------------------------------------------------|-------------------|-------------|--------| +| trigger | 触发方式 | String | 'click', 'focus', 'hover' | click | +| title | 标题 | String | | | +| content | 显示的内容 | String | | | +| width | 宽度 | String, Number | | 最小宽度 150px | +| placement | 出现位置 | String | `top`, `top-start`, `top-end`, `bottom`, `bottom-start`, `bottom-end`, `left`, `left-start`, `left-end`, `right`, `right-start`, `right-end` | bottom | +| visible | 初始状态是否可见 | Boolean | | false | +| offset | 出现位置的偏移量 | Number, String | | 0 | +| options | [popper.js](https://popper.js.org/documentation.html) 的参数 | Object | 参考 [popper.js](https://popper.js.org/documentation.html) 文档 | { boundariesElement: 'body' } | + +## Slot +| 参数 | 说明 | +|--- | ---| +| - | 触发 popover 显示的元素| +| content | 显示的 HTML 内容 | diff --git a/examples/docs/progress.md b/examples/docs/progress.md new file mode 100644 index 000000000..da8bda964 --- /dev/null +++ b/examples/docs/progress.md @@ -0,0 +1,48 @@ + + +## 基础使用 + +一个基础的进度条 + +
    + + + + + + +
    + +```html + + + + + + +``` + +## 带条纹效果 + +
    + + +
    + +```html + + +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|------------- |---------------- |---------------- |---------------------- |-------- | +| percentage | 百分比 | number | | 0 | +| type | 类型 | string | blue,green,blue-stripe,green-stripe | blue | +| size | 尺寸 | string | large, small | | diff --git a/examples/docs/quickstart.md b/examples/docs/quickstart.md new file mode 100644 index 000000000..8faae8dfc --- /dev/null +++ b/examples/docs/quickstart.md @@ -0,0 +1,7 @@ +element 是为饿了么定制的一套 Vue.js 后台组件库。帮助你更轻松更快速的开发后台项目。 + +------------- + +## 安装 + +待补充 diff --git a/examples/docs/radio.md b/examples/docs/radio.md new file mode 100644 index 000000000..598dff6a1 --- /dev/null +++ b/examples/docs/radio.md @@ -0,0 +1,138 @@ + + +## 基本用法 + +
    + + + +
    + +```html + + + +``` + +## Radio Group + +
    + + + + + +
    + +```html + + + + + +``` + +## Radio Group Button + +
    + + + + + + +
    +
    + + + + + + +
    +
    + + + + + + +
    + +```html + + + + + + + + + + + + + + + + + + +``` + +## 禁用 + +
    + + +
    + +```html + + +``` + + +## Radio API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| value | 绑定值 | string | | | +| label | 真实值 | string | | | +| disabled | 禁用 | boolean | true, false | false | + +## Radio Group API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| value | 绑定值 | string | | | +| size | 尺寸 | string | large, small | | + +## Radio Button API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| label | 真实值 | string | | | +| disabled | 禁用 | boolean | true, false | false | diff --git a/examples/docs/select.md b/examples/docs/select.md new file mode 100644 index 000000000..7dfac17f0 --- /dev/null +++ b/examples/docs/select.md @@ -0,0 +1,779 @@ + + +## 基本用法 + + + + + +```html + + + +``` + +## 禁用状态 + + + + + +```html + + + +``` + +## 有禁用选项 + + + + + +```html + + + +``` + +## 可清空单选 + + + + + +```html + + + +``` + +## 指定初始被选项 + + + + + +要指定 `select` 的初始被选项有两种方法。可以通过指定 `el-option` 的 `selected` 属性: + +```html + + + +``` + +或者通过为绑定到 `value` 的变量赋予初始值: + +```html + + + +``` + +## 自定义模板 + + + + + + +```html + + + +``` + +## 多选 + + + + + + +```html + + + +``` + +## 自定义宽度 + + + + + +```html + + + +``` + +## 分组 + + + + + + + +```html + + + +``` + +## 选项较多时的交互 + + + + + +```html + + + +``` + +## 可搜索 + + + + + +```html + + + +``` + +## 服务端搜索 + + + + + +```html + + + +``` + +## API +### el-select +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| value | select 目前选中的值 | string/number/array | | | +| multiple | 是否多选 | boolean | | false | +| disabled | 是否禁用 | boolean | | false | +| clearable | 单选时是否可以清空选项 | boolean | | false | +| width | select 的宽度 | number | | 180(单选)/220(多选) | +| dropdown-width | 下拉菜单的宽度,不设置则与输入框同宽 | number | | | +| name | select input 的 name 属性 | string | | | +| placeholder | 占位符 | string | | '请选择' | +| filterable | 是否可搜索 | boolean | | false | +| filter-method | 自定义过滤方法 | function | | | +| remote | 是否为远程搜索 | boolean | | false | +| remote-method | 远程搜索方法,当搜索关键字变化时会调用该方法,参数为目前的搜索关键字 | function | | | +| loading | 是否正在从远程获取数据 | boolean | | false | +| change | value 发生变化时的回调函数,参数为 value 的值 | function | | | + +### el-option-group +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| label | 分组的标签 | string | | | +| disabled | 是否将该分组下所有选项置为禁用 | boolean | | false | + +### el-option +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| value | 选项的值 | string/number | | | +| label | 选项的标签,若不设置则默认与 `value` 相同 | string/number | | | +| disabled | 是否禁用该选项 | boolean | | false | +| selected | 选项是否被初始选中 | boolean | | false | +| template | 选项的自定义模板 | String | | `{{ label }}` | diff --git a/examples/docs/slider.md b/examples/docs/slider.md new file mode 100644 index 000000000..57e9ee094 --- /dev/null +++ b/examples/docs/slider.md @@ -0,0 +1,89 @@ + + +## 基本用法 + + + +```html + +``` + +## 定义初始值 + + + +```html + +``` + +## 定义区间 + + + +```html + +``` + +## 定义步长 + + + +```html + +``` + +## 显示间断点 + + + +```html + +``` + +## 带有输入框 + + + +```html + +``` + +## 回调函数 + + + +```html + + + +``` + + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| value | 绑定值 | number | | 最小值 min | +| min | 最小值 | number | | 0 | +| max | 最大值 | number | | 100 | +| step | 步长 | number | | 1 | +| showInput | 是否显示输入框 | boolean | | false | +| showStops | 是否显示间断点 | boolean | | false | +| change | 值改变时的回调函数 | function | | | diff --git a/examples/docs/switch.md b/examples/docs/switch.md new file mode 100644 index 000000000..bc84d7bae --- /dev/null +++ b/examples/docs/switch.md @@ -0,0 +1,61 @@ + + +## 基本用法 + +
    + +
    + +```html + +``` + +## 禁用状态 + +
    + + + + +
    + +```html + + + + +``` + +## 自定义颜色 + +
    + + +
    + +```html + + +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| value | switch 的选中状态 | boolean | | true | +| disabled | 禁用 | boolean | true, false | false | +| width | switch 的宽度(像素) | number | | 58(有文字)/ 46(无文字) | +| on-icon-class | switch 打开时所显示图标的类名 | string | | | +| off-icon-class | switch 关闭时所显示图标的类名 | string | | | +| on-text | switch 打开时的文字 | string | | 'ON' | +| off-text | switch 关闭时的文字 | string | | 'OFF' | +| on-color | switch 打开时的背景色 | string | | | +| off-color | switch 关闭时的背景色 | string | | | +| name | 对应 input 的 name 属性 | string | | | + diff --git a/examples/docs/table.md b/examples/docs/table.md new file mode 100644 index 000000000..0aac0e794 --- /dev/null +++ b/examples/docs/table.md @@ -0,0 +1,606 @@ + + +## 基础表格 + + + + + + + +```html + + + +``` + +## 带斑马纹表格 + + + + + + + +```html + + + +``` + +## 带边框表格 + + + + + + + +```html + + + +``` + +## 带状态表格 + + + + + + + +```html + + + +``` + +## 固定表头 + + + + + + + +```html + + + +``` + +## 固定列 + + + + + + +```html + + + +``` + +## 固定列和表头 + + + + + + +```html + + + +``` + +## 单选 + + + + + + +

    {{ singleSelection | json }}

    + +```html + + + +``` + +## 多选 + + + + + + + +

    {{ multipleSelection | json }}

    + +```html + + + +``` + +## 排序 + + + + + + + +```html + + + +``` + +## el-table API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| data | 显示的数据 | array | | | +| height | table 的高度,默认高度为空,即自动高度 | string | | '' | +| stripe | 是否为斑马纹 table | boolean | | false | +| border | 是否带有纵向边框 | boolean | | false | +| selectionMode | 列表项选择模式 | string | 'single', 'multiple', 'none' | 'none' | +| allowNoSelection | 单选模式是否允许选项为空 | boolean | | false | +| selection | 多选模式下返回数组,单选模式下返回选中的元素。 | array/object | | | +| fixedColumnCount | 固定列的个数 | number | | 0 | + +## el-table 事件 +| 事件名 | 说明 | 参数 | +| ---- | ---- | ---- | +| selection-change | 当选择项发生变化时会触发该事件 | selected | +| cell-mouse-enter | 当单元格 hover 进入时会触发该事件 | row, column, cell, event | +| cell-mouse-leave | 当单元格 hover 退出时会触发该事件 | row, column, cell, event | +| cell-click | 当某个单元格被点击时会触发该事件 | row, column, cell, event | + +## el-table-column API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| label | 显示的标题 | string | | '' | +| property | 对应列内容的字段名 | string | | '' | +| width | 对应列的宽度 | string | | | +| sortable | 对应列是否可以排序 | boolean | | false | +| type | 对应列的类型。如果设置了 `selection` 则显示多选按钮,如果设置了 `index` 则显示该行的索引(从 1 开始计算) | string | 'selection', 'index' | 0 | +| formatter | 用来格式化内容,在 formatter 执行的时候,会传入 row 和 column | function | | | diff --git a/examples/docs/tabs.md b/examples/docs/tabs.md new file mode 100644 index 000000000..ba2152f07 --- /dev/null +++ b/examples/docs/tabs.md @@ -0,0 +1,125 @@ + + + +## 基础使用 + +
    + + 选项卡一内容 + 选项卡二内容 + 选项卡三内容 + 选项卡四内容 + +
    + +```html + + 选项卡一内容 + 选项卡二内容 + 选项卡三内容 + 选项卡四内容 + +``` + +## 标签风格 + +
    + + 选项卡一内容 + 选项卡二内容 + 选项卡三内容 + 选项卡四内容 + +
    + +```html + + 选项卡一内容 + 选项卡二内容 + 选项卡三内容 + 选项卡四内容 + +``` + +## 可关闭的标签 + +
    + + 选项卡一内容 + 选项卡二内容 + 选项卡三内容 + 选项卡四内容 + +
    +{{activeKey2}} + +```html + + 选项卡一内容 + 选项卡二内容 + 选项卡三内容 + 选项卡四内容 + +``` + +## 卡片风格 + +
    + + 选项卡一内容 + 选项卡二内容 + 选项卡三内容 + 选项卡四内容 + +
    + +```html + + 选项卡一内容 + 选项卡二内容 + 选项卡三内容 + 选项卡四内容 + +``` + +## TABS API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| type | 风格类型 | string | card, border-card | | +| closable | 真实值 | boolean | true, false | false | +| defaultActiveKey | 如果没有设置 activeKey, 则使用该值 | string | | 第一个面板 | +| activeKey | 当前选中面板的key | string | | | +| tab.click | tab 被点击的回调 | string | | | +| tab.remove | tab 被删除的回调 | string | | | + +## TAB-PANE API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| label | 选项卡标题 | string | | | +| key | 与选项卡activeKey对应的标识符 | string | | 该选项卡在选项卡中的index值,如第一个选项卡则为'1' | diff --git a/examples/docs/tag.md b/examples/docs/tag.md new file mode 100644 index 000000000..c4e63e728 --- /dev/null +++ b/examples/docs/tag.md @@ -0,0 +1,95 @@ + + + + +## 基础使用 + +
    + 标签一 + 标签二 + 标签三 + 标签四 + 标签五 + 标签六 +
    + +```html +标签一 +标签二 +标签三 +标签四 +标签五 +标签六 +``` + +## 可移除的标签 + +
    + + {{tag.name}} + +
    + +```html + +{{tag.name}} + + + +``` diff --git a/examples/docs/time-picker.md b/examples/docs/time-picker.md new file mode 100644 index 000000000..6b7d58033 --- /dev/null +++ b/examples/docs/time-picker.md @@ -0,0 +1,234 @@ + + +## 固定时间点 +提供几个固定的时间点供用户选择。 + +
    + + +
    + +```html + + +``` + +## 任意时间点 +可以选择任意时间。 + +
    + + +
    + +```html + + +``` + +### 通用 - 手动输入规则 +
    + + +
    + +```html + + +``` + +### 通用 - 限制时间范围 + +
    + + +
    + +```html + + +``` + + +## 固定时间范围 + +### 先选择开始时间 +先选择开始时间,结束时间内备选项的状态会随之改变。 + +
    + + + + +
    + +```html + + + + +``` + +### 先选择结束时间 +开始时间的备选项不随结束时间的选择改变。 + +
    + + + + +
    + +```html + + + + +``` + +## 任意时间范围 +可选择任意的时间范围。 + +
    + + +
    + +```html + + +``` + + + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| value | 绑定值,需双向绑定 | String | | | +| readonly | 只读 | Boolean | | false | +| placeholder | 占位内容 | String | | | +| format | 时间格式化 | String | 小时: `HH`, 分`mm`, 秒`ss` | 'HH:mm:ss' | +| picker-options | 当前时间日期选择器特有的选项,参考下表 | Object | | {} | + +### time-select Options +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| start | 开始时间 | String | |09:00 | +| end | 结束时间 | String | |18:00 | +| step | 间隔时间 | String | | 00:30 | +| minTime | 最小时间,小于该时间的时间段将被禁用 | String | | 00:00 | + +### time-picker Options +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| selectableRange | 可选时间段,例如 `18:30:00 - 20:30:00`,或者传入数组 `['09:30:00 - 12:00:00', '14:30:00 - 18:30:00']` | String, Array | | | + diff --git a/examples/docs/tooltip.md b/examples/docs/tooltip.md new file mode 100644 index 000000000..64ad2f9d5 --- /dev/null +++ b/examples/docs/tooltip.md @@ -0,0 +1,128 @@ +## 基础用法 +鼠标 hover 的时候显示,可选择提示出现的位置。 + + + + + + + + +
    +
    + + 上左 + + + 上边 + + + 上右 + +
    +
    + + 左上 + + + 左边 + + + 左下 + +
    + +
    + + 右上 + + + 右边 + + + 右下 + +
    +
    + + 下左 + + + 下边 + + + 下右 + +
    +
    + +## 适用于不同情景 + +
    + + Top center + +
    + + +
    + + Bottom center + +
    + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|--------------------|----------------------------------------------------------|-------------------|-------------|--------| +| effect | 默认提供的样式 | String | `dark`, `light` | dark | +| content | 显示的内容 | String | | | +| placement | 出现位置 | String | `top`, `top-start`, `top-end`, `bottom`, `bottom-start`, `bottom-end`, `left`, `left-start`, `left-end`, `right`, `right-start`, `right-end` | bottom | +| visible | 初始状态是否可见 | Boolean | | false | +| disabled | 控制是否不可见 | Boolean | | false | +| options | [popper.js](https://popper.js.org/documentation.html) 的参数 | Object | 参考 [popper.js](https://popper.js.org/documentation.html) 文档 | { boundariesElement: 'body' } | diff --git a/examples/docs/tree.md b/examples/docs/tree.md new file mode 100644 index 000000000..664c916e4 --- /dev/null +++ b/examples/docs/tree.md @@ -0,0 +1,101 @@ + + + + +## Basic + + + +## Have Checkbox + + diff --git a/examples/docs/upload.md b/examples/docs/upload.md new file mode 100644 index 000000000..939af4eb4 --- /dev/null +++ b/examples/docs/upload.md @@ -0,0 +1,118 @@ + + + +## 基础使用 + +
    + + 点击上传 +
    只能上传jpg/png文件,且不超过500kb
    +
    +
    + +```html + + 点击上传 +
    只能上传jpg/png文件,且不超过500kb
    +
    +``` + +## 拖拽文件上传 + +
    + +
    只能上传jpg/png文件,且不超过500kb
    +
    +
    + +```html + +
    只能上传jpg/png文件,且不超过500kb
    +
    +``` + +## 图片缩略图模式 + +上传文件类型限制为只能上传图片,并可展示本地缩略图,该模式暂不支持多选 + +
    + +
    + 分享图片 + 删除 +
    +
    只能上传jpg/png文件,且不超过500kb
    +
    +
    + +```html + +
    + 分享图片 + 删除 +
    +
    只能上传jpg/png文件,且不超过500kb
    +
    +``` + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| action | 必选参数, 上传的地址 | string | | | +| headers | 可选参数, 设置上传的请求头部 | object | | | +| multiple | 可选参数, 是否支持多选文件 | boolean | | | +| file | 可选参数, 上传的文件字段名 | string | | file | +| withCredentials | 支持发送 cooking 凭证信息 | boolean | | false | +| showUploadList | 是否显示已上传文件列表 | boolean | | true | +| type | 上传控件类型 | string | select,drag | select | +| accept | 可选参数, 接受上传的[文件类型](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept), 拖拽文件上传时不受此参数影响 | string | | | +| filechange | 可选参数, 上传文件改变时的回调 | function(file, fileList, event) | | | +| fileremove | 可选参数, 文件列表移除文件时的回调 | function(file, fileList) | | | +| type | 上传控件类型 | string | select,drag | select | diff --git a/examples/dom/class.js b/examples/dom/class.js new file mode 100644 index 000000000..f0dd03dfe --- /dev/null +++ b/examples/dom/class.js @@ -0,0 +1,22 @@ +export const hasClass = function(obj, cls) { + return obj.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); +}; + +export const addClass = function(obj, cls) { + if (!hasClass(obj, cls)) obj.className += ' ' + cls; +}; + +export const removeClass = function(obj, cls) { + if (hasClass(obj, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); + obj.className = obj.className.replace(reg, ' '); + } +}; + +export const toggleClass = function(obj, cls) { + if (hasClass(obj, cls)) { + removeClass(obj, cls); + } else { + addClass(obj, cls); + } +}; diff --git a/examples/entry.js b/examples/entry.js new file mode 100644 index 000000000..1e19d6b03 --- /dev/null +++ b/examples/entry.js @@ -0,0 +1,26 @@ +import Vue from 'vue'; +import entry from './app'; +import VueRouter from 'vue-router'; +import configRouter from './route.config'; +import Element from 'main/index.js'; +import 'packages/theme-default/src/index.css'; + +// test Released package +// import Element from '../lib/index.js'; +// import '../lib/theme-default/index.css'; + +Vue.use(Element); +Vue.use(VueRouter); + +const router = new VueRouter(); +const app = Vue.extend({ + template: '', + components: { entry } +}); + +configRouter(router); +router.start(app, 'app'); + +router.afterEach(() => { + document.body.scrollTop = 0; +}); diff --git a/examples/icon.json b/examples/icon.json new file mode 100644 index 000000000..a186b92d7 --- /dev/null +++ b/examples/icon.json @@ -0,0 +1 @@ +["search","share","setting","circle-cross","warning","information","circle-check","delete","d-arrow-left","d-arrow-right","picture","upload","menu","time","circle-close","arrow-down","arrow-up","arrow-right","arrow-left","close","document","d-caret","date","message","loading","ellipsis","plus","caret-left","caret-right","caret-bottom","edit","caret-top","check","minus","angle-left","angle-right","truck"] \ No newline at end of file diff --git a/examples/index.template.html b/examples/index.template.html new file mode 100644 index 000000000..b97e0f9ef --- /dev/null +++ b/examples/index.template.html @@ -0,0 +1,12 @@ + + + + + + Element 后台组件 + + + + + + diff --git a/examples/nav.config.json b/examples/nav.config.json new file mode 100644 index 000000000..cc0386a55 --- /dev/null +++ b/examples/nav.config.json @@ -0,0 +1,222 @@ +[ + { + "group": "使用说明", + "list": [ + { + "path": "/quickstart", + "name": "快速上手" + }, + { + "path": "/development", + "name": "开发指南" + }, + { + "path": "/changelog", + "name": "更新日志" + } + ] + }, + { + "group": "设计指南", + "list": [ + { + "path": "/menu", + "name": "导航", + "description": "导航(Navigation) 可以解决用户在访问页面时:在哪里,去哪里,怎么去的问题。一般导航会有「侧栏导航」和「顶部导航」2种类型。" + } + ] + }, + { + "group": "Basic", + "list": [ + { + "path": "/button", + "name": "按钮 (button)", + "title": "Button 按钮", + "description": "常用的操作按钮" + }, + { + "path": "/icon", + "name": "图标 (icon)", + "title": "Icon 图标" + }, + { + "path": "/tag", + "name": "标签 (tag)", + "title": "Tag 标签", + "description": "Tag 标签" + }, + { + "path": "/dialog", + "name": "对话框 (dialog)", + "title": "Dialog 对话框", + "description": "在保留当前页面状态的情况下, 告知用户信息并承载相关操作。" + }, + { + "path": "/message-box", + "name": "弹框 (message-box)", + "title": "message-box 弹框" + }, + { + "path": "/popover", + "name": "弹出框 (popover)", + "title": "popover 弹出框", + "description": "收纳具体内容和相关操作, 激活后弹出展现" + }, + { + "path": "/tooltip", + "name": "文字提示 (tooltip)", + "title": "tooltip 文字提示", + "description": "优雅地展示文字提示信息" + }, + { + "path": "/alert", + "name": "警告 (alert)", + "title": "alert 警告", + "description": "用于页面中展示重要的提示信息" + }, + { + "path": "/notification", + "name": "通知 (notification)", + "title": "notification 通知", + "description": "悬浮出现在页面右上角, 显示全局的通知提醒消息" + }, + { + "path": "/loading", + "name": "加载 (loading)", + "title": "loading 加载", + "description": "加载数据时显示" + } + ] + }, + { + "group": "Form", + "list": [ + { + "path": "/autocomplete", + "name": "自动完成 (autocomplete)", + "title": "Autocomplete 自动完成" + }, + { + "path": "/select", + "name": "选择器 (select)", + "title": "Select 选择器", + "description": "当选项过多时, 使用下拉菜单展示并选择内容" + }, + { + "path": "/checkbox", + "name": "多选框 (checkbox)", + "title": "Checkbox 多选框", + "description": "用于在多个可选项中进行多项选择。" + }, + { + "path": "/radio", + "name": "单选框 (radio)", + "title": "Radio 单选框", + "description": "用于在多个备选项选中单个选项。" + }, + { + "path": "/input", + "name": "输入框 (input)", + "title": "Input 输入框" + }, + { + "path": "/input-number", + "name": "计数器 (input-number)", + "title": "Input Number 计数器" + }, + { + "path": "/switch", + "name": "开关 (switch)", + "title": "Switch 开关", + "description": "用于两种对立状态的切换。" + }, + { + "path": "/time-picker", + "name": "时间选择器(time-picker)", + "title": "时间选择器", + "description": "用于选择或输入时间" + }, + { + "path": "/date-picker", + "name": "日期选择器(date-picker)", + "title": "日期选择器", + "description": "用于选择或输入时间" + }, + { + "path": "/datetime-picker", + "name": "日期时间选择器", + "title": "日期时间选择器", + "description": "用于选择或输入日期时间" + }, + { + "path": "/upload", + "name": "上传 (upload)", + "title": "upload 上传", + "description": "文件上传组件" + }, + { + "path": "/form", + "name": "表单 (form)", + "title": "form 表单", + "description": "一个多功能的并带有字段验证的表单组件" + }, + { + "path": "/slider", + "name": "滑块 (slider)", + "title": "slider 滑块", + "description": "通过拖动滑块在一个固定区间内进行选择" + } + ] + }, + { + "group": "Nav", + "list": [ + { + "path": "/tabs", + "name": "标签页 (tabs)", + "title": "tabs 标签页", + "description": "富展现的标签页" + }, + { + "path": "/breadcrumb", + "name": "面包屑 (breadcrumb)", + "title": "breadcrumb 面包屑", + "description": "" + }, + { + "path": "/dropdown", + "name": "下拉菜单 (dropdown)", + "title": "Dropdown 下拉菜单" + } + ] + }, + { + "group": "Data", + "list": [ + { + "path": "/table", + "name": "表格 (table)", + "title": "Table 表格", + "description": "用于展示多条结构类似的数据, 可对数据进行排序、筛选、对比或其他自定义操作。" + }, + { + "path": "/progress", + "name": "进度条 (progress)", + "title": "progress 进度条", + "description": "" + }, + { + "path": "/pagination", + "name": "分页 (pagination)", + "title": "Pagination 分页", + "description": "当数据量过多时, 使用分页分解数据" + }, + { + "path": "/tree", + "name": "tree (tree)", + "title": "tree" + } + ] + } +] diff --git a/examples/route.config.js b/examples/route.config.js new file mode 100644 index 000000000..10f78bd4e --- /dev/null +++ b/examples/route.config.js @@ -0,0 +1,40 @@ +import navConfig from './nav.config.json'; + +const registerRoute = (config) => { + let route = {}; + config.map(nav => nav.list.map(page => { + try { + route[page.path] = page.path !== '/changelog' ? { + component: require(`./docs${page.path}.md`), + title: page.title || page.name, + description: page.description + } : { + component: require('../CHANGELOG.md'), + title: page.title || page.name, + description: page.description + }; + } catch (e) { + console.error(e); + page.disabled = true; + } + })); + + return { route, navs: config }; +}; + +const route = registerRoute(navConfig); + +export const navs = route.navs; +export default function configRouter(router) { + router.map(Object.assign({ + '*': { + component: require('./docs/home.md') + } + }, route.route)); + + router.beforeEach(transition => { + document.title = transition.to.title || document.title; + transition.to.router.app.$broadcast('element.example.reload'); + transition.next(); + }); +}; diff --git a/fe.element/appspec.yml b/fe.element/appspec.yml new file mode 100644 index 000000000..76c138afd --- /dev/null +++ b/fe.element/appspec.yml @@ -0,0 +1,5 @@ +appid: fe.element +version: 1.0.0 +os: linux +language: cdnjs +apptype: web diff --git a/fe.element_build.yml b/fe.element_build.yml new file mode 100644 index 000000000..db0e608cc --- /dev/null +++ b/fe.element_build.yml @@ -0,0 +1,11 @@ +language: fe.cooking +version: 0.5.4 +script: + - make deploy +outfile: + - ./fe.element +notifications: + email: + - haiping.zeng@ele.me +branch: + master: alpha_stable diff --git a/lerna.json b/lerna.json new file mode 100644 index 000000000..e87c3ad87 --- /dev/null +++ b/lerna.json @@ -0,0 +1,10 @@ +{ + "lerna": "2.0.0-beta.18", + "version": "independent", + "publishConfig": { + "ignore": [ + "node_modules", + "log.*" + ] + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..0bee6c402 --- /dev/null +++ b/package.json @@ -0,0 +1,50 @@ +{ + "name": "element-ui", + "version": "0.1.9", + "description": "A Component Library for Vue.js.", + "main": "lib/index.js", + "files": [ + "lib", + "src" + ], + "scripts": { + "dev": "node bin/build-entry.js && cooking watch -c scripts/cooking.demo.js", + "dist": "rm -rf lib && cooking build -p && cooking build -c scripts/cooking.component.js -p", + "deploy": "cooking build -c scripts/cooking.demo.js -p", + "gh-docs": "cooking build -c scripts/cooking.demo.js -p && gh-pages -d examples/element-ui --remote origin", + "prepublish": "make dist" + }, + "repository": { + "type": "git", + "url": "git@github.com:eleme/element-ui.git" + }, + "keywords": [ + "eleme", + "vue", + "components" + ], + "bugs": { + "url": "https://github.com/eleme/element-ui/issues" + }, + "dependencies": { + "object-assign": "^4.1.0" + }, + "devDependencies": { + "file-save": "^0.2.0", + "gh-pages": "^0.11.0", + "highlight.js": "^9.3.0", + "json-templater": "^1.0.4", + "lerna": "2.0.0-beta.18", + "markdown-it-toc-and-anchor": "^4.1.1", + "oui-dom-events": "^0.2.1", + "postcss": "^5.0.21", + "purecss": "^0.6.0", + "q": "^1.4.1", + "uppercamelcase": "^1.1.0", + "vue": "^1.0.26", + "vue-loader": "^8.5.3", + "vue-markdown-loader": "^0.4.0", + "vue-popup": "^0.1.8", + "vue-router": "^0.7.13" + } +} diff --git a/packages/alert/cooking.conf.js b/packages/alert/cooking.conf.js new file mode 100644 index 000000000..4adcd4e0e --- /dev/null +++ b/packages/alert/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElAlert', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/alert/index.js b/packages/alert/index.js new file mode 100644 index 000000000..0242beae3 --- /dev/null +++ b/packages/alert/index.js @@ -0,0 +1,7 @@ +const Alert = require('./src/main'); + +Alert.install = function(Vue) { + Vue.component(Alert.name, Alert); +}; + +module.exports = Alert; diff --git a/packages/alert/package.json b/packages/alert/package.json new file mode 100644 index 000000000..cbaea01de --- /dev/null +++ b/packages/alert/package.json @@ -0,0 +1,15 @@ +{ + "name": "el-alert", + "version": "0.0.0", + "description": "A alert component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/alert", + "author": "elemefe", + "license": "MIT", + "dependencies": {} +} diff --git a/packages/alert/src/main.vue b/packages/alert/src/main.vue new file mode 100644 index 000000000..8f3db0aa2 --- /dev/null +++ b/packages/alert/src/main.vue @@ -0,0 +1,76 @@ + + + diff --git a/packages/autocomplete/cooking.conf.js b/packages/autocomplete/cooking.conf.js new file mode 100644 index 000000000..d9e2fb03f --- /dev/null +++ b/packages/autocomplete/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElAutocomplete', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/autocomplete/index.js b/packages/autocomplete/index.js new file mode 100644 index 000000000..1f923d5da --- /dev/null +++ b/packages/autocomplete/index.js @@ -0,0 +1,7 @@ +const ElAutocomplete = require('./src/autocomplete'); + +ElAutocomplete.install = function(Vue) { + Vue.component(ElAutocomplete.name, ElAutocomplete); +}; + +module.exports = ElAutocomplete; diff --git a/packages/autocomplete/package.json b/packages/autocomplete/package.json new file mode 100644 index 000000000..46542b179 --- /dev/null +++ b/packages/autocomplete/package.json @@ -0,0 +1,16 @@ +{ + "name": "el-autocomplete", + "version": "1.0.0", + "description": "A autocomplete component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/autocomplete", + "author": "haiping.zeng", + "license": "MIT", + "dependencies": { + } +} diff --git a/packages/autocomplete/src/autocomplete.vue b/packages/autocomplete/src/autocomplete.vue new file mode 100644 index 000000000..7c5374cff --- /dev/null +++ b/packages/autocomplete/src/autocomplete.vue @@ -0,0 +1,129 @@ + + diff --git a/packages/breadcrumb-item/index.js b/packages/breadcrumb-item/index.js new file mode 100644 index 000000000..17263c0c3 --- /dev/null +++ b/packages/breadcrumb-item/index.js @@ -0,0 +1,7 @@ +const ElBreadcrumbItem = require('../breadcrumb/src/breadcrumb-item'); + +ElBreadcrumbItem.install = function(Vue) { + Vue.component(ElBreadcrumbItem.name, ElBreadcrumbItem); +}; + +module.exports = ElBreadcrumbItem; diff --git a/packages/breadcrumb/cooking.conf.js b/packages/breadcrumb/cooking.conf.js new file mode 100644 index 000000000..283dc2251 --- /dev/null +++ b/packages/breadcrumb/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElBreadcrumb', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/breadcrumb/index.js b/packages/breadcrumb/index.js new file mode 100644 index 000000000..18b86ab4a --- /dev/null +++ b/packages/breadcrumb/index.js @@ -0,0 +1,7 @@ +const ElBreadcrumb = require('./src/breadcrumb'); + +ElBreadcrumb.install = function(Vue) { + Vue.component(ElBreadcrumb.name, ElBreadcrumb); +}; + +module.exports = ElBreadcrumb; diff --git a/packages/breadcrumb/package.json b/packages/breadcrumb/package.json new file mode 100644 index 000000000..eb67f32c4 --- /dev/null +++ b/packages/breadcrumb/package.json @@ -0,0 +1,16 @@ +{ + "name": "el-breadcrumb", + "version": "1.0.0", + "description": "A breadcrumb component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/breadcrumb", + "author": "haiping.zeng", + "license": "MIT", + "dependencies": { + } +} diff --git a/packages/breadcrumb/src/breadcrumb-item.vue b/packages/breadcrumb/src/breadcrumb-item.vue new file mode 100644 index 000000000..dc2fe77c7 --- /dev/null +++ b/packages/breadcrumb/src/breadcrumb-item.vue @@ -0,0 +1,24 @@ + + diff --git a/packages/breadcrumb/src/breadcrumb.vue b/packages/breadcrumb/src/breadcrumb.vue new file mode 100644 index 000000000..bca2f50ff --- /dev/null +++ b/packages/breadcrumb/src/breadcrumb.vue @@ -0,0 +1,27 @@ + + diff --git a/packages/button-group/index.js b/packages/button-group/index.js new file mode 100644 index 000000000..40a81a4fa --- /dev/null +++ b/packages/button-group/index.js @@ -0,0 +1,7 @@ +const ElButtonGroup = require('../button/src/button-group'); + +ElButtonGroup.install = function(Vue) { + Vue.component(ElButtonGroup.name, ElButtonGroup); +}; + +module.exports = ElButtonGroup; diff --git a/packages/button/.npmignore b/packages/button/.npmignore new file mode 100644 index 000000000..87ae846ae --- /dev/null +++ b/packages/button/.npmignore @@ -0,0 +1,3 @@ +node_modules/ +npm-debug.log +npm-debug.log.* diff --git a/packages/button/cooking.conf.js b/packages/button/cooking.conf.js new file mode 100644 index 000000000..46dc548c8 --- /dev/null +++ b/packages/button/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElButton', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/button/index.js b/packages/button/index.js new file mode 100644 index 000000000..d71e7b3d6 --- /dev/null +++ b/packages/button/index.js @@ -0,0 +1,9 @@ +const ElButton = require('./src/button'); +const ElButtonGroup = require('./src/button-group'); + +ElButton.install = function(Vue) { + Vue.component(ElButton.name, ElButton); + Vue.component(ElButtonGroup.name, ElButtonGroup); +}; + +module.exports = ElButton; diff --git a/packages/button/package.json b/packages/button/package.json new file mode 100644 index 000000000..30082a757 --- /dev/null +++ b/packages/button/package.json @@ -0,0 +1,16 @@ +{ + "name": "el-button", + "version": "1.0.0", + "description": "A button component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/button", + "author": "haiping.zeng", + "license": "MIT", + "dependencies": { + } +} diff --git a/packages/button/src/button-group.vue b/packages/button/src/button-group.vue new file mode 100644 index 000000000..e562986a5 --- /dev/null +++ b/packages/button/src/button-group.vue @@ -0,0 +1,16 @@ + + diff --git a/packages/button/src/button.vue b/packages/button/src/button.vue new file mode 100644 index 000000000..6a6022014 --- /dev/null +++ b/packages/button/src/button.vue @@ -0,0 +1,49 @@ + + diff --git a/packages/cascader/README.md b/packages/cascader/README.md new file mode 100644 index 000000000..19014cd4b --- /dev/null +++ b/packages/cascader/README.md @@ -0,0 +1,50 @@ +# el-cascader +> A el-cascader component for Vue.js. + +## Installation +```shell +npm i el-cascader -D +``` + +## Usage +```javascript +import Vue from 'vue' +import ElCascader from 'el-cascader' +import 'theme-default/dist/cascader.css' + +Vue.use(ElCascader) +``` + +or + +```javascript +import Vue from 'vue' +import { ElCascader } from 'el-cascader' + +Vue.component('el-cascader', ElCascader) +``` + + +## Options + +### el-cascader + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|--------------------|----------------------------------------------------------|-------------------|-------------|--------| +|model| 绑定值| string | | | +|placeholder| 占位符| string | | | | + + +## Development +```shell +make dev + +## test +make test + +## build +make build +``` + +# License +[MIT](https://opensource.org/licenses/MIT) diff --git a/packages/cascader/cooking.conf.js b/packages/cascader/cooking.conf.js new file mode 100644 index 000000000..6f6df4d40 --- /dev/null +++ b/packages/cascader/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElCascader', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/cascader/index.js b/packages/cascader/index.js new file mode 100644 index 000000000..f601f4ad3 --- /dev/null +++ b/packages/cascader/index.js @@ -0,0 +1,7 @@ +const ElCascader = require('./src/cascader'); + +ElCascader.install = function(Vue) { + Vue.component(ElCascader.name, ElCascader); +}; + +module.exports = ElCascader; diff --git a/packages/cascader/package.json b/packages/cascader/package.json new file mode 100644 index 000000000..4e713caaa --- /dev/null +++ b/packages/cascader/package.json @@ -0,0 +1,18 @@ +{ + "name": "el-cascader", + "version": "1.0.1", + "description": "A cascader component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/cascader", + "author": "qingwei-li", + "license": "MIT", + "dependencies": { + "object-equal": "^1.0.0", + "vue-clickoutside": "0.0.4" + } +} diff --git a/packages/cascader/src/cascader.vue b/packages/cascader/src/cascader.vue new file mode 100644 index 000000000..a3a106002 --- /dev/null +++ b/packages/cascader/src/cascader.vue @@ -0,0 +1,91 @@ + + + diff --git a/packages/cascader/src/dropdown.vue b/packages/cascader/src/dropdown.vue new file mode 100644 index 000000000..f11c49f53 --- /dev/null +++ b/packages/cascader/src/dropdown.vue @@ -0,0 +1,109 @@ + + + diff --git a/packages/cascader/src/menu.vue b/packages/cascader/src/menu.vue new file mode 100644 index 000000000..d8b9b7c64 --- /dev/null +++ b/packages/cascader/src/menu.vue @@ -0,0 +1,35 @@ + \ No newline at end of file diff --git a/packages/checkbox-group/index.js b/packages/checkbox-group/index.js new file mode 100644 index 000000000..85c120a81 --- /dev/null +++ b/packages/checkbox-group/index.js @@ -0,0 +1,7 @@ +const ElCheckboxGroup = require('../checkbox/src/checkbox-group.vue'); + +ElCheckboxGroup.install = function(Vue) { + Vue.component(ElCheckboxGroup.name, ElCheckboxGroup); +}; + +module.exports = ElCheckboxGroup; diff --git a/packages/checkbox/README.md b/packages/checkbox/README.md new file mode 100644 index 000000000..58b8ffdf4 --- /dev/null +++ b/packages/checkbox/README.md @@ -0,0 +1,51 @@ +# el-checkbox +> A el-checkbox component for Vue.js. + +## Demo +http://element-component.github.io/el-checkbox + +## Installation +```shell +npm i el-checkbox -D +``` + +## Usage +```javascript +import Vue from 'vue' +import ElCheckbox from 'el-checkbox' +import 'element-theme-default/dist/checkbox.css' + +Vue.use(ElCheckbox) +``` + +or + +```javascript +import Vue from 'vue' +import { ElCheckbox } from 'el-checkbox' + +Vue.component('el-checkbox', ElCheckbox) +``` + + +## Options +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| model | 绑定值 | string[] | | | +| value | 真实值 | string | | | +| label | 显示值,不填则显示 value | string | | | +| disabled | 禁用 | boolean | true, false | false | + +## Development +```shell +make dev + +## test +make test + +## build +make build +``` + +# License +[MIT](https://opensource.org/licenses/MIT) diff --git a/packages/checkbox/cooking.conf.js b/packages/checkbox/cooking.conf.js new file mode 100644 index 000000000..04758b042 --- /dev/null +++ b/packages/checkbox/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElCheckbox', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/checkbox/index.js b/packages/checkbox/index.js new file mode 100644 index 000000000..ef523ea72 --- /dev/null +++ b/packages/checkbox/index.js @@ -0,0 +1,8 @@ +const ElCheckbox = require('./src/checkbox'); + +ElCheckbox.install = function(Vue) { + Vue.component('el-checkbox', ElCheckbox); +}; + +module.exports = ElCheckbox; + diff --git a/packages/checkbox/package.json b/packages/checkbox/package.json new file mode 100644 index 000000000..73bcfaad6 --- /dev/null +++ b/packages/checkbox/package.json @@ -0,0 +1,15 @@ +{ + "name": "el-checkbox", + "version": "1.0.2", + "description": "A checkbox component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/checkbox", + "author": "haiping.zeng@ele.me", + "license": "MIT", + "dependencies": {} +} diff --git a/packages/checkbox/src/checkbox-group.vue b/packages/checkbox/src/checkbox-group.vue new file mode 100644 index 000000000..a88ae5535 --- /dev/null +++ b/packages/checkbox/src/checkbox-group.vue @@ -0,0 +1,27 @@ + + + diff --git a/packages/checkbox/src/checkbox.vue b/packages/checkbox/src/checkbox.vue new file mode 100644 index 000000000..bfb290150 --- /dev/null +++ b/packages/checkbox/src/checkbox.vue @@ -0,0 +1,129 @@ + + diff --git a/packages/col/cooking.conf.js b/packages/col/cooking.conf.js new file mode 100644 index 000000000..5cc8ca2af --- /dev/null +++ b/packages/col/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElCol', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/col/index.js b/packages/col/index.js new file mode 100644 index 000000000..ee09e61ad --- /dev/null +++ b/packages/col/index.js @@ -0,0 +1,8 @@ +const ElCol = require('./src/col'); + +ElCol.install = function(Vue) { + Vue.component('el-col', ElCol); +}; + +module.exports = ElCol; + diff --git a/packages/col/package.json b/packages/col/package.json new file mode 100644 index 000000000..b4d8b251a --- /dev/null +++ b/packages/col/package.json @@ -0,0 +1,15 @@ +{ + "name": "el-col", + "version": "1.0.0", + "description": "A row component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/col", + "author": "haiping.zeng@ele.me", + "license": "MIT", + "dependencies": {} +} diff --git a/packages/col/src/col.vue b/packages/col/src/col.vue new file mode 100644 index 000000000..8a38c60e2 --- /dev/null +++ b/packages/col/src/col.vue @@ -0,0 +1,55 @@ + + diff --git a/packages/date-picker/README.md b/packages/date-picker/README.md new file mode 100644 index 000000000..e05438cc9 --- /dev/null +++ b/packages/date-picker/README.md @@ -0,0 +1,33 @@ +# __component_name__ + +# Screenshots + +# Demo + +# Installation +> + +# Usage +```javascript + +``` + +# Options + +| name | description | type | default | +|-------------|-------------|-------------|-------------| +| | | | | + +# Development +```shell +make dev + +# test +make test + +# build +make build +``` + +# License +[MIT](https://opensource.org/licenses/MIT) diff --git a/packages/date-picker/cooking.conf.js b/packages/date-picker/cooking.conf.js new file mode 100644 index 000000000..860ef0731 --- /dev/null +++ b/packages/date-picker/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, '_index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElDatePicker', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/date-picker/index.js b/packages/date-picker/index.js new file mode 100644 index 000000000..caa5b7fb6 --- /dev/null +++ b/packages/date-picker/index.js @@ -0,0 +1,2 @@ +import Picker from '../date-picker/src/picker/date-picker'; +module.exports = Picker; diff --git a/packages/date-picker/package.json b/packages/date-picker/package.json new file mode 100644 index 000000000..d863212b9 --- /dev/null +++ b/packages/date-picker/package.json @@ -0,0 +1,21 @@ +{ + "name": "el-datepicker", + "version": "1.0.0", + "description": "A datepicker component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/datepicker", + "author": "long.zhang@ele.me", + "license": "MIT", + "dependencies": { + "wind-dom": "0.0.3", + "vue-clickoutside": "*" + }, + "devDependencies": { + "vue": "^1.0.0" + } +} diff --git a/packages/date-picker/src/basic/date-table.vue b/packages/date-picker/src/basic/date-table.vue new file mode 100644 index 000000000..de4a0cf13 --- /dev/null +++ b/packages/date-picker/src/basic/date-table.vue @@ -0,0 +1,434 @@ + + + diff --git a/packages/date-picker/src/basic/month-table.vue b/packages/date-picker/src/basic/month-table.vue new file mode 100644 index 000000000..7e0fef950 --- /dev/null +++ b/packages/date-picker/src/basic/month-table.vue @@ -0,0 +1,74 @@ + + + diff --git a/packages/date-picker/src/basic/time-spinner.vue b/packages/date-picker/src/basic/time-spinner.vue new file mode 100644 index 000000000..51dcc3f5c --- /dev/null +++ b/packages/date-picker/src/basic/time-spinner.vue @@ -0,0 +1,139 @@ + + + diff --git a/packages/date-picker/src/basic/year-table.vue b/packages/date-picker/src/basic/year-table.vue new file mode 100644 index 000000000..d454bd653 --- /dev/null +++ b/packages/date-picker/src/basic/year-table.vue @@ -0,0 +1,78 @@ + + + diff --git a/packages/date-picker/src/css/date-picker.css b/packages/date-picker/src/css/date-picker.css new file mode 100644 index 000000000..0281ebb1f --- /dev/null +++ b/packages/date-picker/src/css/date-picker.css @@ -0,0 +1,84 @@ +@import "./vars.css"; +@import "./picker-panel.css"; + +@component-namespace el { + @b date-picker { + min-width: 300px; + + .el-picker-panel__content { + min-width: 224px; + } + + table { + table-layout: fixed; + width: 100%; + } + + @e editor { + flex: 1; + margin: 0 5px; + position: relative; + border: 1px solid #c0ccda; + border-radius: 4px; + padding: 5px 10px; + box-sizing: border-box; + height: 28px; + outline: none; + appearance: none; + transition: border 0.3s; + + &:hover { + border-color: #20a0ff; + } + } + + @e time-header { + position: relative; + border-bottom: 1px solid var(--datepicker-inner-border-color); + font-size: 0; + padding: 8px 5px 5px 5px; + display: flex; + } + + @e header { + margin: 12px; + text-align: center; + } + + @e header-label { + font-size: 14px; + padding: 0 5px; + line-height: 22px; + text-align: center; + cursor: pointer; + + &:hover { + color: var(--datepicker-text-hover-color); + } + + &.active { + color: var(--datepicker-active-color); + } + } + + @e prev-btn { + float: left; + } + + @e next-btn { + float: right; + } + + @e time-wrap { + padding: 10px; + text-align: center; + } + + @e time-label { + float: left; + cursor: pointer; + line-height: 30px; + margin-left: 10px; + } + } +} diff --git a/packages/date-picker/src/css/date-range-picker.css b/packages/date-picker/src/css/date-range-picker.css new file mode 100644 index 000000000..7c8a7c135 --- /dev/null +++ b/packages/date-picker/src/css/date-range-picker.css @@ -0,0 +1,118 @@ +@import './vars.css'; + +@component-namespace el { + @b date-range-picker { + table { + table-layout: fixed; + width: 100%; + } + + .el-picker-panel__body { + min-width: 513px; + } + + .el-picker-panel__content { + margin: 0; + } + + @e editor { + flex: 1; + margin: 0 5px; + position: relative; + border: 1px solid #c0ccda; + border-radius: 4px; + padding: 5px 10px; + box-sizing: border-box; + height: 28px; + outline: none; + appearance: none; + transition: border 0.3s; + + &:hover { + border-color: #20a0ff; + } + } + + @e header { + position: relative; + text-align: center; + height: 28px; + + button { + float: left; + } + + div { + font-size: 14px; + margin-right: 50px; + } + } + + @e content { + float: left; + width: 50%; + box-sizing: border-box; + margin: 0; + padding: 16px; + + @when left { + border-right: 1px solid var(--datepicker-inner-border-color); + } + + @when right { + .el-date-range-picker__header { + button { + float: right; + } + + div { + margin-left: 50px; + margin-right: 50px; + } + } + } + } + + @e editors-wrap { + padding-right: 10px; + display: inline-block; + width: 50%; + box-sizing: border-box; + display: flex; + + @when right { + padding-left: 10px; + text-align: right; + } + } + + @e time-header { + position: relative; + border-bottom: 1px solid var(--datepicker-inner-border-color); + font-size: 0; + padding: 8px 5px 5px 5px; + display: flex; + + > .el-icon-arrow-right { + position: absolute; + left: 50%; + margin-left: -10px; + margin-top: 5px; + font-size: 20px; + color: var(--datepicker-icon-color); + } + } + + @e time-picker-wrap { + position: relative; + + .el-picker-panel { + position: absolute; + top: 13px; + right: 0; + z-index: 1; + background: #fff; + } + } + } +} diff --git a/packages/date-picker/src/css/date-table.css b/packages/date-picker/src/css/date-table.css new file mode 100644 index 000000000..abe8e527e --- /dev/null +++ b/packages/date-picker/src/css/date-table.css @@ -0,0 +1,75 @@ +@import "./vars.css"; + +@component-namespace el { + @b date-table { + font-size: 12px; + min-width: 224px; + user-select: none; + + @when week-mode { + .el-date-table__row { + &:hover { + background-color: var(--datepicker-cell-hover-color); + } + + &.current { + background-color: var(--datepicker-inrange-color); + } + } + } + + td { + width: 32px; + height: 32px; + box-sizing: border-box; + text-align: center; + cursor: pointer; + + &.next-month, + &.prev-month { + color: var(--datepicker-off-color); + } + + &.today { + color: var(--datepicker-text-hover-color); + } + + &.available:hover { + background-color: var(--datepicker-cell-hover-color); + } + + &.in-range { + background-color: var(--datepicker-inrange-color); + &:hover { + background-color: var(--datepicker-inrange-hover-color); + } + } + + &.current, + &.start-date, + &.end-date { + background-color: var(--datepicker-active-color) !important; + color: #fff; + } + + &.disabled { + background-color: #f4f4f4; + opacity: 1; + cursor: not-allowed; + color: #ccc; + text-decoration: line-through; + } + + &.week { + font-size: 80%; + color: var(--datepicker-header-color); + } + } + + th { + padding: 5px; + color: var(--datepicker-header-color); + font-weight: 400; + } + } +} diff --git a/packages/date-picker/src/css/month-table.css b/packages/date-picker/src/css/month-table.css new file mode 100644 index 000000000..760e4616f --- /dev/null +++ b/packages/date-picker/src/css/month-table.css @@ -0,0 +1,31 @@ +@import "./vars.css"; + +@component-namespace el { + @b month-table { + font-size: 12px; + margin: -1px; + + td { + text-align: center; + padding: 20px 3px; + cursor: pointer; + + .cell { + width: 48px; + height: 32px; + display: block; + line-height: 32px; + color: var(--datepicker-color); + + &:hover { + background-color: var(--datepicker-cell-hover-color); + } + } + + &.current .cell { + background-color: var(--datepicker-active-color) !important; + color: #fff; + } + } + } +} \ No newline at end of file diff --git a/packages/date-picker/src/css/picker-panel.css b/packages/date-picker/src/css/picker-panel.css new file mode 100644 index 000000000..2af340d2d --- /dev/null +++ b/packages/date-picker/src/css/picker-panel.css @@ -0,0 +1,111 @@ +@import './vars.css'; + +@component-namespace el { + @b picker-panel { + color: var(--datepicker-color); + border: 1px solid var(--datepicker-border-color); + box-shadow: 0 2px 6px #ccc; + background: #fff; + border-radius: 2px; + line-height: 20px; + margin: 5px 0; + + @e body, body-wrapper { + &::after { + content: ""; + display: table; + clear: both; + } + } + + @e content { + position: relative; + margin: 15px; + } + + @e footer { + border-top: 1px solid var(--datepicker-inner-border-color); + padding: 4px; + text-align: right; + background-color: #fff; + position: relative; + } + + @e shortcut { + display: block; + width: 100%; + border: 0; + background-color: transparent; + line-height: 28px; + font-size: 14px; + color: var(--datepicker-color); + padding-left: 12px; + text-align: left; + outline: none; + cursor: pointer; + + &:hover { + background-color: #e5e9f2; + } + + &.active { + background-color: #e6f1fe; + color: var(--datepicker-active-color); + } + } + + @e btn { + border: 1px solid #dcdcdc; + color: #333; + line-height: 24px; + border-radius: 2px; + padding: 0 20px; + cursor: pointer; + background-color: transparent; + outline: none; + + &[disabled] { + color: #cccccc; + cursor: not-allowed; + } + } + + @e icon-btn { + font-size: 12px; + color: var(--datepicker-icon-color); + border: 0; + background: transparent; + cursor: pointer; + outline: none; + margin-top: 3px; + + &:hover { + color: var(--datepicker-text-hover-color); + } + } + + @e link-btn { + color: #55a4ff; + text-decoration: none; + padding: 15px; + font-size: 12px; + } + } + + .el-picker-panel *[slot=sidebar], + .el-picker-panel__sidebar { + position: absolute; + top: 0; + bottom: 0; + width: 110px; + border-right: 1px solid var(--datepicker-inner-border-color); + box-sizing: border-box; + padding-top: 6px; + background-color: #f9fafc; + } + + .el-picker-panel *[slot=sidebar] + .el-picker-panel__body, + .el-picker-panel__sidebar + .el-picker-panel__body { + margin-left: 110px; + } +} diff --git a/packages/date-picker/src/css/picker.css b/packages/date-picker/src/css/picker.css new file mode 100644 index 000000000..715c0e9e8 --- /dev/null +++ b/packages/date-picker/src/css/picker.css @@ -0,0 +1,121 @@ +@import "./vars.css"; +@import "../../../theme-default/src/common/transition.css"; + +@component-namespace el { + @b date-editor { + position: relative; + display: inline-block; + box-sizing: border-box; + height: 36px; + width: 182px; + + @e editor { + border: 1px solid #c0ccda; + border-radius: 4px; + line-height: 18px; + height: 36px; + padding: 3px 10px; + width: 100%; + box-sizing: border-box; + transition: border 0.3s; + color: #666; + font-size: 14px; + + &::-webkit-input-placeholder { + color: var(--input-placeholder-color); + font-size: 14px; + } + + &::-moz-placeholder { + color: #bbb; + font-size: 14px; + } + + &:-ms-input-placeholder { + color: #bbb; + font-size: 14px; + } + + &::placeholder { + color: #bbb; + font-size: 14px; + } + } + + @e trigger { + cursor: pointer; + position: absolute; + display: inline-block; + width: 20px; + right: 0; + top: 0; + bottom: 0; + color: var(--datepicker-trigger-color); + font-size: 13px; + line-height: 38px; + } + + @e clear { + cursor: pointer; + position: absolute; + width: 20px; + right: 0; + top: 0; + bottom: 0; + line-height: 36px; + color: var(--datepicker-icon-color); + + &:hover { + color: var(--datepicker-text-hover-color); + } + } + + @when have-trigger { + .el-date-editor__editor { + padding-right: 20px; + } + + .el-date-editor__clear { + right: 22px; + } + } + + &.lg { + .el-date-editor__editor { + padding: 6px 4px; + } + } + + &.sm { + .el-date-editor__editor { + padding: 1px 1px; + } + } + + .el-picker-panel { + position: absolute; + min-width: 180px; + box-sizing: border-box; + box-shadow: 0 2px 6px #ccc; + background: #fff; + z-index: 10; + top: 41px; + } + } + + .el-date-editor__editor:focus, + .el-date-editor:hover > .el-date-editor__editor, + .el-date-editor.is-active > .el-date-editor__editor { + outline: none; + border-color: var(--datepicker-active-color); + } + + .el-date-editor .el-date-editor__clear { + display: none; + } + + .el-date-editor.is-filled:hover .el-date-editor__clear, + .el-date-editor.is-filled.is-active > .el-date-editor__clear { + display: inline-block; + } +} diff --git a/packages/date-picker/src/css/time-picker.css b/packages/date-picker/src/css/time-picker.css new file mode 100644 index 000000000..c0860c0d5 --- /dev/null +++ b/packages/date-picker/src/css/time-picker.css @@ -0,0 +1,68 @@ +@import "./vars.css"; + +@component-namespace el { + @b time-panel { + margin: 5px 0; + border: solid 1px #d3dce6; + background-color: #fff; + box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px 0 rgba(0, 0, 0, .04); + border-radius: 2px; + position: absolute; + width: 180px; + left: 0; + z-index: 1000; + user-select: none; + + @e content { + font-size: 0; + display: flex; + position: relative; + + &::after, &::before { + content: ":"; + top: 50%; + color: #fff; + position: absolute; + font-size: 14px; + margin-top: -8px; + z-index: 1; + } + + &::after { + left: calc(100%/3); + margin-left: -2px; + } + + &::before { + right: calc(100%/3); + margin-right: -2px; + } + } + + @e footer { + border-top: 1px solid var(--datepicker-inner-border-color); + padding: 4px; + height: 36px; + text-align: right; + box-sizing: border-box; + } + + @e btn { + border: none; + line-height: 28px; + padding: 0 5px; + margin: 0 5px; + cursor: pointer; + background-color: transparent; + outline: none; + font-size: 12px; + color: #8492a6; + + &.confirm { + font-weight: 800; + color: #20a0ff; + } + } + + } +} diff --git a/packages/date-picker/src/css/time-range-picker.css b/packages/date-picker/src/css/time-range-picker.css new file mode 100644 index 000000000..67eca4504 --- /dev/null +++ b/packages/date-picker/src/css/time-range-picker.css @@ -0,0 +1,34 @@ +@import '../css/vars.css'; + +@component-namespace el { + @b time-range-picker { + width: 354px; + overflow: visible; + + @e content { + position: relative; + text-align: center; + padding: 10px; + display: flex; + } + + @e cell { + box-sizing: border-box; + margin: 0; + padding: 4px 7px 7px; + flex: 1; + } + + @e header { + margin-bottom: 5px; + text-align: center; + font-size: 14px; + } + + @e body { + display: flex; + border-radius:2px; + border: 1px solid var(--datepicker-border-color); + } + } +} diff --git a/packages/date-picker/src/css/time-spinner.css b/packages/date-picker/src/css/time-spinner.css new file mode 100644 index 000000000..cd3775a85 --- /dev/null +++ b/packages/date-picker/src/css/time-spinner.css @@ -0,0 +1,57 @@ +@import "./vars.css"; + +@component-namespace el { + @b time-spinner { + @e wrapper { + height: 190px; + overflow: hidden; + flex: 1; + vertical-align: top; + position: relative; + + &:hover { + overflow-y: auto; + } + } + + @e list { + padding: 0; + margin: 0; + list-style: none; + text-align: center; + + &::after, + &::before { + content: ''; + display: block; + width: 100%; + height: 80px; + } + } + + @e item { + height: 32px; + line-height: 32px; + font-size: 12px; + + &:hover:not(.disabled) { + background: #E5E9F2; + cursor: pointer; + } + + &.active:not(.disabled) { + background-color: #20a0ff; + color: #fff; + + &:hover { + background-color: #1D8CE0; + } + } + + &.disabled { + color: var(--datepicker-border-color); + cursor: not-allowed; + } + } + } +} diff --git a/packages/date-picker/src/css/vars.css b/packages/date-picker/src/css/vars.css new file mode 100644 index 000000000..3e5fa2e29 --- /dev/null +++ b/packages/date-picker/src/css/vars.css @@ -0,0 +1,14 @@ +:root { + --datepicker-color: #475669; + --datepicker-off-color: #ddd; + --datepicker-header-color: #8492a6; + --datepicker-icon-color: #99a9bf; + --datepicker-trigger-color: #c0ccda; + --datepicker-border-color: #d3dce6; + --datepicker-inner-border-color: #e4e4e4; + --datepicker-cell-hover-color: #e5e9f2; + --datepicker-inrange-color: #D3ECFF; + --datepicker-inrange-hover-color: #AFDCFF; + --datepicker-active-color: #20a0ff; + --datepicker-text-hover-color: #20a0ff; +} \ No newline at end of file diff --git a/packages/date-picker/src/css/year-table.css b/packages/date-picker/src/css/year-table.css new file mode 100644 index 000000000..ec0fce46d --- /dev/null +++ b/packages/date-picker/src/css/year-table.css @@ -0,0 +1,35 @@ +@import "./vars.css"; + +@component-namespace el { + @b year-table { + font-size: 12px; + margin: -1px; + + .el-icon { + color: var(--datepicker-icon-color); + } + + td { + text-align: center; + padding: 20px 3px; + cursor: pointer; + + .cell { + width: 48px; + height: 32px; + display: block; + line-height: 32px; + color: var(--datepicker-color); + } + + &.available .cell:hover { + background-color: var(--datepicker-cell-hover-color); + } + + &.current .cell { + background-color: var(--datepicker-active-color) !important; + color: #fff; + } + } + } +} \ No newline at end of file diff --git a/packages/date-picker/src/panel/date-range.vue b/packages/date-picker/src/panel/date-range.vue new file mode 100644 index 000000000..33ba27e40 --- /dev/null +++ b/packages/date-picker/src/panel/date-range.vue @@ -0,0 +1,470 @@ + + + diff --git a/packages/date-picker/src/panel/date.vue b/packages/date-picker/src/panel/date.vue new file mode 100644 index 000000000..032667605 --- /dev/null +++ b/packages/date-picker/src/panel/date.vue @@ -0,0 +1,433 @@ + + + diff --git a/packages/date-picker/src/panel/time-range.vue b/packages/date-picker/src/panel/time-range.vue new file mode 100644 index 000000000..6d9564061 --- /dev/null +++ b/packages/date-picker/src/panel/time-range.vue @@ -0,0 +1,240 @@ + + + diff --git a/packages/date-picker/src/panel/time-select.vue b/packages/date-picker/src/panel/time-select.vue new file mode 100644 index 000000000..2af5d8e2e --- /dev/null +++ b/packages/date-picker/src/panel/time-select.vue @@ -0,0 +1,130 @@ + + + diff --git a/packages/date-picker/src/panel/time.vue b/packages/date-picker/src/panel/time.vue new file mode 100644 index 000000000..556ed5ffc --- /dev/null +++ b/packages/date-picker/src/panel/time.vue @@ -0,0 +1,155 @@ + + + diff --git a/packages/date-picker/src/picker.vue b/packages/date-picker/src/picker.vue new file mode 100644 index 000000000..b0fdc217a --- /dev/null +++ b/packages/date-picker/src/picker.vue @@ -0,0 +1,493 @@ + + + diff --git a/packages/date-picker/src/picker/date-picker.js b/packages/date-picker/src/picker/date-picker.js new file mode 100644 index 000000000..17a55b48f --- /dev/null +++ b/packages/date-picker/src/picker/date-picker.js @@ -0,0 +1,25 @@ +import Picker from '../picker'; +import DatePanel from '../panel/date'; +import DateRangePanel from '../panel/date-range'; + +const getPanel = function(type) { + if (type === 'daterange' || type === 'datetimerange') { + return DateRangePanel; + } + return DatePanel; +}; + +export default { + mixins: [Picker], + + name: 'ElDatePicker', + + props: { + type: String, + default: 'date' + }, + + created() { + this.panel = getPanel(this.type); + } +}; diff --git a/packages/date-picker/src/picker/time-picker.js b/packages/date-picker/src/picker/time-picker.js new file mode 100644 index 000000000..d127d2cdd --- /dev/null +++ b/packages/date-picker/src/picker/time-picker.js @@ -0,0 +1,18 @@ +import Picker from '../picker'; +import TimePanel from '../panel/time'; +import TimeRangePanel from '../panel/time-range'; + +export default { + mixins: [Picker], + + name: 'ElTimePicker', + + props: { + isRange: Boolean + }, + + created() { + this.type = this.isRange ? 'timerange' : 'time'; + this.panel = this.isRange ? TimeRangePanel : TimePanel; + } +}; diff --git a/packages/date-picker/src/picker/time-select.js b/packages/date-picker/src/picker/time-select.js new file mode 100644 index 000000000..496849590 --- /dev/null +++ b/packages/date-picker/src/picker/time-select.js @@ -0,0 +1,13 @@ +import Picker from '../picker'; +import Panel from '../panel/time-select'; + +export default { + mixins: [Picker], + + name: 'ElTimeSelect', + + created() { + this.type = 'time-select'; + this.panel = Panel; + } +}; diff --git a/packages/date-picker/src/util/dropdown.js b/packages/date-picker/src/util/dropdown.js new file mode 100644 index 000000000..f78e7e523 --- /dev/null +++ b/packages/date-picker/src/util/dropdown.js @@ -0,0 +1,27 @@ +var dropdowns = []; + +document.addEventListener('click', function(event) { + dropdowns.forEach(function(dropdown) { + var target = event.target; + if (!dropdown || !dropdown.$el) return; + if (target === dropdown.$el || dropdown.$el.contains(target)) { + return; + } + dropdown.onDocumentClick && dropdown.onDocumentClick(event); + }); +}); + +export default { + open(instance) { + if (instance) { + dropdowns.push(instance); + } + }, + + close(instance) { + var index = dropdowns.indexOf(instance); + if (index !== -1) { + dropdowns.splice(instance, 1); + } + } +}; diff --git a/packages/date-picker/src/util/fecha.js b/packages/date-picker/src/util/fecha.js new file mode 100644 index 000000000..2a59b4261 --- /dev/null +++ b/packages/date-picker/src/util/fecha.js @@ -0,0 +1,319 @@ +/*eslint-disable*/ +// 把 YYYY-MM-DD 改成了 yyyy-MM-dd +(function (main) { + 'use strict'; + + /** + * Parse or format dates + * @class fecha + */ + var fecha = {}; + var token = /d{1,4}|M{1,4}|yy(?:yy)?|S{1,3}|Do|ZZ|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g; + var twoDigits = /\d\d?/; + var threeDigits = /\d{3}/; + var fourDigits = /\d{4}/; + var word = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; + var noop = function () { + }; + + function shorten(arr, sLen) { + var newArr = []; + for (var i = 0, len = arr.length; i < len; i++) { + newArr.push(arr[i].substr(0, sLen)); + } + return newArr; + } + + function monthUpdate(arrName) { + return function (d, v, i18n) { + var index = i18n[arrName].indexOf(v.charAt(0).toUpperCase() + v.substr(1).toLowerCase()); + if (~index) { + d.month = index; + } + }; + } + + function pad(val, len) { + val = String(val); + len = len || 2; + while (val.length < len) { + val = '0' + val; + } + return val; + } + + var dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + var monthNamesShort = shorten(monthNames, 3); + var dayNamesShort = shorten(dayNames, 3); + fecha.i18n = { + dayNamesShort: dayNamesShort, + dayNames: dayNames, + monthNamesShort: monthNamesShort, + monthNames: monthNames, + amPm: ['am', 'pm'], + DoFn: function DoFn(D) { + return D + ['th', 'st', 'nd', 'rd'][D % 10 > 3 ? 0 : (D - D % 10 !== 10) * D % 10]; + } + }; + + var formatFlags = { + D: function(dateObj) { + return dateObj.getDay(); + }, + DD: function(dateObj) { + return pad(dateObj.getDay()); + }, + Do: function(dateObj, i18n) { + return i18n.DoFn(dateObj.getDate()); + }, + d: function(dateObj) { + return dateObj.getDate(); + }, + dd: function(dateObj) { + return pad(dateObj.getDate()); + }, + ddd: function(dateObj, i18n) { + return i18n.dayNamesShort[dateObj.getDay()]; + }, + dddd: function(dateObj, i18n) { + return i18n.dayNames[dateObj.getDay()]; + }, + M: function(dateObj) { + return dateObj.getMonth() + 1; + }, + MM: function(dateObj) { + return pad(dateObj.getMonth() + 1); + }, + MMM: function(dateObj, i18n) { + return i18n.monthNamesShort[dateObj.getMonth()]; + }, + MMMM: function(dateObj, i18n) { + return i18n.monthNames[dateObj.getMonth()]; + }, + yy: function(dateObj) { + return String(dateObj.getFullYear()).substr(2); + }, + yyyy: function(dateObj) { + return dateObj.getFullYear(); + }, + h: function(dateObj) { + return dateObj.getHours() % 12 || 12; + }, + hh: function(dateObj) { + return pad(dateObj.getHours() % 12 || 12); + }, + H: function(dateObj) { + return dateObj.getHours(); + }, + HH: function(dateObj) { + return pad(dateObj.getHours()); + }, + m: function(dateObj) { + return dateObj.getMinutes(); + }, + mm: function(dateObj) { + return pad(dateObj.getMinutes()); + }, + s: function(dateObj) { + return dateObj.getSeconds(); + }, + ss: function(dateObj) { + return pad(dateObj.getSeconds()); + }, + S: function(dateObj) { + return Math.round(dateObj.getMilliseconds() / 100); + }, + SS: function(dateObj) { + return pad(Math.round(dateObj.getMilliseconds() / 10), 2); + }, + SSS: function(dateObj) { + return pad(dateObj.getMilliseconds(), 3); + }, + a: function(dateObj, i18n) { + return dateObj.getHours() < 12 ? i18n.amPm[0] : i18n.amPm[1]; + }, + A: function(dateObj, i18n) { + return dateObj.getHours() < 12 ? i18n.amPm[0].toUpperCase() : i18n.amPm[1].toUpperCase(); + }, + ZZ: function(dateObj) { + var o = dateObj.getTimezoneOffset(); + return (o > 0 ? '-' : '+') + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4); + } + }; + + var parseFlags = { + d: [twoDigits, function (d, v) { + d.day = v; + }], + M: [twoDigits, function (d, v) { + d.month = v - 1; + }], + yy: [twoDigits, function (d, v) { + var da = new Date(), cent = +('' + da.getFullYear()).substr(0, 2); + d.year = '' + (v > 68 ? cent - 1 : cent) + v; + }], + h: [twoDigits, function (d, v) { + d.hour = v; + }], + m: [twoDigits, function (d, v) { + d.minute = v; + }], + s: [twoDigits, function (d, v) { + d.second = v; + }], + yyyy: [fourDigits, function (d, v) { + d.year = v; + }], + S: [/\d/, function (d, v) { + d.millisecond = v * 100; + }], + SS: [/\d{2}/, function (d, v) { + d.millisecond = v * 10; + }], + SSS: [threeDigits, function (d, v) { + d.millisecond = v; + }], + D: [twoDigits, noop], + ddd: [word, noop], + MMM: [word, monthUpdate('monthNamesShort')], + MMMM: [word, monthUpdate('monthNames')], + a: [word, function (d, v, i18n) { + var val = v.toLowerCase(); + if (val === i18n.amPm[0]) { + d.isPm = false; + } else if (val === i18n.amPm[1]) { + d.isPm = true; + } + }], + ZZ: [/[\+\-]\d\d:?\d\d/, function (d, v) { + var parts = (v + '').match(/([\+\-]|\d\d)/gi), minutes; + + if (parts) { + minutes = +(parts[1] * 60) + parseInt(parts[2], 10); + d.timezoneOffset = parts[0] === '+' ? minutes : -minutes; + } + }] + }; + parseFlags.DD = parseFlags.DD; + parseFlags.dddd = parseFlags.ddd; + parseFlags.Do = parseFlags.dd = parseFlags.d; + parseFlags.mm = parseFlags.m; + parseFlags.hh = parseFlags.H = parseFlags.HH = parseFlags.h; + parseFlags.MM = parseFlags.M; + parseFlags.ss = parseFlags.s; + parseFlags.A = parseFlags.a; + + + // Some common format strings + fecha.masks = { + 'default': 'ddd MMM dd yyyy HH:mm:ss', + shortDate: 'M/D/yy', + mediumDate: 'MMM d, yyyy', + longDate: 'MMMM d, yyyy', + fullDate: 'dddd, MMMM d, yyyy', + shortTime: 'HH:mm', + mediumTime: 'HH:mm:ss', + longTime: 'HH:mm:ss.SSS' + }; + + /*** + * Format a date + * @method format + * @param {Date|number} dateObj + * @param {string} mask Format of the date, i.e. 'mm-dd-yy' or 'shortDate' + */ + fecha.format = function (dateObj, mask, i18nSettings) { + var i18n = i18nSettings || fecha.i18n; + + if (typeof dateObj === 'number') { + dateObj = new Date(dateObj); + } + + if (Object.prototype.toString.call(dateObj) !== '[object Date]' || isNaN(dateObj.getTime())) { + throw new Error('Invalid Date in fecha.format'); + } + + mask = fecha.masks[mask] || mask || fecha.masks['default']; + + return mask.replace(token, function ($0) { + return $0 in formatFlags ? formatFlags[$0](dateObj, i18n) : $0.slice(1, $0.length - 1); + }); + }; + + /** + * Parse a date string into an object, changes - into / + * @method parse + * @param {string} dateStr Date string + * @param {string} format Date parse format + * @returns {Date|boolean} + */ + fecha.parse = function (dateStr, format, i18nSettings) { + var i18n = i18nSettings || fecha.i18n; + + if (typeof format !== 'string') { + throw new Error('Invalid format in fecha.parse'); + } + + format = fecha.masks[format] || format; + + // Avoid regular expression denial of service, fail early for really long strings + // https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS + if (dateStr.length > 1000) { + return false; + } + + var isValid = true; + var dateInfo = {}; + format.replace(token, function ($0) { + if (parseFlags[$0]) { + var info = parseFlags[$0]; + var index = dateStr.search(info[0]); + if (!~index) { + isValid = false; + } else { + dateStr.replace(info[0], function (result) { + info[1](dateInfo, result, i18n); + dateStr = dateStr.substr(index + result.length); + return result; + }); + } + } + + return parseFlags[$0] ? '' : $0.slice(1, $0.length - 1); + }); + + if (!isValid) { + return false; + } + + var today = new Date(); + if (dateInfo.isPm === true && dateInfo.hour != null && +dateInfo.hour !== 12) { + dateInfo.hour = +dateInfo.hour + 12; + } else if (dateInfo.isPm === false && +dateInfo.hour === 12) { + dateInfo.hour = 0; + } + + var date; + if (dateInfo.timezoneOffset != null) { + dateInfo.minute = +(dateInfo.minute || 0) - +dateInfo.timezoneOffset; + date = new Date(Date.UTC(dateInfo.year || today.getFullYear(), dateInfo.month || 0, dateInfo.day || 1, + dateInfo.hour || 0, dateInfo.minute || 0, dateInfo.second || 0, dateInfo.millisecond || 0)); + } else { + date = new Date(dateInfo.year || today.getFullYear(), dateInfo.month || 0, dateInfo.day || 1, + dateInfo.hour || 0, dateInfo.minute || 0, dateInfo.second || 0, dateInfo.millisecond || 0); + } + return date; + }; + + /* istanbul ignore next */ + if (typeof module !== 'undefined' && module.exports) { + module.exports = fecha; + } else if (typeof define === 'function' && define.amd) { + define(function () { + return fecha; + }); + } else { + main.fecha = fecha; + } +})(this); diff --git a/packages/date-picker/src/util/i18n.js b/packages/date-picker/src/util/i18n.js new file mode 100644 index 000000000..959b29b8a --- /dev/null +++ b/packages/date-picker/src/util/i18n.js @@ -0,0 +1,31 @@ +export default { + datepicker: { + today: '此刻', + clear: '清空', + confirm: '确定', + week: '周次', + weeks: { + sun: '日', + mon: '一', + tue: '二', + wed: '三', + thu: '四', + fri: '五', + sat: '六' + }, + months: { + jan: '一月', + feb: '二月', + mar: '三月', + apr: '四月', + may: '五月', + jun: '六月', + jul: '七月', + aug: '八月', + sep: '九月', + oct: '十月', + nov: '十一月', + dec: '十二月' + } + } +}; diff --git a/packages/date-picker/src/util/index.js b/packages/date-picker/src/util/index.js new file mode 100644 index 000000000..a6ddbeffd --- /dev/null +++ b/packages/date-picker/src/util/index.js @@ -0,0 +1,183 @@ +import dateUtil from './fecha'; + +const newArray = function(start, end) { + let result = []; + for (let i = start; i <= end; i++) { + result.push(i); + } + return result; +}; + +export const merge = function(target) { + for (var i = 1, j = arguments.length; i < j; i++) { + var source = arguments[i]; + for (var prop in source) { + if (source.hasOwnProperty(prop)) { + var value = source[prop]; + if (value !== undefined) { + target[prop] = value; + } + } + } + } + + return target; +}; + +export const formatDate = function(date, format) { + if (!(date instanceof Date)) return ''; + return dateUtil.format(date, format || 'yyyy-MM-dd'); +}; + +export const parseDate = function(string, format) { + return dateUtil.parse(string, format || 'yyyy-MM-dd'); +}; + +export const getDayCountOfMonth = function(year, month) { + if (month === 3 || month === 5 || month === 8 || month === 10) { + return 30; + } + + if (month === 1) { + if (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) { + return 29; + } else { + return 28; + } + } + + return 31; +}; + +export const getFirstDayOfMonth = function(date) { + const temp = new Date(date.getTime()); + temp.setDate(1); + return temp.getDay(); +}; + +export const DAY_DURATION = 86400000; + +export const getStartDateOfMonth = function(year, month) { + const result = new Date(year, month, 1); + const day = result.getDay(); + + if (day === 0) { + result.setTime(result.getTime() - DAY_DURATION * 7); + } else { + result.setTime(result.getTime() - DAY_DURATION * day); + } + + return result; +}; + +export const getWeekNumber = function(src) { + const date = new Date(src.getTime()); + date.setHours(0, 0, 0, 0); + // Thursday in current week decides the year. + date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); + // January 4 is always in week 1. + const week1 = new Date(date.getFullYear(), 0, 4); + // Adjust to Thursday in week 1 and count number of weeks from date to week 1. + return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7); +}; + +export const prevMonth = function(src) { + const year = src.getFullYear(); + const month = src.getMonth(); + const date = src.getDate(); + + const newYear = month === 0 ? year - 1 : year; + const newMonth = month === 0 ? 11 : month - 1; + + const newMonthDayCount = getDayCountOfMonth(newYear, newMonth); + if (newMonthDayCount < date) { + src.setDate(newMonthDayCount); + } + + src.setMonth(newMonth); + src.setFullYear(newYear); + + return new Date(src.getTime()); +}; + +export const nextMonth = function(src) { + const year = src.getFullYear(); + const month = src.getMonth(); + const date = src.getDate(); + + const newYear = month === 11 ? year + 1 : year; + const newMonth = month === 11 ? 0 : month + 1; + + const newMonthDayCount = getDayCountOfMonth(newYear, newMonth); + if (newMonthDayCount < date) { + src.setDate(newMonthDayCount); + } + + src.setMonth(newMonth); + src.setFullYear(newYear); + + return new Date(src.getTime()); +}; + +export const getRangeHours = function(ranges) { + const hours = []; + let disabledHours = []; + + (ranges || []).forEach(range => { + const value = range.map(date => date.getHours()); + + disabledHours = disabledHours.concat(newArray(value[0], value[1])); + }); + + if (disabledHours.length) { + for (let i = 0; i < 24; i++) { + hours[i] = disabledHours.indexOf(i) === -1; + } + } else { + for (let i = 0; i < 24; i++) { + hours[i] = false; + } + } + + return hours; +}; + +export const limitRange = function(date, ranges) { + if (!ranges) return date; + + const len = ranges.length; + const format = 'HH:mm:ss'; + + date = dateUtil.parse(dateUtil.format(date, format), format); + for (let i = 0; i < len; i++) { + const range = ranges[i]; + if (date >= range[0] && date <= range[1]) { + return date; + } + } + + let maxDate = ranges[0][0]; + let minDate = ranges[0][0]; + + ranges.forEach(range => { + minDate = new Date(Math.min(range[0], minDate)); + maxDate = new Date(Math.max(range[1], maxDate)); + }); + + return date < minDate ? minDate : maxDate; +}; + +import i18n from './i18n'; + +export const $t = function(path) { + const array = path.split('.'); + let current = i18n; + for (var i = 0, j = array.length; i < j; i++) { + var property = array[i]; + var value = current[property]; + if (i === j - 1) return value; + if (!value) return ''; + current = value; + } + return ''; +}; diff --git a/packages/dialog/README.md b/packages/dialog/README.md new file mode 100644 index 000000000..ccbc4886b --- /dev/null +++ b/packages/dialog/README.md @@ -0,0 +1,49 @@ +# el-dialog +> A el-dialog component for Vue.js. + +## Demo +http://element-component.github.io/el-dialog + +## Installation +```shell +npm i el-dialog -D +``` + +## Usage +```javascript +import Vue from 'vue' +import ElDialog from 'el-dialog' +import 'element-theme-default' + +Vue.use(ElDialog) +``` + +or + +```javascript +import Vue from 'vue' +import { ElDialog } from 'el-dialog' + +Vue.component('el-dialog', ElDialog) +``` + + +## Options + +| name | description | type | default | +|-------------|-------------|-------------|-------------| +| | | | | + +## Development +```shell +make dev + +## test +make test + +## build +make build +``` + +# License +[MIT](https://opensource.org/licenses/MIT) diff --git a/packages/dialog/cooking.conf.js b/packages/dialog/cooking.conf.js new file mode 100644 index 000000000..319f4825c --- /dev/null +++ b/packages/dialog/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElDialog', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/dialog/index.js b/packages/dialog/index.js new file mode 100644 index 000000000..a93e9c56f --- /dev/null +++ b/packages/dialog/index.js @@ -0,0 +1,7 @@ +const ElDialog = require('./src/component'); + +ElDialog.install = function(Vue) { + Vue.component('el-dialog', ElDialog); +}; + +module.exports = ElDialog; diff --git a/packages/dialog/package.json b/packages/dialog/package.json new file mode 100644 index 000000000..32e0c41d9 --- /dev/null +++ b/packages/dialog/package.json @@ -0,0 +1,17 @@ +{ + "name": "el-dialog", + "version": "1.0.0", + "description": "A dialog component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/dialog", + "author": "elemefe", + "license": "MIT", + "devDependencies": { + "vue-popup": "^0.1.8" + } +} diff --git a/packages/dialog/src/component.vue b/packages/dialog/src/component.vue new file mode 100644 index 000000000..7ffe1e8ac --- /dev/null +++ b/packages/dialog/src/component.vue @@ -0,0 +1,101 @@ + + + diff --git a/packages/dropdown-item/index.js b/packages/dropdown-item/index.js new file mode 100644 index 000000000..4a6dfc24a --- /dev/null +++ b/packages/dropdown-item/index.js @@ -0,0 +1,7 @@ +const ElDropdownItem = require('../dropdown/src/dropdown-item'); + +ElDropdownItem.install = function(Vue) { + Vue.component(ElDropdownItem.name, ElDropdownItem); +}; + +module.exports = ElDropdownItem; diff --git a/packages/dropdown/cooking.conf.js b/packages/dropdown/cooking.conf.js new file mode 100644 index 000000000..d83aa3d94 --- /dev/null +++ b/packages/dropdown/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElDropdown', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/dropdown/index.js b/packages/dropdown/index.js new file mode 100644 index 000000000..1e760ce91 --- /dev/null +++ b/packages/dropdown/index.js @@ -0,0 +1,7 @@ +const ElDropdown = require('./src/dropdown'); + +ElDropdown.install = function(Vue) { + Vue.component(ElDropdown.name, ElDropdown); +}; + +module.exports = ElDropdown; diff --git a/packages/dropdown/package.json b/packages/dropdown/package.json new file mode 100644 index 000000000..8a904231f --- /dev/null +++ b/packages/dropdown/package.json @@ -0,0 +1,17 @@ +{ + "name": "el-dropdown", + "version": "1.0.0", + "description": "A dropdown component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/dropdown", + "author": "haiping.zeng", + "license": "MIT", + "dependencies": { + "vue-clickoutside": "0.0.4" + } +} diff --git a/packages/dropdown/src/dropdown-item.vue b/packages/dropdown/src/dropdown-item.vue new file mode 100644 index 000000000..ca17c4547 --- /dev/null +++ b/packages/dropdown/src/dropdown-item.vue @@ -0,0 +1,8 @@ + + diff --git a/packages/dropdown/src/dropdown-menu.vue b/packages/dropdown/src/dropdown-menu.vue new file mode 100644 index 000000000..e5dda0697 --- /dev/null +++ b/packages/dropdown/src/dropdown-menu.vue @@ -0,0 +1,43 @@ + + diff --git a/packages/dropdown/src/dropdown.vue b/packages/dropdown/src/dropdown.vue new file mode 100644 index 000000000..a43ffe771 --- /dev/null +++ b/packages/dropdown/src/dropdown.vue @@ -0,0 +1,115 @@ + + diff --git a/packages/form-item/index.js b/packages/form-item/index.js new file mode 100644 index 000000000..294bca194 --- /dev/null +++ b/packages/form-item/index.js @@ -0,0 +1,7 @@ +const ElFormItem = require('../form/src/form-item'); + +ElFormItem.install = function(Vue) { + Vue.component(ElFormItem.name, ElFormItem); +}; + +module.exports = ElFormItem; diff --git a/packages/form/cooking.conf.js b/packages/form/cooking.conf.js new file mode 100644 index 000000000..77cd62639 --- /dev/null +++ b/packages/form/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElForm', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/form/index.js b/packages/form/index.js new file mode 100644 index 000000000..bccc57732 --- /dev/null +++ b/packages/form/index.js @@ -0,0 +1,7 @@ +const ElForm = require('./src/form'); + +ElForm.install = function(Vue) { + Vue.component(ElForm.name, ElForm); +}; + +module.exports = ElForm; diff --git a/packages/form/package.json b/packages/form/package.json new file mode 100644 index 000000000..19766626a --- /dev/null +++ b/packages/form/package.json @@ -0,0 +1,17 @@ +{ + "name": "el-form", + "version": "1.0.0", + "description": "A form component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/form", + "author": "haiping.zeng", + "license": "MIT", + "dependencies": { + "async-validator": "^1.6.3" + } +} diff --git a/packages/form/src/form-item.vue b/packages/form/src/form-item.vue new file mode 100644 index 000000000..4ff837b26 --- /dev/null +++ b/packages/form/src/form-item.vue @@ -0,0 +1,183 @@ + + diff --git a/packages/form/src/form.vue b/packages/form/src/form.vue new file mode 100644 index 000000000..9800457cd --- /dev/null +++ b/packages/form/src/form.vue @@ -0,0 +1,82 @@ + + diff --git a/packages/group/index.js b/packages/group/index.js new file mode 100644 index 000000000..979f21c67 --- /dev/null +++ b/packages/group/index.js @@ -0,0 +1 @@ +module.exports = require('./src/group.vue'); diff --git a/packages/group/src/group.vue b/packages/group/src/group.vue new file mode 100644 index 000000000..25fa1e17c --- /dev/null +++ b/packages/group/src/group.vue @@ -0,0 +1,26 @@ + + + diff --git a/packages/icon/index.js b/packages/icon/index.js new file mode 100644 index 000000000..2b43b70df --- /dev/null +++ b/packages/icon/index.js @@ -0,0 +1 @@ +module.exports = require('./src/icon.vue'); diff --git a/packages/icon/src/icon.vue b/packages/icon/src/icon.vue new file mode 100644 index 000000000..b53ac30b8 --- /dev/null +++ b/packages/icon/src/icon.vue @@ -0,0 +1,13 @@ + + + diff --git a/packages/input-group/index.js b/packages/input-group/index.js new file mode 100644 index 000000000..66500245c --- /dev/null +++ b/packages/input-group/index.js @@ -0,0 +1,7 @@ +const ElInputGroup = require('../input/src/input-group'); + +ElInputGroup.install = function(Vue) { + Vue.component(ElInputGroup.name, ElInputGroup); +}; + +module.exports = ElInputGroup; diff --git a/packages/input-number/README.md b/packages/input-number/README.md new file mode 100644 index 000000000..1dd4f26e3 --- /dev/null +++ b/packages/input-number/README.md @@ -0,0 +1,53 @@ +# el-input +> A el-input component for Vue.js. + +# Demo +http://element-component.github.io/el-input + +# Installation +```shell +npm i el-input -D +``` + +# Usage +```javascript +import Vue from 'vue' +import ElInput from 'el-input' +import 'element-theme-default/dist/input.css' + +Vue.use(ElInput) +``` + +or + +```javascript +import Vue from 'vue' +import { ElInput } from 'el-input' + +Vue.component('el-input', ElInput) +``` + +# Options + +| name | description | type | default | +|-------------|-------------|-------------|-------------| +| model | 绑定值,需双向绑定 | string|number || +| placeholder | 输入框占位文本 | string || +| suggestion | 输入建议 | string[] || +| disabled | 是否禁用 | boolean | false | +| cache | 是否需要缓存 | boolean | false | +| effect | 输入框效果,允许 text, number, special | string | text | + +# Development +```shell +make dev + +# test +make test + +# build +make build +``` + +# License +[MIT](https://opensource.org/licenses/MIT) diff --git a/packages/input-number/cooking.conf.js b/packages/input-number/cooking.conf.js new file mode 100644 index 000000000..10f755083 --- /dev/null +++ b/packages/input-number/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElInputNumber', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/input-number/index.js b/packages/input-number/index.js new file mode 100644 index 000000000..cded5e786 --- /dev/null +++ b/packages/input-number/index.js @@ -0,0 +1,7 @@ +const ElInputNumber = require('./src/input-number'); + +ElInputNumber.install = function(Vue) { + Vue.component(ElInputNumber.name, ElInputNumber); +}; + +module.exports = ElInputNumber; diff --git a/packages/input-number/package.json b/packages/input-number/package.json new file mode 100644 index 000000000..43d91f619 --- /dev/null +++ b/packages/input-number/package.json @@ -0,0 +1,21 @@ +{ + "name": "el-input-number", + "version": "1.0.1", + "description": "A input component for Vue.", + "main": "lib/index.js", + "keywords": [ + "element", + "vue", + "component", + "input" + ], + "author": "haipingZeng ", + "license": "MIT", + "repository": "https://github.com/element-component/element/tree/master/packages/input-number", + "dependencies": { + "wind-dom": "0.0.3" + }, + "devDependencies": { + "vue-clickoutside": "0.0.4" + } +} diff --git a/packages/input-number/src/input-number.vue b/packages/input-number/src/input-number.vue new file mode 100644 index 000000000..e60de7468 --- /dev/null +++ b/packages/input-number/src/input-number.vue @@ -0,0 +1,137 @@ + + diff --git a/packages/input/README.md b/packages/input/README.md new file mode 100644 index 000000000..1dd4f26e3 --- /dev/null +++ b/packages/input/README.md @@ -0,0 +1,53 @@ +# el-input +> A el-input component for Vue.js. + +# Demo +http://element-component.github.io/el-input + +# Installation +```shell +npm i el-input -D +``` + +# Usage +```javascript +import Vue from 'vue' +import ElInput from 'el-input' +import 'element-theme-default/dist/input.css' + +Vue.use(ElInput) +``` + +or + +```javascript +import Vue from 'vue' +import { ElInput } from 'el-input' + +Vue.component('el-input', ElInput) +``` + +# Options + +| name | description | type | default | +|-------------|-------------|-------------|-------------| +| model | 绑定值,需双向绑定 | string|number || +| placeholder | 输入框占位文本 | string || +| suggestion | 输入建议 | string[] || +| disabled | 是否禁用 | boolean | false | +| cache | 是否需要缓存 | boolean | false | +| effect | 输入框效果,允许 text, number, special | string | text | + +# Development +```shell +make dev + +# test +make test + +# build +make build +``` + +# License +[MIT](https://opensource.org/licenses/MIT) diff --git a/packages/input/cooking.conf.js b/packages/input/cooking.conf.js new file mode 100644 index 000000000..76456fecc --- /dev/null +++ b/packages/input/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElInput', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/input/index.js b/packages/input/index.js new file mode 100644 index 000000000..eaf673150 --- /dev/null +++ b/packages/input/index.js @@ -0,0 +1,9 @@ +const ElInput = require('./src/input'); +const ElInputGroup = require('./src/input-group'); + +ElInput.install = function(Vue) { + Vue.component(ElInput.name, ElInput); + Vue.component(ElInputGroup.name, ElInputGroup); +}; + +module.exports = ElInput; diff --git a/packages/input/package.json b/packages/input/package.json new file mode 100644 index 000000000..5e59f5ef9 --- /dev/null +++ b/packages/input/package.json @@ -0,0 +1,17 @@ +{ + "name": "el-input", + "version": "1.0.1", + "description": "A input component for Vue.", + "main": "lib/index.js", + "keywords": [ + "element", + "vue", + "component", + "input" + ], + "author": "haiping.zeng ", + "license": "MIT", + "repository": "https://github.com/element-component/element/tree/master/packages/input", + "devDependencies": { + } +} diff --git a/packages/input/src/input-group.vue b/packages/input/src/input-group.vue new file mode 100644 index 000000000..0370addbe --- /dev/null +++ b/packages/input/src/input-group.vue @@ -0,0 +1,20 @@ + + diff --git a/packages/input/src/input.vue b/packages/input/src/input.vue new file mode 100644 index 000000000..b9f3879f7 --- /dev/null +++ b/packages/input/src/input.vue @@ -0,0 +1,105 @@ + + diff --git a/packages/loading/cooking.conf.js b/packages/loading/cooking.conf.js new file mode 100644 index 000000000..db5d5df19 --- /dev/null +++ b/packages/loading/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElLoading', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/loading/index.js b/packages/loading/index.js new file mode 100644 index 000000000..a6f62af52 --- /dev/null +++ b/packages/loading/index.js @@ -0,0 +1,4 @@ +import Loading from './src/directive'; +Loading.name = 'loading'; + +module.exports = Loading; diff --git a/packages/loading/package.json b/packages/loading/package.json new file mode 100644 index 000000000..b2db41026 --- /dev/null +++ b/packages/loading/package.json @@ -0,0 +1,15 @@ +{ + "name": "el-loading", + "version": "0.0.0", + "description": "A loading component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/loading", + "author": "elemefe", + "license": "MIT", + "dependencies": {} +} diff --git a/packages/loading/src/directive.js b/packages/loading/src/directive.js new file mode 100644 index 000000000..5999bb5c0 --- /dev/null +++ b/packages/loading/src/directive.js @@ -0,0 +1,120 @@ +exports.install = Vue => { + let insertDom = (parent, directive) => { + if (!directive.domVisible) { + Object.keys(directive.maskStyle).forEach(property => { + directive.mask.style[property] = directive.maskStyle[property]; + }); + + Object.keys(directive.spinnerStyle).forEach(property => { + directive.spinner.style[property] = directive.spinnerStyle[property]; + }); + + if (directive.originalPosition !== 'absolute') { + parent.style.position = 'relative'; + } + if (directive.modifiers.fullscreen) { + parent.style.overflow = 'hidden'; + } + directive.mask.style.display = 'block'; + directive.spinner.style.display = 'inline-block'; + directive.domVisible = true; + + parent.appendChild(directive.mask); + directive.mask.appendChild(directive.spinner); + directive.domInserted = true; + } + }; + + Vue.directive('loading', { + bind: function() { + this.mask = document.createElement('div'); + this.mask.className = 'el-loading-mask'; + this.maskStyle = { + position: 'absolute', + zIndex: '10000', + backgroundColor: 'rgba(0, 0, 0, .7)', + margin: '0' + }; + + this.spinner = document.createElement('i'); + this.spinner.className = 'el-icon-loading'; + this.spinnerStyle = { + color: '#ddd', + fontSize: '32px', + position: 'absolute', + top: '50%', + left: '50%', + marginTop: '-19px', + marginLeft: '-16px', + zIndex: '10001' + }; + }, + + update: function(val) { + if (val) { + Vue.nextTick(() => { + if (this.modifiers.fullscreen) { + this.originalPosition = document.body.style.position; + this.originalOverflow = document.body.style.overflow; + + ['top', 'right', 'bottom', 'left'].forEach(property => { + this.maskStyle[property] = '0'; + }); + this.maskStyle.position = 'fixed'; + this.spinnerStyle.position = 'fixed'; + + insertDom(document.body, this); + } else { + if (this.modifiers.body) { + this.originalPosition = document.body.style.position; + + ['top', 'left'].forEach(property => { + this.maskStyle[property] = this.el.getBoundingClientRect()[property] + document.body[`scroll${ property[0].toUpperCase() + property.slice(1) }`] + 'px'; + }); + ['height', 'width'].forEach(property => { + this.maskStyle[property] = this.el.getBoundingClientRect()[property] + 'px'; + }); + + insertDom(document.body, this); + } else { + this.originalPosition = this.el.style.position; + + ['top', 'right', 'bottom', 'left'].forEach(property => { + this.maskStyle[property] = '0'; + }); + + insertDom(this.el, this); + } + } + }); + } else { + if (this.domVisible) { + this.mask.style.display = 'none'; + this.spinner.style.display = 'none'; + this.domVisible = false; + + if (this.modifiers.fullscreen) { + document.body.style.overflow = this.originalOverflow; + } + if (this.modifiers.fullscreen || this.modifiers.body) { + document.body.style.position = this.originalPosition; + } else { + this.el.style.position = this.originalPosition; + } + } + } + }, + + unbind: function() { + if (this.domInserted) { + if (this.modifiers.fullscreen || this.modifiers.body) { + document.body.removeChild(this.mask); + this.mask.removeChild(this.spinner); + } else { + this.el.removeChild(this.mask); + this.mask.removeChild(this.spinner); + } + } + } + }); +}; diff --git a/packages/menu-item/index.js b/packages/menu-item/index.js new file mode 100644 index 000000000..6bf758618 --- /dev/null +++ b/packages/menu-item/index.js @@ -0,0 +1,7 @@ +const ElMenuItem = require('../menu/src/menu-item'); + +ElMenuItem.install = function(Vue) { + Vue.component(ElMenuItem.name, ElMenuItem); +}; + +module.exports = ElMenuItem; diff --git a/packages/menu/cooking.conf.js b/packages/menu/cooking.conf.js new file mode 100644 index 000000000..9f8a6e78f --- /dev/null +++ b/packages/menu/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElMenu', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/menu/index.js b/packages/menu/index.js new file mode 100644 index 000000000..06fb8b6d6 --- /dev/null +++ b/packages/menu/index.js @@ -0,0 +1,7 @@ +const ElMenu = require('./src/menu'); + +ElMenu.install = function(Vue) { + Vue.component(ElMenu.name, ElMenu); +}; + +module.exports = ElMenu; diff --git a/packages/menu/src/menu-item.vue b/packages/menu/src/menu-item.vue new file mode 100644 index 000000000..abd374ff0 --- /dev/null +++ b/packages/menu/src/menu-item.vue @@ -0,0 +1,52 @@ + + + diff --git a/packages/menu/src/menu.vue b/packages/menu/src/menu.vue new file mode 100644 index 000000000..e3b81b82d --- /dev/null +++ b/packages/menu/src/menu.vue @@ -0,0 +1,73 @@ + + diff --git a/packages/menu/src/submenu.vue b/packages/menu/src/submenu.vue new file mode 100644 index 000000000..0e4e9c4b9 --- /dev/null +++ b/packages/menu/src/submenu.vue @@ -0,0 +1,63 @@ + + + diff --git a/packages/message-box/cooking.conf.js b/packages/message-box/cooking.conf.js new file mode 100644 index 000000000..c57c267bb --- /dev/null +++ b/packages/message-box/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElMessageBox', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/message-box/index.js b/packages/message-box/index.js new file mode 100644 index 000000000..110d20fbe --- /dev/null +++ b/packages/message-box/index.js @@ -0,0 +1,3 @@ +import MessageBox from './src/main.js'; + +module.exports = MessageBox; diff --git a/packages/message-box/package.json b/packages/message-box/package.json new file mode 100644 index 000000000..75e7f42f3 --- /dev/null +++ b/packages/message-box/package.json @@ -0,0 +1,17 @@ +{ + "name": "el-message-box", + "version": "0.0.0", + "description": "A message-box component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/message-box", + "author": "elemefe", + "license": "MIT", + "dependencies": { + "vue-popup": "^0.1.8" + } +} diff --git a/packages/message-box/src/main.js b/packages/message-box/src/main.js new file mode 100644 index 000000000..ec38a877e --- /dev/null +++ b/packages/message-box/src/main.js @@ -0,0 +1,197 @@ +var CONFIRM_TEXT = '确定'; +var CANCEL_TEXT = '取消'; + +var defaults = { + title: '提示', + message: '', + type: '', + showInput: false, + showClose: true, + closeOnClickModal: true, + inputValue: null, + inputPlaceholder: '', + inputPattern: null, + inputValidator: null, + inputErrorMessage: '', + showConfirmButton: true, + showCancelButton: false, + confirmButtonPosition: 'right', + confirmButtonHighlight: false, + cancelButtonHighlight: false, + confirmButtonText: CONFIRM_TEXT, + cancelButtonText: CANCEL_TEXT, + confirmButtonClass: '', + cancelButtonClass: '' +}; + +import Vue from 'vue'; +import msgboxVue from './main.vue'; + +var merge = function(target) { + for (var i = 1, j = arguments.length; i < j; i++) { + var source = arguments[i]; + for (var prop in source) { + if (source.hasOwnProperty(prop)) { + var value = source[prop]; + if (value !== undefined) { + target[prop] = value; + } + } + } + } + + return target; +}; + +var MessageBoxConstructor = Vue.extend(msgboxVue); + +var currentMsg, instance; +var msgQueue = []; + +var initInstance = function() { + instance = new MessageBoxConstructor({ + el: document.createElement('div') + }); + + instance.callback = function(action) { + if (currentMsg) { + var callback = currentMsg.callback; + if (typeof callback === 'function') { + if (instance.showInput) { + callback(instance.inputValue, action); + } else { + callback(action); + } + } + if (currentMsg.resolve) { + var $type = currentMsg.options.$type; + if ($type === 'confirm' || $type === 'prompt') { + if (action === 'confirm') { + if (instance.showInput) { + currentMsg.resolve({ value: instance.inputValue, action }); + } else { + currentMsg.resolve(action); + } + } else if (action === 'cancel' && currentMsg.reject) { + currentMsg.reject(action); + } + } else { + currentMsg.resolve(action); + } + } + } + }; +}; + +var showNextMsg = function() { + if (!instance) { + initInstance(); + } + + if (!instance.visible || instance.closeTimer) { + if (msgQueue.length > 0) { + currentMsg = msgQueue.shift(); + + var options = currentMsg.options; + for (var prop in options) { + if (options.hasOwnProperty(prop)) { + instance[prop] = options[prop]; + } + } + instance.$appendTo(document.body); + + Vue.nextTick(() => { + instance.visible = true; + }); + } + } +}; + +var MessageBox = function(options, callback) { + if (typeof options === 'string') { + options = { + title: options + }; + if (arguments[1]) { + options.message = arguments[1]; + } + if (arguments[2]) { + options.type = arguments[2]; + } + } else if (options.callback && !callback) { + callback = options.callback; + } + + if (typeof Promise !== 'undefined') { + return new Promise(function(resolve, reject) { // eslint-disable-line + msgQueue.push({ + options: merge({}, defaults, MessageBox.defaults || {}, options), + callback: callback, + resolve: resolve, + reject: reject + }); + + showNextMsg(); + }); + } else { + msgQueue.push({ + options: merge({}, defaults, MessageBox.defaults || {}, options), + callback: callback + }); + + showNextMsg(); + } +}; + +MessageBox.setDefaults = function(defaults) { + MessageBox.defaults = defaults; +}; + +MessageBox.alert = function(message, title, options) { + if (typeof title === 'object') { + options = title; + title = ''; + } + return MessageBox(merge({ + title: title, + message: message, + $type: 'alert', + closeOnClickModal: false + }, options)); +}; + +MessageBox.confirm = function(message, title, options) { + if (typeof title === 'object') { + options = title; + title = ''; + } + return MessageBox(merge({ + title: title, + message: message, + $type: 'confirm', + showCancelButton: true + }, options)); +}; + +MessageBox.prompt = function(message, title, options) { + if (typeof title === 'object') { + options = title; + title = ''; + } + return MessageBox(merge({ + title: title, + message: message, + showCancelButton: true, + showInput: true, + $type: 'prompt' + }, options)); +}; + +MessageBox.close = function() { + instance.visible = false; + msgQueue = []; + currentMsg = null; +}; + +export default MessageBox; +export { MessageBox }; diff --git a/packages/message-box/src/main.vue b/packages/message-box/src/main.vue new file mode 100644 index 000000000..6fffb8c18 --- /dev/null +++ b/packages/message-box/src/main.vue @@ -0,0 +1,147 @@ + + + diff --git a/packages/notification/cooking.conf.js b/packages/notification/cooking.conf.js new file mode 100644 index 000000000..21bef7f12 --- /dev/null +++ b/packages/notification/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElNotification', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/notification/index.js b/packages/notification/index.js new file mode 100644 index 000000000..aaac00867 --- /dev/null +++ b/packages/notification/index.js @@ -0,0 +1,3 @@ +import Notification from './src/main.js'; + +module.exports = Notification; diff --git a/packages/notification/package.json b/packages/notification/package.json new file mode 100644 index 000000000..e6c08b741 --- /dev/null +++ b/packages/notification/package.json @@ -0,0 +1,15 @@ +{ + "name": "el-notification", + "version": "0.0.0", + "description": "A notification component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/notification", + "author": "elemefe", + "license": "MIT", + "dependencies": {} +} diff --git a/packages/notification/src/main.js b/packages/notification/src/main.js new file mode 100644 index 000000000..cb220082c --- /dev/null +++ b/packages/notification/src/main.js @@ -0,0 +1,54 @@ +import Vue from 'vue'; +var NotificationConstructor = Vue.extend(require('./main.vue')); + +var instance; +var instances = []; +var seed = 1; + +var Notification = function(options) { + options = options || {}; + var userOnClose = options.onClose; + var id = 'notification_' + seed++; + + options.onClose = function() { + Notification.close(id, userOnClose); + }; + + instance = new NotificationConstructor({ + data: options + }); + instance.id = id; + instance.vm = instance.$mount(); + instance.vm.$appendTo('body'); + instance.dom = instance.vm.$el; + + var topDist = 0; + for (var i = 0, len = instances.length; i < len; i++) { + topDist += instances[i].$el.offsetHeight + 16; + } + topDist += 16; + instance.top = topDist; + instances.push(instance); +}; + +Notification.close = function(id, userOnClose) { + for (var i = 0, len = instances.length; i < len; i++) { + if (id === instances[i].id) { + if (typeof userOnClose === 'function') { + userOnClose(instances[i]); + } + var index = i; + var removedHeight = instances[i].dom.offsetHeight; + instances.splice(i, 1); + break; + } + } + + if (len > 1) { + for (i = index; i < len - 1 ; i++) { + instances[i].dom.style.top = parseInt(instances[i].dom.style.top, 10) - removedHeight - 10 + 'px'; + } + } +}; + +export default Notification; diff --git a/packages/notification/src/main.vue b/packages/notification/src/main.vue new file mode 100644 index 000000000..3c31e3eb1 --- /dev/null +++ b/packages/notification/src/main.vue @@ -0,0 +1,84 @@ + + + \ No newline at end of file diff --git a/packages/option-group/index.js b/packages/option-group/index.js new file mode 100644 index 000000000..7d6f845e4 --- /dev/null +++ b/packages/option-group/index.js @@ -0,0 +1,7 @@ +const ElOptionGroup = require('../select/src/option-group'); + +ElOptionGroup.install = function(Vue) { + Vue.component(ElOptionGroup.name, ElOptionGroup); +}; + +module.exports = ElOptionGroup; diff --git a/packages/option/index.js b/packages/option/index.js new file mode 100644 index 000000000..05970c6aa --- /dev/null +++ b/packages/option/index.js @@ -0,0 +1,7 @@ +const ElOption = require('../select/src/option'); + +ElOption.install = function(Vue) { + Vue.component(ElOption.name, ElOption); +}; + +module.exports = ElOption; diff --git a/packages/pagination/README.md b/packages/pagination/README.md new file mode 100644 index 000000000..9b9a79bb3 --- /dev/null +++ b/packages/pagination/README.md @@ -0,0 +1 @@ +## el-pagination diff --git a/packages/pagination/cooking.conf.js b/packages/pagination/cooking.conf.js new file mode 100644 index 000000000..bc357e541 --- /dev/null +++ b/packages/pagination/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElPagination', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/pagination/index.js b/packages/pagination/index.js new file mode 100644 index 000000000..1085962d3 --- /dev/null +++ b/packages/pagination/index.js @@ -0,0 +1,7 @@ +const Pagination = require('./src/pagination'); + +Pagination.install = function(Vue) { + Vue.component(Pagination.name, Pagination); +}; + +module.exports = Pagination; diff --git a/packages/pagination/package.json b/packages/pagination/package.json new file mode 100644 index 000000000..1e3aca5f6 --- /dev/null +++ b/packages/pagination/package.json @@ -0,0 +1,15 @@ +{ + "name": "el-pagination", + "version": "0.0.0", + "description": "A pagination component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/pagination", + "author": "qingwei-li", + "license": "MIT", + "dependencies": {} +} diff --git a/packages/pagination/src/pager.vue b/packages/pagination/src/pager.vue new file mode 100644 index 000000000..de69c9a27 --- /dev/null +++ b/packages/pagination/src/pager.vue @@ -0,0 +1,138 @@ + + + diff --git a/packages/pagination/src/pagination.vue b/packages/pagination/src/pagination.vue new file mode 100644 index 000000000..6aa000d2c --- /dev/null +++ b/packages/pagination/src/pagination.vue @@ -0,0 +1,267 @@ + + + diff --git a/packages/popover/cooking.conf.js b/packages/popover/cooking.conf.js new file mode 100644 index 000000000..43e29dd80 --- /dev/null +++ b/packages/popover/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElPopover', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/popover/index.js b/packages/popover/index.js new file mode 100644 index 000000000..c09e2e33f --- /dev/null +++ b/packages/popover/index.js @@ -0,0 +1,7 @@ +const Popover = require('./src/main'); + +Popover.install = function(Vue) { + Vue.component(Popover.name, Popover); +}; + +module.exports = Popover; diff --git a/packages/popover/package.json b/packages/popover/package.json new file mode 100644 index 000000000..6fc2ca3b6 --- /dev/null +++ b/packages/popover/package.json @@ -0,0 +1,17 @@ +{ + "name": "el-popover", + "version": "0.0.0", + "description": "A popover component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/popover", + "author": "elemefe", + "license": "MIT", + "devDependencies": { + "wind-dom": "0.0.3" + } +} diff --git a/packages/popover/src/main.vue b/packages/popover/src/main.vue new file mode 100644 index 000000000..1334b57d1 --- /dev/null +++ b/packages/popover/src/main.vue @@ -0,0 +1,116 @@ + + + diff --git a/packages/progress/cooking.conf.js b/packages/progress/cooking.conf.js new file mode 100644 index 000000000..e0851f192 --- /dev/null +++ b/packages/progress/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElProgress', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/progress/index.js b/packages/progress/index.js new file mode 100644 index 000000000..ae7767fc8 --- /dev/null +++ b/packages/progress/index.js @@ -0,0 +1,7 @@ +const ElProgress = require('./src/progress'); + +ElProgress.install = function(Vue) { + Vue.component(ElProgress.name, ElProgress); +}; + +module.exports = ElProgress; diff --git a/packages/progress/package.json b/packages/progress/package.json new file mode 100644 index 000000000..6fa5933cc --- /dev/null +++ b/packages/progress/package.json @@ -0,0 +1,17 @@ +{ + "name": "el-progress", + "version": "1.0.1", + "description": "A progress component for Vue.", + "main": "lib/index.js", + "keywords": [ + "element", + "vue", + "component", + "progress" + ], + "author": "haiping.zeng ", + "license": "MIT", + "repository": "https://github.com/element-component/element/tree/master/packages/progress", + "devDependencies": { + } +} diff --git a/packages/progress/src/progress.vue b/packages/progress/src/progress.vue new file mode 100644 index 000000000..d553c4504 --- /dev/null +++ b/packages/progress/src/progress.vue @@ -0,0 +1,35 @@ + + diff --git a/packages/radio-button/index.js b/packages/radio-button/index.js new file mode 100644 index 000000000..828a1a18a --- /dev/null +++ b/packages/radio-button/index.js @@ -0,0 +1,7 @@ +const RadioButton = require('../radio/src/radio-button.vue'); + +RadioButton.install = function(Vue) { + Vue.component(RadioButton.name, RadioButton); +}; + +module.exports = RadioButton; diff --git a/packages/radio-group/index.js b/packages/radio-group/index.js new file mode 100644 index 000000000..917f236bd --- /dev/null +++ b/packages/radio-group/index.js @@ -0,0 +1,7 @@ +const RadioGroup = require('../radio/src/radio-group.vue'); + +RadioGroup.install = function(Vue) { + Vue.component(RadioGroup.name, RadioGroup); +}; + +module.exports = RadioGroup; diff --git a/packages/radio/README.md b/packages/radio/README.md new file mode 100644 index 000000000..7253378a1 --- /dev/null +++ b/packages/radio/README.md @@ -0,0 +1,51 @@ +# el-radio +> A el-radio component for Vue.js. + +## Demo +http://element-component.github.io/el-radio + +## Installation +```shell +npm i el-radio -D +``` + +## Usage +```javascript +import Vue from 'vue' +import ElRadio from 'el-radio' +import 'element-theme-default/dist/radio.css' + +Vue.use(ElRadio) +``` + +or + +```javascript +import Vue from 'vue' +import { ElRadio } from 'el-radio' + +Vue.component('el-radio', ElRadio) +``` + + +## Options +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| model | 绑定值 | string | | | +| value | 真实值 | string | | | +| label | 显示值,默认显示 value | string | | | +| disabled | 禁用 | boolean | true, false | false | + +## Development +```shell +make dev + +## test +make test + +## build +make build +``` + +# License +[MIT](https://opensource.org/licenses/MIT) diff --git a/packages/radio/cooking.conf.js b/packages/radio/cooking.conf.js new file mode 100644 index 000000000..6f6df4d40 --- /dev/null +++ b/packages/radio/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElCascader', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/radio/index.js b/packages/radio/index.js new file mode 100644 index 000000000..38273264a --- /dev/null +++ b/packages/radio/index.js @@ -0,0 +1,8 @@ +const ElRadio = require('./src/radio'); + +ElRadio.install = function(Vue) { + Vue.component('el-radio', ElRadio); +}; + +module.exports = ElRadio; + diff --git a/packages/radio/package.json b/packages/radio/package.json new file mode 100644 index 000000000..deb5418bb --- /dev/null +++ b/packages/radio/package.json @@ -0,0 +1,15 @@ +{ + "name": "el-radio", + "version": "1.0.0", + "description": "A radio component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/radio", + "author": "qingwei-li", + "license": "MIT", + "dependencies": {} +} diff --git a/packages/radio/src/radio-button.vue b/packages/radio/src/radio-button.vue new file mode 100644 index 000000000..8c27e1541 --- /dev/null +++ b/packages/radio/src/radio-button.vue @@ -0,0 +1,50 @@ + + + diff --git a/packages/radio/src/radio-group.vue b/packages/radio/src/radio-group.vue new file mode 100644 index 000000000..d232025bb --- /dev/null +++ b/packages/radio/src/radio-group.vue @@ -0,0 +1,29 @@ + + + diff --git a/packages/radio/src/radio.vue b/packages/radio/src/radio.vue new file mode 100644 index 000000000..5906bc097 --- /dev/null +++ b/packages/radio/src/radio.vue @@ -0,0 +1,66 @@ + + diff --git a/packages/row/cooking.conf.js b/packages/row/cooking.conf.js new file mode 100644 index 000000000..c90fc5cd8 --- /dev/null +++ b/packages/row/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElRow', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/row/index.js b/packages/row/index.js new file mode 100644 index 000000000..b4420c459 --- /dev/null +++ b/packages/row/index.js @@ -0,0 +1,8 @@ +const ElRow = require('./src/row'); + +ElRow.install = function(Vue) { + Vue.component('el-row', ElRow); +}; + +module.exports = ElRow; + diff --git a/packages/row/package.json b/packages/row/package.json new file mode 100644 index 000000000..f84c87e51 --- /dev/null +++ b/packages/row/package.json @@ -0,0 +1,15 @@ +{ + "name": "el-row", + "version": "1.0.2", + "description": "A row component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/row", + "author": "qingwei-li", + "license": "MIT", + "dependencies": {} +} diff --git a/packages/row/src/row.vue b/packages/row/src/row.vue new file mode 100644 index 000000000..48ed82f3e --- /dev/null +++ b/packages/row/src/row.vue @@ -0,0 +1,34 @@ + + diff --git a/packages/select-dropdown/cooking.conf.js b/packages/select-dropdown/cooking.conf.js new file mode 100644 index 000000000..25b86b89a --- /dev/null +++ b/packages/select-dropdown/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElSelectMenu', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/select-dropdown/index.js b/packages/select-dropdown/index.js new file mode 100644 index 000000000..60756cab8 --- /dev/null +++ b/packages/select-dropdown/index.js @@ -0,0 +1,7 @@ +const SelectDropdown = require('./src/select-dropdown.vue'); + +SelectDropdown.install = function(Vue) { + Vue.component(SelectDropdown.name, SelectDropdown); +}; + +module.exports = SelectDropdown; diff --git a/packages/select-dropdown/package.json b/packages/select-dropdown/package.json new file mode 100644 index 000000000..83a9d2424 --- /dev/null +++ b/packages/select-dropdown/package.json @@ -0,0 +1,16 @@ +{ + "name": "el-select-dropdown", + "version": "0.0.0", + "description": "A select-dropdown component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/select-dropdown", + "author": "elemefe", + "license": "MIT", + "dependencies": { + } +} diff --git a/packages/select-dropdown/src/select-dropdown.vue b/packages/select-dropdown/src/select-dropdown.vue new file mode 100644 index 000000000..cb8917294 --- /dev/null +++ b/packages/select-dropdown/src/select-dropdown.vue @@ -0,0 +1,67 @@ + + + diff --git a/packages/select/README.md b/packages/select/README.md new file mode 100644 index 000000000..6b948716f --- /dev/null +++ b/packages/select/README.md @@ -0,0 +1,80 @@ +# el-select +> A el-select component for Vue.js. + +## dependencies +- vue-clickoutside + +## Installation +```shell +npm i el-select -D +``` + +## Usage +```javascript + import vue from 'Vue' + + // import component + import Component from 'el-select' + import ''el-select/lib/style.css' + + Vue.use(Component) +``` + +or +```javascript +import { + ElSelect, + ElOption, + ElOptionGroup +} from 'el-select' + +Vue.component('el-select', ElSelect) +Vue.component('el-option', ElOption) +Vue.component('el-option-group', ElOptionGroup) +``` + +## API + +### el-elect + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|--------------------|----------------------------------------------------------|-------------------|-------------|--------| +| model | 绑定值 | string, string[] | | | +| type | 显示类型, search 会带搜索框,multiple 允许多选 | string | normal, search, multiple | normal | +| placeholder | 输入框占位文本 | string | | | +| hint-placeholder | 获取焦点后显示的占位文本 | string | | | +| search-placeholder | 搜索框占位内容 | string | | | +| search-model | 搜索结果绑定值 | string | | | +| disabled | 禁用 | boolean | true, false | false | +| change | 选中值后的回调函数, 返回当前选中的值 | function | | || + +### el-option +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|--------------------|----------------------------------------------------------|-------------------|-------------|--------| +| label| 显示值,可选 | string| | | +| value| 真实值| string| | | +| remark| 额外信息,将显示在右边| string| | | +| disabled | 禁用| boolean | true, false | false | + +### el-option-group + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|--------------------|----------------------------------------------------------|-------------------|-------------|--------| +| hide-title | 隐藏标题| boolean |true, false |false| +| hide-list | 隐藏列表| boolean |true, false |false| +| title| 真实值| 分组标题| | | + + +## Development +```shell +make dev + +## test +make test + +## build +make build +``` + +# License +[MIT](https://opensource.org/licenses/MIT) diff --git a/packages/select/cooking.conf.js b/packages/select/cooking.conf.js new file mode 100644 index 000000000..b582e5547 --- /dev/null +++ b/packages/select/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElSelect', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/select/index.js b/packages/select/index.js new file mode 100644 index 000000000..6c130d151 --- /dev/null +++ b/packages/select/index.js @@ -0,0 +1,7 @@ +var ElSelect = require('./src/select'); + +ElSelect.install = function(Vue) { + Vue.component(ElSelect.name, ElSelect); +}; + +module.exports = ElSelect; diff --git a/packages/select/package.json b/packages/select/package.json new file mode 100644 index 000000000..95fd7126f --- /dev/null +++ b/packages/select/package.json @@ -0,0 +1,18 @@ +{ + "name": "el-select", + "version": "1.0.1", + "description": "A el-select component for Vue.", + "main": "lib/index.js", + "keywords": [ + "element", + "vue", + "component" + ], + "author": "qingwei.li", + "license": "MIT", + "repository": "https://github.com/element-component/element/tree/master/packages/select", + "devDependencies": { + "throttle-debounce": "^1.0.1", + "vue-clickoutside": "0.0.4" + } +} diff --git a/packages/select/src/option-group.vue b/packages/select/src/option-group.vue new file mode 100644 index 000000000..fbd95bf6b --- /dev/null +++ b/packages/select/src/option-group.vue @@ -0,0 +1,32 @@ + + + \ No newline at end of file diff --git a/packages/select/src/option.vue b/packages/select/src/option.vue new file mode 100644 index 000000000..0db5c4058 --- /dev/null +++ b/packages/select/src/option.vue @@ -0,0 +1,120 @@ + + + diff --git a/packages/select/src/select.vue b/packages/select/src/select.vue new file mode 100644 index 000000000..0b11a47d0 --- /dev/null +++ b/packages/select/src/select.vue @@ -0,0 +1,487 @@ + + + diff --git a/packages/slider/cooking.conf.js b/packages/slider/cooking.conf.js new file mode 100644 index 000000000..e61b101e4 --- /dev/null +++ b/packages/slider/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElSlider', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/slider/index.js b/packages/slider/index.js new file mode 100644 index 000000000..f39136b4e --- /dev/null +++ b/packages/slider/index.js @@ -0,0 +1,7 @@ +const Slider = require('./src/main'); + +Slider.install = function(Vue) { + Vue.component(Slider.name, Slider); +}; + +module.exports = Slider; diff --git a/packages/slider/package.json b/packages/slider/package.json new file mode 100644 index 000000000..e686d5b14 --- /dev/null +++ b/packages/slider/package.json @@ -0,0 +1,17 @@ +{ + "name": "el-slider", + "version": "0.0.0", + "description": "A slider component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/slider", + "author": "elemefe", + "license": "MIT", + "dependencies": { + "wind-dom": "0.0.3" + } +} diff --git a/packages/slider/src/main.vue b/packages/slider/src/main.vue new file mode 100644 index 000000000..2fbc8c556 --- /dev/null +++ b/packages/slider/src/main.vue @@ -0,0 +1,234 @@ + + + diff --git a/packages/spinner/cooking.conf.js b/packages/spinner/cooking.conf.js new file mode 100644 index 000000000..56c9a6afd --- /dev/null +++ b/packages/spinner/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElSpinner', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/spinner/index.js b/packages/spinner/index.js new file mode 100644 index 000000000..de2419e39 --- /dev/null +++ b/packages/spinner/index.js @@ -0,0 +1,7 @@ +const ElSpinner = require('./src/spinner'); + +ElSpinner.install = function(Vue) { + Vue.component(ElSpinner.name, ElSpinner); +}; + +module.exports = ElSpinner; diff --git a/packages/spinner/package.json b/packages/spinner/package.json new file mode 100644 index 000000000..3e08578ff --- /dev/null +++ b/packages/spinner/package.json @@ -0,0 +1,18 @@ +{ + "name": "el-spinner", + "version": "1.0.0", + "description": "A input component for Vue.", + "main": "lib/index.js", + "keywords": [ + "element", + "vue", + "component", + "input" + ], + "author": "haiping.zeng@ele.me", + "license": "MIT", + "repository": "https://github.com/element-component/element/tree/master/packages/spinner", + "devDependencies": { + "vue-clickoutside": "0.0.4" + } +} diff --git a/packages/spinner/src/spinner.vue b/packages/spinner/src/spinner.vue new file mode 100644 index 000000000..ab58e0a55 --- /dev/null +++ b/packages/spinner/src/spinner.vue @@ -0,0 +1,27 @@ + + diff --git a/packages/submenu/index.js b/packages/submenu/index.js new file mode 100644 index 000000000..486650ea0 --- /dev/null +++ b/packages/submenu/index.js @@ -0,0 +1,7 @@ +const ElSubmenu = require('../menu/src/submenu'); + +ElSubmenu.install = function(Vue) { + Vue.component(ElSubmenu.name, ElSubmenu); +}; + +module.exports = ElSubmenu; diff --git a/packages/switch/README.md b/packages/switch/README.md new file mode 100644 index 000000000..99201710a --- /dev/null +++ b/packages/switch/README.md @@ -0,0 +1,46 @@ +# el-switch +> A el-switch component for Vue.js. + +## Installation +```shell +npm i el-switch -D +``` + +## Usage +```javascript +import Vue from 'vue' +import ElSwitch from 'el-switch' +import 'element-theme-default' + +Vue.use(ElSwitch) +``` + +or + +```javascript +import Vue from 'vue' +import { ElSwitch } from 'el-switch' + +Vue.component('el-switch', ElSwitch) +``` + + +## Options + +| name | description | type | default | +|-------------|-------------|-------------|-------------| +| | | | | + +## Development +```shell +make dev + +## test +make test + +## build +make build +``` + +# License +[MIT](https://opensource.org/licenses/MIT) diff --git a/packages/switch/cooking.conf.js b/packages/switch/cooking.conf.js new file mode 100644 index 000000000..6f6df4d40 --- /dev/null +++ b/packages/switch/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElCascader', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/switch/index.js b/packages/switch/index.js new file mode 100644 index 000000000..79fb26423 --- /dev/null +++ b/packages/switch/index.js @@ -0,0 +1,8 @@ +const ElSwitch = require('./src/component'); + +ElSwitch.install = function(Vue) { + Vue.component('el-switch', ElSwitch); +}; + +module.exports = ElSwitch; + diff --git a/packages/switch/package.json b/packages/switch/package.json new file mode 100644 index 000000000..a4d7e1aa8 --- /dev/null +++ b/packages/switch/package.json @@ -0,0 +1,15 @@ +{ + "name": "el-switch", + "version": "1.0.0", + "description": "A switch component for vue.js", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/switch", + "author": "elemefe", + "license": "MIT", + "dependencies": {} +} diff --git a/packages/switch/src/component.vue b/packages/switch/src/component.vue new file mode 100644 index 000000000..927fb7bb1 --- /dev/null +++ b/packages/switch/src/component.vue @@ -0,0 +1,116 @@ + + + diff --git a/packages/tab-pane/index.js b/packages/tab-pane/index.js new file mode 100644 index 000000000..3ddfa853a --- /dev/null +++ b/packages/tab-pane/index.js @@ -0,0 +1,7 @@ +const TabPane = require('../tabs/src/tab-pane.vue'); + +TabPane.install = function(Vue) { + Vue.component(TabPane.name, TabPane); +}; + +module.exports = TabPane; diff --git a/packages/table-column/index.js b/packages/table-column/index.js new file mode 100644 index 000000000..0f0651b3f --- /dev/null +++ b/packages/table-column/index.js @@ -0,0 +1,7 @@ +const ElTableColumn = require('../table/src/table-column'); + +ElTableColumn.install = function(Vue) { + Vue.component(ElTableColumn.name, ElTableColumn); +}; + +module.exports = ElTableColumn; diff --git a/packages/table/README.md b/packages/table/README.md new file mode 100644 index 000000000..154652806 --- /dev/null +++ b/packages/table/README.md @@ -0,0 +1,49 @@ +# el-table +> A el-table component for Vue.js. + +## Demo +http://element-component.github.io/el-table + +## Installation +```shell +npm i el-table -D +``` + +## Usage +```javascript +import Vue from 'vue' +import ElTable from 'el-table' +import 'element-theme-default' + +Vue.use(ElTable) +``` + +or + +```javascript +import Vue from 'vue' +import { ElTable } from 'el-table' + +Vue.component('el-table', ElTable) +``` + + +## Options + +| name | description | type | default | +|-------------|-------------|-------------|-------------| +| | | | | + +## Development +```shell +make dev + +## test +make test + +## build +make build +``` + +# License +[MIT](https://opensource.org/licenses/MIT) diff --git a/packages/table/cooking.conf.js b/packages/table/cooking.conf.js new file mode 100644 index 000000000..18ba95ef1 --- /dev/null +++ b/packages/table/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElTable', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/table/index.js b/packages/table/index.js new file mode 100644 index 000000000..fa5b207da --- /dev/null +++ b/packages/table/index.js @@ -0,0 +1,7 @@ +const ElTable = require('./src/table'); + +ElTable.install = function(Vue) { + Vue.component('el-table', ElTable); +}; + +module.exports = ElTable; diff --git a/packages/table/package.json b/packages/table/package.json new file mode 100644 index 000000000..6a5dd86a9 --- /dev/null +++ b/packages/table/package.json @@ -0,0 +1,17 @@ +{ + "name": "el-table", + "version": "1.0.0", + "description": "A table component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/dialog", + "author": "elemefe", + "license": "MIT", + "devDependencies": { + "throttle-debounce": "^1.0.1" + } +} diff --git a/packages/table/src/table-body.vue b/packages/table/src/table-body.vue new file mode 100644 index 000000000..0ee203196 --- /dev/null +++ b/packages/table/src/table-body.vue @@ -0,0 +1,142 @@ + + + \ No newline at end of file diff --git a/packages/table/src/table-column.vue b/packages/table/src/table-column.vue new file mode 100644 index 000000000..d5e1659d4 --- /dev/null +++ b/packages/table/src/table-column.vue @@ -0,0 +1,254 @@ + + + diff --git a/packages/table/src/table-header.vue b/packages/table/src/table-header.vue new file mode 100644 index 000000000..e5de0ecb4 --- /dev/null +++ b/packages/table/src/table-header.vue @@ -0,0 +1,198 @@ + + + \ No newline at end of file diff --git a/packages/table/src/table.vue b/packages/table/src/table.vue new file mode 100644 index 000000000..89a1c7062 --- /dev/null +++ b/packages/table/src/table.vue @@ -0,0 +1,441 @@ + + + diff --git a/packages/table/src/util.js b/packages/table/src/util.js new file mode 100644 index 000000000..758a555a9 --- /dev/null +++ b/packages/table/src/util.js @@ -0,0 +1,87 @@ +var scrollBarWidth; + +export const getScrollBarWidth = () => { + if (scrollBarWidth !== undefined) return scrollBarWidth; + + const outer = document.createElement('div'); + outer.style.visibility = 'hidden'; + outer.style.width = '100px'; + outer.style.position = 'absolute'; + outer.style.top = '-9999px'; + document.body.appendChild(outer); + + const widthNoScroll = outer.offsetWidth; + outer.style.overflow = 'scroll'; + + const inner = document.createElement('div'); + inner.style.width = '100%'; + outer.appendChild(inner); + + const widthWithScroll = inner.offsetWidth; + outer.parentNode.removeChild(outer); + + return widthNoScroll - widthWithScroll; +}; + +export const getCell = function(event) { + let cell = event.target; + + while (cell && cell.tagName.toUpperCase() !== 'HTML') { + if (cell.tagName.toUpperCase() === 'TD') { + return cell; + } + cell = cell.parentNode; + } + + return null; +}; + +export const getValueByPath = function(object, prop) { + prop = prop || ''; + const paths = prop.split('.'); + let current = object; + let result = null; + for (let i = 0, j = paths.length; i < j; i++) { + const path = paths[i]; + if (!current) break; + + if (i === j - 1) { + result = current[path]; + break; + } + current = current[path]; + } + return result; +}; + +const isObject = function(obj) { + return obj !== null && typeof obj === 'object'; +}; + +export const orderBy = function(array, sortKey, reverse) { + if (!sortKey) { + return array; + } + const order = (reverse && reverse < 0) ? -1 : 1; + + // sort on a copy to avoid mutating original array + return array.slice().sort(function(a, b) { + if (sortKey !== '$key') { + if (isObject(a) && '$value' in a) a = a.$value; + if (isObject(b) && '$value' in b) b = b.$value; + } + a = isObject(a) ? getValueByPath(a, sortKey) : a; + b = isObject(b) ? getValueByPath(b, sortKey) : b; + return a === b ? 0 : a > b ? order : -order; + }); +}; + +export const getChild = function(event) { + let cell = event.target; + + while (cell.children.length) { + cell = cell.children[0]; + } + + return cell; +}; diff --git a/packages/tabs/cooking.conf.js b/packages/tabs/cooking.conf.js new file mode 100644 index 000000000..42d91beb1 --- /dev/null +++ b/packages/tabs/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElTabs', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/tabs/index.js b/packages/tabs/index.js new file mode 100644 index 000000000..3cf7a3a9c --- /dev/null +++ b/packages/tabs/index.js @@ -0,0 +1,9 @@ +const ElTabs = require('./src/tabs'); +const ElTabPane = require('./src/tab-pane'); + +ElTabs.install = function(Vue) { + Vue.component(ElTabs.name, ElTabs); + Vue.component(ElTabPane.name, ElTabPane); +}; + +module.exports = ElTabs; diff --git a/packages/tabs/package.json b/packages/tabs/package.json new file mode 100644 index 000000000..0baa36cfe --- /dev/null +++ b/packages/tabs/package.json @@ -0,0 +1,16 @@ +{ + "name": "el-tabs", + "version": "1.0.0", + "description": "A tabs component for Vue.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/tabs", + "author": "haiping.zeng", + "license": "MIT", + "dependencies": { + } +} diff --git a/packages/tabs/src/tab-pane.vue b/packages/tabs/src/tab-pane.vue new file mode 100644 index 000000000..888001f66 --- /dev/null +++ b/packages/tabs/src/tab-pane.vue @@ -0,0 +1,57 @@ + + + diff --git a/packages/tabs/src/tab.vue b/packages/tabs/src/tab.vue new file mode 100644 index 000000000..e1a66f45b --- /dev/null +++ b/packages/tabs/src/tab.vue @@ -0,0 +1,27 @@ + + + diff --git a/packages/tabs/src/tabs.vue b/packages/tabs/src/tabs.vue new file mode 100644 index 000000000..ab371f641 --- /dev/null +++ b/packages/tabs/src/tabs.vue @@ -0,0 +1,113 @@ + + + diff --git a/packages/tag/cooking.conf.js b/packages/tag/cooking.conf.js new file mode 100644 index 000000000..dd83ef37b --- /dev/null +++ b/packages/tag/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElTag', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/tag/index.js b/packages/tag/index.js new file mode 100644 index 000000000..029de5b9a --- /dev/null +++ b/packages/tag/index.js @@ -0,0 +1,7 @@ +const ElTag = require('./src/tag'); + +ElTag.install = function(Vue) { + Vue.component(ElTag.name, ElTag); +}; + +module.exports = ElTag; diff --git a/packages/tag/package.json b/packages/tag/package.json new file mode 100644 index 000000000..25956f4db --- /dev/null +++ b/packages/tag/package.json @@ -0,0 +1,18 @@ +{ + "name": "el-tag", + "version": "1.0.1", + "description": "A input component for Vue.", + "main": "lib/index.js", + "keywords": [ + "element", + "vue", + "component", + "input" + ], + "author": "haipingZeng ", + "license": "MIT", + "repository": "https://github.com/element-component/element/tree/master/packages/tag", + "devDependencies": { + "vue-clickoutside": "0.0.4" + } +} diff --git a/packages/tag/src/tag.vue b/packages/tag/src/tag.vue new file mode 100644 index 000000000..28b2c6872 --- /dev/null +++ b/packages/tag/src/tag.vue @@ -0,0 +1,29 @@ + + diff --git a/packages/theme-default/README.md b/packages/theme-default/README.md new file mode 100644 index 000000000..7c214858d --- /dev/null +++ b/packages/theme-default/README.md @@ -0,0 +1,27 @@ +# element-theme-default +> element component defualt theme. + +## Usage + +Use Sass Or postcss-import +```css +@import 'element-theme-default'; +``` + +Or Use webpack +```javascript +import 'element-theme-default'; +``` + +Or +```html + +``` + +## Import your need +```javascript +import 'element-theme-default/dist/input.css'; +import 'element-theme-default/dist/select.css'; + +// ... +``` diff --git a/packages/theme-default/gulpfile.js b/packages/theme-default/gulpfile.js new file mode 100644 index 000000000..91b1984da --- /dev/null +++ b/packages/theme-default/gulpfile.js @@ -0,0 +1,37 @@ +'use strict'; + +const gulp = require('gulp'); +const postcss = require('gulp-postcss'); +const cssmin = require('gulp-cssmin'); + +const salad = require('postcss-salad')({ + browser: ['ie > 9', 'last 2 version'], + features: { + 'bem': { + 'shortcuts': { + 'component': 'b', + 'modifier': 'm', + 'descendent': 'e' + }, + 'separators': { + 'descendent': '__', + 'modifier': '--' + } + } + } +}); + +gulp.task('compile', function () { + return gulp.src('./src/*.css') + .pipe(postcss([salad])) + .pipe(cssmin()) + .pipe(gulp.dest('./lib')); +}); + +gulp.task('copyfont', function () { + return gulp.src('./src/fonts/**') + .pipe(cssmin()) + .pipe(gulp.dest('./lib/fonts')); +}); + +gulp.task('build', ['compile', 'copyfont']); diff --git a/packages/theme-default/package.json b/packages/theme-default/package.json new file mode 100644 index 000000000..fdd673398 --- /dev/null +++ b/packages/theme-default/package.json @@ -0,0 +1,32 @@ +{ + "name": "element-theme-default", + "version": "1.0.0", + "description": "Element component default theme.", + "main": "lib/index.css", + "files": [ + "lib" + ], + "scripts": { + "build": "postcss -c postcss.conf.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/element-component/element-theme-default.git" + }, + "keywords": [ + "element", + "theme" + ], + "author": "haiping.zeng ", + "license": "MIT", + "bugs": { + "url": "https://github.com/element-component/element-theme-default/issues" + }, + "homepage": "https://github.com/element-component/element-theme-default#readme", + "devDependencies": { + "gulp": "^3.9.1", + "gulp-cssmin": "^0.1.7", + "gulp-postcss": "^6.1.1", + "postcss-salad": "^1.0.5" + } +} diff --git a/packages/theme-default/src/alert.css b/packages/theme-default/src/alert.css new file mode 100644 index 000000000..bbb99e7c4 --- /dev/null +++ b/packages/theme-default/src/alert.css @@ -0,0 +1,89 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + + @b alert { + width: 100%; + padding: 8px 16px; + margin: 0; + box-sizing: border-box; + border-radius: 4px; + position: relative; + background-color: #fff; + overflow: hidden; + color: #fff; + opacity: 1; + display: table; + + @modifier success { + background-color: #13ce66; + } + + @modifier info { + background-color: #50bfff; + } + + @modifier warning { + background-color: #f7ba2a; + } + + @modifier error { + background-color: #ff4949; + } + + @e content { + display: table-cell; + padding-left: 8px; + } + + @e icon { + font-size: 16px; + width: 16px; + display: table-cell; + color: #fff; + vertical-align: middle; + @when big { + font-size: 28px; + width: 28px; + } + } + + @e title { + font-size: 13px; + line-height: 18px; + @when bold { + font-weight: bold; + } + } + + & .el-alert__description { + color: #fff; + font-size: 12px; + margin: 5px 0 0 0; + } + + @e closebtn { + font-size: 12px; + color: #fff; + opacity: 1; + position: absolute 12px 15px * *; + cursor: pointer; + + @when customed { + font-style: normal; + font-size: 13px; + top: 9px; + } + } + } + + .el-alert-fade-transition { + transition: opacity .2s; + } + + .el-alert-fade-enter, + .el-alert-fade-leave { + opacity: 0; + } +} diff --git a/packages/theme-default/src/autocomplete.css b/packages/theme-default/src/autocomplete.css new file mode 100644 index 000000000..dba8c6d6e --- /dev/null +++ b/packages/theme-default/src/autocomplete.css @@ -0,0 +1,63 @@ +@charset "UTF-8"; +@import "./input.css"; +@import "./common/var.css"; + +@component-namespace el { + @b autocomplete { + position: relative; + + @e suggestions { + position: absolute; + left: 0; + top: 110%; + margin: 0; + background-color: #fff; + border: 1px solid #D3DCE6; + width: 100%; + padding: 6px 0; + z-index: 10; + border-radius: 2px; + max-height: 280px; + box-sizing: border-box; + overflow: auto; + box-shadow: 0 0 6px 0 rgba(0,0,0,0.04), 0 2px 4px 0 rgba(0,0,0,0.12); + + & li { + list-style: none; + line-height: 36px; + padding: 0 27px 0 10px; + margin: 0; + cursor: pointer; + + &.highlighted, + &:hover { + background-color: var(--color-primary); + color: #fff; + } + &:active { + background-color: darken(var(--color-primary), 0.2); + } + &.divider { + margin-top: 6px; + border-top: 1px solid #D3DCE6; + } + &.divider:last-child { + margin-bottom: -6px; + } + } + + @when loading { + text-align: center; + height: 100px; + line-height: 100px; + font-size: 20px; + color: #999; + @utils-vertical-center; + + & .el-icon-loading { + vertical-align: middle; + } + } + } + } +} diff --git a/packages/theme-default/src/base.css b/packages/theme-default/src/base.css new file mode 100644 index 000000000..d5da9e9e1 --- /dev/null +++ b/packages/theme-default/src/base.css @@ -0,0 +1,2 @@ +@import "./common/transition.css"; +@import "./icon.css"; diff --git a/packages/theme-default/src/breadcrumb.css b/packages/theme-default/src/breadcrumb.css new file mode 100644 index 000000000..7fe4bfdbf --- /dev/null +++ b/packages/theme-default/src/breadcrumb.css @@ -0,0 +1,38 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + + @b breadcrumb { + font-size:13px; + color:#475669; + @utils-clearfix; + + @e separator { + margin: 0 8px; + color: #c0ccda; + } + @e item { + float: left; + + &:not(:last-child) { + .el-breadcrumb__item__text { + transition: color .15s linear; + + &:hover { + color: var(--color-primary); + cursor: pointer; + } + } + } + + &:last-child { + color: #99a9bf; + + .el-breadcrumb__separator { + display: none; + } + } + } + } +} diff --git a/packages/theme-default/src/button.css b/packages/theme-default/src/button.css new file mode 100644 index 000000000..91a1acd6b --- /dev/null +++ b/packages/theme-default/src/button.css @@ -0,0 +1,197 @@ +@charset "UTF-8"; +@import "./common/var.css"; +@import './mixins/button'; + +@component-namespace el { + @b button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: var(--button-default-fill); + border: var(--border-base); + color: var(--button-default-color); + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: none; + margin: 0; + @utils-user-select none; + + @mixin button-size var(--button-padding-vertical), var(--button-padding-horizontal), var(--button-font-size), var(--button-border-radius); + + &:hover { + color: var(--color-primary); + border-color: @color; + } + + &:active { + color: shade(var(--color-primary), var(--button-active-shade-percent)); + border-color: @color; + outline: none; + } + + &::-moz-focus-inner { + border: 0; + } + + & .el-icon-right { + margin-left: 5px; + } + & .el-icon-left { + margin-right: 5px; + } + + & [class*="el-icon-"] { + /*line-height: 0.9;*/ + + & + span { + margin-left: 5px; + } + } + + @when plain { + &:hover { + background: #fff; + border-color: var(--color-primary); + color: var(--color-primary); + } + + &:active { + background: #fff; + border-color: shade(var(--color-primary), var(--button-active-shade-percent)); + color: shade(var(--color-primary), var(--button-active-shade-percent)); + outline: none; + } + } + + @when active { + color: shade(var(--color-primary), var(--button-active-shade-percent)); + border-color: @color; + } + + @when disabled { + &, + &:hover { + color: var(--button-disabled-color); + cursor: not-allowed; + background-image: none; + background-color: var(--button-disabled-fill); + border-color: var(--button-disabled-border); + } + + &.is-plain { + &, + &:hover { + background-color: #fff; + border-color: #d3dce6; + color: #C0CCDA; + } + } + } + + @when loading { + position: relative; + pointer-events: none; + + &:before { + pointer-events: none; + content: ''; + position: absolute; + left: -1px; + top: -1px; + right: -1px; + bottom: -1px; + border-radius: inherit; + background-color: rgba(255,255,255,.35); + } + } + } + + @b button-primary { + @mixin button-variant var(--button-primary-color), var(--button-primary-fill), var(--button-primary-border); + } + @b button-success { + @mixin button-variant var(--button-success-color), var(--button-success-fill), var(--button-success-border); + } + @b button-warning { + @mixin button-variant var(--button-warning-color), var(--button-warning-fill), var(--button-warning-border); + } + @b button-danger { + @mixin button-variant var(--button-danger-color), var(--button-danger-fill), var(--button-danger-border); + } + @b button-info { + @mixin button-variant var(--button-info-color), var(--button-info-fill), var(--button-info-border); + } + @b button-text { + border: none; + color: var(--color-primary); + background: transparent; + + &:hover { + color: tint(var(--color-primary), var(--button-hover-tint-percent)); + } + &:active { + color: shade(var(--color-primary), var(--button-active-shade-percent)); + } + } + + @b button-large { + @mixin button-size var(--button-large-padding-vertical), var(--button-large-padding-horizontal), var(--button-large-font-size), var(--button-border-radius); + } + @b button-small { + @mixin button-size var(--button-small-padding-vertical), var(--button-small-padding-horizontal), var(--button-small-font-size), var(--button-border-radius); + } + @b button-mini { + @mixin button-size var(--button-mini-padding-vertical), var(--button-mini-padding-horizontal), var(--button-mini-font-size), var(--button-border-radius); + } + + @b button-group { + @utils-clearfix; + display: inline-block; + + & .el-button { + float: left; + position: relative; + + &:first-child { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + &:last-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + &:not(:first-child):not(:last-child) { + border-radius: 0; + } + &:not(:last-child) { + margin-right: -1px; + } + + &:hover, + &:active { + z-index: 1; + } + + @when active { + z-index: 1; + } + } + + @each $type in (primary, success, warning, danger, info) { + .el-button-$type { + &:first-child { + border-right-color: rgba(#fff, 0.5); + } + &:last-child { + border-left-color: rgba(#fff, 0.5); + } + &:not(:first-child):not(:last-child) { + border-left-color: rgba(#fff, 0.5); + border-right-color: rgba(#fff, 0.5); + } + } + } + } +} diff --git a/packages/theme-default/src/cascader.css b/packages/theme-default/src/cascader.css new file mode 100644 index 000000000..a79fe34e2 --- /dev/null +++ b/packages/theme-default/src/cascader.css @@ -0,0 +1,45 @@ +@charset "UTF-8"; +@import "./input.css"; +@import "./common/var.css"; +/*@import "./core/dropdown.css";*/ + +@component-namespace element { + + @b cascader { + display: inline-block; + position: relative; + + @e dropdown { + background-color: var(--cascader-menu-fill); + border: var(--cascader-menu-border); + border-radius: var(--cascader-menu-radius); + box-shadow: var(--cascader-menu-submenu-shadow); + margin-top: 5px; + max-height: var(--cascader-height); + position: absolute; + white-space: nowrap; + z-index: 10; + } + + @e wrap { + overflow: hidden; + } + + @e menu { + border: 0; + box-shadow: none; + display: inline-block; + margin: 0; + position: relative; + vertical-align: top; + + &::before { + border-left: var(--cascader-menu-border); + content: " "; + height: var(--cascader-height); + left: 0; + position: absolute; + } + } + } +} diff --git a/packages/theme-default/src/checkbox.css b/packages/theme-default/src/checkbox.css new file mode 100644 index 000000000..658c08dff --- /dev/null +++ b/packages/theme-default/src/checkbox.css @@ -0,0 +1,133 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + + @b checkbox { + color: var(--checkbox-color); + position: relative; + cursor: pointer; + display: inline-block; + white-space: nowrap; + @utils-user-select none; + + @e input { + white-space: nowrap; + cursor: pointer; + outline: none; + display: inline-block; + line-height: 1; + position: relative; + vertical-align: middle; + } + @e inner { + display: inline-block; + position: relative; + border: var(--checkbox-input-border); + border-radius: var(--checkbox-input-border-radius); + box-sizing: border-box; + rect: var(--checkbox-input-width) var(--checkbox-input-height) var(--checkbox-input-fill); + z-index: var(--index-normal); + transition: border-color .25s cubic-bezier(.71,-.46,.29,1.46), + background-color .25s cubic-bezier(.71,-.46,.29,1.46); + + &:not(.is-disabled):hover { + border-color: var(--checkbox-input-border-color-hover); + } + + &::after { + content: ""; + border: 2px solid var(--checkbox-checked-icon-color); + border-left: 0; + border-top: 0; + height: 8px; + left: 5px; + position: absolute; + top: 1px; + transform: rotate(45deg) scaleY(0); + width: 4px; + transition: transform .15s cubic-bezier(.71,-.46,.88,.6) .05s; + transform-origin: center; + } + + @when disabled { + background-color: var(--checkbox-disabled-input-fill); + border-color: var(--checkbox-disabled-input-border-color); + cursor: not-allowed; + + &::after { + cursor: not-allowed; + border-color: var(--checkbox-disabled-icon-color); + } + + & + .el-checkbox__label { + cursor: not-allowed; + } + } + + @when checked { + background-color: var(--checkbox-checked-input-fill); + border-color: var(--checkbox-checked-input-border-color); + + &::after { + transform: rotate(45deg) scaleY(1); + } + } + + @when focus { + border-color: var(--checkbox-input-border-color-hover); + } + + &.is-disabled.is-checked { + background-color: var(--checkbox-disabled-checked-input-fill); + border-color: var(--checkbox-disabled-checked-input-border-color); + + &::after { + border-color: var(--checkbox-disabled-checked-icon-color); + } + } + + @when indeterminate { + background-color: var(--checkbox-checked-input-fill); + border-color: var(--checkbox-checked-input-border-color); + + &::before { + content: ''; + position: absolute; + display: block; + border: 1px solid var(--checkbox-checked-icon-color); + margin-top: -1px; + left: 3px; + right: 3px; + top: 50%; + } + + &::after { + display: none; + } + } + + &.is-disabled.is-indeterminate { + background-color: var(--checkbox-disabled-checked-input-fill); + border-color: var(--checkbox-disabled-checked-input-border-color); + + &::before { + border-color: var(--checkbox-disabled-checked-icon-color); + } + } + } + + @e original { + opacity: 0; + outline: none; + position: absolute; + margin: 0; + left: -999px; + } + + @e label { + font-size: var(--checkbox-font-size); + padding-left: 5px; + } + } +} diff --git a/packages/theme-default/src/col.css b/packages/theme-default/src/col.css new file mode 100644 index 000000000..b2a9f58b4 --- /dev/null +++ b/packages/theme-default/src/col.css @@ -0,0 +1,23 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +.el-col-1, .el-col-2, .el-col-3, .el-col-4, .el-col-5, .el-col-6, .el-col-7, .el-col-8, .el-col-9, .el-col-10, .el-col-11, .el-col-12, .el-col-13, .el-col-14, .el-col-15, .el-col-16, .el-col-17, .el-col-18, .el-col-19, .el-col-20, .el-col-21, .el-col-22, .el-col-23, .el-col-24 { + float: left; +} + +@for $i from 1 to 24 { + .el-col-$i { + width: calc(1 / 24 * $(i) * 100)%; + } + .el-col-offset-$i { + margin-left: calc(1 / 24 * $(i) * 100)%; + } + .el-col-pull-$i { + position: relative; + right: calc(1 / 24 * $(i) * 100)%; + } + .el-col-push-$i { + position: relative; + left: calc(1 / 24 * $(i) * 100)%; + } +} diff --git a/packages/theme-default/src/common/transition.css b/packages/theme-default/src/common/transition.css new file mode 100644 index 000000000..03f41b713 --- /dev/null +++ b/packages/theme-default/src/common/transition.css @@ -0,0 +1,111 @@ +@charset "UTF-8"; +@import './var.css'; + +.fade-in-transition { + opacity: 1; + transition: var(--fade-transition); +} + +.fade-in-linear-transition { + opacity: 1; + transition: var(--fade-linear-transition); +} + +.fade-in-enter, +.fade-in-leave, +.fade-in-linear-enter, +.fade-in-linear-leave { + opacity: 0; +} + +.md-fade-center-transition { + opacity: 1; + transform: scaleY(1); + transition: var(--md-fade-transition); + transform-origin: center center; +} +.md-fade-center-enter, +.md-fade-center-leave { + opacity: 0; + transform: scaleY(0); +} + +.md-fade-bottom-transition { + opacity: 1; + transform: scaleY(1); + transition: var(--md-fade-transition); + transform-origin: center top; +} +.md-fade-bottom-enter, +.md-fade-bottom-leave { + opacity: 0; + transform: scaleY(0); +} + +.md-fade-top-transition { + opacity: 1; + transform: scaleY(1); + transition: var(--md-fade-transition); + transform-origin: center bottom; +} + +.md-fade-top-enter, +.md-fade-top-leave { + opacity: 0; + transform: scaleY(0); +} + +.md-fade-left-transition { + opacity: 1; + transform: scaleX(1); + transition: var(--md-fade-transition); + transform-origin: right center; +} +.md-fade-left-enter, +.md-fade-left-leave { + opacity: 0; + transform: scaleX(0); +} + +.md-fade-right-transition { + opacity: 1; + transform: scaleX(1); + transition: var(--md-fade-transition); + transform-origin: left center; +} +.md-fade-right-enter, +.md-fade-right-leave { + opacity: 0; + transform: scaleX(0); +} + +.zoom-x-transition { + transform: scaleX(1); + transition: var(--md-fade-transition); + transform-origin: center center; +} +.zoom-x-enter, +.zoom-x-leave { + transform: scaleX(0); +} + +.slide-in-bottom-enter { + animation: slideInBottomEnter .3s; +} +.slide-in-bottom-leave { + animation: slideInBottomLeave .3s; +} + +@keyframes slideInBottomEnter { + 0% { + opacity: 0; + transform: translate3d(0,50%,0); + } +} + +@keyframes slideInBottomLeave { + to { + opacity: 0; + transform: translate3d(0,50%,0); + } +} diff --git a/packages/theme-default/src/common/var.css b/packages/theme-default/src/common/var.css new file mode 100644 index 000000000..1eaf69414 --- /dev/null +++ b/packages/theme-default/src/common/var.css @@ -0,0 +1,356 @@ +:root { + + /* + Transition + -------------------------- */ + --fade-transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); + --fade-linear-transition: opacity 200ms linear; + --md-fade-transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1) 100ms, opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) 100ms; + --border-transition-base: border-color .2s cubic-bezier(.645,.045,.355,1); + --color-transition-base: color .2s cubic-bezier(.645,.045,.355,1); + + /* Colors + -------------------------- */ + --color-primary: #20a0ff; + --color-success: #13ce66; + --color-warning: #f7ba2a; + --color-danger: #ff4949; + --color-info: #50BFFF; + --color-blue: #2e90fe; + --color-blue-light: #5da9ff; + --color-blue-lighter: rgba(var(--color-blue), 0.12); + --color-white: #fff; + --color-black: #000; + --color-grey: #C0CCDA; + + /* Border + -------------------------- */ + --border-width-base: 1px; + --border-style-base: solid; + --border-color-base: var(--color-grey); + --border-base: var(--border-width-base) var(--border-style-base) var(--border-color-base); + --border-radius-base: 4px; + --border-radius-circle: 100%; + --shadow-base: 0 0 2px rgba(var(--color-black), 0.18), 0 0 1px var(--color-blue-light); + + /* Fill + -------------------------- */ + --fill-base: var(--color-white); + + /* Font + -------------------------- */ + --font-size-base: 14px; + --font-color-base: #1f2d3d; + --font-color-disabled-base: #bbb; + + /* Size + -------------------------- */ + --size-base: 14px; + + /* z-index + -------------------------- */ + --index-normal: 1; + --index-top: 1000; + --index-popper: 2000; + + /* Disable base + -------------------------- */ + --disabled-fill-base: #EFF2F7; + --disabled-color-base: #bbb; + --disabled-border-base: #D3DCE6; + + /* Icon + -------------------------- */ + --icon-color: #666; + + /* Checkbox + -------------------------- */ + --checkbox-font-size: 14px; + --checkbox-color: #1f2d3d; + --checkbox-input-height: 18px; + --checkbox-input-width: 18px; + --checkbox-input-border-radius: var(--border-radius-base); + --checkbox-input-fill: #fff; + --checkbox-input-border: var(--border-base); + --checkbox-input-border-color: var(--border-color-base); + --checkbox-icon-color: #fff; + + --checkbox-disabled-input-border-color: var(--disabled-border-base); + --checkbox-disabled-input-fill: var(--disabled-fill-base); + --checkbox-disabled-icon-color: var(--disabled-fill-base); + + --checkbox-disabled-checked-input-fill: var(--disabled-border-base); + --checkbox-disabled-checked-input-border-color: var(--disabled-border-base); + --checkbox-disabled-checked-icon-color: #fff; + + --checkbox-checked-input-border-color: var(--color-blue); + --checkbox-checked-input-fill: var(--color-primary); + --checkbox-checked-icon-color: var(--fill-base); + + --checkbox-input-shadow-hover: var(--shadow-base); + --checkbox-input-border-color-hover: var(--color-primary); + + /* Radio + -------------------------- */ + --radio-font-size: 14px; + --radio-color: #1f2d3d; + --radio-input-height: 18px; + --radio-input-width: 18px; + --radio-input-border-radius: var(--border-radius-circle); + --radio-input-fill: #fff; + --radio-input-border: var(--border-base); + --radio-input-border-color: var(--border-color-base); + --radio-icon-color: #fff; + + --radio-disabled-input-border-color: var(--disabled-border-base); + --radio-disabled-input-fill: var(--disabled-fill-base); + --radio-disabled-icon-color: var(--disabled-fill-base); + + --radio-disabled-checked-input-fill: var(--disabled-border-base); + --radio-disabled-checked-input-border-color: var(--disabled-border-base); + --radio-disabled-checked-icon-color: #fff; + + --radio-checked-input-border-color: var(--color-primary); + --radio-checked-input-fill: #fff; + --radio-checked-icon-color: var(--color-primary); + + --radio-input-shadow-hover: var(--shadow-base); + --radio-input-border-color-hover: var(--color-primary); + + --radio-button-font-size: var(--font-size-base); + + /* Input + -------------------------- */ + --input-font-size: var(--font-size-base); + --input-color: var(--font-color-base); + --input-width: 140px; + --input-height: 36px; + --input-shadow-hover: var(--shadow-base); + --input-border: var(--border-base); + --input-border-color: var(--border-color-base); + --input-border-radius: var(--border-radius-base); + --input-border-color-hover: #8492A6; + --input-fill: #fff; + --input-fill-disabled: var(--disabled-fill-base); + --input-color-disabled: var(--font-color-disabled-base); + --input-icon-color: var(--color-grey); + --input-placeholder-color: #99a9bf; + --input-max-width: 314px; + + --input-hover-border: #8492A6; + + --input-focus-border: var(--color-primary); + --input-focus-fill: #fff; + + --input-disabled-fill: var(--disabled-fill-base); + --input-disabled-border: var(--disabled-border-base); + --input-disabled-color: var(--disabled-color-base); + --input-disabled-placeholder-color: #C0CCDA; + + --input-large-font-size: 16px; + --input-large-height: 42px; + + --input-small-font-size: 13px; + --input-small-height: 28px; + + --input-mini-font-size: 12px; + --input-mini-height: 22px; + + /* Cascader + -------------------------- */ + --cascader-menu-fill: var(--fill-base); + --cascader-menu-font-size: var(--font-size-base); + --cascader-menu-radius: var(--border-radius-base); + --cascader-menu-border: var(--border-base); + --cascader-menu-border-color: var(--border-color-base); + --cascader-menu-border-width: var(--border-width-base); + --cascader-menu-color: var(--font-color-base); + --cascader-menu-option-color-active: var(--color-blue); + --cascader-menu-option-fill-active: rgba(var(--color-blue), 0.12); + --cascader-menu-option-color-hover: var(--font-color-base); + --cascader-menu-option-fill-hover: rgba(var(--color-black), 0.06); + --cascader-menu-option-color-disabled: #999; + --cascader-menu-option-fill-disabled: rgba(var(--color-black), 0.06); + --cascader-menu-option-empty-color: #666; + --cascader-menu-group-color: #999; + --cascader-menu-shadow: 0 1px 2px rgba(var(--color-black), 0.14), 0 0 3px rgba(var(--color-black), 0.14); + --cascader-menu-option-pinyin-color: #999; + --cascader-menu-submenu-shadow: 1px 1px 2px rgba(var(--color-black), 0.14), 1px 0 2px rgba(var(--color-black), 0.14); + + /* Tag + -------------------------- */ + --tag-color: var(--font-color-base); + --tag-fill: #e2e2e2; + --tag-border-radius: var(--border-radius-base); + --tag-close-color: #666; + + /* Group + -------------------------- */ + --group-option-flex: 0 0 (1/5) * 100%; + --group-option-offset-bottom: 12px; + --group-option-fill-hover: rgba(var(--color-black), 0.06); + --group-title-color: var(--color-black); + --group-title-font-size: var(--font-size-base); + --group-title-width: 66px; + + /* Tab + -------------------------- */ + --tab-font-size: var(--font-size-base); + --tab-border-line: 1px solid #e4e4e4; + --tab-header-color-active: var(--color-blue); + --tab-header-color-hover: var(--font-color-base); + --tab-header-color: var(--font-color-base); + --tab-header-fill-active: rgba(var(--color-black), 0.06); + --tab-header-fill-hover: rgba(var(--color-black), 0.06); + --tab-vertical-header-width: 90px; + --tab-vertical-header-count-color: var(--color-white); + --tab-vertical-header-count-fill: var(--color-blue); + --tab-horizontal-border: 2px solid #438de0; + + /* Button + -------------------------- */ + --button-font-size: 14px; + --button-border-radius: var(--border-radius-base); + --button-paddding: 5px 20px; + --button-padding-vertical: 10px; + --button-padding-horizontal: 15px; + + --button-large-font-size: 16px; + --button-large-padding-vertical: 11px; + --button-large-padding-horizontal: 19px; + + --button-small-font-size: 12px; + --button-small-padding-vertical: 7px; + --button-small-padding-horizontal: 9px; + + --button-mini-font-size: 12px; + --button-mini-padding-vertical: 4px; + --button-mini-padding-horizontal: 4px; + + --button-default-color: #1F2D3D; + --button-default-fill: #fff; + --button-default-border: #c4c4c4; + + --button-ghost-color: #666; + --button-ghost-fill: transparent; + --button-ghost-border: none; + + --button-disabled-color: #C0CCDA; + --button-disabled-fill: #EFF2F7; + --button-disabled-border: #D3DCE6; + + --button-primary-border: var(--color-primary); + --button-primary-color: #fff; + --button-primary-fill: var(--color-primary); + + --button-success-border: var(--color-success); + --button-success-color: #fff; + --button-success-fill: var(--color-success); + + --button-warning-border: var(--color-warning); + --button-warning-color: #fff; + --button-warning-fill: var(--color-warning); + + --button-danger-border: var(--color-danger); + --button-danger-color: #fff; + --button-danger-fill: var(--color-danger); + + --button-info-border: var(--color-info); + --button-info-color: #fff; + --button-info-fill: var(--color-info); + + --button-hover-tint-percent: 20%; + --button-active-shade-percent: 10%; + + + /* cascader + -------------------------- */ + --cascader-height: 200px; + + /* Switch + -------------------------- */ + --switch-on-color: #20a0ff; + --switch-off-color: #C0CCDA; + --switch-disabled-color: #E5E9F3; + --switch-disabled-text-color: #F9FAFC; + + /* Dialog + -------------------------- */ + --dialog-background-color: var(--color-blue); + --dialog-footer-background: var(--color-blue-lighter); + + /* Table + -------------------------- */ + --table-border-color: #e0e6ed; + --table-text-color: #1f2d3d; + --table-header-background: #EFF2F7; + + /* Pagination + -------------------------- */ + --pagination-font-size: 13px; + --pagination-fill: var(--color-white); + --pagination-color: #475669; + --pagination-border-radius: 2px; + --pagination-button-color: #99a9bf; + --pagination-button-disabled-color: #D3DCE6; + --pagination-button-size: 28px; + --pagination-button-disabled-color: #e4e4e4; + --pagination-button-disabled-fill: var(--color-white); + --pagination-border-color: #D3DCE6; + --pagination-hover-fill: var(--color-primary); + --pagination-hover-color: var(--color-white); + + /* Popover + -------------------------- */ + --popover-fill: #fff; + --popover-font-size: 12px; + --popover-border-color: #d3dce6; + --popover-arrow-size: 6px; + --popover-padding: 10px; + --popover-title-font-size: 13px; + --popover-title-color: #1f2d3d; + + /* Tooltip + -------------------------- */ + --tooltip-fill: #1f2d3d; + --tooltip-color: #fff; + --tooltip-font-size: 12px; + --tooltip-border-color: #1f2d3d; + --tooltip-arrow-size: 6px; + --tooltip-padding: 10px; + + /* Tag + -------------------------- */ + --tag-padding: 0 5px; + --tag-fill: #8492a6; + --tag-border: #8492a6; + --tag-color: #fff; + --tag-font-size: 12px; + --tag-border-radius: 4px; + + --tag-gray-fill: #e5e9f2; + --tag-gray-border: #e5e9f2; + --tag-gray-color: #475669; + + --tag-primary-fill: rgba(32,159,255,0.10); + --tag-primary-border: rgba(32,159,255,0.20); + --tag-primary-color: #20a0ff; + + --tag-success-fill: rgba(18,206,102,0.10); + --tag-success-border: rgba(18,206,102,0.20); + --tag-success-color: #13ce66; + + --tag-warning-fill: rgba(247,186,41,0.10); + --tag-warning-border: rgba(247,186,41,0.20); + --tag-warning-color: #f7ba2a; + + --tag-danger-fill: rgba(255,73,73,0.10); + --tag-danger-border: rgba(255,73,73,0.20); + --tag-danger-color: #ff4949; + + /* Dropdown + -------------------------- */ + --dropdown-menu-box-shadow: 0 0 6px 0 rgba(0,0,0,.12), 0 2px 4px 0 rgba(0,0,0,.12); + --dropdown-menuItem-hover-fill: #e5e9f2; + --dropdown-menuItem-hover-color: #475669; +} diff --git a/packages/theme-default/src/core/dropdown.css b/packages/theme-default/src/core/dropdown.css new file mode 100644 index 000000000..15a045db2 --- /dev/null +++ b/packages/theme-default/src/core/dropdown.css @@ -0,0 +1,45 @@ +@charset "UTF-8"; +@import "../common/var.css"; +@import "./option.css"; + +@component-namespace element { + + @b dropdown { + background-color: var(--dropdown-fill); + border: var(--dropdown-border); + border-radius: var(--dropdown-radius); + box-shadow: var(--dropdown-shadow); + left: 0; + list-style-type: none; + margin: -var(--dropdown-border-width); + margin-top: 5px; + min-width: calc(var(--input-width) + 4); + padding: 0; + position: absolute; + white-space: nowrap; + z-index: var(--index-normal); + + @e empty { + color: var(--dropdown-option-empty-color); + font-size: var(--input-font-size); + padding: 7px; + text-align: center; + } + + @e list { + margin: 0; + max-height: var(--cascader-height); + overflow: auto; + padding: 0; + } + + @e option { + max-height: 250px; + overflow: auto; + } + + &:empty { + display: none; + } + } +} diff --git a/packages/theme-default/src/core/input.css b/packages/theme-default/src/core/input.css new file mode 100644 index 000000000..1630321ee --- /dev/null +++ b/packages/theme-default/src/core/input.css @@ -0,0 +1,55 @@ +@charset "UTF-8"; +@import "../common/var.css"; + +@component-namespace element-core { + + @b input { + background-color: var(--input-fill); + border: var(--input-border); + border-radius: var(--input-border-radius); + box-sizing: border-box; + color: var(--input-color); + cursor: text; + display: inline-block; + font-size: var(--input-font-size); + min-height: var(--input-height); + min-width: var(--input-width); + padding: 2px; + position: relative; + vertical-align: middle; + + @when disabled { + background-color: var(--input-fill-disabled); + border-color: inherit; + box-shadow: none; + color: var(--input-color-disabled); + cursor: not-allowed; + } + + @when readonly { + cursor: pointer; + } + + @when multiple { + cursor: text; + } + + &:hover, + &.is-active { + border-color: var(--input-focus-border); + box-shadow: var(--input-shadow-hover); + } + + @e original { + background-color: inherit; + border: none; + box-sizing: border-box; + cursor: inherit; + height: 100%; + line-height: 1.2; + outline: none; + padding: 5px 7px; + width: auto; + } + } +} diff --git a/packages/theme-default/src/core/option.css b/packages/theme-default/src/core/option.css new file mode 100644 index 000000000..dde059657 --- /dev/null +++ b/packages/theme-default/src/core/option.css @@ -0,0 +1,72 @@ +@charset "UTF-8"; +@import "../common/var.css"; + +@component-namespace element { + + @b option { + box-sizing: border-box; + color: var(--dropdown-color); + cursor: pointer; + display: block; + font-size: var(--dropdown-font-size); + padding: 9px; + + @e remark { + color: var(--dropdown-option-pinyin-color); + float: right; + } + + @m arrow { + + &:not(.is-last)::after { + border-left: 1px solid var(--dropdown-border-color); + border-top: 1px solid var(--dropdown-border-color); + content: " "; + height: 4px; + margin-top: 6px; + position: absolute; + right: 12px; + transform: rotate(135deg); + width: 4px; + } + } + + @when disabled { + background-color: transparent; + color: var(--dropdown-option-color-disabled); + cursor: not-allowed; + } + + &:hover, + &.is-hover { + background-color: var(--dropdown-option-fill-hover); + color: var(--dropdown-option-color-hover); + } + + @when selected { + background-color: var(--dropdown-option-fill-active); + color: var(--dropdown-option-color-active); + } + } + + @b optiongroup { + list-style: none; + padding-left: 0; + + & .element-option { + padding-left: 21px; + } + + @e title { + box-sizing: border-box; + color: var(--dropdown-group-color); + display: inline-block; + font-size: var(--dropdown-font-size); + padding: 8px; + + &:hover { + background-color: inherit; + } + } + } +} diff --git a/packages/theme-default/src/core/tag.css b/packages/theme-default/src/core/tag.css new file mode 100644 index 000000000..2fc4aabf5 --- /dev/null +++ b/packages/theme-default/src/core/tag.css @@ -0,0 +1,29 @@ +@charset "UTF-8"; +@import "../common/var.css"; + +@component-namespace element-core { + + @b tag { + background-color: var(--tag-fill); + border: 0; + border-radius: var(--tag-border-radius); + color: var(--tag-color); + height: 22px; + margin: 1px; + outline: 0; + padding: 3px 16px 3px 3px; + position: relative; + + @e button { + color: var(--tag-close-color); + cursor: pointer; + line-height: 1; + + /* 增大可点击面积 */ + padding: 5px; + position: absolute; + right: 0; + top: 0; + } + } +} diff --git a/packages/theme-default/src/date-picker.css b/packages/theme-default/src/date-picker.css new file mode 100644 index 000000000..ed0a12d10 --- /dev/null +++ b/packages/theme-default/src/date-picker.css @@ -0,0 +1,8 @@ +@import "../../date-picker/src/css/date-table.css"; +@import "../../date-picker/src/css/month-table.css"; +@import "../../date-picker/src/css/year-table.css"; +@import "../../date-picker/src/css/time-spinner.css"; +@import "../../date-picker/src/css/picker.css"; +@import "../../date-picker/src/css/date-picker.css"; +@import "../../date-picker/src/css/date-range-picker.css"; +@import "../../date-picker/src/css/time-range-picker.css"; diff --git a/packages/theme-default/src/dialog.css b/packages/theme-default/src/dialog.css new file mode 100644 index 000000000..ec4ad7c59 --- /dev/null +++ b/packages/theme-default/src/dialog.css @@ -0,0 +1,110 @@ +@charset "UTF-8"; +@import "./common/var.css"; +@import '../../dialog/node_modules/vue-popup/lib/popup.css'; + +@component-namespace el { + + @b dialog { + position: relative; + margin-left: auto; + margin-right: auto; + overflow: hidden; + background: #fff; + border-radius: 2px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + box-sizing: border-box; + + @modifier tiny { + width: 30%; + } + + @modifier small { + width: 50%; + } + + @modifier large { + width: 90%; + } + + @modifier full { + width: 100%; + top: 0; + height: 100%; + overflow: auto; + } + + @e wrapper { + position: fixed 0 0 0 0; + overflow: auto; + margin: 0; + } + + @e header { + padding: 20px 20px 0; + } + + @e close { + cursor: pointer; + color: #C0CCDA; + + &:hover { + color: var(--dialog-background-color); + } + } + + @e title { + line-height: 1; + font-size: 16px; + font-weight: bold; + color: #1f2d3d; + } + + @e body { + padding: 30px 20px; + color: #475669; + font-size: 14px; + } + + @e headerbtn { + float: right; + } + + & *[slot=footer] { + padding: 10px 20px 15px; + text-align: right; + width: 100%; + display: block; + box-sizing: border-box; + } + } + + .dialog-fade-enter { + animation: dialog-fade-in .3s; + } + + .dialog-fade-leave { + animation: dialog-fade-out .3s; + } + + @keyframes dialog-fade-in { + 0% { + transform: translate3d(0, -20px, 0); + opacity: 0; + } + 100% { + transform: translate3d(0, 0, 0); + opacity: 1; + } + } + + @keyframes dialog-fade-out { + 0% { + transform: translate3d(0, 0, 0); + opacity: 1; + } + 100% { + transform: translate3d(0, -20px, 0); + opacity: 0; + } + } +} diff --git a/packages/theme-default/src/dropdown.css b/packages/theme-default/src/dropdown.css new file mode 100644 index 000000000..d4be874c6 --- /dev/null +++ b/packages/theme-default/src/dropdown.css @@ -0,0 +1,65 @@ +@charset "UTF-8"; +@import "./common/var.css"; +@import "./button.css"; + +@component-namespace el { + @b dropdown { + display: inline-block; + position: relative; + color: #475669; + font-size: var(--font-size-base); + + .el-button-group { + display: block; + } + + @e menu{ + margin: 5px 0; + background-color: #fff; + border: 1px solid #D3DCE6; + box-shadow: var(--dropdown-menu-box-shadow); + padding: 6px 0; + z-index: 10; + position: absolute; + top: 0; + left: 0; + min-width: 100px; + + & li { + list-style: none; + line-height: 36px; + padding: 0 10px; + margin: 0; + cursor: pointer; + + &:hover { + background-color: var(--dropdown-menuItem-hover-fill); + color: var(--dropdown-menuItem-hover-color); + } + &.divider { + margin-top: 6px; + border-top: 1px solid #D3DCE6; + } + &.divider:last-child { + margin-bottom: -6px; + } + } + } + @e icon-button { + padding: * 5px; + + & .el-dropdown__icon { + padding-left: 0; + } + } + @e icon { + padding-left: 5px; + transform: scale(.8, .8); + } + @m text { + .el-button-text { + padding: 0; + } + } + } +} diff --git a/packages/theme-default/src/fonts/element-icons.svg b/packages/theme-default/src/fonts/element-icons.svg new file mode 100755 index 000000000..5a3ec43c2 --- /dev/null +++ b/packages/theme-default/src/fonts/element-icons.svg @@ -0,0 +1,47 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/theme-default/src/fonts/element-icons.ttf b/packages/theme-default/src/fonts/element-icons.ttf new file mode 100755 index 000000000..87d83d546 Binary files /dev/null and b/packages/theme-default/src/fonts/element-icons.ttf differ diff --git a/packages/theme-default/src/fonts/element-icons.woff b/packages/theme-default/src/fonts/element-icons.woff new file mode 100755 index 000000000..ae127ac0c Binary files /dev/null and b/packages/theme-default/src/fonts/element-icons.woff differ diff --git a/packages/theme-default/src/form.css b/packages/theme-default/src/form.css new file mode 100644 index 000000000..0b44df059 --- /dev/null +++ b/packages/theme-default/src/form.css @@ -0,0 +1,88 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + @b form { + @when label-left { + & .el-form-item__label { + text-align: left; + } + } + } + @b form-item { + margin-bottom: 22px; + @utils-clearfix; + + & .el-form-item { + margin-bottom: 0; + + & .el-form-item__content { + margin-left: 0 !important; + } + } + + @e label { + text-align: right; + vertical-align: middle; + float: left; + font-size: 14px; + color: #5e6d82; + line-height: 1; + padding: 11px 12px 11px 0; + box-sizing: border-box; + } + @e content { + line-height: 36px; + position: relative; + font-size: 14px; + @utils-clearfix; + } + @e error { + color: #ff4949; + font-size: 12px; + line-height: 1; + padding-top: 4px; + position: absolute; + top: 100%; + left: 0; + } + & .el-button + .el-button, + & .el-checkbox + .el-checkbox, + & .el-radio + .el-radio { + margin-left: 10px; + } + + @when required { + .el-form-item__label:before { + content: '*'; + color: var(--color-danger); + margin-right: 4px; + } + } + + @when error { + & .el-input__inner, + & .el-textarea__inner { + border-color: var(--color-danger); + } + } + } + + @b form-inline { + & .el-form-item { + display: inline-block; + margin-right: 10px; + + > * { + vertical-align: top; + } + } + } + @b form-stacked { + & .el-form-item__label { + float: none; + display: inline-block; + padding: 0 0 10px 0; + } + } +} diff --git a/packages/theme-default/src/icon.css b/packages/theme-default/src/icon.css new file mode 100644 index 000000000..7256b7eca --- /dev/null +++ b/packages/theme-default/src/icon.css @@ -0,0 +1,151 @@ +@font-face { + font-family: 'element-icons'; + src: + url('fonts/element-icons.ttf?deq2r7') format('truetype'), + url('fonts/element-icons.woff?deq2r7') format('woff'), + url('fonts/element-icons.svg?deq2r7#element-icons') format('svg'); + font-weight: normal; + font-style: normal; +} + +[class^="el-icon-"], [class*=" el-icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'element-icons' !important; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + vertical-align: bottom; + display: inline-block; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.el-icon-search:before { + content: "\e90e"; +} +.el-icon-share:before { + content: "\e900"; +} +.el-icon-setting:before { + content: "\e901"; +} +.el-icon-circle-cross:before { + content: "\e902"; +} +.el-icon-warning:before { + content: "\e903"; +} +.el-icon-information:before { + content: "\e904"; +} +.el-icon-circle-check:before { + content: "\e905"; +} +.el-icon-delete:before { + content: "\e906"; +} +.el-icon-d-arrow-left:before { + content: "\e907"; +} +.el-icon-d-arrow-right:before { + content: "\e908"; +} +.el-icon-picture:before { + content: "\e909"; +} +.el-icon-upload:before { + content: "\e90a"; +} +.el-icon-menu:before { + content: "\e90b"; +} +.el-icon-time:before { + content: "\e90c"; +} +.el-icon-circle-close:before { + content: "\e90d"; +} +.el-icon-arrow-down:before { + content: "\e90f"; +} +.el-icon-arrow-up:before { + content: "\e910"; +} +.el-icon-arrow-right:before { + content: "\e911"; +} +.el-icon-arrow-left:before { + content: "\e912"; +} +.el-icon-close:before { + content: "\e913"; +} +.el-icon-document:before { + content: "\e914"; +} +.el-icon-d-caret:before { + content: "\e915"; +} +.el-icon-date:before { + content: "\e916"; +} +.el-icon-message:before { + content: "\e917"; +} +.el-icon-loading:before { + content: "\e918"; +} +.el-icon-ellipsis:before { + content: "\e919"; +} +.el-icon-plus:before { + content: "\e91a"; +} +.el-icon-caret-left:before { + content: "\e91b"; +} +.el-icon-caret-right:before { + content: "\e91c"; +} +.el-icon-caret-bottom:before { + content: "\e91d"; +} +.el-icon-edit:before { + content: "\e91e"; +} +.el-icon-caret-top:before { + content: "\e91f"; +} +.el-icon-check:before { + content: "\e920"; +} +.el-icon-minus:before { + content: "\e921"; +} +.el-icon-angle-left:before { + content: "\f104"; +} +.el-icon-angle-right:before { + content: "\f105"; +} +.el-icon-truck:before { + content: "\e922"; +} + +.el-icon-loading { + animation: rotating 1s linear infinite; +} + +@keyframes rotating { + 0% { + transform: rotateZ(0deg); + } + 100% { + transform: rotateZ(360deg); + } +} diff --git a/packages/theme-default/src/index.css b/packages/theme-default/src/index.css new file mode 100644 index 000000000..f0900aa88 --- /dev/null +++ b/packages/theme-default/src/index.css @@ -0,0 +1,33 @@ +@import "./base.css"; +@import "./button.css"; +@import "./input.css"; +@import "./select.css"; +@import "./alert.css"; +@import "./notification.css"; +@import "./slider.css"; +@import "./checkbox.css"; +@import "./radio.css"; +@import "./switch.css"; +@import "./dropdown.css"; +@import "./dialog.css"; +@import "./table.css"; +@import "./pagination.css"; +@import "./popover.css"; +@import "./tooltip.css"; +@import "./autocomplete.css"; +@import "./message-box.css"; +@import "./date-picker.css"; +@import "./time-picker.css"; +@import "./time-select.css"; +@import "./input-number.css"; +@import "./tag.css"; +@import "./breadcrumb.css"; +@import "./form.css"; +@import "./tabs.css"; +@import "./progress.css"; +@import "./tree.css"; +@import "./menu.css"; +@import "./upload.css"; +@import "./row.css"; +@import "./col.css"; +@import "./spinner.css"; diff --git a/packages/theme-default/src/input-number.css b/packages/theme-default/src/input-number.css new file mode 100644 index 000000000..cc3c41f9b --- /dev/null +++ b/packages/theme-default/src/input-number.css @@ -0,0 +1,70 @@ +@charset "UTF-8"; +@import "./input.css"; +@import "./common/var.css"; + +@component-namespace el { + @b input-number { + display: inline-block; + + & .el-input__inner { + appearance: none; + } + & .el-input { + float: left; + margin-right: calc(-(var(--input-height) + 1px) * 2); + } + @e increase, decrease { + height: 100%; + border-left: var(--border-base); + width: var(--input-height); + line-height: var(--input-height); + top: 0; + text-align: center; + color: #99A9BF; + cursor: pointer; + float: left; + position: relative; + + &:hover { + color: var(--color-primary); + } + + @when disabled { + color: var(--disabled-border-base); + cursor: not-allowed; + } + } + + @when disabled { + & .el-input-number__increase, .el-input-number__decrease { + border-color: var(--disabled-border-base); + color: var(--disabled-border-base); + + &:hover { + color: var(--disabled-border-base); + cursor: not-allowed; + } + } + } + @when large { + & .el-input { + margin-right: calc(-(var(--input-large-height) + 1px) * 2); + } + & .el-input-number__increase, .el-input-number__decrease { + line-height: var(--input-large-height); + width: var(--input-large-height); + font-size: var(--input-large-font-size); + } + } + @when small { + & .el-input { + margin-right: calc(-(var(--input-small-height) + 1px) * 2); + } + & .el-input-number__increase, .el-input-number__decrease { + line-height: var(--input-small-height); + width: var(--input-small-height); + font-size: var(--input-small-font-size); + } + } + } +} diff --git a/packages/theme-default/src/input-recommend.css b/packages/theme-default/src/input-recommend.css new file mode 100644 index 000000000..f803bda50 --- /dev/null +++ b/packages/theme-default/src/input-recommend.css @@ -0,0 +1,29 @@ +@charset "UTF-8"; +@import "./common/var.css"; +/*@import "./core/dropdown.css";*/ +@import "./core/tag.css"; +@import "./core/input.css"; + +@component-namespace element { + @b input { + display: inline-block; + font-size: var(--input-font-size); + position: relative; + + @e placeholder { + background-color: #fff; + color: var(--input-border-color); + left: 4px; + padding: 0 2px; + position: absolute; + top: calc(var(--input-height) / 2 - var(--input-font-size) + 4); + transition: all 0.2s ease-out; + z-index: var(--index-normal); + + @when enter { + color: var(--input-border-color-hover); + top: calc((-var(--input-height) + var(--input-font-size)) / 2 + 1); + } + } + } +} diff --git a/packages/theme-default/src/input.css b/packages/theme-default/src/input.css new file mode 100644 index 000000000..8e6048fa3 --- /dev/null +++ b/packages/theme-default/src/input.css @@ -0,0 +1,199 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + @b input { + position: relative; + + @e inner { + display: block; + padding: 3px 10px; + box-sizing: border-box; + width: 100%; + height: var(--input-height); + font-size: var(--font-size-base); + color: var(--input-color); + background-color: #fff; + background-image: none; + border: var(--input-border); + border-radius: 4px; + transition: var(--border-transition-base); + outline: none; + + &::placeholder { + color: var(--input-placeholder-color); + } + + &:hover { + border-color: var(--input-hover-border); + } + &:focus { + outline: none; + border-color: var(--input-focus-border); + } + } + + @e icon { + position: absolute; + right: 10px; + top: 50%; + transform: translate(0, -50%); + color: var(--input-icon-color); + + & + .el-input__inner { + padding-right: 35px; + } + } + + @when active { + .el-input__inner { + outline: none; + border-color: var(--input-focus-border); + } + } + + @when disabled { + .el-input__inner { + background-color: var(--disabled-fill-base); + border-color: var(--disabled-border-base); + color: var(--disabled-color-base); + cursor: not-allowed; + + &::placeholder { + color: var(--input-disabled-placeholder-color); + } + } + } + + .el-icon-loading { + animation: validating-rotating 1s linear infinite; + } + } + + @b input-large { + & .el-input__inner { + font-size: var(--input-large-font-size); + height: var(--input-large-height); + } + } + @b input-small { + & .el-input__inner { + font-size: var(--input-small-font-size); + height: var(--input-small-height); + } + } + @b input-mini { + & .el-input__inner { + font-size: var(--input-mini-font-size); + height: var(--input-mini-height); + } + } + + @b input-group { + display: table; + + & .el-input { + vertical-align: middle; + display: table-cell; + } + @e label { + padding: 0 10px; + font-size: 13px; + } + @e prepend { + vertical-align: middle; + display: table-cell; + position: relative; + border: var(--border-base); + border-right: 0; + border-radius: 4px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + + & .el-dropdown--text { + padding: 0 10px; + } + } + @e append { + vertical-align: middle; + display: table-cell; + position: relative; + border: var(--border-base); + border-left: 0; + border-radius: 4px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + & .el-input:first-child { + .el-input__inner { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + } + & .el-input:last-child { + .el-input__inner { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + } + & .el-input:not(:first-child):not(:last-child) { + .el-input__inner { + border-radius: 0; + } + } + } + + @b textarea { + + @e inner { + display: block; + resize: vertical; + padding: 10px 5px; + box-sizing: border-box; + width: 100%; + min-height: 88px; + font-size: var(--font-size-base); + color: #666; + background-color: #fff; + background-image: none; + border: var(--input-border); + border-radius: 4px; + transition: var(--border-transition-base); + + &::placeholder { + color: var(--input-placeholder-color); + } + + &:hover { + border-color: var(--input-hover-border); + } + + &:focus { + outline: none; + border-color: var(--input-focus-border); + } + } + + @when disabled { + .el-textarea__inner { + background-color: var(--disabled-fill-base); + border-color: var(--disabled-border-base); + color: var(--disabled-color-base); + cursor: not-allowed; + + &::placeholder { + color: var(--input-disabled-placeholder-color); + } + } + } + } +} + +@keyframes validating-rotating { + 0% { + transform: translate(0, -50%) rotateZ(0deg); + } + 100% { + transform: translate(0, -50%) rotateZ(360deg); + } +} diff --git a/packages/theme-default/src/menu.css b/packages/theme-default/src/menu.css new file mode 100644 index 000000000..a1c525751 --- /dev/null +++ b/packages/theme-default/src/menu.css @@ -0,0 +1,159 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + @b menu { + height: 60px; + line-height: 60px; + background-color: #eff2f7; + list-style: none; + position: relative; + margin: 0; + padding-left: 0; + + @e active-bar { + position: absolute; + bottom: 0; + left: 0; + height: 5px; + background-color: var(--color-primary); + z-index: 1; + will-change: transform; + transition: transform .3s cubic-bezier(.645,.045,.355,1), width .3s cubic-bezier(.645,.045,.355,1); + } + @m vertical { + height: auto; + + & .el-menu-item { + float: none; + height: 56px; + line-height: 56px; + margin: 0; + padding-left: 20px; + cursor: pointer; + position: relative; + color: #475669; + + &:hover { + background-color: #d3dce6; + } + &.is-active .el-menu-item__bar { + display: none; + } + } + & .el-menu-item.is-active { + color: var(--color-primary); + } + } + @m dark { + background-color: #324057; + + & .el-menu-item, + & .el-submenu__title { + color: #c0ccda; + } + + &.el-menu--vertical { + & .el-menu-item, + & .el-submenu__title { + &:hover { + background-color: #475669; + } + &.is-active { + background-color: #5e6d82; + color: #fff; + } + } + } + + & .el-submenu .el-menu-item { + background-color: #1f2d3d; + color: #99a9bf; + + &:hover { + background-color: #475669; + } + &.is-active { + background-color: #5e6d82; + color: #fff; + } + } + } + } + @b menu-item { + font-size: 16px; + color: #475669; + float: left; + height: 100%; + padding: 0 10px; + margin: 0 10px; + cursor: pointer; + position: relative; + + @when active { + .el-menu-item__bar { + content: ''; + width: 100%; + height: 5px; + background-color: var(--color-primary); + bottom: 0; + left: 0; + position: absolute; + display: block; + } + } + & [class^="el-icon-"] { + vertical-align: baseline; + margin-right: 10px; + } + &:first-child { + margin-left: 0; + } + &:last-child { + margin-right: 0; + } + } + + @b submenu { + & .el-menu { + height: auto; + background-color: darken(#eff2f7, 5%); + } + & .el-menu-item { + padding-left: 46px; + } + @e title { + float: none; + height: 56px; + line-height: 56px; + margin: 0; + padding-left: 20px; + cursor: pointer; + position: relative; + color: #475669; + + &:hover { + background-color: #d3dce6; + } + & [class^="el-icon-"] { + vertical-align: baseline; + margin-right: 10px; + } + } + @e icon-arrow { + position: absolute; + margin: 0; + top: 50%; + right: 20px; + transform: rotateZ(180deg) translateY(-50%); + font-size: 12px; + transition: transform .3s ease-in-out; + transform-origin: center 0; + } + @when opened { + & .el-submenu__icon-arrow { + transform: translateY(-50%); + } + } + } +} diff --git a/packages/theme-default/src/message-box.css b/packages/theme-default/src/message-box.css new file mode 100644 index 000000000..cd19cc41a --- /dev/null +++ b/packages/theme-default/src/message-box.css @@ -0,0 +1,162 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + + @b message-box { + position: fixed; + top: 50%; + left: 50%; + transform: translate3d(-50%, -50%, 0); + background-color: #fff; + width: 420px; + border-radius: 3px; + font-size: 16px; + -webkit-user-select: none; + overflow: hidden; + backface-visibility: hidden; + + @e header { + padding: 20px 20px 0; + } + + @e content { + padding: 30px 20px; + color: #475669; + font-size: 14px; + position: relative; + } + + @e close { + display: inline-block; + position: absolute; + top: 19px; + right: 20px; + color: #999; + cursor: pointer; + line-height: 20px; + text-align: center; + } + + @e input { + & > input { + border: 1px solid #dedede; + border-radius: 5px; + padding: 4px 5px; + margin-top: 10px; + width: 100%; + box-sizing: border-box; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + outline: none; + } + } + + @e errormsg { + color: red; + font-size: 12px; + min-height: 16px; + } + + @e title { + padding-left: 0; + margin-bottom: 0; + font-size: 16px; + font-weight: bold; + height: 18px; + color: #333; + } + + @e message { + margin: 0; + + & p { + margin: 0; + line-height: 1.4; + } + } + + @e btns { + padding: 10px 20px 15px; + text-align: right; + + & button:nth-child(2) { + margin-left: 10px; + } + } + + @e btns-reverse { + flex-direction: row-reverse; + } + + @e status { + position: absolute; + top: 50%; + transform: translateY(-50%); + font-size: 36px !important; + + &.el-icon-circle-check { + color: #13ce66; + } + + &.el-icon-information { + color: #50bfff; + } + + &.el-icon-warning { + color: #f7ba2a; + } + + &.el-icon-circle-cross { + color: #ff4949; + } + } + } +} + +.msgbox-bounce-enter { + -webkit-animation: msgbox-bounce-in .3s cubic-bezier(0.3, 0, 0, 1.5); + animation: msgbox-bounce-in .3s cubic-bezier(0.3, 0, 0, 1.5); +} + +.msgbox-bounce-leave { + -webkit-animation: msgbox-bounce-out .2s cubic-bezier(0.895, 0.03, 0.685, 0.22); + animation: msgbox-bounce-out .2s cubic-bezier(0.895, 0.03, 0.685, 0.22); +} + +@keyframes msgbox-bounce-in { + 0% { + transform: translate3d(-50%, -50%, 0) scale(0.8); + } + 100% { + transform: translate3d(-50%, -50%, 0) scale(1); + } +} + +@keyframes msgbox-bounce-out { + 0% { + transform: translate3d(-50%, -50%, 0) scale(1); + } + 100% { + transform: translate3d(-50%, -50%, 0) scale(0.7); + } +} + +@-webkit-keyframes msgbox-bounce-in { + 0% { + -webkit-transform: translate3d(-50%, -50%, 0) scale(0.8); + } + 100% { + -webkit-transform: translate3d(-50%, -50%, 0) scale(1); + } +} + +@-webkit-keyframes msgbox-bounce-out { + 0% { + -webkit-transform: translate3d(-50%, -50%, 0) scale(1); + } + 100% { + -webkit-transform: translate3d(-50%, -50%, 0) scale(0.7); + } +} diff --git a/packages/theme-default/src/mixins/_button.css b/packages/theme-default/src/mixins/_button.css new file mode 100644 index 000000000..55288e9e4 --- /dev/null +++ b/packages/theme-default/src/mixins/_button.css @@ -0,0 +1,49 @@ +@define-mixin button-variant $color, $background-color, $border-color { + color: $color; + background-color: $background-color; + border-color: $border-color; + + &:hover { + background: tint($background-color, var(--button-hover-tint-percent)); + border-color: tint($border-color, var(--button-hover-tint-percent)); + color: $color; + } + + &:active { + background: shade($background-color, var(--button-active-shade-percent)); + border-color: shade($border-color, var(--button-active-shade-percent)); + color: $color; + outline: none; + } + + &.is-active { + background: shade($background-color, var(--button-active-shade-percent)); + border-color: shade($border-color, var(--button-active-shade-percent)); + color: $color; + } + + &.is-plain { + background: var(--button-default-fill); + border: var(--border-base); + color: var(--button-default-color); + + &:hover { + background: #fff; + border-color: $border-color; + color: $background-color; + } + + &:active { + background: #fff; + border-color: shade($border-color, var(--button-active-shade-percent)); + color: shade($background-color, var(--button-active-shade-percent)); + outline: none; + } + } +} + +@define-mixin button-size $padding-vertical, $padding-horizontal, $font-size, $border-radius { + padding: $padding-vertical $padding-horizontal; + font-size: $font-size; + border-radius: $border-radius; +} diff --git a/packages/theme-default/src/notification.css b/packages/theme-default/src/notification.css new file mode 100644 index 000000000..3a03d56c5 --- /dev/null +++ b/packages/theme-default/src/notification.css @@ -0,0 +1,78 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + + @b notification { + width: 330px; + padding: 20px; + box-sizing: border-box; + border-radius: 2px; + position: fixed; + right: 16px; + background-color: #fff; + box-shadow: 0 0 6px rgba(0, 0, 0, .04), 0 2px 4px rgba(0, 0, 0, .12); + transition: opacity 0.3s, transform .3s, right .3s, top 0.4s; + overflow: hidden; + z-index: 1000; + + @e group { + & span { + font-size: 16px; + color: #1f2d3d; + } + + & p { + font-size: 14px; + line-height: 21px; + margin: 10px 0 0 0; + color: #8492a6; + text-align: justify; + } + } + + @e icon { + size: 40px; + font-size: 40px; + float: left; + position: relative; + top: 3px; + } + + @e closeBtn { + position: absolute 20px 20px * *; + cursor: pointer; + color: #C0CCDA; + font-size: 14px; + + &:hover { + color: #99A9BF; + } + } + + & .el-icon-circle-check { + color: #13ce66; + } + + & .el-icon-circle-cross { + color: #ff4949; + } + + & .el-icon-information { + color: #50bfff; + } + + & .el-icon-warning { + color: #f7ba2a; + } + } + + .el-notification-fade-enter { + transform: translateX(100%); + right: 0; + } + + .el-notification-fade-leave { + opacity: 0; + } +} diff --git a/packages/theme-default/src/option-group.css b/packages/theme-default/src/option-group.css new file mode 100644 index 000000000..bd7bbc2e7 --- /dev/null +++ b/packages/theme-default/src/option-group.css @@ -0,0 +1,22 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + + @b select-group { + margin: 0; + padding: 0; + + @e title { + padding-left: 10px; + font-size: 12px; + color: #999; + height: 30px; + line-height: 30px; + } + + & .el-select-dropdown__item { + padding-left: 20px; + } + } +} diff --git a/packages/theme-default/src/option.css b/packages/theme-default/src/option.css new file mode 100644 index 000000000..48f545dd0 --- /dev/null +++ b/packages/theme-default/src/option.css @@ -0,0 +1,47 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + + @b select-dropdown { + @e item { + font-size: 14px; + padding: 8px 10px; + position: relative; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: #475669; + height: 36px; + line-height: 1.5; + box-sizing: border-box; + cursor: pointer; + + @when disabled { + color: #c0ccda; + cursor: not-allowed; + + &:hover { + background-color: #fff; + } + } + + &.hover { + background-color: #e5e9f2; + } + + &.selected { + color: #fff; + background-color: #20A0FF; + + &.hover { + background-color: #1D8CE0; + } + } + + & span { + line-height: 1.5 !important; + } + } + } +} diff --git a/packages/theme-default/src/pagination.css b/packages/theme-default/src/pagination.css new file mode 100644 index 000000000..e724f94c7 --- /dev/null +++ b/packages/theme-default/src/pagination.css @@ -0,0 +1,196 @@ +@charset "UTF-8"; +@import "./select.css"; +@import "./common/var.css"; + +@component-namespace el { + @b pagination { + white-space: nowrap; + padding: 2px 5px; + background: var(--pagination-fill); + color: var(--pagination-color); + + span, + button { + display: inline-block; + font-size: var(--pagination-font-size); + min-width: var(--pagination-button-size); + height: var(--pagination-button-size); + line-height: var(--pagination-button-size); + vertical-align: top; + box-sizing: border-box; + } + + button { + border: none; + padding: 0 6px; + background: transparent; + + &:focus { + outline: none; + } + + &:hover { + color: var(--pagination-hover-fill); + } + + &.disabled { + color: var(--pagination-button-disabled-color); + background-color: var(--pagination-button-disabled-fill); + cursor: not-allowed; + } + } + + .btn-prev, + .btn-next { + background: center center no-repeat; + background-size: 16px; + border: 1px solid var(--pagination-border-color); + cursor: pointer; + margin: 0; + color: var(--pagination-button-color); + + .el-icon { + display: block; + font-size: 12px; + } + } + + .btn-prev { + border-radius: var(--pagination-border-radius) 0 0 var(--pagination-border-radius); + border-right: 0; + } + + .btn-next { + border-radius: 0 var(--pagination-border-radius) var(--pagination-border-radius) 0; + border-left: 0; + } + + @m small { + .btn-prev, + .btn-next, + .el-pager li, + .el-pager li:last-child { + border-color: transparent; + font-size: 12px; + line-height: 22px; + height: 22px; + min-width: 22px; + } + + .arrow.disabled { + visibility: hidden; + } + + .el-pager li { + border-radius: var(--pagination-border-radius); + } + } + + @e sizes { + margin: 0 10px 0 0; + + .el-input .el-input__inner { + font-size: 13px; + border-color: var(--pagination-border-color); + + &:hover { + border-color: var(--pagination-hover-fill); + } + } + } + + @e jump { + margin-left: 10px; + } + + @e total { + margin-left: 10px; + } + + @e rightwrapper { + float: right; + } + + @e editor { + border: 1px solid var(--pagination-border-color); + border-radius: var(--pagination-border-radius); + line-height: 18px; + padding: 4px 2px; + width: 30px; + text-align: center; + margin: 0 6px; + box-sizing: border-box; + transition: border .3s; + + &::-webkit-inner-spin-button, + &::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; + } + + &:focus { + outline: none; + border-color: var(--pagination-hover-fill); + }; + } + } + + @b pager { + user-select: none; + list-style: none; + display: inline-block; + vertical-align: top; + font-size: 0; + padding: 0; + + li { + padding: 0 4px; + border: 1px solid var(--pagination-border-color); + border-right: 0; + background: var(--pagination-fill); + vertical-align: top; + display: inline-block; + font-size: var(--pagination-font-size); + min-width: var(--pagination-button-size); + height: var(--pagination-button-size); + line-height: var(--pagination-button-size); + cursor: pointer; + box-sizing: border-box; + text-align: center; + + &:last-child { + border-right: 1px solid var(--pagination-border-color); + } + + &.btn-quicknext, + &.btn-quickprev { + line-height: 28px; + color: var(--pagination-button-color); + } + + &.btn-quickprev:hover { + cursor: pointer; + } + + &.btn-quicknext:hover { + cursor: pointer; + } + + &.active + li { + border-left: 0; + padding-left: 5px; + } + + &:hover { + color: var(--pagination-hover-fill); + } + + &.active { + border-color: var(--pagination-hover-fill); + background-color: var(--pagination-hover-fill); + color: var(--pagination-hover-color); + cursor: default; + } + } + } +} diff --git a/packages/theme-default/src/popover.css b/packages/theme-default/src/popover.css new file mode 100644 index 000000000..ff0b99434 --- /dev/null +++ b/packages/theme-default/src/popover.css @@ -0,0 +1,120 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + @b popover { + position: absolute; + background: var(--popover-fill); + min-width: 150px; + border-radius: 2px; + border: 1px solid var(--popover-border-color); + padding: var(--popover-padding); + z-index: var(--index-popper); + font-size: var(--popover-font-size); + box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, .12), + 0px 0px 6px 0px rgba(0, 0, 0, .04); + + .popper__arrow, + .popper__arrow::after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + } + + .popper__arrow { + border-width: var(--popover-arrow-size); + } + + .popper__arrow::after { + content: " "; + border-width: var(--popover-arrow-size); + } + + &[x-placement^="top"] { + margin-bottom: calc(var(--popover-arrow-size) + 6); + } + + &[x-placement^="top"] .popper__arrow { + bottom: -var(--popover-arrow-size); + left: 50%; + margin-right: calc(var(--tooltip-arrow-size) / 2); + border-top-color: var(--popover-border-color); + border-bottom-width: 0; + + &::after { + bottom: 1px; + margin-left: -var(--popover-arrow-size); + border-top-color: var(--popover-fill); + border-bottom-width: 0; + } + } + + &[x-placement^="bottom"] { + margin-top: calc(var(--popover-arrow-size) + 6); + } + + &[x-placement^="bottom"] .popper__arrow { + top: -var(--popover-arrow-size); + left: 50%; + margin-right: calc(var(--tooltip-arrow-size) / 2); + border-top-width: 0; + border-bottom-color: var(--popover-border-color); + + &::after { + top: 1px; + margin-left: -var(--popover-arrow-size); + border-top-width: 0; + border-bottom-color: var(--popover-fill); + } + } + + &[x-placement^="right"] { + margin-left: calc(var(--popover-arrow-size) + 6); + } + + &[x-placement^="right"] .popper__arrow { + top: 50%; + left: -var(--popover-arrow-size); + margin-bottom: calc(var(--tooltip-arrow-size) / 2); + border-right-color: var(--popover-border-color); + border-left-width: 0; + + &::after { + bottom: -var(--popover-arrow-size); + left: 1px; + border-right-color: var(--popover-fill); + border-left-width: 0; + } + } + + &[x-placement^="left"] { + margin-right: calc(var(--popover-arrow-size) + 6); + } + + &[x-placement^="left"] .popper__arrow { + top: 50%; + right: -var(--popover-arrow-size); + margin-bottom: calc(var(--tooltip-arrow-size) / 2); + border-right-width: 0; + border-left-color: var(--popover-border-color); + + &::after { + right: 1px; + bottom: -var(--popover-arrow-size); + margin-left: -var(--popover-arrow-size); + border-right-width: 0; + border-left-color: var(--popover-fill); + } + } + + @e title { + color: var(--popover-title-color); + font-size: var(--popover-title-font-size); + line-height: 1; + margin-bottom: 9px; + } + } +} diff --git a/packages/theme-default/src/progress.css b/packages/theme-default/src/progress.css new file mode 100644 index 000000000..ca79794e2 --- /dev/null +++ b/packages/theme-default/src/progress.css @@ -0,0 +1,59 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + @b progress { + height: 4px; + border-radius: 2px; + background-color: #e5e9f2; + overflow: hidden; + position: relative; + + @e bar { + position: absolute; + left: 0; + top: 0; + height: 100%; + border-radius: 2px 0 0 2px; + } + @m green { + .el-progress__bar { + background-color: var(--color-success); + } + } + @m blue { + .el-progress__bar { + background-color: var(--color-primary); + } + } + @m green-stripe { + .el-progress__bar { + background: linear-gradient(45deg, var(--color-success) 25%, #5adc94 25%, #5adc94 75%, var(--color-success) 75%); + background-size: 16px 100%; + animation: progress 1.5s linear infinite; + } + } + @m blue-stripe { + .el-progress__bar { + background: linear-gradient(45deg, var(--color-primary) 25%, #63bcff 25%, #63bcff 75%, var(--color-primary) 75%); + background-size: 16px 100%; + animation: progress 1.5s linear infinite; + } + } + @m large { + height: 6px; + } + @m small { + height: 2px; + } + } +} + +@keyframes progress { + 0% { + background-position: 0 0; + } + 100% { + background-position: 32px 0; + } +} diff --git a/packages/theme-default/src/radio.css b/packages/theme-default/src/radio.css new file mode 100644 index 000000000..158356867 --- /dev/null +++ b/packages/theme-default/src/radio.css @@ -0,0 +1,217 @@ +@charset "UTF-8"; +@import "./common/var.css"; +@import './mixins/button'; + +@component-namespace el { + + @b radio { + color: var(--radio-color); + position: relative; + cursor: pointer; + display: inline-block; + white-space: nowrap; + @utils-user-select none; + + & + .el-radio { + margin-left: 30px; + } + + @e input { + white-space: nowrap; + cursor: pointer; + outline: none; + display: inline-block; + line-height: 1; + position: relative; + vertical-align: middle; + } + @e inner { + border: var(--radio-input-border); + border-radius: var(--radio-input-border-radius); + circle: var(--radio-input-width) var(--radio-input-fill); + position: relative; + cursor: pointer; + display: inline-block; + box-sizing: border-box; + + &:not(.is-disabled):hover { + border-color: var(--radio-input-border-color-hover); + } + + &::after { + circle: 6px #fff; + content: ""; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%) scale(0); + transition: transform .15s cubic-bezier(.71,-.46,.88,.6); + } + + @when disabled { + background-color: var(--radio-disabled-input-fill); + border-color: var(--radio-disabled-input-border-color); + cursor: not-allowed; + + &::after { + cursor: not-allowed; + background-color: var(--radio-disabled-icon-color); + } + + & + .el-radio__label { + cursor: not-allowed; + } + } + + @when checked { + border-color: var(--radio-checked-input-border-color); + background: var(--radio-checked-icon-color); + + &::after { + transform: translate(-50%, -50%) scale(1); + } + } + + @when focus { + border-color: var(--radio-input-border-color-hover); + } + + &.is-disabled.is-checked { + background-color: var(--radio-disabled-checked-input-fill); + border-color: var(--radio-disabled-checked-input-border-color); + + &::after { + background-color: var(--radio-disabled-checked-icon-color); + } + } + } + + @e original { + opacity: 0; + outline: none; + position: absolute; + z-index: -1; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: 0; + } + + @e label { + font-size: var(--radio-font-size); + padding-left: 5px; + } + } + + @b radio-group { + display: inline-block; + + & .el-radio { + font-size: var(--radio-font-size); + } + + } + @b radio-button { + position: relative; + overflow: hidden; + display: inline-block; + + @e inner { + display: inline-block; + line-height: 1; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + background: var(--button-default-fill); + border: var(--border-base); + color: var(--button-default-color); + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: none; + margin: 0; + position: relative; + cursor: pointer; + transition: var(--border-transition-base), var(--color-transition-base); + @utils-user-select none; + + @mixin button-size var(--button-padding-vertical), var(--button-padding-horizontal), var(--button-font-size), 0; + + &:hover { + color: var(--color-primary); + } + + & .el-icon-right { + margin-left: 5px; + } + & .el-icon-left { + margin-right: 5px; + } + + & [class*="el-icon-"] { + line-height: 0.9; + + & + span { + margin-left: 5px; + } + } + } + + @e orig-radio { + opacity: 0; + outline: none; + position: absolute; + z-index: -1; + left: -999px; + + &:checked { + & + .el-radio-button__inner { + z-index: 1; + color: var(--color-primary); + border-color: @color; + } + } + + &:disabled { + & + .el-radio-button__inner { + z-index: -1; + color: var(--button-disabled-color); + cursor: not-allowed; + background-image: none; + background-color: var(--button-disabled-fill); + border-color: var(--button-disabled-border); + } + } + } + + &:not(:last-child) { + margin-right: -1px; + } + &:first-child { + .el-radio-button__inner { + border-radius: var(--border-radius-base) 0 0 var(--border-radius-base); + } + } + &:last-child { + .el-radio-button__inner { + border-radius: 0 var(--border-radius-base) var(--border-radius-base) 0; + } + } + } + @b radio-button-large { + & .el-radio-button__inner { + @mixin button-size var(--button-large-padding-vertical), var(--button-large-padding-horizontal), var(--button-large-font-size), 0; + } + } + @b radio-button-small { + & .el-radio-button__inner { + @mixin button-size var(--button-small-padding-vertical), var(--button-small-padding-horizontal), var(--button-small-font-size), 0; + } + } + @b radio-button-mini { + & .el-radio-button__inner { + @mixin button-size var(--button-mini-padding-vertical), var(--button-mini-padding-horizontal), var(--button-mini-font-size), 0; + } + } +} diff --git a/packages/theme-default/src/row.css b/packages/theme-default/src/row.css new file mode 100644 index 000000000..186ebb2c2 --- /dev/null +++ b/packages/theme-default/src/row.css @@ -0,0 +1,10 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + @b row { + position: relative; + box-sizing: border-box; + @utils-clearfix; + } +} diff --git a/packages/theme-default/src/select-dropdown.css b/packages/theme-default/src/select-dropdown.css new file mode 100644 index 000000000..d887089df --- /dev/null +++ b/packages/theme-default/src/select-dropdown.css @@ -0,0 +1,16 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + + @b select-dropdown { + width: 100%; + position: absolute; + z-index: 1001; + border: solid 1px #d3dce6; + border-radius: 2px; + background-color: #fff; + box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04); + box-sizing: border-box; + } +} diff --git a/packages/theme-default/src/select.css b/packages/theme-default/src/select.css new file mode 100644 index 000000000..fc51a0d5e --- /dev/null +++ b/packages/theme-default/src/select.css @@ -0,0 +1,179 @@ +@charset "UTF-8"; +@import "./common/var.css"; +@import "./select-dropdown.css"; +@import "./input.css"; +@import "./tag.css"; +@import "./option.css"; +@import "./option-group.css"; + +@component-namespace el { + + @b select { + display: inline-block; + position: relative; + + @when small { + & input { + border-radius: 2px; + height: 28px; + } + } + + &:hover { + .el-input__inner { + border-color: #8492a6; + } + } + + & .el-select-dropdown { + margin: 5px 0; + + & p.el-select-dropdown__nodata { + padding: 10px 0; + margin: 0; + text-align: center; + color: #999; + } + } + + & .el-select-dropdown__list { + list-style: none; + padding: 6px 0; + margin: 0; + width: 100%; + max-height: 274px; + box-sizing: border-box; + overflow-y: auto; + } + + & .el-input { + display: inline-block; + + & .el-input__icon { + color: #c0ccda; + font-size: 12px; + transition: transform .3s; + transform: translateY(-50%) rotateZ(180deg); + line-height: 16px; + top: 50%; + cursor: pointer; + + @when reverse { + transform: translateY(-50%); + } + + @when show-close { + transition: 0s; + size: 16px; + font-size: 14px; + right: 8px; + text-align: center; + transform: translateY(-50%) rotateZ(180deg); + border-radius: 50%; + color: #C0CCDA; + + &:hover { + color: #99A9BF; + } + } + } + + & .el-input__inner { + cursor: pointer; + + &:focus { + border-color: #2ea0ff; + } + } + + &.is-disabled { + & .el-input__inner { + cursor: not-allowed; + + &:hover { + border-color: #D3DCE6; + } + } + } + } + + @when multiple { + & .el-input { + width: 220px; + } + + & .el-select-dropdown__item.selected { + color: #20A0FF; + background-color: #fff; + + &.hover { + background-color: #E5E9F2; + } + + &::after { + position: absolute; + right: 10px; + font-family: 'element-icons'; + content: "\e920"; + } + } + } + + @e input { + border: none; + outline: none; + padding: 0; + margin: 4px 0 -3px 10px; + color: #666; + font-size: 14px; + appearance: none; + height: 28px; + background-color: transparent; + } + + @e close { + cursor: pointer; + position: absolute; + top: 8px; + z-index: var(--index-top); + right: 25px; + color: #c0ccda; + line-height: 18px; + font-size: 12px; + + &:hover { + color: #99A9BF; + } + } + + @e tags { + position: absolute; + z-index: var(--index-top); + } + + & .el-tag__close { + margin-top: -2px; + } + + & .el-tag { + height: 24px; + line-height: 24px; + box-sizing: border-box; + margin: 6px 0 0 6px; + } + + @e tag { + display: inline-block; + height: 24px; + line-height: 24px; + font-size: 14px; + border-radius: 4px; + color: #fff; + background-color: #20a0ff; + + & .el-icon-close { + font-size: 12px; + } + } + } +} diff --git a/packages/theme-default/src/slider.css b/packages/theme-default/src/slider.css new file mode 100644 index 000000000..0e2531f59 --- /dev/null +++ b/packages/theme-default/src/slider.css @@ -0,0 +1,120 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + @b slider { + @e runway { + width: 100%; + height: 4px; + margin: 20px 0; + background-color: #E5E9F2; + border-radius: 3px; + position: relative; + cursor: pointer; + vertical-align: middle; + + &.show-input { + margin-right: 160px; + width: auto; + } + } + + @e input { + float: right; + margin-top: -10px; + + & .el-input { + width: 130px; + } + } + + @e bar { + height: 4px; + background-color: #20A0FF; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + position: absolute; + } + + @e button-wrapper { + size: 36px; + position: absolute; + z-index: 1001; + top: -16px; + transform: translateX(-50%); + background-color: transparent; + } + + @e button { + size: 12px; + background-color: #20A0FF; + border-radius: 50%; + position: absolute; + top: 12px; + left: 12px; + cursor: pointer; + transition: .2s; + + &:hover, + &.hover, + &.dragging { + transform: scale(1.5); + background-color: #1d8ce0; + } + } + + @e pop { + @utils-user-select none; + font-size: 12px; + line-height: 26px; + text-align: center; + size: 26px; + border-radius: 50%; + background-color: #20A0FF; + color: #fff; + cursor: default; + z-index: var(--index-top); + + &::before { + triangle: 9px top #20A0FF; + position: absolute; + top: -14px; + left: 4px; + } + + &::after { + triangle: 9px bottom #20A0FF; + position: absolute; + bottom: -14px; + left: 4px; + } + + &.top::after { + content: ''; + } + + &.bottom::before { + content: ''; + } + } + + @e stop { + position: absolute; + size: 4px; + border-radius: 50%; + background-color: #c0ccda; + transform: translateX(-50%); + } + + .popper-fade-transition { + transition: transform .3s, opacity .3s; + transform-origin: center bottom; + } + + .popper-fade-enter, + .popper-fade-leave { + transform: scale(0.1); + opacity: 0; + } + } +} diff --git a/packages/theme-default/src/spinner.css b/packages/theme-default/src/spinner.css new file mode 100644 index 000000000..6d9d3a0c3 --- /dev/null +++ b/packages/theme-default/src/spinner.css @@ -0,0 +1,41 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + @b spinner { + display: inline-block; + vertical-align: middle; + } + @b spinner-inner { + animation: rotate 2s linear infinite; + width: 50px; + height: 50px; + + & .path { + stroke: #ececec; + stroke-linecap: round; + animation: dash 1.5s ease-in-out infinite; + } + + } +} +@keyframes rotate { + 100% { + transform: rotate(360deg); + } +} + +@keyframes dash { + 0% { + stroke-dasharray: 1, 150; + stroke-dashoffset: 0; + } + 50% { + stroke-dasharray: 90, 150; + stroke-dashoffset: -35; + } + 100% { + stroke-dasharray: 90, 150; + stroke-dashoffset: -124; + } +} diff --git a/packages/theme-default/src/switch.css b/packages/theme-default/src/switch.css new file mode 100644 index 000000000..f340376e8 --- /dev/null +++ b/packages/theme-default/src/switch.css @@ -0,0 +1,123 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + + @b switch { + display: inline-block; + position: relative; + line-height: 20px; + font-size: 14px; + @when disabled { + & .el-switch__core, + & .el-switch__label { + cursor: not-allowed; + } + } + + @e label { + position: absolute; + z-index: 10; + size: 46px 22px; + left: 0; + top: 0; + display: inline-block; + font-size: 14px; + cursor: pointer; + user-select: none; + @m left { + i { + left: 6px; + } + } + @m right { + i { + right: 6px; + } + } + & * { + line-height: 1; + top: 4px; + position: absolute; + font-size: 14px; + display: inline-block; + color: var(--color-white); + } + } + + @e input { + &:checked + .el-switch__core { + border-color: var(--switch-on-color); + background-color: var(--switch-on-color); + } + } + + @e core { + margin: 0; + display: inline-block; + vertical-align: middle; + position: relative; + size: 46px 22px; + border: 1px solid var(--switch-off-color); + outline: none; + border-radius: 12px; + box-sizing: border-box; + background: var(--switch-off-color); + cursor: pointer; + transition: border-color .3s, background-color .3s; + + & .el-switch__button { + position: absolute 0 * * 0; + border-radius: 15px; + transition: transform .3s; + size: 16px; + z-index: 100; + background-color: var(--color-white); + } + } + + @when disabled { + .el-switch__core { + border-color: var(--switch-disabled-color); + background: var(--switch-disabled-color); + + & span { + background-color: var(--switch-disabled-text-color); + } + + & ~ .el-switch__label * { + color: var(--switch-disabled-text-color); + } + } + + .el-switch__input:checked + .el-switch__core { + border-color: var(--switch-disabled-color); + background-color: var(--switch-disabled-color); + } + } + + @modifier wide { + .el-switch__label { + &.el-switch__label--left { + span { + left: 10px; + } + } + &.el-switch__label--right { + span { + right: 10px; + } + } + } + } + + & .label-fade-transition { + transition: .2s; + } + + & .label-fade-enter, + & .label-fade-leave { + opacity: 0; + } + } +} diff --git a/packages/theme-default/src/table.css b/packages/theme-default/src/table.css new file mode 100644 index 000000000..74d846218 --- /dev/null +++ b/packages/theme-default/src/table.css @@ -0,0 +1,298 @@ +@charset "UTF-8"; +@import "./checkbox.css"; +@import "./tag.css"; +@import "./common/var.css"; + +@component-namespace el { + + @b table { + position: relative; + overflow: hidden; + box-sizing: border-box; + width: 100%; + max-width: 100%; + background-color: #fff; + border-collapse: collapse; + border: 1px solid var(--table-border-color); + font-size: 14px; + color: var(--table-text-color); + + &::before { + content: ''; + position: absolute; + left: 0; + bottom: 0; + width: 100%; + height: 1px; + background-color: var(--table-border-color); + z-index: 1; + } + + &::after { + content: ''; + position: absolute; + top: 0; + right: 0; + width: 1px; + height: 100%; + background-color: var(--table-border-color); + z-index: 1; + } + + @modifier fit { + border-right: 0; + border-bottom: 0; + + & th.gutter, td.gutter { + border-right-width: 1px; + } + } + + & th, td { + height: 20px; + max-width: 250px; + min-width: 0; + box-sizing: border-box; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: middle; + position: relative; + border-bottom: 1px solid var(--table-border-color); + } + + @modifier border { + & th, td { + border-right: 1px solid var(--table-border-color); + } + } + + & th { + background-color: var(--table-header-background); + text-align: left; + } + + & th > div { + display: inline-block; + padding-left: 18px; + line-height: 40px; + box-sizing: border-box; + } + + & td > div { + box-sizing: border-box; + } + + & td > div > div { + width: inherit; + } + + & td div { + display: block; + } + + @e fixed { + position: absolute; + top: 0; + left: 0; + box-shadow: 1px 0 8px #d3d4d6; + + &::before { + content: ''; + position: absolute; + left: 0; + bottom: 0; + width: 100%; + height: 1px; + background-color: var(--table-border-color); + z-index: 4; + } + } + + @e fixed-header-wrapper { + position: absolute; + left: 0; + top: 0; + z-index: 3; + + & thead div { + background-color: var(--table-header-background); + color: var(--table-text-color); + } + } + + @e fixed-body-wrapper { + position: absolute; + left: 0; + top: 37px; + overflow: hidden; + z-index: 3; + } + + @e header-wrapper, body-wrapper { + width: 100%; + } + + @e header, body { + table-layout: fixed; + } + + @e header-wrapper { + overflow: hidden; + + & thead div { + background-color: var(--table-header-background); + color: var(--table-text-color); + } + } + + @e body-wrapper { + overflow: auto; + position: relative; + } + + & th.required > div::before { + display: inline-block; + content: ""; + width: 8px; + height: 8px; + border-radius: 50%; + background: #ff4d51; + margin-right: 5px; + vertical-align: middle; + } + + & th > .cell { + position: relative; + word-wrap: normal; + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + line-height: 20px; + vertical-align: middle; + width: 100%; + box-sizing: border-box; + } + + & div.caret-wrapper { + position: absolute; + top: 50%; + transform: translateY(-50%); + right: 10px; + width: 10px; + height: 12px; + padding: 0; + } + + & .sort-caret { + display: inline-block; + width: 0; + height: 0; + border: 0; + content: ""; + position: absolute; + z-index: 2; + + &.ascending { + top: 0; + border-top: none; + border-right: 5px solid transparent; + border-bottom: 5px solid #99A9BF; + border-left: 5px solid transparent; + } + + &.descending { + bottom: 0; + border-top: 5px solid #99A9BF; + border-right: 5px solid transparent; + border-bottom: none; + border-left: 5px solid transparent; + } + } + + & .ascending .sort-caret.ascending { + border-bottom-color: #475669; + } + + & .descending .sort-caret.descending { + border-top-color: #475669; + } + + & th.gutter, td.gutter { + width: 15px; + border-right-width: 0; + border-bottom-width: 0; + padding: 0; + } + + & td.gutter { + width: 0; + } + + & td .cell { + box-sizing: border-box; + overflow: hidden; + text-overflow: ellipsis; + line-height: 40px; + padding-left: 18px; + } + + & tr input[type="checkbox"] { + margin: 0; + } + + & tr { + background-color: #fff; + } + + @modifier striped { + & .el-table__body { + & tr:nth-child(2n) { + background: #FAFAFA; + } + } + } + + & tr.positive-row { + background: #E2F0E4; + } + + & tr.info-row { + background: #C9E5F5; + } + + & tr.warning-row { + background: #FEEED9; + } + + & tr.negative-row { + background: #F7D2D3; + } + + & tr.current-row { + background: #EFF7FF; + } + + @e column-resize-proxy { + position: absolute; + left: 200px; + top: 0; + bottom: 0; + width: 0; + border-left: 1px solid var(--table-border-color); + z-index: 10; + } + + & .hidden-columns { + visibility: hidden; + position: absolute; + z-index: -1; + } + + @e column-filter-label { + & i { + color: #99a9bf; + } + } + } +} diff --git a/packages/theme-default/src/tabs.css b/packages/theme-default/src/tabs.css new file mode 100644 index 000000000..f9c3c99a4 --- /dev/null +++ b/packages/theme-default/src/tabs.css @@ -0,0 +1,229 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + @b tabs { + display: inline-block; + + @e header { + border-bottom: 1px solid #d3dce6; + padding: 0; + position: relative; + margin: 0 0 15px; + @utils-clearfix; + } + @e active-bar { + position: absolute; + bottom: -1px; + left: 0; + height: 3px; + background-color: var(--color-primary); + z-index: 1; + transition: transform .3s cubic-bezier(.645,.045,.355,1); + list-style: none; + } + @e item { + padding: 0 16px; + height: 42px; + box-sizing: border-box; + line-height: @height; + float: left; + list-style: none; + font-size: 14px; + color: #8492a6; + margin-bottom: -1px; + position: relative; + + @when active { + color: var(--color-primary); + } + + &:hover { + color: #1f2d3d; + cursor: pointer; + } + } + @e content { + white-space: nowrap; + overflow: hidden; + position: relative; + } + @m card { + .el-tabs__active-bar { + display: none; + } + & .el-icon-close { + font-size: 12px; + vertical-align: middle; + line-height: 15px; + overflow: hidden; + width: 0; + height: 14px; + border-radius: 50%; + text-align: center; + transform-origin: 100% 50%; + transition: all .3s cubic-bezier(.645,.045,.355,1); + position: relative; + top: -1px; + right: -2px; + + &:before { + transform: scale(.7, .7); + display: inline-block; + } + + &:hover { + background-color: #99a9bf; + color: #fff; + } + } + .el-tabs__item { + border: 1px solid transparent; + transition: all .3s cubic-bezier(.645,.045,.355,1); + + &.is-closable { + &:hover { + padding: * 9px; + + & .el-icon-close { + width: 14px; + } + } + } + &.is-active { + border: 1px solid #d3dce6; + border-bottom-color: #fff; + border-radius: 4px 4px 0 0; + + &.is-closable { + padding: * 16px; + + .el-icon-close { + width: 14px; + } + } + } + } + } + @m border-card { + background: #fff; + border: 1px solid #d3dce6; + box-shadow: 0px 2px 4px 0px rgba(0,0,0,0.12), 0px 0px 6px 0px rgba(0,0,0,0.04); + + .el-tabs__content { + padding: 15px; + } + .el-tabs__header { + background-color: #eff2f7; + margin: 0; + } + .el-tabs__item { + transition: all .3s cubic-bezier(.645,.045,.355,1); + border: 1px solid transparent; + + &.is-active { + background-color: #fff; + border-right: 1px solid #d3dce6; + border-left: 1px solid #d3dce6; + margin: * -1px; + } + } + } + } + @b tab-pane { + width: 100%; + display: inline-block; + } +} + +.slideInRight-transition, +.slideInLeft-transition { + display: inline-block; +} +.slideInRight-enter { + animation: slideInRight-enter .3s; +} +.slideInRight-leave { + position: absolute; + left: 0; + right: 0; + animation: slideInRight-leave .3s; +} +.slideInLeft-enter { + animation: slideInLeft-enter .3s; +} +.slideInLeft-leave { + position: absolute; + left: 0; + right: 0; + animation: slideInLeft-leave .3s; +} + +@keyframes slideInRight-enter { + 0% { + opacity: 0; + -webkit-transform-origin: 0 0; + transform-origin: 0 0; + -webkit-transform: translateX(100%); + transform: translateX(100%) + } + + to { + opacity: 1; + -webkit-transform-origin: 0 0; + transform-origin: 0 0; + -webkit-transform: translateX(0); + transform: translateX(0) + } +} +@keyframes slideInRight-leave { + 0% { + -webkit-transform-origin: 0 0; + transform-origin: 0 0; + -webkit-transform: translateX(0); + transform: translateX(0); + opacity: 1 + } + + 100% { + -webkit-transform-origin: 0 0; + transform-origin: 0 0; + -webkit-transform: translateX(100%); + transform: translateX(100%); + opacity: 0 + } +} +@keyframes slideInLeft-enter { + 0% { + opacity: 0; + -webkit-transform-origin: 0 0; + transform-origin: 0 0; + -webkit-transform: translateX(-100%); + transform: translateX(-100%) + } + + to { + opacity: 1; + -webkit-transform-origin: 0 0; + transform-origin: 0 0; + -webkit-transform: translateX(0); + transform: translateX(0) + } +} +@keyframes slideInLeft-leave { + 0% { + -webkit-transform-origin: 0 0; + transform-origin: 0 0; + -webkit-transform: translateX(0); + transform: translateX(0); + opacity: 1 + } + + 100% { + -webkit-transform-origin: 0 0; + transform-origin: 0 0; + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + opacity: 0 + } +} diff --git a/packages/theme-default/src/tag.css b/packages/theme-default/src/tag.css new file mode 100644 index 000000000..504dfd43f --- /dev/null +++ b/packages/theme-default/src/tag.css @@ -0,0 +1,92 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + + @b tag { + background-color: var(--tag-fill); + display: inline-block; + padding: var(--tag-padding); + height: 22px; + line-height: @height; + font-size: var(--tag-font-size); + color: var(--tag-color); + border-radius: var(--tag-border-radius); + border: 1px solid var(--tag-border); + + @when hit { + border-color: var(--tag-primary-color); + } + + & .el-icon-close { + border-radius: 50%; + text-align: center; + position: relative; + cursor: pointer; + font-size: 12px; + transform: scale(.75, .75); + height: 18px; + width: 18px; + line-height: 18px; + vertical-align: middle; + top: -1px; + right: -2px; + + &:hover { + background-color: #fff; + color: var(--tag-fill); + } + } + + @m gray { + background-color: var(--tag-gray-fill); + border-color: var(--tag-gray-border); + color: var(--tag-gray-color); + + & .el-tag__close:hover { + background-color: var(--tag-gray-color); + color: #fff; + } + } + @m primary { + background-color: var(--tag-primary-fill); + border-color: var(--tag-primary-border); + color: var(--tag-primary-color); + + & .el-tag__close:hover { + background-color: var(--tag-primary-color); + color: #fff; + } + } + @m success { + background-color: var(--tag-success-fill); + border-color: var(--tag-success-border); + color: var(--tag-success-color); + + & .el-tag__close:hover { + background-color: var(--tag-success-color); + color: #fff; + } + } + @m warning { + background-color: var(--tag-warning-fill); + border-color: var(--tag-warning-border); + color: var(--tag-warning-color); + + & .el-tag__close:hover { + background-color: var(--tag-warning-color); + color: #fff; + } + } + @m danger { + background-color: var(--tag-danger-fill); + border-color: var(--tag-danger-border); + color: var(--tag-danger-color); + + & .el-tag__close:hover { + background-color: var(--tag-danger-color); + color: #fff; + } + } + } +} diff --git a/packages/theme-default/src/time-picker.css b/packages/theme-default/src/time-picker.css new file mode 100644 index 000000000..6676a18be --- /dev/null +++ b/packages/theme-default/src/time-picker.css @@ -0,0 +1,3 @@ +@import "../../date-picker/src/css/picker.css"; +@import "../../date-picker/src/css/time-spinner.css"; +@import "../../date-picker/src/css/time-picker.css"; diff --git a/packages/theme-default/src/time-select.css b/packages/theme-default/src/time-select.css new file mode 100644 index 000000000..949dadb87 --- /dev/null +++ b/packages/theme-default/src/time-select.css @@ -0,0 +1,43 @@ +@import "../../date-picker/src/css/picker.css"; +@import "../../date-picker/src/css/date-picker.css"; +@import "../../date-picker/src/css/vars.css"; + +.time-select { + min-width: 200px; + margin: 5px 0; + width: 100%; +} + +.time-select .el-picker-panel__content { + max-height: 200px; + overflow: hidden; + margin: 0; + + &:hover { + overflow-y: auto; + } +} + +.time-select-item { + padding: 8px 10px; + font-size: 14px; +} + +.time-select-item.selected:not(.disabled) { + background-color: var(--datepicker-active-color); + color: #fff; + + &:hover { + background-color: #1d8ce0; + } +} + +.time-select-item.disabled { + color: var(--datepicker-border-color); + cursor: not-allowed; +} + +.time-select-item:hover { + background-color: var(--datepicker-cell-hover-color); + cursor: pointer; +} diff --git a/packages/theme-default/src/tooltip.css b/packages/theme-default/src/tooltip.css new file mode 100644 index 000000000..ed462d1d6 --- /dev/null +++ b/packages/theme-default/src/tooltip.css @@ -0,0 +1,153 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + @b tooltip { + display: inline-block; + + @e rel { + display: inline-block; + position: relative; + } + + @e popper { + position: absolute; + border-radius: 4px; + padding: var(--tooltip-padding); + z-index: var(--index-popper); + font-size: var(--tooltip-font-size); + line-height: 1; + + .popper__arrow, + .popper__arrow::after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + } + + .popper__arrow { + border-width: var(--tooltip-arrow-size); + } + + .popper__arrow::after { + content: " "; + border-width: 5px; + } + + &[x-placement^="top"] { + margin-bottom: calc(var(--tooltip-arrow-size) + 6px); + } + + &[x-placement^="top"] .popper__arrow { + bottom: -var(--tooltip-arrow-size); + left: 50%; + margin-right: calc(var(--tooltip-arrow-size) / 2); + border-top-color: var(--tooltip-border-color); + border-bottom-width: 0; + + &::after { + bottom: 1px; + margin-left: -5px; + border-top-color: var(--tooltip-fill); + border-bottom-width: 0; + } + } + + &[x-placement^="bottom"] { + margin-top: calc(var(--tooltip-arrow-size) + 6px); + } + + &[x-placement^="bottom"] .popper__arrow { + top: -var(--tooltip-arrow-size); + left: 50%; + margin-right: calc(var(--tooltip-arrow-size) / 2); + border-top-width: 0; + border-bottom-color: var(--tooltip-border-color); + + &::after { + top: 1px; + margin-left: -5px; + border-top-width: 0; + border-bottom-color: var(--tooltip-fill); + } + } + + &[x-placement^="right"] { + margin-left: calc(var(--tooltip-arrow-size) + 6px); + } + + &[x-placement^="right"] .popper__arrow { + top: 50%; + left: -var(--tooltip-arrow-size); + margin-bottom: calc(var(--tooltip-arrow-size) / 2); + border-right-color: var(--tooltip-border-color); + border-left-width: 0; + + &::after { + bottom: -5px; + left: 1px; + border-right-color: var(--tooltip-fill); + border-left-width: 0; + } + } + + &[x-placement^="left"] { + margin-right: calc(var(--tooltip-arrow-size) + 6px); + } + + &[x-placement^="left"] .popper__arrow { + top: 50%; + right: -var(--tooltip-arrow-size); + margin-bottom: calc(var(--tooltip-arrow-size) / 2); + border-right-width: 0; + border-left-color: var(--tooltip-border-color); + + &::after { + right: 1px; + bottom: -5px; + margin-left: -5px; + border-right-width: 0; + border-left-color: var(--tooltip-fill); + } + } + + @when dark { + background: var(--tooltip-fill); + color: var(--tooltip-color); + } + + @when light { + background: var(--tooltip-color); + border: 1px solid var(--tooltip-fill); + + &[x-placement^="top"] .popper__arrow { + border-top-color: var(--tooltip-fill); + &::after { + border-top-color: var(--tooltip-color); + } + } + &[x-placement^="bottom"] .popper__arrow { + border-bottom-color: var(--tooltip-fill); + &::after { + border-bottom-color: var(--tooltip-color); + } + } + &[x-placement^="left"] .popper__arrow { + border-left-color: var(--tooltip-fill); + &::after { + border-left-color: var(--tooltip-color); + } + } + &[x-placement^="right"] .popper__arrow { + border-right-color: var(--tooltip-fill); + &::after { + border-right-color: var(--tooltip-color); + } + } + } + } + } +} diff --git a/packages/theme-default/src/tree.css b/packages/theme-default/src/tree.css new file mode 100644 index 000000000..312ac2f46 --- /dev/null +++ b/packages/theme-default/src/tree.css @@ -0,0 +1,87 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + @b tree { + overflow: auto; + cursor: default; + background: #ffffff; + border: 1px solid #d3dce6; + } + + @b tree-node { + white-space: nowrap; + + @e content { + line-height: 36px; + height: 36px; + cursor: pointer; + + & > input { + vertical-align: middle; + } + & > * { + margin-right: 8px; + } + &:hover { + background: #e5e9f2; + } + } + + @e expand-icon { + display: inline-block; + cursor: pointer; + width: 0; + height: 0; + vertical-align: middle; + margin-left: 10px; + border: 6px solid transparent; + border-right-width: 0; + border-left-color: #99a9bf; + border-left-width: 7px; + + transform: rotate(0deg); + transition: transform 0.3s ease-in-out; + + &:hover { + border-left-color: #999; + } + + &.expanded { + transform: rotate(90deg); + } + + &.is-leaf { + border-color: transparent; + cursor: default; + } + } + + @e label { + font-size: 14px; + vertical-align: middle; + display: inline-block; + } + + @e icon { + display: inline-block; + vertical-align: middle; + font-size: 14px; + color: #99a9bf; + } + + & > .el-tree-node__children { + overflow: hidden; + background-color: transparent; + /** padding-left: 16px; */ + display: none; + } + + &.expanded > .el-tree-node__children { + display: block; + } + } +} +.collapse-transition { + transition: 0.3s height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out; +} diff --git a/packages/theme-default/src/upload.css b/packages/theme-default/src/upload.css new file mode 100644 index 000000000..d5125c888 --- /dev/null +++ b/packages/theme-default/src/upload.css @@ -0,0 +1,191 @@ +@charset "UTF-8"; +@import "./progress.css"; +@import "./common/var.css"; + +@component-namespace el { + @b upload { + width: 360px; + + @e input { + display: none; + } + @e inner { + display: inline-block; + } + @e files { + margin: 0; + padding: 0; + list-style: none; + margin-bottom: 10px; + } + @e file { + font-size: 14px; + color: #475669; + line-height: 32px; + position: relative; + padding-right: 40px; + padding-left: 4px; + box-sizing: border-box; + border-radius: 4px; + width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + position: relative; + + [class^="el-icon"] { + color: #99a9bf; + margin-right: 7px; + height: 100%; + line-height: inherit; + } + + &:hover { + background-color: #eff2f7; + + .el-upload__btn-delete { + display: block; + cursor: pointer; + } + } + & .el-progress { + position: absolute; + bottom: 0; + width: 100%; + } + & .el-icon-check { + margin-right: 0; + color: #13ce66; + position: absolute; + right: 0; + top: 0; + height: 100%; + } + } + @e tip { + font-size: 12px; + color: #8492a6; + margin-top: 7px; + } + @e btn-delete { + position: absolute; + right: 15px; + top: 0; + font-size: 12px; + color: var(--color-primary); + display: none; + } + } + @b dragger { + background-color: #f9fafc; + border: 1px solid #c0ccda; + box-sizing: border-box; + width: 360px; + height: 180px; + border-radius: 4px; + text-align: center; + cursor: pointer; + position: relative; + overflow: hidden; + + & .el-upload__inner { + display: block; + height: 100%; + } + & .el-icon-upload { + font-size: 67px; + color: #99a9bf; + margin: 40px 0 16px; + line-height: 50px; + } + + & + .el-upload__tip { + text-align: center; + } + & ~ .el-upload__files { + margin-top: 7px; + padding-top: 5px; + border-top: 1px solid rgba(#c0ccda, .2); + } + + @e progress { + position: absolute 0 60px 0 60px; + margin: auto; + + & + .el-upload__inner { + opacity: 0; + } + } + + @e uploaded-image { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: hidden; + z-index: 10; + + & img { + display: block; + width: 100%; + height: auto; + } + + @e interact { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(#000, .72); + text-align: center; + } + + @e title { + position: absolute; + bottom: 0; + left: 0; + background-color: #fff; + height: 36px; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: normal; + text-align: left; + padding: 0 10px; + margin: 0; + line-height: 36px; + font-size: 14px; + color: #475669; + } + + & + .el-upload__inner { + opacity: 0; + position: relative; + z-index: 1; + } + } + + @e text { + color: #99a9bf; + font-size: 14px; + text-align: center; + + & em { + color: var(--color-primary); + font-style: normal; + } + } + + &:not(.is-showImage):hover { + border-color: var(--color-primary); + } + + @when dragOver { + background-color: rgba(32, 159, 255, .06); + border: 2px dashed var(--color-primary); + } + } +} diff --git a/packages/time-picker/index.js b/packages/time-picker/index.js new file mode 100644 index 000000000..60af80fb7 --- /dev/null +++ b/packages/time-picker/index.js @@ -0,0 +1,2 @@ +import Picker from '../date-picker/src/picker/time-picker'; +module.exports = Picker; diff --git a/packages/time-select/index.js b/packages/time-select/index.js new file mode 100644 index 000000000..6363ddc4c --- /dev/null +++ b/packages/time-select/index.js @@ -0,0 +1,2 @@ +import Picker from '../date-picker/src/picker/time-select'; +module.exports = Picker; diff --git a/packages/tooltip/cooking.conf.js b/packages/tooltip/cooking.conf.js new file mode 100644 index 000000000..0a4da21de --- /dev/null +++ b/packages/tooltip/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElTooltip', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/tooltip/index.js b/packages/tooltip/index.js new file mode 100644 index 000000000..2ee1ba5a0 --- /dev/null +++ b/packages/tooltip/index.js @@ -0,0 +1,7 @@ +const Tooltip = require('./src/main'); + +Tooltip.install = function(Vue) { + Vue.component(Tooltip.name, Tooltip); +}; + +module.exports = Tooltip; diff --git a/packages/tooltip/package.json b/packages/tooltip/package.json new file mode 100644 index 000000000..25f2f91f6 --- /dev/null +++ b/packages/tooltip/package.json @@ -0,0 +1,16 @@ +{ + "name": "el-tooltip", + "version": "0.0.0", + "description": "A tooltip component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/tooltip", + "author": "elemefe", + "license": "MIT", + "dependencies": {}, + "devDependencies": {} +} diff --git a/packages/tooltip/src/main.vue b/packages/tooltip/src/main.vue new file mode 100644 index 000000000..6d71b3521 --- /dev/null +++ b/packages/tooltip/src/main.vue @@ -0,0 +1,53 @@ + + + diff --git a/packages/tree/cooking.conf.js b/packages/tree/cooking.conf.js new file mode 100644 index 000000000..1d2acd4aa --- /dev/null +++ b/packages/tree/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElTree', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/tree/index.js b/packages/tree/index.js new file mode 100644 index 000000000..1f6ebac9b --- /dev/null +++ b/packages/tree/index.js @@ -0,0 +1,7 @@ +const Tree = require('./src/tree.vue'); + +Tree.install = function(Vue) { + Vue.component(Tree.name, Tree); +}; + +module.exports = Tree; diff --git a/packages/tree/package.json b/packages/tree/package.json new file mode 100644 index 000000000..1ad41211c --- /dev/null +++ b/packages/tree/package.json @@ -0,0 +1,15 @@ +{ + "name": "el-tree", + "version": "0.0.0", + "description": "A tree component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/tree", + "author": "elemefe", + "license": "MIT", + "dependencies": {} +} diff --git a/packages/tree/src/model/node.js b/packages/tree/src/model/node.js new file mode 100644 index 000000000..1118437e2 --- /dev/null +++ b/packages/tree/src/model/node.js @@ -0,0 +1,244 @@ +let idSeed = 0; +import objectAssign from 'object-assign'; + +const reInitChecked = function(node) { + const siblings = node.children; + + let all = true; + let none = true; + + for (let i = 0, j = siblings.length; i < j; i++) { + const sibling = siblings[i]; + if (sibling.checked !== true) { + all = false; + } + if (sibling.checked !== false) { + none = false; + } + } + + if (all) { + node.setChecked(true); + } else if (!all && !none) { + node.setChecked('half'); + } else if (none) { + node.setChecked(false); + } +}; + +const getPropertyFromData = function(node, prop) { + const props = node.props; + const data = node.data; + const config = props[prop]; + + if (typeof config === 'function') { + return config(data, node); + } else if (typeof config === 'string') { + return data[config]; + } else if (typeof config === 'undefined') { + return ''; + } +}; + +export default class Node { + constructor(options) { + this.id = idSeed++; + this.text = null; + this.checked = false; + this.indeterminate = false; + this.data = null; + this.expanded = false; + this.props = null; + this.parent = null; + this.lazy = false; + + for (let name in options) { + if (options.hasOwnProperty(name)) { + this[name] = options[name]; + } + } + + // internal + this.level = -1; + this.loaded = false; + this.children = []; + this.loading = false; + + if (this.parent) { + this.level = this.parent.level + 1; + } + + if (this.lazy !== true && this.data) { + let children; + if (this.level === -1 && this.data instanceof Array) { + children = this.data; + } else { + children = getPropertyFromData(this, 'children') || []; + } + + for (let i = 0, j = children.length; i < j; i++) { + const child = children[i]; + this.insertChild(new Node({ + data: child, + parent: this, + lazy: this.lazy, + load: this.load, + props: this.props + })); + } + } + } + + get label() { + return getPropertyFromData(this, 'label'); + } + + get icon() { + return getPropertyFromData(this, 'icon'); + } + + insertChild(child, index) { + if (!child) throw new Error('insertChild error: child is required.'); + + if (!child instanceof Node) { + throw new Error('insertChild error: child should an instance of Node.'); + } + + child.parent = this; + child.level = this.level + 1; + + if (typeof index === 'undefined') { + this.children.push(child); + } else { + this.children.splice(index, 0, child); + } + } + + removeChild(child) { + const index = this.children.indexOf(child); + + if (index > -1) { + child.parent = null; + this.children.splice(child, index); + } + } + + expand(callback) { + if (this.shouldLoadData()) { + this.loadData((data) => { + if (data instanceof Array) { + callback(); + } + }); + } else { + this.expanded = true; + if (callback) { + callback(); + } + } + } + + doCreateChildren(array, defaultProps = {}) { + array.forEach((item) => { + const node = new Node(objectAssign({ + data: item, + lazy: this.lazy, + load: this.load, + props: this.props + }, defaultProps)); + this.insertChild(node); + }); + } + + collapse() { + this.expanded = false; + } + + shouldLoadData() { + return this.lazy === true && this.load && !this.loaded; + } + + get isLeaf() { + return !this.hasChild(); + } + + hasChild() { + const children = this.children; + if (!this.lazy || (this.lazy === true && this.loaded === true)) { + return children && children.length > 0; + } + return true; + } + + setChecked(value, deep) { + this.indeterminate = value === 'half'; + this.checked = value === true; + + const handleDeep = () => { + if (deep) { + const children = this.children; + for (let i = 0, j = children.length; i < j; i++) { + const child = children[i]; + child.setChecked(value !== false, deep); + } + } + }; + + if (this.shouldLoadData()) { + // Only work on lazy load data. + this.loadData(() => { + handleDeep(); + }, { + checked: value !== false + }); + } else { + handleDeep(); + } + + const parent = this.parent; + if (parent.level === -1) return; + + reInitChecked(parent); + } + + getChildren() { // this is data + const data = this.data; + if (!data) return null; + + const props = this.props; + let children = 'children'; + if (props) { + children = props.children || 'children'; + } + + if (data[children] === undefined) { + data[children] = null; + } + + return data[children]; + } + + loadData(callback, defaultProps = {}) { + if (this.lazy === true && this.load && !this.loaded) { + this.loading = true; + + const loadFn = this.load; + const resolve = (children) => { + this.loaded = true; + this.loading = false; + + this.doCreateChildren(children, defaultProps); + + if (callback) { + callback.call(this, children); + } + }; + + loadFn(this, resolve); + } else { + if (callback) { + callback.call(this); + } + } + } +} diff --git a/packages/tree/src/model/tree.js b/packages/tree/src/model/tree.js new file mode 100644 index 000000000..8519a1ff0 --- /dev/null +++ b/packages/tree/src/model/tree.js @@ -0,0 +1,48 @@ +import Node from './node'; + +export default class Tree { + constructor(options) { + for (let option in options) { + if (options.hasOwnProperty(option)) { + this[option] = options[option]; + } + } + + this._isTree = true; + + this.root = new Node({ + data: this.data, + lazy: this.lazy, + props: this.props, + load: this.load + }); + + if (this.lazy && this.load) { + const loadFn = this.load; + loadFn(this.root, (data) => { + this.root.doCreateChildren(data); + }); + } + } + + getCheckedNodes(leafOnly) { + const checkedNodes = []; + const walk = function(node) { + const children = node.children; + + children.forEach(function(child) { + if ((!leafOnly && child.checked) || (leafOnly && !child.hasChild && child.checked)) { + checkedNodes.push(child.data); + } else { + checkedNodes.push(child.data); + } + + walk(child); + }); + }; + + walk(this); + + return checkedNodes; + } +}; diff --git a/packages/tree/src/transition.js b/packages/tree/src/transition.js new file mode 100644 index 000000000..3594f581e --- /dev/null +++ b/packages/tree/src/transition.js @@ -0,0 +1,61 @@ +export default { + beforeEnter(el) { + el.dataset.oldPaddingTop = el.style.paddingTop; + el.dataset.oldPaddingBottom = el.style.paddingBottom; + el.style.height = '0'; + el.style.paddingTop = 0; + el.style.paddingBottom = 0; + }, + + enter(el) { + el.dataset.oldOverflow = el.style.overflow; + + el.style.display = 'block'; + if (el.scrollHeight !== 0) { + el.style.height = el.scrollHeight + 'px'; + el.style.paddingTop = el.dataset.oldPaddingTop; + el.style.paddingBottom = el.dataset.oldPaddingBottom; + } else { + el.style.height = ''; + el.style.paddingTop = el.dataset.oldPaddingTop; + el.style.paddingBottom = el.dataset.oldPaddingBottom; + } + + el.style.overflow = 'hidden'; + }, + + afterEnter(el) { + el.style.display = ''; + el.style.height = ''; + el.style.overflow = el.dataset.oldOverflow; + }, + + beforeLeave(el) { + el.dataset.oldPaddingTop = el.style.paddingTop; + el.dataset.oldPaddingBottom = el.style.paddingBottom; + el.dataset.oldOverflow = el.style.overflow; + + el.style.display = 'block'; + if (el.scrollHeight !== 0) { + el.style.height = el.scrollHeight + 'px'; + } + el.style.overflow = 'hidden'; + }, + + leave(el) { + if (el.scrollHeight !== 0) { + setTimeout(() => { + el.style.height = 0; + el.style.paddingTop = 0; + el.style.paddingBottom = 0; + }); + } + }, + + afterLeave(el) { + el.style.display = el.style.height = ''; + el.style.overflow = el.dataset.oldOverflow; + el.style.paddingTop = el.dataset.oldPaddingTop; + el.style.paddingBottom = el.dataset.oldPaddingBottom; + } +}; diff --git a/packages/tree/src/tree-node.vue b/packages/tree/src/tree-node.vue new file mode 100644 index 000000000..739212c81 --- /dev/null +++ b/packages/tree/src/tree-node.vue @@ -0,0 +1,89 @@ + + + diff --git a/packages/tree/src/tree.vue b/packages/tree/src/tree.vue new file mode 100644 index 000000000..cf239793f --- /dev/null +++ b/packages/tree/src/tree.vue @@ -0,0 +1,77 @@ + + + diff --git a/packages/upload/cooking.conf.js b/packages/upload/cooking.conf.js new file mode 100644 index 000000000..535bf3722 --- /dev/null +++ b/packages/upload/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElUpload', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/upload/index.js b/packages/upload/index.js new file mode 100644 index 000000000..5c575d081 --- /dev/null +++ b/packages/upload/index.js @@ -0,0 +1,7 @@ +const Upload = require('./src/upload'); + +Upload.install = function(Vue) { + Vue.component(Upload.name, Upload); +}; + +module.exports = Upload; diff --git a/packages/upload/package.json b/packages/upload/package.json new file mode 100644 index 000000000..988b6cd78 --- /dev/null +++ b/packages/upload/package.json @@ -0,0 +1,18 @@ +{ + "name": "el-upload", + "version": "0.0.0", + "description": "A upload component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/upload", + "author": "elemefe", + "license": "MIT", + "dependencies": { + "vue-resource": "^0.9.3" + }, + "devDependencies": {} +} diff --git a/packages/upload/src/ajax-upload.vue b/packages/upload/src/ajax-upload.vue new file mode 100644 index 000000000..57f85d7e9 --- /dev/null +++ b/packages/upload/src/ajax-upload.vue @@ -0,0 +1,142 @@ + + + diff --git a/packages/upload/src/ajax.js b/packages/upload/src/ajax.js new file mode 100644 index 000000000..fb4c7d73a --- /dev/null +++ b/packages/upload/src/ajax.js @@ -0,0 +1,94 @@ +function getError(action, option, xhr) { + const msg = `cannot post ${action} ${xhr.status}'`; + const err = new Error(msg); + err.status = xhr.status; + err.method = 'post'; + err.url = action; + return err; +} + +function getBody(xhr) { + const text = xhr.responseText || xhr.response; + if (!text) { + return text; + } + + try { + return JSON.parse(text); + } catch (e) { + return text; + } +} + +// option { +// onProgress: (event: { percent: number }): void, +// onError: (event: Error, body?: Object): void, +// onSuccess: (body: Object): void, +// data: Object, +// filename: String, +// file: File, +// withCredentials: Boolean, +// action: String, +// headers: Object, +// } +export default function upload(action, option) { + if (typeof XMLHttpRequest === 'undefined') { + return; + } + + const xhr = new XMLHttpRequest(); + if (xhr.upload) { + xhr.upload.onprogress = function progress(e) { + if (e.total > 0) { + e.percent = e.loaded / e.total * 100; + } + option.onProgress(e); + }; + } + + const formData = new FormData(); + + if (option.data) { + Object.keys(option.data).map(key => { + formData.append(key, option.data[key]); + }); + } + + formData.append(option.filename, option.file); + + xhr.onerror = function error(e) { + option.onError(e); + }; + + xhr.onload = function onload() { + // allow success when 2xx status + // see https://github.com/react-component/upload/issues/34 + if (xhr.status < 200 || xhr.status >= 300) { + return option.onError(getError(action, option, xhr), getBody(xhr)); + } + + option.onSuccess(getBody(xhr)); + }; + + xhr.open('post', action, true); + + // Has to be after `.open()`. See https://github.com/enyo/dropzone/issues/179 + if (option.withCredentials && 'withCredentials' in xhr) { + xhr.withCredentials = true; + } + + const headers = option.headers || {}; + + // when set headers['X-Requested-With'] = null , can close default XHR header + // see https://github.com/react-component/upload/issues/33 + // if (headers['X-Requested-With'] !== null) { + // xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + // } + + for (let item in headers) { + if (headers.hasOwnProperty(item) && headers[item] !== null) { + xhr.setRequestHeader(item, headers[item]); + } + } + xhr.send(formData); +} diff --git a/packages/upload/src/iframe-upload.vue b/packages/upload/src/iframe-upload.vue new file mode 100644 index 000000000..6d8ad9b55 --- /dev/null +++ b/packages/upload/src/iframe-upload.vue @@ -0,0 +1,138 @@ + + + diff --git a/packages/upload/src/upload.vue b/packages/upload/src/upload.vue new file mode 100644 index 000000000..3fc7ef2e5 --- /dev/null +++ b/packages/upload/src/upload.vue @@ -0,0 +1,248 @@ + + + diff --git a/scripts/cooking.component.js b/scripts/cooking.component.js new file mode 100644 index 000000000..87d61054c --- /dev/null +++ b/scripts/cooking.component.js @@ -0,0 +1,54 @@ +var cooking = require('cooking'); +var path = require('path'); +var Components = require('../components.json'); + +cooking.set({ + entry: Components.map(function (compo) { + compo[0] = path.join(process.cwd(), compo[0]); + return compo; + }), + dist: './lib', + clean: true, + template: false, + format: 'umd', + moduleName: ['ELEMENT', '[name]'], + extractCSS: '[name]/style.css', + extends: ['vue'] +}); + +cooking.add('output.filename', '[name]/index.js'); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../src'), + 'packages': path.join(__dirname, '../packages'), + 'examples': path.join(__dirname, '../examples') +}); + +var externals = {}; +Object.keys(Components).forEach(function(key) { + externals[`packages/${key}/index.js`] = { + root: `ELEMENT.${key}`, + commonjs: `element-ui/lib/${key}`, + commonjs2: `element-ui/lib/${key}`, + amd: `element-ui/lib/${key}` + }; + externals[`packages/${key}/style.css`] = { + root: `ELEMENT.${key}/style.css`, + commonjs: `element-ui/lib/${key}/style.css`, + commonjs2: `element-ui/lib/${key}/style.css`, + amd: `element-ui/lib/${key}/style.css` + }; +}); + +cooking.add('externals', Object.assign({ + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}, externals)); + +cooking.add('loader.js.exclude', /node_modules|utils\/popper\.js|util\/fecha.\js/); + +module.exports = cooking.resolve(); diff --git a/scripts/cooking.conf.js b/scripts/cooking.conf.js new file mode 100644 index 000000000..34d2167e5 --- /dev/null +++ b/scripts/cooking.conf.js @@ -0,0 +1,32 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: './src/index.js', + dist: './lib', + clean: false, + template: false, + format: 'umd', + moduleName: 'ELEMENT', + extractCSS: 'style.css', + extends: ['vue'] +}); + +cooking.add('output.filename', 'index.js'); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../src'), + 'packages': path.join(__dirname, '../packages'), + 'examples': path.join(__dirname, '../examples') +}); + +cooking.add('externals.vue', { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' +}); + +cooking.add('loader.js.exclude', /node_modules|utils\/popper\.js|util\/fecha.\js/); + +module.exports = cooking.resolve(); diff --git a/scripts/cooking.demo.js b/scripts/cooking.demo.js new file mode 100644 index 000000000..99e120469 --- /dev/null +++ b/scripts/cooking.demo.js @@ -0,0 +1,89 @@ +var cooking = require('cooking'); +var path = require('path'); +var Components = require('../components.json'); + +cooking.set({ + entry: { + app: './examples/entry.js', + vendor: ['vue', 'vue-router'] + }, + dist: './examples/element-ui/', + template: './examples/index.template.html', + + publicPath: '/element-ui/', + hash: true, + devServer: { + port: 8085, + log: false, + publicPath: '/' + }, + chunk: 'vendor', + extractCSS: true, + extends: { + vue: true, + lint: true, + saladcss: { + browser: ['ie > 8', 'last 2 version'], + features: { + 'bem': { + 'shortcuts': { + 'component': 'b', + 'modifier': 'm', + 'descendent': 'e' + }, + 'separators': { + 'descendent': '__', + 'modifier': '--' + } + } + } + } + } +}); + +cooking.add('loader.md', { + test: /\.md$/, + loader: 'vue-markdown-loader' +}); + +cooking.add('vueMarkdown', { + use: [ + [require('markdown-it-toc-and-anchor').default, { + anchorLinkSymbol: '', + anchorClassName: 'anchor' + }] + ], + preprocess: function (MarkdownIt, source) { + MarkdownIt.renderer.rules.table_open = function () { + return ''; + }; + MarkdownIt.renderer.rules.fence = wrap(MarkdownIt.renderer.rules.fence); + return source; + } +}); + +var wrap = function (render) { + return function () { + return render.apply(this, arguments) + .replace('', '') + }; +}; + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../src'), + 'packages': path.join(__dirname, '../packages'), + 'examples': path.join(__dirname, '../examples') +}); + +var externals = {}; +Object.keys(Components).forEach(function (key) { + externals[`packages/${key}/style.css`] = 'null'; +}); + +// 开发模式不需要将不存在的 style.css 打包进去 +cooking.add('externals', externals); + +cooking.config.vue.loaders.html = 'html?minimize=true&conservativeCollapse=false'; + +module.exports = cooking.resolve(); diff --git a/src/index.js b/src/index.js new file mode 100644 index 000000000..75bdedf72 --- /dev/null +++ b/src/index.js @@ -0,0 +1,170 @@ +import Group from '../packages/group/index.js'; +import SelectDropdown from '../packages/select-dropdown/index.js'; +import Pagination from '../packages/pagination/index.js'; +import Dialog from '../packages/dialog/index.js'; +import Cascader from '../packages/cascader/index.js'; +import Autocomplete from '../packages/autocomplete/index.js'; +import Dropdown from '../packages/dropdown/index.js'; +import DropdownItem from '../packages/dropdown-item/index.js'; +import Menu from '../packages/menu/index.js'; +import Submenu from '../packages/submenu/index.js'; +import MenuItem from '../packages/menu-item/index.js'; +import Input from '../packages/input/index.js'; +import InputNumber from '../packages/input-number/index.js'; +import InputGroup from '../packages/input-group/index.js'; +import Radio from '../packages/radio/index.js'; +import RadioGroup from '../packages/radio-group/index.js'; +import RadioButton from '../packages/radio-button/index.js'; +import Checkbox from '../packages/checkbox/index.js'; +import CheckboxGroup from '../packages/checkbox-group/index.js'; +import Switch from '../packages/switch/index.js'; +import Select from '../packages/select/index.js'; +import Option from '../packages/option/index.js'; +import OptionGroup from '../packages/option-group/index.js'; +import Button from '../packages/button/index.js'; +import ButtonGroup from '../packages/button-group/index.js'; +import Table from '../packages/table/index.js'; +import TableColumn from '../packages/table-column/index.js'; +import DatePicker from '../packages/date-picker/index.js'; +import TimeSelect from '../packages/time-select/index.js'; +import TimePicker from '../packages/time-picker/index.js'; +import Popover from '../packages/popover/index.js'; +import Tooltip from '../packages/tooltip/index.js'; +import MessageBox from '../packages/message-box/index.js'; +import Breadcrumb from '../packages/breadcrumb/index.js'; +import BreadcrumbItem from '../packages/breadcrumb-item/index.js'; +import Form from '../packages/form/index.js'; +import FormItem from '../packages/form-item/index.js'; +import Tabs from '../packages/tabs/index.js'; +import TabPane from '../packages/tab-pane/index.js'; +import Tag from '../packages/tag/index.js'; +import Tree from '../packages/tree/index.js'; +import Alert from '../packages/alert/index.js'; +import Notification from '../packages/notification/index.js'; +import Slider from '../packages/slider/index.js'; +import Loading from '../packages/loading/index.js'; +import Icon from '../packages/icon/index.js'; +import Row from '../packages/row/index.js'; +import Col from '../packages/col/index.js'; +import Upload from '../packages/upload/index.js'; +import Progress from '../packages/progress/index.js'; +import Spinner from '../packages/spinner/index.js'; + +const install = function(Vue) { + Vue.component(Group.name, Group); + Vue.component(SelectDropdown.name, SelectDropdown); + Vue.component(Pagination.name, Pagination); + Vue.component(Dialog.name, Dialog); + Vue.component(Cascader.name, Cascader); + Vue.component(Autocomplete.name, Autocomplete); + Vue.component(Dropdown.name, Dropdown); + Vue.component(DropdownItem.name, DropdownItem); + Vue.component(Menu.name, Menu); + Vue.component(Submenu.name, Submenu); + Vue.component(MenuItem.name, MenuItem); + Vue.component(Input.name, Input); + Vue.component(InputNumber.name, InputNumber); + Vue.component(InputGroup.name, InputGroup); + Vue.component(Radio.name, Radio); + Vue.component(RadioGroup.name, RadioGroup); + Vue.component(RadioButton.name, RadioButton); + Vue.component(Checkbox.name, Checkbox); + Vue.component(CheckboxGroup.name, CheckboxGroup); + Vue.component(Switch.name, Switch); + Vue.component(Select.name, Select); + Vue.component(Option.name, Option); + Vue.component(OptionGroup.name, OptionGroup); + Vue.component(Button.name, Button); + Vue.component(ButtonGroup.name, ButtonGroup); + Vue.component(Table.name, Table); + Vue.component(TableColumn.name, TableColumn); + Vue.component(DatePicker.name, DatePicker); + Vue.component(TimeSelect.name, TimeSelect); + Vue.component(TimePicker.name, TimePicker); + Vue.component(Popover.name, Popover); + Vue.component(Tooltip.name, Tooltip); + Vue.component(Breadcrumb.name, Breadcrumb); + Vue.component(BreadcrumbItem.name, BreadcrumbItem); + Vue.component(Form.name, Form); + Vue.component(FormItem.name, FormItem); + Vue.component(Tabs.name, Tabs); + Vue.component(TabPane.name, TabPane); + Vue.component(Tag.name, Tag); + Vue.component(Tree.name, Tree); + Vue.component(Alert.name, Alert); + Vue.component(Slider.name, Slider); + Vue.component(Icon.name, Icon); + Vue.component(Row.name, Row); + Vue.component(Col.name, Col); + Vue.component(Upload.name, Upload); + Vue.component(Progress.name, Progress); + Vue.component(Spinner.name, Spinner); + + Vue.use(Loading); + + Vue.prototype.$msgbox = MessageBox; + Vue.prototype.$alert = MessageBox.alert; + Vue.prototype.$confirm = MessageBox.confirm; + Vue.prototype.$prompt = MessageBox.prompt; + Vue.prototype.$notify = Notification; +}; + +// auto install +if (typeof window !== 'undefined' && window.Vue) { + install(window.Vue); +}; + +module.exports = { + install, + Group, + SelectDropdown, + Pagination, + Dialog, + Cascader, + Autocomplete, + Dropdown, + DropdownItem, + Menu, + Submenu, + MenuItem, + Input, + InputNumber, + InputGroup, + Radio, + RadioGroup, + RadioButton, + Checkbox, + CheckboxGroup, + Switch, + Select, + Option, + OptionGroup, + Button, + ButtonGroup, + Table, + TableColumn, + DatePicker, + TimeSelect, + TimePicker, + Popover, + Tooltip, + MessageBox, + Breadcrumb, + BreadcrumbItem, + Form, + FormItem, + Tabs, + TabPane, + Tag, + Tree, + Alert, + Notification, + Slider, + Loading, + Icon, + Row, + Col, + Upload, + Progress, + Spinner +}; diff --git a/src/mixins/emitter.js b/src/mixins/emitter.js new file mode 100644 index 000000000..515705c13 --- /dev/null +++ b/src/mixins/emitter.js @@ -0,0 +1,33 @@ +function broadcast(componentName, eventName, params) { + this.$children.forEach(child => { + var name = child.$options.componentName; + + if (name === componentName) { + child.$emit.apply(child, [eventName].concat(params)); + } else { + broadcast.apply(child, [componentName, eventName].concat(params)); + } + }); +} +export default { + methods: { + dispatch(componentName, eventName, params) { + var parent = this.$parent; + var name = parent.$options.componentName; + + while (parent && (!name || name !== componentName)) { + parent = parent.$parent; + + if (parent) { + name = parent.$options.componentName; + } + } + if (parent) { + parent.$emit.apply(parent, [eventName].concat(params)); + } + }, + broadcast(componentName, eventName, params) { + broadcast.call(this, componentName, eventName, params); + } + } +}; diff --git a/src/utils/popper.js b/src/utils/popper.js new file mode 100644 index 000000000..19b9338f6 --- /dev/null +++ b/src/utils/popper.js @@ -0,0 +1,1249 @@ +/** + * @fileOverview Kickass library to create and place poppers near their reference elements. + * @version {{version}} + * @license + * Copyright (c) 2016 Federico Zivolo and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// +// Cross module loader +// Supported: Node, AMD, Browser globals +// +;(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(factory); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + // Browser globals (root is window) + root.Popper = factory(); + } +}(this, function () { + + 'use strict'; + + var root = window; + + // default options + var DEFAULTS = { + // placement of the popper + placement: 'bottom', + + gpuAcceleration: true, + + // shift popper from its origin by the given amount of pixels (can be negative) + offset: 0, + + // the element which will act as boundary of the popper + boundariesElement: 'viewport', + + // amount of pixel used to define a minimum distance between the boundaries and the popper + boundariesPadding: 5, + + // popper will try to prevent overflow following this order, + // by default, then, it could overflow on the left and on top of the boundariesElement + preventOverflowOrder: ['left', 'right', 'top', 'bottom'], + + // the behavior used by flip to change the placement of the popper + flipBehavior: 'flip', + + arrowElement: '[x-arrow]', + + // list of functions used to modify the offsets before they are applied to the popper + modifiers: [ 'shift', 'offset', 'preventOverflow', 'keepTogether', 'arrow', 'flip', 'applyStyle'], + + modifiersIgnored: [], + + forceAbsolute: false + }; + + /** + * Create a new Popper.js instance + * @constructor Popper + * @param {HTMLElement} reference - The reference element used to position the popper + * @param {HTMLElement|Object} popper + * The HTML element used as popper, or a configuration used to generate the popper. + * @param {String} [popper.tagName='div'] The tag name of the generated popper. + * @param {Array} [popper.classNames=['popper']] Array of classes to apply to the generated popper. + * @param {Array} [popper.attributes] Array of attributes to apply, specify `attr:value` to assign a value to it. + * @param {HTMLElement|String} [popper.parent=window.document.body] The parent element, given as HTMLElement or as query string. + * @param {String} [popper.content=''] The content of the popper, it can be text, html, or node; if it is not text, set `contentType` to `html` or `node`. + * @param {String} [popper.contentType='text'] If `html`, the `content` will be parsed as HTML. If `node`, it will be appended as-is. + * @param {String} [popper.arrowTagName='div'] Same as `popper.tagName` but for the arrow element. + * @param {Array} [popper.arrowClassNames='popper__arrow'] Same as `popper.classNames` but for the arrow element. + * @param {String} [popper.arrowAttributes=['x-arrow']] Same as `popper.attributes` but for the arrow element. + * @param {Object} options + * @param {String} [options.placement=bottom] + * Placement of the popper accepted values: `top(-start, -end), right(-start, -end), bottom(-start, -right), + * left(-start, -end)` + * + * @param {HTMLElement|String} [options.arrowElement='[x-arrow]'] + * The DOM Node used as arrow for the popper, or a CSS selector used to get the DOM node. It must be child of + * its parent Popper. Popper.js will apply to the given element the style required to align the arrow with its + * reference element. + * By default, it will look for a child node of the popper with the `x-arrow` attribute. + * + * @param {Boolean} [options.gpuAcceleration=true] + * When this property is set to true, the popper position will be applied using CSS3 translate3d, allowing the + * browser to use the GPU to accelerate the rendering. + * If set to false, the popper will be placed using `top` and `left` properties, not using the GPU. + * + * @param {Number} [options.offset=0] + * Amount of pixels the popper will be shifted (can be negative). + * + * @param {String|Element} [options.boundariesElement='viewport'] + * The element which will define the boundaries of the popper position, the popper will never be placed outside + * of the defined boundaries (except if `keepTogether` is enabled) + * + * @param {Number} [options.boundariesPadding=5] + * Additional padding for the boundaries + * + * @param {Array} [options.preventOverflowOrder=['left', 'right', 'top', 'bottom']] + * Order used when Popper.js tries to avoid overflows from the boundaries, they will be checked in order, + * this means that the last ones will never overflow + * + * @param {String|Array} [options.flipBehavior='flip'] + * The behavior used by the `flip` modifier to change the placement of the popper when the latter is trying to + * overlap its reference element. Defining `flip` as value, the placement will be flipped on + * its axis (`right - left`, `top - bottom`). + * You can even pass an array of placements (eg: `['right', 'left', 'top']` ) to manually specify + * how alter the placement when a flip is needed. (eg. in the above example, it would first flip from right to left, + * then, if even in its new placement, the popper is overlapping its reference element, it will be moved to top) + * + * @param {Array} [options.modifiers=[ 'shift', 'offset', 'preventOverflow', 'keepTogether', 'arrow', 'flip', 'applyStyle']] + * List of functions used to modify the data before they are applied to the popper, add your custom functions + * to this array to edit the offsets and placement. + * The function should reflect the @params and @returns of preventOverflow + * + * @param {Array} [options.modifiersIgnored=[]] + * Put here any built-in modifier name you want to exclude from the modifiers list + * The function should reflect the @params and @returns of preventOverflow + * + * @param {Boolean} [options.removeOnDestroy=false] + * Set to true if you want to automatically remove the popper when you call the `destroy` method. + */ + function Popper(reference, popper, options) { + this._reference = reference.jquery ? reference[0] : reference; + this.state = {}; + + // if the popper variable is a configuration object, parse it to generate an HTMLElement + // generate a default popper if is not defined + var isNotDefined = typeof popper === 'undefined' || popper === null; + var isConfig = popper && Object.prototype.toString.call(popper) === '[object Object]'; + if (isNotDefined || isConfig) { + this._popper = this.parse(isConfig ? popper : {}); + } + // otherwise, use the given HTMLElement as popper + else { + this._popper = popper.jquery ? popper[0] : popper; + } + + // with {} we create a new object with the options inside it + this._options = Object.assign({}, DEFAULTS, options); + + // refactoring modifiers' list + this._options.modifiers = this._options.modifiers.map(function(modifier){ + // remove ignored modifiers + if (this._options.modifiersIgnored.indexOf(modifier) !== -1) return; + + // set the x-placement attribute before everything else because it could be used to add margins to the popper + // margins needs to be calculated to get the correct popper offsets + if (modifier === 'applyStyle') { + this._popper.setAttribute('x-placement', this._options.placement); + } + + // return predefined modifier identified by string or keep the custom one + return this.modifiers[modifier] || modifier; + }.bind(this)); + + // make sure to apply the popper position before any computation + this.state.position = this._getPosition(this._popper, this._reference); + setStyle(this._popper, { position: this.state.position}); + + // fire the first update to position the popper in the right place + this.update(); + + // setup event listeners, they will take care of update the position in specific situations + this._setupEventListeners(); + return this; + } + + + // + // Methods + // + /** + * Destroy the popper + * @method + * @memberof Popper + */ + Popper.prototype.destroy = function() { + this._popper.removeAttribute('x-placement'); + this._popper.style.left = ''; + this._popper.style.position = ''; + this._popper.style.top = ''; + this._popper.style[getSupportedPropertyName('transform')] = ''; + this._removeEventListeners(); + + // remove the popper if user explicity asked for the deletion on destroy + if (this._options.removeOnDestroy) { + this._popper.remove(); + } + return this; + }; + + /** + * Updates the position of the popper, computing the new offsets and applying the new style + * @method + * @memberof Popper + */ + Popper.prototype.update = function() { + var data = { instance: this, styles: {} }; + + // store placement inside the data object, modifiers will be able to edit `placement` if needed + // and refer to _originalPlacement to know the original value + data.placement = this._options.placement; + data._originalPlacement = this._options.placement; + + // compute the popper and reference offsets and put them inside data.offsets + data.offsets = this._getOffsets(this._popper, this._reference, data.placement); + + // get boundaries + data.boundaries = this._getBoundaries(data, this._options.boundariesPadding, this._options.boundariesElement); + + data = this.runModifiers(data, this._options.modifiers); + + if (typeof this.state.updateCallback === 'function') { + this.state.updateCallback(data); + } + + }; + + /** + * If a function is passed, it will be executed after the initialization of popper with as first argument the Popper instance. + * @method + * @memberof Popper + * @param {Function} callback + */ + Popper.prototype.onCreate = function(callback) { + // the createCallbacks return as first argument the popper instance + callback(this); + return this; + }; + + /** + * If a function is passed, it will be executed after each update of popper with as first argument the set of coordinates and informations + * used to style popper and its arrow. + * NOTE: it doesn't get fired on the first call of the `Popper.update()` method inside the `Popper` constructor! + * @method + * @memberof Popper + * @param {Function} callback + */ + Popper.prototype.onUpdate = function(callback) { + this.state.updateCallback = callback; + return this; + }; + + /** + * Helper used to generate poppers from a configuration file + * @method + * @memberof Popper + * @param config {Object} configuration + * @returns {HTMLElement} popper + */ + Popper.prototype.parse = function(config) { + var defaultConfig = { + tagName: 'div', + classNames: [ 'popper' ], + attributes: [], + parent: root.document.body, + content: '', + contentType: 'text', + arrowTagName: 'div', + arrowClassNames: [ 'popper__arrow' ], + arrowAttributes: [ 'x-arrow'] + }; + config = Object.assign({}, defaultConfig, config); + + var d = root.document; + + var popper = d.createElement(config.tagName); + addClassNames(popper, config.classNames); + addAttributes(popper, config.attributes); + if (config.contentType === 'node') { + popper.appendChild(config.content.jquery ? config.content[0] : config.content); + }else if (config.contentType === 'html') { + popper.innerHTML = config.content; + } else { + popper.textContent = config.content; + } + + if (config.arrowTagName) { + var arrow = d.createElement(config.arrowTagName); + addClassNames(arrow, config.arrowClassNames); + addAttributes(arrow, config.arrowAttributes); + popper.appendChild(arrow); + } + + var parent = config.parent.jquery ? config.parent[0] : config.parent; + + // if the given parent is a string, use it to match an element + // if more than one element is matched, the first one will be used as parent + // if no elements are matched, the script will throw an error + if (typeof parent === 'string') { + parent = d.querySelectorAll(config.parent); + if (parent.length > 1) { + console.warn('WARNING: the given `parent` query(' + config.parent + ') matched more than one element, the first one will be used'); + } + if (parent.length === 0) { + throw 'ERROR: the given `parent` doesn\'t exists!'; + } + parent = parent[0]; + } + // if the given parent is a DOM nodes list or an array of nodes with more than one element, + // the first one will be used as parent + if (parent.length > 1 && parent instanceof Element === false) { + console.warn('WARNING: you have passed as parent a list of elements, the first one will be used'); + parent = parent[0]; + } + + // append the generated popper to its parent + parent.appendChild(popper); + + return popper; + + /** + * Adds class names to the given element + * @function + * @ignore + * @param {HTMLElement} target + * @param {Array} classes + */ + function addClassNames(element, classNames) { + classNames.forEach(function(className) { + element.classList.add(className); + }); + } + + /** + * Adds attributes to the given element + * @function + * @ignore + * @param {HTMLElement} target + * @param {Array} attributes + * @example + * addAttributes(element, [ 'data-info:foobar' ]); + */ + function addAttributes(element, attributes) { + attributes.forEach(function(attribute) { + element.setAttribute(attribute.split(':')[0], attribute.split(':')[1] || ''); + }); + } + + }; + + /** + * Helper used to get the position which will be applied to the popper + * @method + * @memberof Popper + * @param config {HTMLElement} popper element + * @returns {HTMLElement} reference element + */ + Popper.prototype._getPosition = function(popper, reference) { + var container = getOffsetParent(reference); + + if (this._options.forceAbsolute) { + return 'absolute'; + } + + // Decide if the popper will be fixed + // If the reference element is inside a fixed context, the popper will be fixed as well to allow them to scroll together + var isParentFixed = isFixed(reference, container); + return isParentFixed ? 'fixed' : 'absolute'; + }; + + /** + * Get offsets to the popper + * @method + * @memberof Popper + * @access private + * @param {Element} popper - the popper element + * @param {Element} reference - the reference element (the popper will be relative to this) + * @returns {Object} An object containing the offsets which will be applied to the popper + */ + Popper.prototype._getOffsets = function(popper, reference, placement) { + placement = placement.split('-')[0]; + var popperOffsets = {}; + + popperOffsets.position = this.state.position; + var isParentFixed = popperOffsets.position === 'fixed'; + + // + // Get reference element position + // + var referenceOffsets = getOffsetRectRelativeToCustomParent(reference, getOffsetParent(popper), isParentFixed); + + // + // Get popper sizes + // + var popperRect = getOuterSizes(popper); + + // + // Compute offsets of popper + // + + // depending by the popper placement we have to compute its offsets slightly differently + if (['right', 'left'].indexOf(placement) !== -1) { + popperOffsets.top = referenceOffsets.top + referenceOffsets.height / 2 - popperRect.height / 2; + if (placement === 'left') { + popperOffsets.left = referenceOffsets.left - popperRect.width; + } else { + popperOffsets.left = referenceOffsets.right; + } + } else { + popperOffsets.left = referenceOffsets.left + referenceOffsets.width / 2 - popperRect.width / 2; + if (placement === 'top') { + popperOffsets.top = referenceOffsets.top - popperRect.height; + } else { + popperOffsets.top = referenceOffsets.bottom; + } + } + + // Add width and height to our offsets object + popperOffsets.width = popperRect.width; + popperOffsets.height = popperRect.height; + + + return { + popper: popperOffsets, + reference: referenceOffsets + }; + }; + + + /** + * Setup needed event listeners used to update the popper position + * @method + * @memberof Popper + * @access private + */ + Popper.prototype._setupEventListeners = function() { + // NOTE: 1 DOM access here + this.state.updateBound = this.update.bind(this); + root.addEventListener('resize', this.state.updateBound); + // if the boundariesElement is window we don't need to listen for the scroll event + if (this._options.boundariesElement !== 'window') { + var target = getScrollParent(this._reference); + // here it could be both `body` or `documentElement` thanks to Firefox, we then check both + if (target === root.document.body || target === root.document.documentElement) { + target = root; + } + target.addEventListener('scroll', this.state.updateBound); + } + }; + + /** + * Remove event listeners used to update the popper position + * @method + * @memberof Popper + * @access private + */ + Popper.prototype._removeEventListeners = function() { + // NOTE: 1 DOM access here + root.removeEventListener('resize', this.state.updateBound); + if (this._options.boundariesElement !== 'window') { + var target = getScrollParent(this._reference); + // here it could be both `body` or `documentElement` thanks to Firefox, we then check both + if (target === root.document.body || target === root.document.documentElement) { + target = root; + } + target.removeEventListener('scroll', this.state.updateBound); + } + this.state.updateBound = null; + }; + + /** + * Computed the boundaries limits and return them + * @method + * @memberof Popper + * @access private + * @param {Object} data - Object containing the property "offsets" generated by `_getOffsets` + * @param {Number} padding - Boundaries padding + * @param {Element} boundariesElement - Element used to define the boundaries + * @returns {Object} Coordinates of the boundaries + */ + Popper.prototype._getBoundaries = function(data, padding, boundariesElement) { + // NOTE: 1 DOM access here + var boundaries = {}; + var width, height; + if (boundariesElement === 'window') { + var body = root.document.body, + html = root.document.documentElement; + + height = Math.max( body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight ); + width = Math.max( body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth ); + + boundaries = { + top: 0, + right: width, + bottom: height, + left: 0 + }; + } else if (boundariesElement === 'viewport') { + var offsetParent = getOffsetParent(this._popper); + var scrollParent = getScrollParent(this._popper); + var offsetParentRect = getOffsetRect(offsetParent); + + // if the popper is fixed we don't have to substract scrolling from the boundaries + var scrollTop = data.offsets.popper.position === 'fixed' ? 0 : scrollParent.scrollTop; + var scrollLeft = data.offsets.popper.position === 'fixed' ? 0 : scrollParent.scrollLeft; + + boundaries = { + top: 0 - (offsetParentRect.top - scrollTop), + right: root.document.documentElement.clientWidth - (offsetParentRect.left - scrollLeft), + bottom: root.document.documentElement.clientHeight - (offsetParentRect.top - scrollTop), + left: 0 - (offsetParentRect.left - scrollLeft) + }; + } else { + if (getOffsetParent(this._popper) === boundariesElement) { + boundaries = { + top: 0, + left: 0, + right: boundariesElement.clientWidth, + bottom: boundariesElement.clientHeight + }; + } else { + boundaries = getOffsetRect(boundariesElement); + } + } + boundaries.left += padding; + boundaries.right -= padding; + boundaries.top = boundaries.top + padding; + boundaries.bottom = boundaries.bottom - padding; + return boundaries; + }; + + + /** + * Loop trough the list of modifiers and run them in order, each of them will then edit the data object + * @method + * @memberof Popper + * @access public + * @param {Object} data + * @param {Array} modifiers + * @param {Function} ends + */ + Popper.prototype.runModifiers = function(data, modifiers, ends) { + var modifiersToRun = modifiers.slice(); + if (ends !== undefined) { + modifiersToRun = this._options.modifiers.slice(0, getArrayKeyIndex(this._options.modifiers, ends)); + } + + modifiersToRun.forEach(function(modifier) { + if (isFunction(modifier)) { + data = modifier.call(this, data); + } + }.bind(this)); + + return data; + }; + + /** + * Helper used to know if the given modifier depends from another one. + * @method + * @memberof Popper + * @returns {Boolean} + */ + + Popper.prototype.isModifierRequired = function(requesting, requested) { + var index = getArrayKeyIndex(this._options.modifiers, requesting); + return !!this._options.modifiers.slice(0, index).filter(function(modifier) { + return modifier === requested; + }).length; + }; + + // + // Modifiers + // + + /** + * Modifiers list + * @namespace Popper.modifiers + * @memberof Popper + * @type {Object} + */ + Popper.prototype.modifiers = {}; + + /** + * Apply the computed styles to the popper element + * @method + * @memberof Popper.modifiers + * @argument {Object} data - The data object generated by `update` method + * @returns {Object} The same data object + */ + Popper.prototype.modifiers.applyStyle = function(data) { + // apply the final offsets to the popper + // NOTE: 1 DOM access here + var styles = { + position: data.offsets.popper.position + }; + + // round top and left to avoid blurry text + var left = Math.round(data.offsets.popper.left); + var top = Math.round(data.offsets.popper.top); + + // if gpuAcceleration is set to true and transform is supported, we use `translate3d` to apply the position to the popper + // we automatically use the supported prefixed version if needed + var prefixedProperty; + if (this._options.gpuAcceleration && (prefixedProperty = getSupportedPropertyName('transform'))) { + styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)'; + styles.top = 0; + styles.left = 0; + } + // othwerise, we use the standard `left` and `top` properties + else { + styles.left =left; + styles.top = top; + } + + // any property present in `data.styles` will be applied to the popper, + // in this way we can make the 3rd party modifiers add custom styles to it + // Be aware, modifiers could override the properties defined in the previous + // lines of this modifier! + Object.assign(styles, data.styles); + + setStyle(this._popper, styles); + + // set an attribute which will be useful to style the tooltip (use it to properly position its arrow) + // NOTE: 1 DOM access here + this._popper.setAttribute('x-placement', data.placement); + + // if the arrow modifier is required and the arrow style has been computed, apply the arrow style + if (this.isModifierRequired(this.modifiers.applyStyle, this.modifiers.arrow) && data.offsets.arrow) { + setStyle(data.arrowElement, data.offsets.arrow); + } + + return data; + }; + + /** + * Modifier used to shift the popper on the start or end of its reference element side + * @method + * @memberof Popper.modifiers + * @argument {Object} data - The data object generated by `update` method + * @returns {Object} The data object, properly modified + */ + Popper.prototype.modifiers.shift = function(data) { + var placement = data.placement; + var basePlacement = placement.split('-')[0]; + var shiftVariation = placement.split('-')[1]; + + // if shift shiftVariation is specified, run the modifier + if (shiftVariation) { + var reference = data.offsets.reference; + var popper = getPopperClientRect(data.offsets.popper); + + var shiftOffsets = { + y: { + start: { top: reference.top }, + end: { top: reference.top + reference.height - popper.height } + }, + x: { + start: { left: reference.left }, + end: { left: reference.left + reference.width - popper.width } + } + }; + + var axis = ['bottom', 'top'].indexOf(basePlacement) !== -1 ? 'x' : 'y'; + + data.offsets.popper = Object.assign(popper, shiftOffsets[axis][shiftVariation]); + } + + return data; + }; + + + /** + * Modifier used to make sure the popper does not overflows from it's boundaries + * @method + * @memberof Popper.modifiers + * @argument {Object} data - The data object generated by `update` method + * @returns {Object} The data object, properly modified + */ + Popper.prototype.modifiers.preventOverflow = function(data) { + var order = this._options.preventOverflowOrder; + var popper = getPopperClientRect(data.offsets.popper); + + var check = { + left: function() { + var left = popper.left; + if (popper.left < data.boundaries.left) { + left = Math.max(popper.left, data.boundaries.left); + } + return { left: left }; + }, + right: function() { + var left = popper.left; + if (popper.right > data.boundaries.right) { + left = Math.min(popper.left, data.boundaries.right - popper.width); + } + return { left: left }; + }, + top: function() { + var top = popper.top; + if (popper.top < data.boundaries.top) { + top = Math.max(popper.top, data.boundaries.top); + } + return { top: top }; + }, + bottom: function() { + var top = popper.top; + if (popper.bottom > data.boundaries.bottom) { + top = Math.min(popper.top, data.boundaries.bottom - popper.height); + } + return { top: top }; + } + }; + + order.forEach(function(direction) { + data.offsets.popper = Object.assign(popper, check[direction]()); + }); + + return data; + }; + + /** + * Modifier used to make sure the popper is always near its reference + * @method + * @memberof Popper.modifiers + * @argument {Object} data - The data object generated by _update method + * @returns {Object} The data object, properly modified + */ + Popper.prototype.modifiers.keepTogether = function(data) { + var popper = getPopperClientRect(data.offsets.popper); + var reference = data.offsets.reference; + var f = Math.floor; + + if (popper.right < f(reference.left)) { + data.offsets.popper.left = f(reference.left) - popper.width; + } + if (popper.left > f(reference.right)) { + data.offsets.popper.left = f(reference.right); + } + if (popper.bottom < f(reference.top)) { + data.offsets.popper.top = f(reference.top) - popper.height; + } + if (popper.top > f(reference.bottom)) { + data.offsets.popper.top = f(reference.bottom); + } + + return data; + }; + + /** + * Modifier used to flip the placement of the popper when the latter is starting overlapping its reference element. + * Requires the `preventOverflow` modifier before it in order to work. + * **NOTE:** This modifier will run all its previous modifiers everytime it tries to flip the popper! + * @method + * @memberof Popper.modifiers + * @argument {Object} data - The data object generated by _update method + * @returns {Object} The data object, properly modified + */ + Popper.prototype.modifiers.flip = function(data) { + // check if preventOverflow is in the list of modifiers before the flip modifier. + // otherwise flip would not work as expected. + if (!this.isModifierRequired(this.modifiers.flip, this.modifiers.preventOverflow)) { + console.warn('WARNING: preventOverflow modifier is required by flip modifier in order to work, be sure to include it before flip!'); + return data; + } + + if (data.flipped && data.placement === data._originalPlacement) { + // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides + return data; + } + + var placement = data.placement.split('-')[0]; + var placementOpposite = getOppositePlacement(placement); + var variation = data.placement.split('-')[1] || ''; + + var flipOrder = []; + if(this._options.flipBehavior === 'flip') { + flipOrder = [ + placement, + placementOpposite + ]; + } else { + flipOrder = this._options.flipBehavior; + } + + flipOrder.forEach(function(step, index) { + if (placement !== step || flipOrder.length === index + 1) { + return; + } + + placement = data.placement.split('-')[0]; + placementOpposite = getOppositePlacement(placement); + + var popperOffsets = getPopperClientRect(data.offsets.popper); + + // this boolean is used to distinguish right and bottom from top and left + // they need different computations to get flipped + var a = ['right', 'bottom'].indexOf(placement) !== -1; + + // using Math.floor because the reference offsets may contain decimals we are not going to consider here + if ( + a && Math.floor(data.offsets.reference[placement]) > Math.floor(popperOffsets[placementOpposite]) || + !a && Math.floor(data.offsets.reference[placement]) < Math.floor(popperOffsets[placementOpposite]) + ) { + // we'll use this boolean to detect any flip loop + data.flipped = true; + data.placement = flipOrder[index + 1]; + if (variation) { + data.placement += '-' + variation; + } + data.offsets.popper = this._getOffsets(this._popper, this._reference, data.placement).popper; + + data = this.runModifiers(data, this._options.modifiers, this._flip); + } + }.bind(this)); + return data; + }; + + /** + * Modifier used to add an offset to the popper, useful if you more granularity positioning your popper. + * The offsets will shift the popper on the side of its reference element. + * @method + * @memberof Popper.modifiers + * @argument {Object} data - The data object generated by _update method + * @returns {Object} The data object, properly modified + */ + Popper.prototype.modifiers.offset = function(data) { + var offset = this._options.offset; + var popper = data.offsets.popper; + + if (data.placement.indexOf('left') !== -1) { + popper.top -= offset; + } + else if (data.placement.indexOf('right') !== -1) { + popper.top += offset; + } + else if (data.placement.indexOf('top') !== -1) { + popper.left -= offset; + } + else if (data.placement.indexOf('bottom') !== -1) { + popper.left += offset; + } + return data; + }; + + /** + * Modifier used to move the arrows on the edge of the popper to make sure them are always between the popper and the reference element + * It will use the CSS outer size of the arrow element to know how many pixels of conjuction are needed + * @method + * @memberof Popper.modifiers + * @argument {Object} data - The data object generated by _update method + * @returns {Object} The data object, properly modified + */ + Popper.prototype.modifiers.arrow = function(data) { + var arrow = this._options.arrowElement; + + // if the arrowElement is a string, suppose it's a CSS selector + if (typeof arrow === 'string') { + arrow = this._popper.querySelector(arrow); + } + + // if arrow element is not found, don't run the modifier + if (!arrow) { + return data; + } + + // the arrow element must be child of its popper + if (!this._popper.contains(arrow)) { + console.warn('WARNING: `arrowElement` must be child of its popper element!'); + return data; + } + + // arrow depends on keepTogether in order to work + if (!this.isModifierRequired(this.modifiers.arrow, this.modifiers.keepTogether)) { + console.warn('WARNING: keepTogether modifier is required by arrow modifier in order to work, be sure to include it before arrow!'); + return data; + } + + var arrowStyle = {}; + var placement = data.placement.split('-')[0]; + var popper = getPopperClientRect(data.offsets.popper); + var reference = data.offsets.reference; + var isVertical = ['left', 'right'].indexOf(placement) !== -1; + + var len = isVertical ? 'height' : 'width'; + var side = isVertical ? 'top' : 'left'; + var altSide = isVertical ? 'left' : 'top'; + var opSide = isVertical ? 'bottom' : 'right'; + var arrowSize = getOuterSizes(arrow)[len]; + + // + // extends keepTogether behavior making sure the popper and its reference have enough pixels in conjuction + // + + // top/left side + if (reference[opSide] - arrowSize < popper[side]) { + data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowSize); + } + // bottom/right side + if (reference[side] + arrowSize > popper[opSide]) { + data.offsets.popper[side] += (reference[side] + arrowSize) - popper[opSide]; + } + + // compute center of the popper + var center = reference[side] + (reference[len] / 2) - (arrowSize / 2); + + var sideValue = center - popper[side]; + + // prevent arrow from being placed not contiguously to its popper + sideValue = Math.max(Math.min(popper[len] - arrowSize, sideValue), 0); + arrowStyle[side] = sideValue; + arrowStyle[altSide] = ''; // make sure to remove any old style from the arrow + + data.offsets.arrow = arrowStyle; + data.arrowElement = arrow; + + return data; + }; + + + // + // Helpers + // + + /** + * Get the outer sizes of the given element (offset size + margins) + * @function + * @ignore + * @argument {Element} element + * @returns {Object} object containing width and height properties + */ + function getOuterSizes(element) { + // NOTE: 1 DOM access here + var _display = element.style.display, _visibility = element.style.visibility; + element.style.display = 'block'; element.style.visibility = 'hidden'; + var calcWidthToForceRepaint = element.offsetWidth; + + // original method + var styles = root.getComputedStyle(element); + var x = parseFloat(styles.marginTop) + parseFloat(styles.marginBottom); + var y = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight); + var result = { width: element.offsetWidth + y, height: element.offsetHeight + x }; + + // reset element styles + element.style.display = _display; element.style.visibility = _visibility; + return result; + } + + /** + * Get the opposite placement of the given one/ + * @function + * @ignore + * @argument {String} placement + * @returns {String} flipped placement + */ + function getOppositePlacement(placement) { + var hash = {left: 'right', right: 'left', bottom: 'top', top: 'bottom' }; + return placement.replace(/left|right|bottom|top/g, function(matched){ + return hash[matched]; + }); + } + + /** + * Given the popper offsets, generate an output similar to getBoundingClientRect + * @function + * @ignore + * @argument {Object} popperOffsets + * @returns {Object} ClientRect like output + */ + function getPopperClientRect(popperOffsets) { + var offsets = Object.assign({}, popperOffsets); + offsets.right = offsets.left + offsets.width; + offsets.bottom = offsets.top + offsets.height; + return offsets; + } + + /** + * Given an array and the key to find, returns its index + * @function + * @ignore + * @argument {Array} arr + * @argument keyToFind + * @returns index or null + */ + function getArrayKeyIndex(arr, keyToFind) { + var i = 0, key; + for (key in arr) { + if (arr[key] === keyToFind) { + return i; + } + i++; + } + return null; + } + + /** + * Get CSS computed property of the given element + * @function + * @ignore + * @argument {Eement} element + * @argument {String} property + */ + function getStyleComputedProperty(element, property) { + // NOTE: 1 DOM access here + var css = root.getComputedStyle(element, null); + return css[property]; + } + + /** + * Returns the offset parent of the given element + * @function + * @ignore + * @argument {Element} element + * @returns {Element} offset parent + */ + function getOffsetParent(element) { + // NOTE: 1 DOM access here + var offsetParent = element.offsetParent; + return offsetParent === root.document.body || !offsetParent ? root.document.documentElement : offsetParent; + } + + /** + * Returns the scrolling parent of the given element + * @function + * @ignore + * @argument {Element} element + * @returns {Element} offset parent + */ + function getScrollParent(element) { + if (element === root.document) { + // Firefox puts the scrollTOp value on `documentElement` instead of `body`, we then check which of them is + // greater than 0 and return the proper element + if (root.document.body.scrollTop) { + return root.document.body; + } else { + return root.document.documentElement; + } + } + + // Firefox want us to check `-x` and `-y` variations as well + if ( + ['scroll', 'auto'].indexOf(getStyleComputedProperty(element, 'overflow')) !== -1 || + ['scroll', 'auto'].indexOf(getStyleComputedProperty(element, 'overflow-x')) !== -1 || + ['scroll', 'auto'].indexOf(getStyleComputedProperty(element, 'overflow-y')) !== -1 + ) { + return element; + } + return element.parentNode ? getScrollParent(element.parentNode) : element; + } + + /** + * Check if the given element is fixed or is inside a fixed parent + * @function + * @ignore + * @argument {Element} element + * @argument {Element} customContainer + * @returns {Boolean} answer to "isFixed?" + */ + function isFixed(element) { + if (element === root.document.body) { + return false; + } + if (getStyleComputedProperty(element, 'position') === 'fixed') { + return true; + } + return element.parentNode ? isFixed(element.parentNode) : element; + } + + /** + * Set the style to the given popper + * @function + * @ignore + * @argument {Element} element - Element to apply the style to + * @argument {Object} styles - Object with a list of properties and values which will be applied to the element + */ + function setStyle(element, styles) { + function is_numeric(n) { + return (n !== '' && !isNaN(parseFloat(n)) && isFinite(n)); + } + Object.keys(styles).forEach(function(prop) { + var unit = ''; + // add unit if the value is numeric and is one of the following + if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && is_numeric(styles[prop])) { + unit = 'px'; + } + element.style[prop] = styles[prop] + unit; + }); + } + + /** + * Check if the given variable is a function + * @function + * @ignore + * @argument {Element} element - Element to check + * @returns {Boolean} answer to: is a function? + */ + function isFunction(functionToCheck) { + var getType = {}; + return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; + } + + /** + * Get the position of the given element, relative to its offset parent + * @function + * @ignore + * @param {Element} element + * @return {Object} position - Coordinates of the element and its `scrollTop` + */ + function getOffsetRect(element) { + var elementRect = { + width: element.offsetWidth, + height: element.offsetHeight, + left: element.offsetLeft, + top: element.offsetTop + }; + + elementRect.right = elementRect.left + elementRect.width; + elementRect.bottom = elementRect.top + elementRect.height; + + // position + return elementRect; + } + + /** + * Get bounding client rect of given element + * @function + * @ignore + * @param {HTMLElement} element + * @return {Object} client rect + */ + function getBoundingClientRect(element) { + var rect = element.getBoundingClientRect(); + return { + left: rect.left, + top: rect.top, + right: rect.right, + bottom: rect.bottom, + width: rect.right - rect.left, + height: rect.bottom - rect.top + }; + } + + /** + * Given an element and one of its parents, return the offset + * @function + * @ignore + * @param {HTMLElement} element + * @param {HTMLElement} parent + * @return {Object} rect + */ + function getOffsetRectRelativeToCustomParent(element, parent, fixed) { + var elementRect = getBoundingClientRect(element); + var parentRect = getBoundingClientRect(parent); + + if (fixed) { + var scrollParent = getScrollParent(parent); + parentRect.top += scrollParent.scrollTop; + parentRect.bottom += scrollParent.scrollTop; + parentRect.left += scrollParent.scrollLeft; + parentRect.right += scrollParent.scrollLeft; + } + + var rect = { + top: elementRect.top - parentRect.top , + left: elementRect.left - parentRect.left , + bottom: (elementRect.top - parentRect.top) + elementRect.height, + right: (elementRect.left - parentRect.left) + elementRect.width, + width: elementRect.width, + height: elementRect.height + }; + return rect; + } + + /** + * Get the prefixed supported property name + * @function + * @ignore + * @argument {String} property (camelCase) + * @returns {String} prefixed property (camelCase) + */ + function getSupportedPropertyName(property) { + var prefixes = ['', 'ms', 'webkit', 'moz', 'o']; + + for (var i = 0; i < prefixes.length; i++) { + var toCheck = prefixes[i] ? prefixes[i] + property.charAt(0).toUpperCase() + property.slice(1) : property; + if (typeof root.document.body.style[toCheck] !== 'undefined') { + return toCheck; + } + } + return null; + } + + /** + * The Object.assign() method is used to copy the values of all enumerable own properties from one or more source + * objects to a target object. It will return the target object. + * This polyfill doesn't support symbol properties, since ES5 doesn't have symbols anyway + * Source: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign + * @function + * @ignore + */ + if (!Object.assign) { + Object.defineProperty(Object, 'assign', { + enumerable: false, + configurable: true, + writable: true, + value: function(target) { + if (target === undefined || target === null) { + throw new TypeError('Cannot convert first argument to object'); + } + + var to = Object(target); + for (var i = 1; i < arguments.length; i++) { + var nextSource = arguments[i]; + if (nextSource === undefined || nextSource === null) { + continue; + } + nextSource = Object(nextSource); + + var keysArray = Object.keys(nextSource); + for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { + var nextKey = keysArray[nextIndex]; + var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); + if (desc !== undefined && desc.enumerable) { + to[nextKey] = nextSource[nextKey]; + } + } + } + return to; + } + }); + } + + return Popper; +})); diff --git a/src/utils/vue-popper.js b/src/utils/vue-popper.js new file mode 100644 index 000000000..9f8c58e09 --- /dev/null +++ b/src/utils/vue-popper.js @@ -0,0 +1,144 @@ +import PopperJS from './popper'; + +/** + * @param {HTMLElement} [reference=$els.reference] - The reference element used to position the popper. + * @param {HTMLElement} [popper=$els.popper] - The HTML element used as popper, or a configuration used to generate the popper. + * @param {String} [placement=button] - Placement of the popper accepted values: top(-start, -end), right(-start, -end), bottom(-start, -right), left(-start, -end) + * @param {Number} [offset=0] - Amount of pixels the popper will be shifted (can be negative). + * @param {Boolean} [visible=false] Visibility of the popup element. + * @param {Boolean} [visible-arrow=false] Visibility of the arrow, no style. + */ +export default { + props: { + placement: { + type: String, + default: 'bottom' + }, + boundariesPadding: { + type: Number, + default: 5 + }, + reference: Object, + popper: Object, + offset: { + default: 0 + }, + visible: Boolean, + visibleArrow: Boolean, + transition: String, + options: { + type: Object, + default() { + return {}; + } + } + }, + + watch: { + 'visible'(val) { + if (this.popperDestroying) return; + val ? this.updatePopper() : this.destroyPopper(); + } + }, + + methods: { + createPopper() { + if (!/^(top|bottom|left|right)(-start|-end)?$/g.test(this.placement)) { + return; + } + + this.popper = this.popper || this.$els.popper; + this.reference = this.reference || this.$els.reference; + + if (!this.popper || !this.reference) { + return; + } + + if (this.visibleArrow) { + this.appendArrow(this.popper); + } + + if (this.popperJS && this.popperJS.hasOwnProperty('destroy')) { + this.popperJS.destroy(); + } + + this.$set('options.placement', this.placement); + this.$set('options.offset', this.offset); + + this.popperJS = new PopperJS( + this.reference, + this.popper, + this.options + ); + this.popperJS.onCreate(popper => { + this.resetTransformOrigin(popper); + this.$emit('created', this); + }); + }, + + updatePopper() { + if (this.popperJS) { + this.popperJS.update(); + } else { + this.createPopper(); + } + }, + + doDestroy() { + if (this.visible) return; + + this.popperJS._popper.removeEventListener('transitionend', this.doDestroy); + this.popperJS.destroy(); + this.popperJS = null; + }, + + destroyPopper() { + if (this.popperJS) { + this.resetTransformOrigin(this.popperJS); + if (this.transition) { + this.popperJS._popper.addEventListener('transitionend', this.doDestroy); + } else { + this.doDestroy(); + } + } + }, + + resetTransformOrigin(popper) { + let placementMap = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' }; + let placement = popper._popper.getAttribute('x-placement').split('-')[0]; + let origin = placementMap[placement]; + popper._popper.style.transformOrigin = ['top', 'bottom'].indexOf(placement) > -1 ? `center ${ origin }` : `${ origin } center`; + }, + + appendArrow(element) { + let hash; + if (this.appended) { + return; + } + + this.appended = true; + + for (let item in element.attributes) { + if (/^_v-/.test(element.attributes[item].name)) { + hash = element.attributes[item].name; + break; + } + } + + const arrow = document.createElement('div'); + + if (hash) { + arrow.setAttribute(hash, ''); + } + arrow.setAttribute('x-arrow', ''); + arrow.className = 'popper__arrow'; + element.appendChild(arrow); + } + }, + + beforeDestroy() { + if (this.popperJS) { + this.popperJS.destroy(); + } + } +};