Commit e355dd49d3434c503c21db078ee684a490e250fc

Authored by 梁灏
1 parent c13471b5

add Select Component

add Select Component
components/collapse/panel.vue
... ... @@ -4,7 +4,7 @@
4 4 <Icon type="arrow-right-b"></Icon>
5 5 <slot></slot>
6 6 </div>
7   - <div :class="concentClasses" v-show="isActive" transition="height">
  7 + <div :class="concentClasses" v-show="isActive">
8 8 <div :class="boxClasses"><slot name="content"></slot></div>
9 9 </div>
10 10 </div>
... ...
components/page/options.vue
1 1 <template>
2 2 <div v-if="showSizer || showElevator" :class="optsClasses">
3 3 <div v-if="showSizer" :class="sizerClasses">
4   - <select v-model="pageSize" @change="changeSize">
5   - <option :value="item" v-for="item in pageSizeOpts">{{ item }} 条/页</option>
6   - </select>
  4 + <i-select :model.sync="pageSize" @on-change="changeSize">
  5 + <i-option v-for="item in pageSizeOpts" :value="item" style="text-align:center;">{{ item }} 条/页</i-option>
  6 + </i-select>
7 7 </div>
8 8 <div v-if="showElevator" :class="ElevatorClasses">
9 9 跳至
... ... @@ -13,6 +13,9 @@
13 13 </div>
14 14 </template>
15 15 <script>
  16 + import iSelect from '../../components/select/select.vue';
  17 + import iOption from '../../components/select/option.vue';
  18 +
16 19 const prefixCls = 'ivu-page';
17 20  
18 21 function isValueNumber (value) {
... ... @@ -20,6 +23,7 @@
20 23 }
21 24  
22 25 export default {
  26 + components: { iSelect, iOption },
23 27 props: {
24 28 pageSizeOpts: Array,
25 29 showSizer: Boolean,
... ...
components/select/dropdown.vue 0 → 100644
  1 +<template>
  2 + <div class="ivu-select-dropdown"><slot></slot></div>
  3 +</template>
  4 +<script>
  5 + import Popper from 'popper.js';
  6 +
  7 + export default {
  8 + data () {
  9 + return {
  10 + popper: null
  11 + }
  12 + },
  13 + methods: {
  14 + update () {
  15 + if (this.popper) {
  16 + this.$nextTick(() => {
  17 + this.popper.update();
  18 + });
  19 + } else {
  20 + this.$nextTick(() => {
  21 + this.popper = new Popper(this.$parent.$els.reference, this.$el, {
  22 + gpuAcceleration: false,
  23 + placement: 'bottom-start',
  24 + boundariesPadding: 0,
  25 + forceAbsolute: true
  26 + });
  27 + this.popper.onCreate(popper => {
  28 + this.resetTransformOrigin(popper);
  29 + });
  30 + });
  31 + }
  32 + },
  33 + destroy () {
  34 + if (this.popper) {
  35 + this.resetTransformOrigin(this.popper);
  36 + setTimeout(() => {
  37 + this.popper.destroy();
  38 + this.popper = null;
  39 + }, 300);
  40 + }
  41 + },
  42 + resetTransformOrigin(popper) {
  43 + let placementMap = {top: 'bottom', bottom: 'top'};
  44 + let placement = popper._popper.getAttribute('x-placement').split('-')[0];
  45 + let origin = placementMap[placement];
  46 + popper._popper.style.transformOrigin = `center ${ origin }`;
  47 + }
  48 + },
  49 + ready () {
  50 + this.$on('on-update-popper', this.update);
  51 + this.$on('on-destroy-popper', this.destroy);
  52 + },
  53 + beforeDestroy () {
  54 + if (this.popper) {
  55 + this.popper.destroy();
  56 + }
  57 + }
  58 + }
  59 +</script>
0 60 \ No newline at end of file
... ...
components/select/index.js 0 → 100644
  1 +import Select from './select.vue';
  2 +import Option from './option.vue';
  3 +import OptionGroup from './option-group.vue';
  4 +
  5 +export { Select, Option, OptionGroup };
0 6 \ No newline at end of file
... ...
components/select/option-group.vue 0 → 100644
  1 +<template>
  2 + <li :class="[`${prefixCls}-wrap`]">
  3 + <div :class="[`${prefixCls}-title`]">{{ label }}</div>
  4 + <ul>
  5 + <li :class="[`${prefixCls}`]"><slot></slot></li>
  6 + </ul>
  7 + </li>
  8 +</template>
  9 +<script>
  10 + const prefixCls = 'ivu-select-group';
  11 +
  12 + export default {
  13 + props: {
  14 + label: {
  15 + type: String,
  16 + default: ''
  17 + }
  18 + },
  19 + data () {
  20 + return {
  21 + prefixCls: prefixCls
  22 + }
  23 + }
  24 + }
  25 +</script>
0 26 \ No newline at end of file
... ...
components/select/option.vue 0 → 100644
  1 +<template>
  2 + <li :class="classes" @click.stop="select" @mouseout.stop="blur"><slot>{{ showLabel }}</slot></li>
  3 +</template>
  4 +<script>
  5 + const prefixCls = 'ivu-select-item';
  6 +
  7 + export default {
  8 + props: {
  9 + value: {
  10 + type: [String, Number],
  11 + required: true
  12 + },
  13 + label: {
  14 + type: [String, Number]
  15 + },
  16 + disabled: {
  17 + type: Boolean,
  18 + default: false
  19 + }
  20 + },
  21 + componentName: 'select-item',
  22 + data () {
  23 + return {
  24 + selected: false,
  25 + index: 0, // for up and down to focus
  26 + isFocus: false
  27 + }
  28 + },
  29 + computed: {
  30 + classes () {
  31 + return [
  32 + `${prefixCls}`,
  33 + {
  34 + [`${prefixCls}-disabled`]: this.disabled,
  35 + [`${prefixCls}-selected`]: this.selected,
  36 + [`${prefixCls}-focus`]: this.isFocus
  37 + }
  38 + ]
  39 + },
  40 + showLabel () {
  41 + return (!!this.label) ? this.label : this.value;
  42 + }
  43 + },
  44 + methods: {
  45 + select () {
  46 + if (this.disabled) {
  47 + return false;
  48 + }
  49 +
  50 + this.$dispatch('on-select-selected', this.value);
  51 + },
  52 + blur () {
  53 + this.isFocus = false;
  54 + }
  55 + },
  56 + ready () {
  57 +
  58 + },
  59 + events: {
  60 + 'on-select-close' () {
  61 + this.isFocus = false;
  62 + }
  63 + }
  64 + }
  65 +</script>
0 66 \ No newline at end of file
... ...
components/select/select.vue 0 → 100644
  1 +<template>
  2 + <div :class="classes" v-clickoutside="handleClose">
  3 + <div
  4 + :class="[`${prefixCls}-selection`]"
  5 + v-el:reference
  6 + @click="toggleMenu">
  7 + <div class="ivu-tag" v-if="multiple" v-for="item in selectedMultiple">
  8 + <span class="ivu-tag-text">{{ item.label }}</span>
  9 + <Icon type="ios-close-empty" @click.stop="removeTag($index)"></Icon>
  10 + </div>
  11 + <span :class="[`${prefixCls}-placeholder`]" v-show="showPlaceholder && !filterable">{{ placeholder }}</span>
  12 + <span :class="[`${prefixCls}-selected-value`]" v-show="!showPlaceholder && !multiple && !filterable">{{ selectedSingle }}</span>
  13 + <input
  14 + type="text"
  15 + :class="[`${prefixCls}-input`]"
  16 + v-if="filterable"
  17 + v-model="query"
  18 + :placeholder="placeholder"
  19 + :style="inputStyle">
  20 + <Icon type="ios-close" :class="[`${prefixCls}-arrow`]" v-show="showCloseIcon" @click.stop="clearSingleSelect"></Icon>
  21 + <Icon type="arrow-down-b" :class="[`${prefixCls}-arrow`]"></Icon>
  22 + </div>
  23 + <Dropdown v-show="visible" transition="slide-up" v-ref:dropdown>
  24 + <ul :class="[`${prefixCls}-dropdown-list`]"><slot></slot></ul>
  25 + </Dropdown>
  26 + </div>
  27 +</template>
  28 +<script>
  29 + import Icon from '../icon';
  30 + import Dropdown from './dropdown.vue';
  31 + import clickoutside from '../../directives/clickoutside';
  32 + import { oneOf } from '../../utils/assist';
  33 +
  34 + const prefixCls = 'ivu-select';
  35 +
  36 + export default {
  37 + components: { Icon, Dropdown },
  38 + directives: { clickoutside },
  39 + props: {
  40 + model: {
  41 + type: [String, Number, Array],
  42 + default: ''
  43 + },
  44 + multiple: {
  45 + type: Boolean,
  46 + default: false
  47 + },
  48 + disabled: {
  49 + type: Boolean,
  50 + default: false
  51 + },
  52 + clearable: {
  53 + type: Boolean,
  54 + default: false
  55 + },
  56 + placeholder: {
  57 + type: String,
  58 + default: '请选择'
  59 + },
  60 + filterable: {
  61 + type: Boolean,
  62 + default: false
  63 + },
  64 + filterMethod: {
  65 + type: Function
  66 + },
  67 + size: {
  68 + validator (value) {
  69 + return oneOf(value, ['small', 'large']);
  70 + }
  71 + },
  72 + labelInValue: {
  73 + type: Boolean,
  74 + default: false
  75 + }
  76 + },
  77 + data () {
  78 + return {
  79 + prefixCls: prefixCls,
  80 + visible: false,
  81 + options: [],
  82 + optionInstances: [],
  83 + selectedSingle: '', // label
  84 + selectedMultiple: [],
  85 + focusIndex: 0,
  86 + query: '',
  87 + inputLength: 20
  88 + }
  89 + },
  90 + computed: {
  91 + classes () {
  92 + return [
  93 + `${prefixCls}`,
  94 + {
  95 + [`${prefixCls}-visible`]: this.visible,
  96 + [`${prefixCls}-disabled`]: this.disabled,
  97 + [`${prefixCls}-multiple`]: this.multiple,
  98 + [`${prefixCls}-single`]: !this.multiple,
  99 + [`${prefixCls}-show-clear`]: this.showCloseIcon,
  100 + [`${prefixCls}-${this.size}`]: !!this.size
  101 + }
  102 + ]
  103 + },
  104 + showPlaceholder () {
  105 + let status = false;
  106 +
  107 + if ((typeof this.model) === 'string') {
  108 + if (this.model === '') {
  109 + status = true;
  110 + }
  111 + } else if (Array.isArray(this.model)) {
  112 + if (!this.model.length) {
  113 + status = true;
  114 + }
  115 + }
  116 +
  117 + return status;
  118 + },
  119 + showCloseIcon () {
  120 + return !this.multiple && this.clearable && !this.showPlaceholder;
  121 + },
  122 + inputStyle () {
  123 + let style = {};
  124 +
  125 + if (this.multiple) {
  126 + style.width = `${this.inputLength}px`;
  127 + }
  128 +
  129 + return style;
  130 + }
  131 + },
  132 + methods: {
  133 + toggleMenu () {
  134 + if (this.disabled) {
  135 + return false;
  136 + }
  137 +
  138 + this.visible = !this.visible;
  139 + },
  140 + hideMenu () {
  141 + this.visible = false;
  142 + this.focusIndex = 0;
  143 + this.$broadcast('on-select-close');
  144 + },
  145 + // find option component
  146 + findChild (cb) {
  147 + const find = function (child) {
  148 + const name = child.$options.componentName;
  149 +
  150 + if (name) {
  151 + cb(child);
  152 + } else if (child.$children.length) {
  153 + child.$children.forEach((innerChild) => {
  154 + find(innerChild, cb);
  155 + });
  156 + }
  157 + };
  158 +
  159 + if (this.optionInstances.length) {
  160 + this.optionInstances.forEach((child) => {
  161 + find(child);
  162 + })
  163 + } else {
  164 + this.$children.forEach((child) => {
  165 + find(child);
  166 + });
  167 + }
  168 + },
  169 + updateOptions (init) {
  170 + let options = [];
  171 + let index = 1;
  172 +
  173 + this.findChild((child) => {
  174 + options.push({
  175 + value: child.value,
  176 + label: (child.label === undefined) ? child.$el.innerHTML : child.label
  177 + });
  178 + child.index = index++;
  179 +
  180 + if (init) {
  181 + this.optionInstances.push(child);
  182 + }
  183 + });
  184 +
  185 + this.options = options;
  186 +
  187 + if (init) {
  188 + this.updateSingleSelected(true);
  189 + this.updateMultipleSelected(true);
  190 + }
  191 + },
  192 + updateSingleSelected (init = false) {
  193 + const type = typeof this.model;
  194 +
  195 + if (type === 'string' || type === 'number') {
  196 + for (let i = 0; i < this.options.length; i++) {
  197 + if (this.model === this.options[i].value) {
  198 + this.selectedSingle = this.options[i].label;
  199 + break;
  200 + }
  201 + }
  202 + }
  203 +
  204 + this.toggleSingleSelected(this.model, init);
  205 + },
  206 + clearSingleSelect () {
  207 + if (this.showCloseIcon) {
  208 + this.findChild((child) => {
  209 + child.selected = false;
  210 + });
  211 + this.model = '';
  212 + }
  213 + },
  214 + updateMultipleSelected (init = false) {
  215 + if (this.multiple && Array.isArray(this.model)) {
  216 + let selected = [];
  217 +
  218 + for (let i = 0; i < this.model.length; i++) {
  219 + const model = this.model[i];
  220 +
  221 + for (let j = 0; j < this.options.length; j++) {
  222 + const option = this.options[j];
  223 +
  224 + if (model === option.value) {
  225 + selected.push({
  226 + value: option.value,
  227 + label: option.label
  228 + })
  229 + }
  230 + }
  231 + }
  232 +
  233 + this.selectedMultiple = selected;
  234 + }
  235 +
  236 + this.toggleMultipleSelected(this.model, init);
  237 + },
  238 + removeTag (index) {
  239 + if (this.disabled) {
  240 + return false;
  241 + }
  242 + this.model.splice(index, 1);
  243 + },
  244 + // to select option for single
  245 + toggleSingleSelected (value, init = false) {
  246 + if (!this.multiple) {
  247 + let label = '';
  248 +
  249 + this.findChild((child) => {
  250 + if (child.value === value) {
  251 + child.selected = true;
  252 + label = (child.label === undefined) ? child.$el.innerHTML : child.label;
  253 + } else {
  254 + child.selected = false;
  255 + }
  256 + });
  257 +
  258 + this.hideMenu();
  259 +
  260 + if (!init) {
  261 + if (this.labelInValue) {
  262 + this.$emit('on-change', {
  263 + value: value,
  264 + label: label
  265 + });
  266 + } else {
  267 + this.$emit('on-change', value);
  268 + }
  269 + }
  270 + }
  271 + },
  272 + // to select option for multiple
  273 + toggleMultipleSelected (value, init = false) {
  274 + if (this.multiple) {
  275 + let hybridValue = [];
  276 + for (let i = 0; i < value.length; i++) {
  277 + hybridValue.push({
  278 + value: value[i]
  279 + });
  280 + }
  281 +
  282 + this.findChild((child) => {
  283 + const index = value.indexOf(child.value);
  284 +
  285 + if (index >= 0) {
  286 + child.selected = true;
  287 + hybridValue[index].label = (child.label === undefined) ? child.$el.innerHTML : child.label;
  288 + } else {
  289 + child.selected = false;
  290 + }
  291 + });
  292 +
  293 + if (!init) {
  294 + if (this.labelInValue) {
  295 + this.$emit('on-change', hybridValue);
  296 + } else {
  297 + this.$emit('on-change', value);
  298 + }
  299 + }
  300 + }
  301 + },
  302 + handleClose () {
  303 + this.hideMenu();
  304 + },
  305 + handleKeydown (e) {
  306 + if (this.visible) {
  307 + const keyCode = e.keyCode;
  308 + // Esc slide-up
  309 + if (keyCode === 27) {
  310 + e.preventDefault();
  311 + this.hideMenu();
  312 + }
  313 + // next
  314 + if (keyCode === 40) {
  315 + e.preventDefault();
  316 + this.navigateOptions('next');
  317 + }
  318 + // prev
  319 + if (keyCode === 38) {
  320 + e.preventDefault();
  321 + this.navigateOptions('prev');
  322 + }
  323 + // enter
  324 + if (keyCode === 13) {
  325 + e.preventDefault();
  326 +
  327 + this.findChild((child) => {
  328 + if (child.isFocus) {
  329 + child.select();
  330 + }
  331 + });
  332 + }
  333 + }
  334 + },
  335 + navigateOptions (direction) {
  336 + if (direction === 'next') {
  337 + const next = this.focusIndex + 1;
  338 + this.focusIndex = (this.focusIndex === this.options.length) ? 1 : next;
  339 + } else if (direction === 'prev') {
  340 + const prev = this.focusIndex - 1;
  341 + this.focusIndex = (this.focusIndex <= 1) ? this.options.length : prev;
  342 + }
  343 +
  344 + let child_status = {
  345 + disabled: false
  346 + };
  347 +
  348 + this.findChild((child) => {
  349 + if (child.index === this.focusIndex) {
  350 + child_status.disabled = child.disabled;
  351 +
  352 + if (!child.disabled) {
  353 + child.isFocus = true;
  354 + }
  355 + } else {
  356 + child.isFocus = false;
  357 + }
  358 + });
  359 +
  360 + this.resetScrollTop();
  361 +
  362 + if (child_status.disabled) {
  363 + this.navigateOptions(direction);
  364 + }
  365 + },
  366 + resetScrollTop () {
  367 + const index = this.focusIndex - 1;
  368 + let bottomOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().bottom - this.$refs.dropdown.$el.getBoundingClientRect().bottom;
  369 + let topOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().top - this.$refs.dropdown.$el.getBoundingClientRect().top;
  370 +
  371 + if (bottomOverflowDistance > 0) {
  372 + this.$refs.dropdown.$el.scrollTop += bottomOverflowDistance;
  373 + }
  374 + if (topOverflowDistance < 0) {
  375 + this.$refs.dropdown.$el.scrollTop += topOverflowDistance;
  376 + }
  377 + }
  378 + },
  379 + ready () {
  380 + this.updateOptions(true);
  381 + document.addEventListener('keydown', this.handleKeydown);
  382 + },
  383 + beforeDestroy () {
  384 + document.removeEventListener('keydown', this.handleKeydown);
  385 + },
  386 + watch: {
  387 + model () {
  388 + if (this.multiple) {
  389 + this.updateMultipleSelected();
  390 + } else {
  391 + this.updateSingleSelected();
  392 + }
  393 + },
  394 + visible (val) {
  395 + if (val) {
  396 + this.$broadcast('on-update-popper');
  397 + } else {
  398 +
  399 + }
  400 + }
  401 + },
  402 + events: {
  403 + 'on-select-selected' (value) {
  404 + if (this.model === value) {
  405 + this.hideMenu();
  406 + } else {
  407 + if (this.multiple) {
  408 + const index = this.model.indexOf(value);
  409 + if (index >= 0) {
  410 + this.removeTag(index);
  411 + } else {
  412 + this.model.push(value);
  413 + }
  414 + } else {
  415 + this.model = value;
  416 + }
  417 + }
  418 + }
  419 + }
  420 + }
  421 +</script>
0 422 \ No newline at end of file
... ...
directives/clickoutside.js 0 → 100644
  1 +export default {
  2 + bind () {
  3 + this.documentHandler = (e) => {
  4 + if (this.el.contains(e.target)) {
  5 + return false;
  6 + }
  7 + if (this.expression) {
  8 + this.vm[this.expression]();
  9 + }
  10 + };
  11 + document.addEventListener('click', this.documentHandler);
  12 + },
  13 + update () {
  14 +
  15 + },
  16 + unbind () {
  17 + document.removeEventListener('click', this.documentHandler);
  18 + }
  19 +}
0 20 \ No newline at end of file
... ...
index.js
... ... @@ -24,6 +24,7 @@ import Message from &#39;./components/message&#39;;
24 24 import Notice from './components/notice';
25 25 import LoadingBar from './components/loading-bar';
26 26 import Modal from './components/modal';
  27 +import { Select, Option, OptionGroup } from './components/select';
27 28  
28 29 const iview = {
29 30 Button,
... ... @@ -52,7 +53,10 @@ const iview = {
52 53 Message,
53 54 Notice,
54 55 LoadingBar,
55   - Modal
  56 + Modal,
  57 + iSelect: Select,
  58 + iOption: Option,
  59 + iOptionGroup: OptionGroup
56 60 };
57 61  
58 62 module.exports = iview;
59 63 \ No newline at end of file
... ...
local/main.js
... ... @@ -60,6 +60,11 @@ router.map({
60 60 component: function (resolve) {
61 61 require(['./routers/tag.vue'], resolve);
62 62 }
  63 + },
  64 + '/select': {
  65 + component: function (resolve) {
  66 + require(['./routers/select.vue'], resolve);
  67 + }
63 68 }
64 69 });
65 70  
... ...
local/routers/index.vue
... ... @@ -2,14 +2,7 @@
2 2  
3 3 </style>
4 4 <template>
5   - <Button @click="info">info</Button>
6   - <Button @click="success">success</Button>
7   - <Button @click="warning">warning</Button>
8   - <Button @click="error">error</Button>
9   - <Button @click="confirm">confirm</Button>
10   - <Page :total="11" :page-size="11"></Page>
11   - <Page :total="11" size="small"></Page>
12   - <Page :current="1" :total="0" simple></Page>
  5 + <Page :total="100" show-sizer></Page>
13 6 </template>
14 7 <script>
15 8 import { Modal, Button, Message, Page } from 'iview';
... ...
local/routers/select.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <!--<br><br><br><br><br><br><br><br><br><br><br>-->
  4 + {{ city | json }}<br>
  5 + <Button @click="city = 'hangzhou'">切换城市</Button>
  6 + <br>
  7 + <i-select :model.sync="city" style="width:200px" @on-change="change">
  8 + <i-option-group label="热门城市">
  9 + <i-option value="beijing">北京市</i-option>
  10 + <i-option value="shanghai" disabled label="上海市">上海市2</i-option>
  11 + <i-option value="shenzhen">深圳市</i-option>
  12 + </i-option-group>
  13 + <i-option-group label="二线城市">
  14 + <i-option value="nanjing">南京市</i-option>
  15 + <i-option value="hangzhou">杭州市</i-option>
  16 + </i-option-group>
  17 + <i-option value="heilongjiang" disabled>黑龙江市</i-option>
  18 + <i-option-group label="热门城市">
  19 + <i-option value="beijing">北京市</i-option>
  20 + <i-option value="shanghai" disabled label="上海市">上海市2</i-option>
  21 + <i-option value="shenzhen">深圳市</i-option>
  22 + </i-option-group>
  23 + <i-option-group label="二线城市">
  24 + <i-option value="nanjing">南京市</i-option>
  25 + <i-option value="hangzhou">杭州市</i-option>
  26 + </i-option-group>
  27 + <i-option value="heilongjiang" disabled>黑龙江市</i-option>
  28 + <i-option-group label="热门城市">
  29 + <i-option value="beijing">北京市</i-option>
  30 + <i-option value="shanghai" disabled label="上海市">上海市2</i-option>
  31 + <i-option value="shenzhen">深圳市</i-option>
  32 + </i-option-group>
  33 + <i-option-group label="二线城市">
  34 + <i-option value="nanjing">南京市</i-option>
  35 + <i-option value="hangzhou">杭州市</i-option>
  36 + </i-option-group>
  37 + <i-option value="heilongjiang" disabled>黑龙江市</i-option>
  38 + </i-select>
  39 +
  40 + <i-select :model.sync="focus" style="width:200px" @on-change="change" clearable filterable label-in-value>
  41 + <i-option value="beijing">北京市</i-option>
  42 + <i-option value="shanghai" label="上海市">上海市</i-option>
  43 + <i-option value="shenzhen" disabled>深圳市</i-option>
  44 + <i-option value="guangzhou" label="广州市">广州市2</i-option>
  45 + <i-option value="shijiazhuang" disabled>石家庄市</i-option>
  46 + <i-option value="a">a市</i-option>
  47 + <i-option value="b">b市</i-option>
  48 + <i-option value="c">c市</i-option>
  49 + <i-option value="d">d市</i-option>
  50 + <i-option value="e">e市</i-option>
  51 + </i-select>
  52 +
  53 + <i-select :model.sync="focus2" style="width:300px" @on-change="change" clearable multiple>
  54 + <i-option value="beijing">北京市</i-option>
  55 + <i-option value="shanghai">上海市</i-option>
  56 + <i-option value="shenzhen" disabled>深圳市</i-option>
  57 + <i-option value="guangzhou">广州市</i-option>
  58 + <i-option value="shijiazhuang">石家庄市</i-option>
  59 + <i-option value="a">a市</i-option>
  60 + <i-option value="b">b市</i-option>
  61 + <i-option value="c">c市</i-option>
  62 + <i-option value="d">d市</i-option>
  63 + <i-option value="e">e市</i-option>
  64 + </i-select>
  65 +
  66 + <br><br><br><br><br><br><br><br><br><br><br><br>
  67 + </div>
  68 +</template>
  69 +<script>
  70 + import { iSelect, iOption, iOptionGroup, Button } from 'iview';
  71 +
  72 + export default {
  73 + components: {
  74 + iSelect,
  75 + iOption,
  76 + iOptionGroup,
  77 + Button
  78 + },
  79 + props: {
  80 +
  81 + },
  82 + data () {
  83 + return {
  84 + city: '',
  85 + focus: '',
  86 + focus2: ['beijing', 'guangzhou', 'b']
  87 +// focus2: []
  88 + }
  89 + },
  90 + computed: {
  91 +
  92 + },
  93 + methods: {
  94 + change (data) {
  95 + console.log(data)
  96 + }
  97 + }
  98 + }
  99 +</script>
0 100 \ No newline at end of file
... ...
local/routers/tag.vue
1 1 <template>
2   - <div>
3   - <Button @click="start">start</Button>
4   - <Button @click="destroy">destroy</Button>
5   - <Button @click="finish">finish</Button>
6   - <Button @click="error">error</Button>
7   - <Button @click="update">update</Button>
8   - <br><br>
9   - <Page :total="100"></Page>
10   - <Page :total="100" show-sizer></Page>
11   - <Page :total="100" show-elevator></Page>
12   - <Page :total="100" show-total></Page>
13   - <br><br>
14   - <Page :current="2" :total="50" simple></Page>
15   - <br>
16   - <Page :total="400" size="small"></Page>
17   - <br>
18   - <Page :total="400" size="small" show-elevator show-sizer></Page>
19   - <br>
20   - <Page :total="400" size="small" show-total></Page>
21   - </div>
  2 + <Collapse active-key="1">
  3 + <Panel key="1">
  4 + 史蒂夫·乔布斯
  5 + <p slot="content">史蒂夫·乔布斯(Steve Jobs),1955年2月24日生于美国加利福尼亚州旧金山,美国发明家、企业家、美国苹果公司联合创办人。</p>
  6 + </Panel>
  7 + <Panel key="2">
  8 + 斯蒂夫·盖瑞·沃兹尼亚克
  9 + <p slot="content">斯蒂夫·盖瑞·沃兹尼亚克(Stephen Gary Wozniak),美国电脑工程师,曾与史蒂夫·乔布斯合伙创立苹果电脑(今之苹果公司)。斯蒂夫·盖瑞·沃兹尼亚克曾就读于美国科罗拉多大学,后转学入美国著名高等学府加州大学伯克利分校(UC Berkeley)并获得电机工程及计算机(EECS)本科学位(1987年)。</p>
  10 + </Panel>
  11 + <Panel key="3">
  12 + 乔纳森·伊夫
  13 + <p slot="content">乔纳森·伊夫是一位工业设计师,现任Apple公司设计师兼资深副总裁,英国爵士。他曾参与设计了iPod,iMac,iPhone,iPad等众多苹果产品。除了乔布斯,他是对苹果那些著名的产品最有影响力的人。</p>
  14 + </Panel>
  15 + </Collapse>
  16 + <Input-number :max="10" :min="1" :value="1" @on-change="change"></Input-number>
  17 + <Input-number :max="10" :min="1" :step="1.2" :value="1"></Input-number>
22 18 </template>
23 19 <script>
24   - import { Tag, LoadingBar, Button, Progress, Icon, Timeline, Page } from 'iview';
25   - const ButtonGroup = Button.Group;
26   - const TimelineItem = Timeline.Item;
  20 + import { Collapse, InputNumber } from 'iview';
  21 + const Panel = Collapse.Panel;
27 22 export default {
28   - components: {
29   - Tag,
30   - Button,
31   - Progress,
32   - ButtonGroup,
33   - Timeline,
34   - TimelineItem,
35   - Icon,
36   - Page
37   - },
38   - props: {
39   -
40   - },
41   - data () {
42   - return {
43   - percent: 0
44   - }
45   - },
46   - computed: {
47   -
48   - },
  23 + components: { Collapse, Panel, InputNumber },
49 24 methods: {
50   - start () {
51   - LoadingBar.start();
52   - },
53   - destroy () {
54   - LoadingBar.destroy();
55   - },
56   - finish () {
57   - LoadingBar.finish();
58   - },
59   - error () {
60   - LoadingBar.error();
61   - },
62   - update () {
63   - LoadingBar.update(50);
64   - },
65   - add () {
66   - if (this.percent >= 100) {
67   - return false;
68   - }
69   - this.percent += 10;
70   - },
71   - minus () {
72   - if (this.percent <= 0) {
73   - return false;
74   - }
75   - this.percent -= 10;
  25 + change (data) {
  26 + console.log(data);
76 27 }
77 28 }
78 29 }
79   -</script>
80 30 \ No newline at end of file
  31 +</script>
... ...
package.json
1 1 {
2 2 "name": "iview",
3   - "version": "0.9.3",
  3 + "version": "0.9.4",
4 4 "title": "iView",
5 5 "description": "A high quality UI components Library with Vue.js",
6 6 "homepage": "http://www.iviewui.com",
... ... @@ -29,6 +29,7 @@
29 29 "url": "https://github.com/iview/iview/issues"
30 30 },
31 31 "dependencies": {
  32 + "popper.js": "^0.6.4",
32 33 "vue": "^1.0.26"
33 34 },
34 35 "devDependencies": {
... ...
styles/animation/index.less
... ... @@ -24,4 +24,5 @@
24 24  
25 25 @import "fade";
26 26 @import "move";
27   -@import "ease";
28 27 \ No newline at end of file
  28 +@import "ease";
  29 +@import "slide";
29 30 \ No newline at end of file
... ...
styles/animation/slide.less 0 → 100644
  1 +.slide-motion(@className, @keyframeName) {
  2 + .make-motion(@className, @keyframeName);
  3 + .@{className}-enter, .@{className}-appear {
  4 + opacity: 0;
  5 + animation-timing-function: @ease-in-out;
  6 + }
  7 + .@{className}-leave {
  8 + animation-timing-function: @ease-in-out;
  9 + }
  10 +}
  11 +
  12 +.slide-motion(slide-up, ivuSlideUp);
  13 +.slide-motion(slide-down, ivuSlideDown);
  14 +.slide-motion(slide-left, ivuSlideLeft);
  15 +.slide-motion(slide-right, ivuSlideRight);
  16 +
  17 +@keyframes ivuSlideUpIn {
  18 + 0% {
  19 + opacity: 0;
  20 + transform-origin: 0% 0%;
  21 + transform: scaleY(.8);
  22 + }
  23 + 100% {
  24 + opacity: 1;
  25 + transform-origin: 0% 0%;
  26 + transform: scaleY(1);
  27 + }
  28 +}
  29 +
  30 +@keyframes ivuSlideUpOut {
  31 + 0% {
  32 + opacity: 1;
  33 + transform-origin: 0% 0%;
  34 + transform: scaleY(1);
  35 + }
  36 + 100% {
  37 + opacity: 0;
  38 + transform-origin: 0% 0%;
  39 + transform: scaleY(.8);
  40 + }
  41 +}
  42 +
  43 +@keyframes ivuSlideDownIn {
  44 + 0% {
  45 + opacity: 0;
  46 + transform-origin: 100% 100%;
  47 + transform: scaleY(.8);
  48 + }
  49 + 100% {
  50 + opacity: 1;
  51 + transform-origin: 100% 100%;
  52 + transform: scaleY(1);
  53 + }
  54 +}
  55 +
  56 +@keyframes ivuSlideDownOut {
  57 + 0% {
  58 + opacity: 1;
  59 + transform-origin: 100% 100%;
  60 + transform: scaleY(1);
  61 + }
  62 + 100% {
  63 + opacity: 0;
  64 + transform-origin: 100% 100%;
  65 + transform: scaleY(.8);
  66 + }
  67 +}
  68 +
  69 +@keyframes ivuSlideLeftIn {
  70 + 0% {
  71 + opacity: 0;
  72 + transform-origin: 0% 0%;
  73 + transform: scaleX(.8);
  74 + }
  75 + 100% {
  76 + opacity: 1;
  77 + transform-origin: 0% 0%;
  78 + transform: scaleX(1);
  79 + }
  80 +}
  81 +
  82 +@keyframes ivuSlideLeftOut {
  83 + 0% {
  84 + opacity: 1;
  85 + transform-origin: 0% 0%;
  86 + transform: scaleX(1);
  87 + }
  88 + 100% {
  89 + opacity: 0;
  90 + transform-origin: 0% 0%;
  91 + transform: scaleX(.8);
  92 + }
  93 +}
  94 +
  95 +@keyframes ivuSlideRightIn {
  96 + 0% {
  97 + opacity: 0;
  98 + transform-origin: 100% 0%;
  99 + transform: scaleX(.8);
  100 + }
  101 + 100% {
  102 + opacity: 1;
  103 + transform-origin: 100% 0%;
  104 + transform: scaleX(1);
  105 + }
  106 +}
  107 +
  108 +@keyframes ivuSlideRightOut {
  109 + 0% {
  110 + opacity: 1;
  111 + transform-origin: 100% 0%;
  112 + transform: scaleX(1);
  113 + }
  114 + 100% {
  115 + opacity: 0;
  116 + transform-origin: 100% 0%;
  117 + transform: scaleX(.8);
  118 + }
  119 +}
0 120 \ No newline at end of file
... ...
styles/common/iconfont/_ionicons-font.less
... ... @@ -12,16 +12,20 @@
12 12 font-style: normal;
13 13 }
14 14  
  15 +.ivu-icon() {
  16 + display: inline-block;
  17 + font-family: @ionicons-font-family;
  18 + speak: none;
  19 + font-style: normal;
  20 + font-weight: normal;
  21 + font-variant: normal;
  22 + text-transform: none;
  23 + text-rendering: auto;
  24 + line-height: 1;
  25 + -webkit-font-smoothing: antialiased;
  26 + -moz-osx-font-smoothing: grayscale;
  27 +}
  28 +
15 29 .ivu-icon {
16   - display: inline-block;
17   - font-family: @ionicons-font-family;
18   - speak: none;
19   - font-style: normal;
20   - font-weight: normal;
21   - font-variant: normal;
22   - text-transform: none;
23   - text-rendering: auto;
24   - line-height: 1;
25   - -webkit-font-smoothing: antialiased;
26   - -moz-osx-font-smoothing: grayscale;
  30 + .ivu-icon();
27 31 }
28 32 \ No newline at end of file
... ...
styles/components/index.less
... ... @@ -19,4 +19,6 @@
19 19 @import "timeline";
20 20 @import "page";
21 21 @import "steps";
22   -@import "modal";
23 22 \ No newline at end of file
  23 +@import "modal";
  24 +@import "select";
  25 +@import "select-dropdown";
24 26 \ No newline at end of file
... ...
styles/components/select-dropdown.less 0 → 100644
  1 +@select-dropdown-prefix-cls: ~"@{css-prefix}select-dropdown";
  2 +
  3 +.@{select-dropdown-prefix-cls} {
  4 + width: 100%;
  5 + max-height: 200px;
  6 + overflow: auto;
  7 + margin: 5px 0;
  8 + padding: 7px 0;
  9 + background-color: #fff;
  10 + box-sizing: border-box;
  11 + border: 1px solid @border-color-base;
  12 + border-radius: @btn-border-radius;
  13 + box-shadow: 0 1px 3px rgba(0,0,0,.2);
  14 + position: absolute;
  15 + z-index: @zindex-select;
  16 +}
0 17 \ No newline at end of file
... ...
styles/components/select.less 0 → 100644
  1 +@select-prefix-cls: ~"@{css-prefix}select";
  2 +@select-item-prefix-cls: ~"@{css-prefix}select-item";
  3 +@select-group-prefix-cls: ~"@{css-prefix}select-group";
  4 +
  5 +.@{select-prefix-cls} {
  6 + display: inline-block;
  7 + width: 100%;
  8 + box-sizing: border-box;
  9 + vertical-align: middle;
  10 + color: @text-color;
  11 + font-size: @font-size-base;
  12 + position: relative;
  13 +
  14 + &-selection {
  15 + display: block;
  16 + box-sizing: border-box;
  17 + outline: none;
  18 + user-select: none;
  19 + cursor: pointer;
  20 +
  21 + background-color: #fff;
  22 + border-radius: @btn-border-radius;
  23 + border: 1px solid @border-color-base;
  24 + .transition(all @transition-time @ease-in-out);
  25 +
  26 + .@{select-prefix-cls}-arrow:nth-of-type(1) {
  27 + display: none;
  28 + cursor: pointer;
  29 + }
  30 +
  31 + &:hover {
  32 + .hover();
  33 + .@{select-prefix-cls}-arrow:nth-of-type(1) {
  34 + display: inline-block;
  35 + }
  36 + }
  37 + }
  38 +
  39 + &-show-clear &-selection:hover .@{select-prefix-cls}-arrow:nth-of-type(2){
  40 + display: none;
  41 + }
  42 +
  43 + &-arrow {
  44 + position: absolute;
  45 + top: 50%;
  46 + right: 8px;
  47 + line-height: 1;
  48 + margin-top: -6px;
  49 + color: @border-color-base;
  50 + .transition(all @transition-time @ease-in-out);
  51 + }
  52 +
  53 + &-visible{
  54 + .@{select-prefix-cls}-selection{
  55 + .active();
  56 + }
  57 +
  58 + .@{select-prefix-cls}-arrow:nth-of-type(2) {
  59 + .transform(rotate(180deg));
  60 + }
  61 + }
  62 +
  63 + &-disabled {
  64 + .@{select-prefix-cls}-selection {
  65 + .disabled();
  66 +
  67 + .@{select-prefix-cls}-arrow:nth-of-type(1) {
  68 + display: none;
  69 + }
  70 +
  71 + &:hover {
  72 + border-color: @border-color-base;
  73 + box-shadow: none;
  74 +
  75 + .@{select-prefix-cls}-arrow:nth-of-type(2) {
  76 + display: inline-block;
  77 + }
  78 + }
  79 + }
  80 + }
  81 +
  82 + &-single &-selection{
  83 + height: 28px;
  84 + position: relative;
  85 +
  86 + .@{select-prefix-cls}-placeholder{
  87 + color: @input-placeholder-color;
  88 + }
  89 +
  90 + .@{select-prefix-cls}-placeholder, .@{select-prefix-cls}-selected-value{
  91 + display: block;
  92 + height: 26px;
  93 + line-height: 26px;
  94 + overflow: hidden;
  95 + text-overflow: ellipsis;
  96 + white-space: nowrap;
  97 + padding-left: 8px;
  98 + padding-right: 24px;
  99 + }
  100 + }
  101 +
  102 + &-large&-single &-selection{
  103 + height: 32px;
  104 +
  105 + .@{select-prefix-cls}-placeholder, .@{select-prefix-cls}-selected-value{
  106 + height: 30px;
  107 + line-height: 30px;
  108 + }
  109 + }
  110 +
  111 + &-small&-single &-selection{
  112 + height: 22px;
  113 + border-radius: @btn-border-radius-small;
  114 +
  115 + .@{select-prefix-cls}-placeholder, .@{select-prefix-cls}-selected-value{
  116 + height: 20px;
  117 + line-height: 20px;
  118 + }
  119 + }
  120 +
  121 + &-multiple &-selection{
  122 + padding: 0 24px 0 2px;
  123 + min-height: 28px;
  124 +
  125 + .@{select-prefix-cls}-placeholder{
  126 + display: block;
  127 + height: 26px;
  128 + line-height: 26px;
  129 + color: @input-placeholder-color;
  130 + overflow: hidden;
  131 + text-overflow: ellipsis;
  132 + white-space: nowrap;
  133 + padding-left: 8px;
  134 + padding-right: 22px;
  135 + }
  136 + }
  137 +
  138 + // input
  139 + &-input{
  140 + display: inline-block;
  141 + height: @input-height-base;
  142 + line-height: @input-height-base;
  143 + padding: 0 24px 0 8px;
  144 + font-size: @font-size-base;
  145 + outline: none;
  146 + border: none;
  147 + box-sizing: border-box;
  148 + color: @input-color;
  149 + background-color: transparent;
  150 + position: relative;
  151 + cursor: pointer;
  152 + .placeholder();
  153 + }
  154 +
  155 + &-single &-input{
  156 + width: 100%;
  157 + }
  158 +
  159 + &-large &-input{
  160 + height: @input-height-large;
  161 + }
  162 +
  163 + &-small &-input{
  164 + height: @input-height-small;
  165 + }
  166 +}
  167 +
  168 +.@{select-item-prefix-cls} {
  169 + margin: 0;
  170 + padding: 7px 15px;
  171 + clear: both;
  172 + color: @text-color;
  173 + //border-radius: @btn-border-radius-small;
  174 + white-space: nowrap;
  175 + cursor: pointer;
  176 + .transition(background @transition-time @ease-in-out);
  177 +
  178 + &:hover{
  179 + background: @background-color-select-hover;
  180 + }
  181 +
  182 + &-focus {
  183 + background: @background-color-select-hover;
  184 + }
  185 +
  186 + &-disabled {
  187 + color: @btn-disable-color;
  188 + cursor: @cursor-disabled;
  189 +
  190 + &:hover {
  191 + color: @btn-disable-color;
  192 + background-color: #fff;
  193 + cursor: @cursor-disabled;
  194 + }
  195 + }
  196 +
  197 + &-selected ,&-selected:hover{
  198 + color: #fff;
  199 + background: @selected-color;
  200 + }
  201 +
  202 + &-selected&-focus {
  203 + background: shade(@selected-color, 10%);
  204 + }
  205 +}
  206 +
  207 +.@{select-prefix-cls}-multiple .@{select-item-prefix-cls} {
  208 + &-selected{
  209 + color: @selected-color;
  210 + background: #fff;
  211 + }
  212 + &-focus,&-selected:hover{
  213 + background: @background-color-select-hover;
  214 + }
  215 +
  216 + &-selected&-focus {
  217 + color: shade(@selected-color, 10%);
  218 + background: #fff;
  219 + }
  220 +
  221 + &-selected:after{
  222 + .ivu-icon();
  223 + float: right;
  224 + font-size: 24px;
  225 + content: '\F3FD';
  226 + color: @selected-color;
  227 + }
  228 +}
  229 +
  230 +.@{select-group-prefix-cls} {
  231 + list-style: none;
  232 + margin: 0;
  233 + padding: 0;
  234 +
  235 + &-title {
  236 + padding-left: 10px;
  237 + font-size: 12px;
  238 + color: @legend-color;
  239 + height: 30px;
  240 + line-height: 30px;
  241 + }
  242 +}
0 243 \ No newline at end of file
... ...
styles/themes/default/custom.less
... ... @@ -10,6 +10,7 @@
10 10 @link-color : #0099e5;
11 11 @link-hover-color : tint(@link-color, 20%);
12 12 @link-active-color : shade(@link-color, 5%);
  13 +@selected-color : fade(@primary-color, 90%);
13 14  
14 15 // Base
15 16 @body-background : #fff;
... ... @@ -28,7 +29,8 @@
28 29 @border-color-split : #e9e9e9; // inside
29 30  
30 31 // Background color
31   -@background-color-base : #f7f7f7; // base
  32 +@background-color-base : #f7f7f7; // base
  33 +@background-color-select-hover: @input-disabled-bg;
32 34  
33 35 // Shadow
34 36 @shadow-color : rgba(100, 100, 100, .2);
... ... @@ -102,6 +104,7 @@
102 104 @zindex-spin : 8;
103 105 @zindex-affix : 10;
104 106 @zindex-back-top : 10;
  107 +@zindex-select : 900;
105 108 @zindex-modal : 1000;
106 109 @zindex-message : 1010;
107 110 @zindex-notification : 1010;
... ...