From 1a16fbb7356373be3ea4f905ca67b65797aab5b6 Mon Sep 17 00:00:00 2001 From: baiyaaaaa Date: Mon, 16 Jan 2017 23:16:34 +0800 Subject: [PATCH] add cascader --- components.json | 3 +- examples/docs/en-US/cascader.md | 1 + examples/docs/zh-CN/cascader.md | 109 +++++++++++++ examples/nav.config.json | 8 + packages/cascader/cooking.conf.js | 18 +++ packages/cascader/index.js | 8 + packages/cascader/package.json | 15 ++ packages/cascader/src/main.vue | 207 ++++++++++++++++++++++++ packages/cascader/src/menu.vue | 133 +++++++++++++++ packages/theme-default/src/cascader.css | 142 +++++++++++++--- packages/theme-default/src/index.css | 1 + src/index.js | 7 +- src/utils/vue-popper.js | 1 + test/unit/specs/cascader.spec.js | 15 ++ 14 files changed, 641 insertions(+), 27 deletions(-) create mode 100644 examples/docs/en-US/cascader.md create mode 100644 examples/docs/zh-CN/cascader.md create mode 100644 packages/cascader/cooking.conf.js create mode 100644 packages/cascader/index.js create mode 100644 packages/cascader/package.json create mode 100644 packages/cascader/src/main.vue create mode 100644 packages/cascader/src/menu.vue create mode 100644 test/unit/specs/cascader.spec.js diff --git a/components.json b/components.json index 966d4a473..314321185 100644 --- a/components.json +++ b/components.json @@ -58,5 +58,6 @@ "scrollbar": "./packages/scrollbar/index.js", "carousel-item": "./packages/carousel-item/index.js", "collapse": "./packages/collapse/index.js", - "collapse-item": "./packages/collapse-item/index.js" + "collapse-item": "./packages/collapse-item/index.js", + "cascader": "./packages/cascader/index.js" } diff --git a/examples/docs/en-US/cascader.md b/examples/docs/en-US/cascader.md new file mode 100644 index 000000000..01bc1fc8f --- /dev/null +++ b/examples/docs/en-US/cascader.md @@ -0,0 +1 @@ +## cascader diff --git a/examples/docs/zh-CN/cascader.md b/examples/docs/zh-CN/cascader.md new file mode 100644 index 000000000..8739e22b6 --- /dev/null +++ b/examples/docs/zh-CN/cascader.md @@ -0,0 +1,109 @@ + + +## 级联选择 + +需要从一组相关联的数据集合进行选择,例如省市区,公司层级,事物分类等。 + +从一个较大的数据集合中进行选择时,用多级分类进行分隔,方便选择。 + +### 基本使用 + +:::demo +```html + +``` +::: + +### 默认值 + +:::demo +```html + +``` +::: + +### 移入展开 + +:::demo +```html + +``` +::: + +### 选择即改变 + +:::demo +```html + +``` +::: + +### 可搜索 + +:::demo +```html + +``` +::: \ No newline at end of file diff --git a/examples/nav.config.json b/examples/nav.config.json index 9f78e055f..fdeb534a1 100644 --- a/examples/nav.config.json +++ b/examples/nav.config.json @@ -215,6 +215,10 @@ { "path": "/collapse", "title": "Collapse 折叠面板" + }, + { + "path": "/cascader", + "title": "Cascader 级联选择" } ] } @@ -437,6 +441,10 @@ { "path": "/collapse", "title": "Collapse" + }, + { + "path": "/cascader", + "title": "Cascader" } ] } diff --git a/packages/cascader/cooking.conf.js b/packages/cascader/cooking.conf.js new file mode 100644 index 000000000..3bbec53de --- /dev/null +++ b/packages/cascader/cooking.conf.js @@ -0,0 +1,18 @@ +var cooking = require('cooking'); +var path = require('path'); +var config = require('../../build/config'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElCascader', + extends: ['vue2'], + alias: config.alias, + externals: { vue: config.vue } +}); + +module.exports = cooking.resolve(); diff --git a/packages/cascader/index.js b/packages/cascader/index.js new file mode 100644 index 000000000..68163ac5e --- /dev/null +++ b/packages/cascader/index.js @@ -0,0 +1,8 @@ +import Cascader from './src/main'; + +/* istanbul ignore next */ +Cascader.install = function(Vue) { + Vue.component(Cascader.name, Cascader); +}; + +export default Cascader; diff --git a/packages/cascader/package.json b/packages/cascader/package.json new file mode 100644 index 000000000..65e1961f9 --- /dev/null +++ b/packages/cascader/package.json @@ -0,0 +1,15 @@ +{ + "name": "element-cascader", + "version": "0.0.0", + "description": "A cascader component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/ElemeFE/element/tree/master/packages/cascader", + "author": "elemefe", + "license": "MIT", + "dependencies": {} +} diff --git a/packages/cascader/src/main.vue b/packages/cascader/src/main.vue new file mode 100644 index 000000000..47e3eaaa6 --- /dev/null +++ b/packages/cascader/src/main.vue @@ -0,0 +1,207 @@ + + + diff --git a/packages/cascader/src/menu.vue b/packages/cascader/src/menu.vue new file mode 100644 index 000000000..927431d96 --- /dev/null +++ b/packages/cascader/src/menu.vue @@ -0,0 +1,133 @@ + \ No newline at end of file diff --git a/packages/theme-default/src/cascader.css b/packages/theme-default/src/cascader.css index a79fe34e2..10370d653 100644 --- a/packages/theme-default/src/cascader.css +++ b/packages/theme-default/src/cascader.css @@ -3,42 +3,136 @@ @import "./common/var.css"; /*@import "./core/dropdown.css";*/ -@component-namespace element { +@component-namespace el { @b cascader { display: inline-block; position: relative; + background-color: #fff; - @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); + .el-input, + .el-input__inner { + cursor: pointer; + background-color: transparent; + z-index: 1; + } + + .el-input__icon { + transition: none; + } + + .el-icon-caret-bottom { + transition: transform .3s; + + @when reverse { + transform: rotateZ(180deg); + } + } + + @e label { position: absolute; + left: 0; + top: 0; + height: var(--input-height); + line-height: @height; + padding: 0 15px 0 10px; + color: var(--input-color); + width: 100%; white-space: nowrap; - z-index: 10; - } - - @e wrap { + text-overflow: ellipsis; overflow: hidden; + box-sizing: border-box; + cursor: pointer; + } + } + + @b cascader-menus { + white-space: nowrap; + background: #fff; + position: absolute; + margin: 5px 0; + z-index: 1001; + border: var(--select-dropdown-border); + border-radius: var(--border-radius-small); + overflow: hidden; + box-shadow: var(--select-dropdown-shadow); + } + + @b cascader-menu { + display: inline-block; + vertical-align: top; + height: 180px; + overflow: auto; + border-right: var(--select-dropdown-border); + background-color: var(--select-dropdown-background); + box-sizing: border-box; + margin: 0; + padding: 0; + min-width: 110px; + + &:last-child { + border-right: 0; } - @e menu { - border: 0; - box-shadow: none; - display: inline-block; - margin: 0; + @e item { + font-size: var(--select-font-size); + padding: 8px 30px 8px 10px; position: relative; - vertical-align: top; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: var(--select-option-color); + height: var(--select-option-height); + line-height: 1.5; + box-sizing: border-box; + cursor: pointer; - &::before { - border-left: var(--cascader-menu-border); - content: " "; - height: var(--cascader-height); - left: 0; - position: absolute; + @e keyword { + color: var(--color-danger); + } + + @m extensible { + &:after { + font-family: 'element-icons'; + content: "\e602"; + font-size: 12px; + transform: scale(0.8); + color: rgb(191, 203, 217); + position: absolute; + right: 10px; + margin-top: 1px; + } + } + + @when disabled { + color: var(--select-option-disabled-color); + cursor: not-allowed; + + &:hover { + background-color: var(--color-white); + } + } + + @when active { + color: var(--color-white); + background-color: var(--select-option-selected); + + &.hover { + background-color: var(--select-option-selected-hover); + } + } + + &:hover { + background-color: var(--select-option-hover-background); + } + + &.selected { + color: var(--color-white); + background-color: var(--select-option-selected); + + &.hover { + background-color: var(--select-option-selected-hover); + } } } } diff --git a/packages/theme-default/src/index.css b/packages/theme-default/src/index.css index 544ee48d7..67e499d73 100644 --- a/packages/theme-default/src/index.css +++ b/packages/theme-default/src/index.css @@ -44,3 +44,4 @@ @import "./carousel.css"; @import "./carousel-item.css"; @import "./collapse.css"; +@import "./cascader.css"; diff --git a/src/index.js b/src/index.js index cfa8c6c8d..36fd0fefc 100644 --- a/src/index.js +++ b/src/index.js @@ -60,6 +60,7 @@ import Scrollbar from '../packages/scrollbar'; import CarouselItem from '../packages/carousel-item'; import Collapse from '../packages/collapse'; import CollapseItem from '../packages/collapse-item'; +import Cascader from '../packages/cascader'; import locale from 'element-ui/src/locale'; const components = [ @@ -118,7 +119,8 @@ const components = [ Scrollbar, CarouselItem, Collapse, - CollapseItem + CollapseItem, + Cascader ]; const install = function(Vue, opts = {}) { @@ -211,5 +213,6 @@ module.exports = { Scrollbar, CarouselItem, Collapse, - CollapseItem + CollapseItem, + Cascader }; diff --git a/src/utils/vue-popper.js b/src/utils/vue-popper.js index 18c02c8f6..0b5ee1504 100644 --- a/src/utils/vue-popper.js +++ b/src/utils/vue-popper.js @@ -83,6 +83,7 @@ export default { this.$slots.reference[0]) { reference = this.referenceElm = this.$slots.reference[0].elm; } + if (!popper || !reference) return; if (this.visibleArrow) this.appendArrow(popper); if (this.appendToBody) document.body.appendChild(this.popperElm); diff --git a/test/unit/specs/cascader.spec.js b/test/unit/specs/cascader.spec.js new file mode 100644 index 000000000..2d6a868e7 --- /dev/null +++ b/test/unit/specs/cascader.spec.js @@ -0,0 +1,15 @@ +import { createTest, destroyVM } from '../util'; +import Cascader from 'packages/cascader'; + +describe('Cascader', () => { + let vm; + afterEach(() => { + destroyVM(vm); + }); + + it('create', () => { + vm = createTest(Cascader, true); + expect(vm.$el).to.exist; + }); +}); +