Commit f2bcd4adaffe9040123e3e55cc4a912a438e3925

Authored by Graham Fairweather
1 parent b924d14d

Color keyboard control

examples/routers/color-picker.vue
1 1 <template>
2 2 <div style="margin: 100px;">
3   - {{ color }}
  3 + {{color}}
4 4 <!--<Input placeholder="请输入..." size="large" style="width: 50px;"></Input>-->
5   - <color-picker @on-change="c1" @on-active-change="c2" v-model="color" placement="bottom-start" size="large"></color-picker>
6   - <Date-picker transfer type="date" placeholder="选择日期" style="width: 200px"></Date-picker>
7   - <color-picker :transfer="true" ref="xxx" v-model="color" format="rgb" alpha :recommend="true"></color-picker>
8   - <color-picker v-model="color2" format="hsv" :alpha="true" :recommend="false"></color-picker>
  5 + <color-picker
  6 + v-model="color"
  7 + placement="bottom-start"
  8 + size="large"
  9 + @on-change="c1"
  10 + @on-active-change="c2"></color-picker>
  11 + <Date-picker
  12 + transfer
  13 + type="date"
  14 + placeholder="选择日期"
  15 + style="width: 200px"></Date-picker>
  16 + <color-picker
  17 + ref="xxx"
  18 + :transfer="true"
  19 + v-model="color"
  20 + :recommend="true"
  21 + format="rgb"
  22 + alpha
  23 + @on-change="onChange"
  24 + @on-active-change="onActiveChange"></color-picker>
  25 + <color-picker
  26 + v-model="color2"
  27 + :alpha="true"
  28 + :recommend="false"
  29 + format="hsv"></color-picker>
9 30 <!--<Date-picker type="date" placeholder="选择日期" style="width: 200px"></Date-picker>-->
10   - <color-picker v-model="color" placement="bottom-start" size="small"></color-picker>
11   - <Date-picker type="date" placeholder="选择日期" size="small" style="width: 200px"></Date-picker>
  31 + <color-picker
  32 + v-model="color"
  33 + placement="bottom-start"
  34 + size="small"></color-picker>
  35 + <Date-picker
  36 + type="date"
  37 + placeholder="选择日期"
  38 + size="small"
  39 + style="width: 200px"></Date-picker>
  40 + <color-picker
  41 + ref="yyy"
  42 + :colors="colors"
  43 + v-model="color"
  44 + transfer
  45 + format="rgb"
  46 + alpha></color-picker>
12 47 <Button @click="setColor">set color</Button>
13 48  
14 49 <br><br><br><br>
15 50 {{openState}}
16   - <ColorPicker v-model="color7" :hue="false" @on-open-change="onOpenChange"></ColorPicker>
  51 + <ColorPicker
  52 + v-model="color7"
  53 + :hue="false"
  54 + @on-open-change="onOpenChange"></ColorPicker>
  55 + <ColorPicker
  56 + v-model="color7"
  57 + :hue="false"
  58 + :hide-drop-down="hideDropDown"
  59 + transfer
  60 + @on-open-change="onOpenChange"></ColorPicker>
  61 +
  62 + <br><br><br><br>
  63 + <ColorPicker
  64 + v-model="color7"
  65 + disabled></ColorPicker>
17 66 </div>
18 67 </template>
  68 +
19 69 <script>
20   - export default {
21   - props: {},
22   - data () {
23   - return {
24   - color: 'rgba(12,34,255,.85)',
25   - color2: '',
26   - color7: '#19be6b',
27   - openState: false,
28   - };
  70 +export default {
  71 + props: {},
  72 +
  73 + data() {
  74 + return {
  75 + color: 'rgba(12,34,255,.85)',
  76 + color2: '',
  77 + color7: '#19be6b',
  78 + openState: false,
  79 + colors: [
  80 + '#2d8cf0',
  81 + '#19be6b',
  82 + '#ff9900',
  83 + '#ed3f14',
  84 + '#00b5ff',
  85 + '#19c919',
  86 + '#f9e31c',
  87 + '#ea1a1a',
  88 + '#9b1dea',
  89 + '#00c2b1',
  90 + '#ac7a33',
  91 + '#1d35ea',
  92 + '#8bc34a',
  93 + '#f16b62',
  94 + '#ea4ca3',
  95 + '#0d94aa',
  96 + '#febd79',
  97 + '#5d4037',
  98 + '#00bcd4',
  99 + '#f06292',
  100 + '#cddc39',
  101 + '#607d8b',
  102 + '#000000',
  103 + '#ffffff',
  104 + '#2d8cf0',
  105 + '#19be6b',
  106 + '#ff9900',
  107 + '#ed3f14',
  108 + '#00b5ff',
  109 + '#19c919',
  110 + '#f9e31c',
  111 + '#ea1a1a',
  112 + '#9b1dea',
  113 + '#00c2b1',
  114 + '#ac7a33',
  115 + '#1d35ea',
  116 + '#8bc34a',
  117 + '#f16b62',
  118 + '#ea4ca3',
  119 + '#0d94aa',
  120 + '#febd79',
  121 + '#5d4037',
  122 + ],
  123 + hideDropDown: false,
  124 + };
  125 + },
  126 +
  127 + computed: {},
  128 +
  129 + mounted() {
  130 + setInterval(this.toggleShowHide, 2000);
  131 + },
  132 +
  133 + methods: {
  134 + setColor() {
  135 + this.color = '#26bc77';
  136 + },
  137 + c1(d) {
  138 + console.log(d);
  139 + },
  140 + c2(d) {
  141 + console.log(d);
  142 + },
  143 + onOpenChange(state) {
  144 + this.openState = state;
  145 + },
  146 + onChange(d) {
  147 + console.log(d);
  148 + },
  149 + onActiveChange(d) {
  150 + console.log(d);
  151 + },
  152 + toggleShowHide() {
  153 + this.hideDropDown = !this.hideDropDown;
29 154 },
30   - computed: {},
31   - methods: {
32   - setColor () {
33   - this.color = '#26bc77';
34   - },
35   - c1 (d) {
36   - console.log(d);
37   - },
38   - c2 (d) {
39   - console.log(d);
40   - },
41   - onOpenChange(state){
42   - this.openState = state;
43   - }
44   - }
45   - };
  155 + },
  156 +};
46 157 </script>
... ...
src/components/color-picker/alpha.vue
1 1 <template>
2   - <div class="ivu-color-picker-alpha">
3   - <div class="ivu-color-picker-alpha-checkboard-wrap">
4   - <div class="ivu-color-picker-alpha-checkerboard"></div>
  2 + <div
  3 + :class="[prefixCls + '-alpha']"
  4 + tabindex="0"
  5 + @click="$el.focus()"
  6 + @keydown.esc="handleEscape"
  7 + @keydown.left="handleLeft"
  8 + @keydown.right="handleRight"
  9 + @keydown.up="handleUp"
  10 + @keydown.down="handleDown"
  11 + >
  12 + <div :class="[prefixCls + '-alpha-checkboard-wrap']">
  13 + <div :class="[prefixCls + '-alpha-checkerboard']"></div>
5 14 </div>
6   - <div class="ivu-color-picker-alpha-gradient" :style="{background: gradientColor}"></div>
7   - <div class="ivu-color-picker-alpha-container" ref="container"
8   - @mousedown="handleMouseDown"
9   - @touchmove="handleChange"
10   - @touchstart="handleChange">
11   - <div class="ivu-color-picker-alpha-pointer" :style="{left: colors.a * 100 + '%'}">
12   - <div class="ivu-color-picker-alpha-picker"></div>
  15 + <div
  16 + :style="gradientStyle"
  17 + :class="[prefixCls + '-alpha-gradient']"></div>
  18 + <div
  19 + ref="container"
  20 + :class="[prefixCls + '-alpha-container']"
  21 + @mousedown="handleMouseDown"
  22 + @touchmove="handleChange"
  23 + @touchstart="handleChange">
  24 + <div
  25 + :style="{top: 0, left: `${value.a * 100}%`}"
  26 + :class="[prefixCls + '-alpha-pointer']">
  27 + <div :class="[prefixCls + '-alpha-picker']"></div>
13 28 </div>
14 29 </div>
15 30 </div>
16 31 </template>
  32 +
17 33 <script>
18   - export default {
19   - name: 'Alpha',
20   - props: {
21   - value: Object,
22   - onChange: Function
  34 +import HSAMixin from './hsaMixin';
  35 +import Prefixes from './prefixMixin';
  36 +import {clamp, toRGBAString} from './utils';
  37 +
  38 +export default {
  39 + name: 'Alpha',
  40 +
  41 + mixins: [HSAMixin, Prefixes],
  42 +
  43 + data() {
  44 + const normalStep = 1;
  45 + const jumpStep = 10;
  46 +
  47 + return {
  48 + left: -normalStep,
  49 + right: normalStep,
  50 + up: jumpStep,
  51 + down: -jumpStep,
  52 + powerKey: 'shiftKey',
  53 + };
  54 + },
  55 +
  56 + computed: {
  57 + gradientStyle() {
  58 + const {r, g, b} = this.value.rgba;
  59 + const start = toRGBAString({r, g, b, a: 0});
  60 + const finish = toRGBAString({r, g, b, a: 1});
  61 +
  62 + return {background: `linear-gradient(to right, ${start} 0%, ${finish} 100%)`};
23 63 },
24   - computed: {
25   - colors () {
26   - return this.value;
27   - },
28   - gradientColor () {
29   - const rgba = this.colors.rgba;
30   - const rgbStr = [rgba.r, rgba.g, rgba.b].join(',');
31   - return 'linear-gradient(to right, rgba(' + rgbStr + ', 0) 0%, rgba(' + rgbStr + ', 1) 100%)';
  64 + },
  65 +
  66 + methods: {
  67 + change(newAlpha) {
  68 + const {h, s, l} = this.value.hsl;
  69 + const {a} = this.value;
  70 +
  71 + if (a !== newAlpha) {
  72 + this.$emit('change', {h, s, l, a: newAlpha, source: 'rgba'});
32 73 }
33 74 },
34   - methods: {
35   - handleChange (e, skip) {
36   - !skip && e.preventDefault();
37   - const container = this.$refs.container;
38   - const containerWidth = container.clientWidth;
  75 + handleSlide(e, direction) {
  76 + e.preventDefault();
  77 + e.stopPropagation();
39 78  
40   - const xOffset = container.getBoundingClientRect().left + window.pageXOffset;
41   - const pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0);
42   - const left = pageX - xOffset;
  79 + this.change(clamp(e[this.powerKey] ? direction : Math.round(this.value.hsl.a * 100 + direction) / 100, 0, 1));
  80 + },
  81 + handleChange(e) {
  82 + e.preventDefault();
  83 + e.stopPropagation();
43 84  
44   - let a;
45   - if (left < 0) {
46   - a = 0;
47   - } else if (left > containerWidth) {
48   - a = 1;
49   - } else {
50   - a = Math.round(left * 100 / containerWidth) / 100;
51   - }
  85 + const left = this.getLeft(e);
52 86  
53   - if (this.colors.a !== a) {
54   - this.$emit('change', {
55   - h: this.colors.hsl.h,
56   - s: this.colors.hsl.s,
57   - l: this.colors.hsl.l,
58   - a: a,
59   - source: 'rgba'
60   - });
61   - }
62   - },
63   - handleMouseDown (e) {
64   - this.handleChange(e, true);
65   - window.addEventListener('mousemove', this.handleChange);
66   - window.addEventListener('mouseup', this.handleMouseUp);
67   - },
68   - handleMouseUp () {
69   - this.unbindEventListeners();
70   - },
71   - unbindEventListeners () {
72   - window.removeEventListener('mousemove', this.handleChange);
73   - window.removeEventListener('mouseup', this.handleMouseUp);
  87 + if (left < 0) {
  88 + this.change(0);
  89 + return;
74 90 }
75   - }
76   - };
77   -</script>
78 91 \ No newline at end of file
  92 +
  93 + const {clientWidth} = this.$refs.container;
  94 +
  95 + if (left > clientWidth) {
  96 + this.change(1);
  97 + return;
  98 + }
  99 +
  100 + this.change(Math.round(left * 100 / clientWidth) / 100);
  101 + },
  102 + },
  103 +};
  104 +</script>
... ...
src/components/color-picker/color-picker.vue
1 1 <template>
2   - <div :class="classes" v-clickoutside="handleClose">
3   - <div ref="reference" @click="toggleVisible" :class="wrapClasses">
4   - <input type="hidden" :name="name" :value="currentValue">
5   - <i class="ivu-icon ivu-icon-arrow-down-b ivu-input-icon ivu-input-icon-normal"></i>
6   - <div :class="inputClasses">
  2 + <div
  3 + v-click-outside.capture="handleClose"
  4 + v-click-outside:mousedown.capture="handleClose"
  5 + :class="classes">
  6 + <div
  7 + ref="reference"
  8 + :class="wrapClasses"
  9 + @click="toggleVisible">
  10 + <input
  11 + :name="name"
  12 + :value="currentValue"
  13 + type="hidden">
  14 + <i :class="arrowClasses"></i>
  15 + <div
  16 + ref="input"
  17 + :tabindex="disabled ? undefined : 0"
  18 + :class="inputClasses"
  19 + @keydown.tab="onTab"
  20 + @keydown.esc="onEscape"
  21 + @keydown.up="onArrow"
  22 + @keydown.down="onArrow"
  23 + >
7 24 <div :class="[prefixCls + '-color']">
8   - <div :class="[prefixCls + '-color-empty']" v-show="value === '' && !visible">
9   - <i class="ivu-icon ivu-icon-ios-close-empty"></i>
  25 + <div
  26 + v-show="value === '' && !visible"
  27 + :class="[prefixCls + '-color-empty']">
  28 + <i :class="[iconPrefixCls, iconPrefixCls + '-ios-close-empty']"></i>
10 29 </div>
11   - <div v-show="value || visible" :style="{backgroundColor: displayedColor}"></div>
  30 + <div
  31 + v-show="value || visible"
  32 + :style="displayedColorStyle"></div>
12 33 </div>
13 34 </div>
14 35 </div>
15 36 <transition name="transition-drop">
16 37 <Drop
  38 + v-transfer-dom
17 39 v-show="visible"
18   - @click.native="handleTransferClick"
19   - :class="{ [prefixCls + '-transfer']: transfer }"
20   - class-name="ivu-transfer-no-max-height"
21   - :placement="placement"
22 40 ref="drop"
  41 + :placement="placement"
23 42 :data-transfer="transfer"
24   - v-transfer-dom>
25   - <div :class="[prefixCls + '-picker']">
26   - <div :class="[prefixCls + '-picker-wrapper']">
27   - <div :class="[prefixCls + '-picker-panel']">
28   - <Saturation v-model="saturationColors" @change="childChange"></Saturation>
  43 + :class="dropClasses"
  44 + >
  45 + <transition name="fade">
  46 + <div
  47 + v-if="visible"
  48 + :class="[prefixCls + '-picker']">
  49 + <div :class="[prefixCls + '-picker-wrapper']">
  50 + <div :class="[prefixCls + '-picker-panel']">
  51 + <Saturation
  52 + ref="saturation"
  53 + v-model="saturationColors"
  54 + :focused="visible"
  55 + @change="childChange"
  56 + @keydown.native.tab="handleFirstTab"
  57 + ></Saturation>
  58 + </div>
  59 + <div
  60 + v-if="hue"
  61 + :class="[prefixCls + '-picker-hue-slider']">
  62 + <Hue
  63 + v-model="saturationColors"
  64 + @change="childChange"></Hue>
  65 + </div>
  66 + <div
  67 + v-if="alpha"
  68 + :class="[prefixCls + '-picker-alpha-slider']">
  69 + <Alpha
  70 + v-model="saturationColors"
  71 + @change="childChange"></Alpha>
  72 + </div>
  73 + <recommend-colors
  74 + v-if="colors.length"
  75 + :list="colors"
  76 + :class="[prefixCls + '-picker-colors']"
  77 + @picker-color="handleSelectColor"></recommend-colors>
  78 + <recommend-colors
  79 + v-if="!colors.length && recommend"
  80 + :list="recommendedColor"
  81 + :class="[prefixCls + '-picker-colors']"
  82 + @picker-color="handleSelectColor"></recommend-colors>
29 83 </div>
30   - <div v-if="hue" :class="[prefixCls + '-picker-hue-slider']">
31   - <Hue v-model="saturationColors" @change="childChange"></Hue>
  84 + <div :class="[prefixCls + '-confirm']">
  85 + <span :class="[prefixCls + '-confirm-color']">{{formatColor}}</span>
  86 + <i-button
  87 + ref="clear"
  88 + :tabindex="0"
  89 + size="small"
  90 + type="ghost"
  91 + @click.native="handleClear"
  92 + @keydown.enter="handleClear"
  93 + @keydown.native.esc="closer"
  94 + >{{t('i.datepicker.clear')}}</i-button>
  95 + <i-button
  96 + ref="ok"
  97 + :tabindex="0"
  98 + size="small"
  99 + type="primary"
  100 + @click.native="handleSuccess"
  101 + @keydown.native.tab="handleLastTab"
  102 + @keydown.enter="handleSuccess"
  103 + @keydown.native.esc="closer"
  104 + >{{t('i.datepicker.ok')}}</i-button>
32 105 </div>
33   - <div v-if="alpha" :class="[prefixCls + '-picker-alpha-slider']">
34   - <Alpha v-model="saturationColors" @change="childChange"></Alpha>
35   - </div>
36   - <recommend-colors
37   - v-if="colors.length"
38   - :list="colors"
39   - :class="[prefixCls + '-picker-colors']"
40   - @picker-color="handleSelectColor"></recommend-colors>
41   - <recommend-colors
42   - v-if="!colors.length && recommend"
43   - :list="recommendedColor"
44   - :class="[prefixCls + '-picker-colors']"
45   - @picker-color="handleSelectColor"></recommend-colors>
46   - </div>
47   - <div :class="[prefixCls + '-confirm']">
48   - <span :class="[prefixCls + '-confirm-color']">{{ formatColor }}</span>
49   - <Confirm @on-pick-success="handleSuccess" @on-pick-clear="handleClear"></Confirm>
50 106 </div>
51   - </div>
  107 + </transition>
52 108 </Drop>
53 109 </transition>
54 110 </div>
55 111 </template>
56   -<script>
57   - import tinycolor from 'tinycolor2';
58   -
59   - import clickoutside from '../../directives/clickoutside';
60   - import TransferDom from '../../directives/transfer-dom';
61   -
62   - import Drop from '../../components/select/dropdown.vue';
63   - import RecommendColors from './recommend-colors.vue';
64   - import Confirm from '../date-picker/base/confirm.vue';
65   - import Saturation from './saturation.vue';
66   - import Hue from './hue.vue';
67   - import Alpha from './alpha.vue';
68   -
69   - import { oneOf } from '../../utils/assist';
70   - import Emitter from '../../mixins/emitter';
71   -
72   - const prefixCls = 'ivu-color-picker';
73   - const inputPrefixCls = 'ivu-input';
74   -
75   - function _colorChange (data, oldHue) {
76   - data = data === '' ? '#2d8cf0' : data;
77   - const alpha = data && data.a;
78   - let color;
79   -
80   - // hsl is better than hex between conversions
81   - if (data && data.hsl) {
82   - color = tinycolor(data.hsl);
83   - } else if (data && data.hex && data.hex.length > 0) {
84   - color = tinycolor(data.hex);
85   - } else {
86   - color = tinycolor(data);
87   - }
88   -
89   - if (color && (color._a === undefined || color._a === null)) {
90   - color.setAlpha(alpha || 1);
91   - }
92 112  
93   - const hsl = color.toHsl();
94   - const hsv = color.toHsv();
  113 +<script>
  114 +import tinycolor from 'tinycolor2';
  115 +import vClickOutside from 'v-click-outside-x/index';
  116 +import TransferDom from '../../directives/transfer-dom';
  117 +import Drop from '../../components/select/dropdown.vue';
  118 +import RecommendColors from './recommend-colors.vue';
  119 +import Saturation from './saturation.vue';
  120 +import Hue from './hue.vue';
  121 +import Alpha from './alpha.vue';
  122 +import Locale from '../../mixins/locale';
  123 +import {oneOf} from '../../utils/assist';
  124 +import Emitter from '../../mixins/emitter';
  125 +import Prefixes from './prefixMixin';
  126 +import {changeColor, toRGBAString} from './utils';
95 127  
96   - if (hsl.s === 0) {
97   - hsv.h = hsl.h = data.h || (data.hsl && data.hsl.h) || oldHue || 0;
98   - }
  128 +export default {
  129 + name: 'ColorPicker',
99 130  
100   - // when the hsv.v is less than 0.0164 (base on test)
101   - // because of possible loss of precision
102   - // the result of hue and saturation would be miscalculated
103   - if (hsv.v < 0.0164) {
104   - hsv.h = data.h || (data.hsv && data.hsv.h) || 0;
105   - hsv.s = data.s || (data.hsv && data.hsv.s) || 0;
106   - }
  131 + components: {Drop, RecommendColors, Saturation, Hue, Alpha},
107 132  
108   - if (hsl.l < 0.01) {
109   - hsl.h = data.h || (data.hsl && data.hsl.h) || 0;
110   - hsl.s = data.s || (data.hsl && data.hsl.s) || 0;
111   - }
  133 + directives: {clickOutside: vClickOutside.directive, TransferDom},
112 134  
113   - return {
114   - hsl: hsl,
115   - hex: color.toHexString().toUpperCase(),
116   - rgba: color.toRgb(),
117   - hsv: hsv,
118   - oldHue: data.h || oldHue || hsl.h,
119   - source: data.source,
120   - a: data.a || color.getAlpha()
121   - };
122   - }
  135 + mixins: [Emitter, Locale, Prefixes],
123 136  
124   - export default {
125   - name: 'ColorPicker',
126   - mixins: [ Emitter ],
127   - components: { Drop, Confirm, RecommendColors, Saturation, Hue, Alpha },
128   - directives: { clickoutside, TransferDom },
129   - props: {
130   - value: {
131   - type: String
132   - },
133   - hue: {
134   - type: Boolean,
135   - default: true
  137 + props: {
  138 + value: {
  139 + type: String,
  140 + default: undefined,
  141 + },
  142 + hue: {
  143 + type: Boolean,
  144 + default: true,
  145 + },
  146 + alpha: {
  147 + type: Boolean,
  148 + default: false,
  149 + },
  150 + recommend: {
  151 + type: Boolean,
  152 + default: false,
  153 + },
  154 + format: {
  155 + type: String,
  156 + validator(value) {
  157 + return oneOf(value, ['hsl', 'hsv', 'hex', 'rgb']);
136 158 },
137   - alpha: {
138   - type: Boolean,
139   - default: false
  159 + default: undefined,
  160 + },
  161 + colors: {
  162 + type: Array,
  163 + default() {
  164 + return [];
140 165 },
141   - recommend: {
142   - type: Boolean,
143   - default: false
  166 + },
  167 + disabled: {
  168 + type: Boolean,
  169 + default: false,
  170 + },
  171 + size: {
  172 + type: String,
  173 + validator(value) {
  174 + return oneOf(value, ['small', 'large', 'default']);
144 175 },
145   - format: {
146   - validator (value) {
147   - return oneOf(value, ['hsl', 'hsv', 'hex', 'rgb']);
148   - }
  176 + default: 'default',
  177 + },
  178 + hideDropDown: {
  179 + type: Boolean,
  180 + default: false,
  181 + },
  182 + placement: {
  183 + type: String,
  184 + validator(value) {
  185 + return oneOf(value, [
  186 + 'top',
  187 + 'top-start',
  188 + 'top-end',
  189 + 'bottom',
  190 + 'bottom-start',
  191 + 'bottom-end',
  192 + 'left',
  193 + 'left-start',
  194 + 'left-end',
  195 + 'right',
  196 + 'right-start',
  197 + 'right-end',
  198 + ]);
149 199 },
150   - colors: {
151   - type: Array,
152   - default () {
153   - return [];
154   - }
  200 + default: 'bottom',
  201 + },
  202 + transfer: {
  203 + type: Boolean,
  204 + default: false,
  205 + },
  206 + name: {
  207 + type: String,
  208 + default: undefined,
  209 + },
  210 + },
  211 +
  212 + data() {
  213 + return {
  214 + val: changeColor(this.value),
  215 + currentValue: this.value,
  216 + dragging: false,
  217 + visible: false,
  218 + recommendedColor: [
  219 + '#2d8cf0',
  220 + '#19be6b',
  221 + '#ff9900',
  222 + '#ed3f14',
  223 + '#00b5ff',
  224 + '#19c919',
  225 + '#f9e31c',
  226 + '#ea1a1a',
  227 + '#9b1dea',
  228 + '#00c2b1',
  229 + '#ac7a33',
  230 + '#1d35ea',
  231 + '#8bc34a',
  232 + '#f16b62',
  233 + '#ea4ca3',
  234 + '#0d94aa',
  235 + '#febd79',
  236 + '#5d4037',
  237 + '#00bcd4',
  238 + '#f06292',
  239 + '#cddc39',
  240 + '#607d8b',
  241 + '#000000',
  242 + '#ffffff',
  243 + ],
  244 + };
  245 + },
  246 +
  247 + computed: {
  248 + arrowClasses() {
  249 + return [
  250 + this.iconPrefixCls,
  251 + `${this.iconPrefixCls}-arrow-down-b`,
  252 + `${this.inputPrefixCls}-icon`,
  253 + `${this.inputPrefixCls}-icon-normal`,
  254 + ];
  255 + },
  256 + transition() {
  257 + return oneOf(this.placement, ['bottom-start', 'bottom', 'bottom-end']) ? 'slide-up' : 'fade';
  258 + },
  259 + saturationColors: {
  260 + get() {
  261 + return this.val;
155 262 },
156   - disabled: {
157   - type: Boolean,
158   - default: false
  263 + set(newVal) {
  264 + this.val = newVal;
  265 + this.$emit('on-active-change', this.formatColor);
159 266 },
160   - size: {
161   - validator (value) {
162   - return oneOf(value, ['small', 'large', 'default']);
  267 + },
  268 + classes() {
  269 + return [
  270 + `${this.prefixCls}`,
  271 + {
  272 + [`${this.prefixCls}-transfer`]: this.transfer,
163 273 },
164   - default: 'default'
165   - },
166   - placement: {
167   - validator (value) {
168   - return oneOf(value, ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end']);
  274 + ];
  275 + },
  276 + wrapClasses() {
  277 + return [
  278 + `${this.prefixCls}-rel`,
  279 + `${this.prefixCls}-${this.size}`,
  280 + `${this.inputPrefixCls}-wrapper`,
  281 + `${this.inputPrefixCls}-wrapper-${this.size}`,
  282 + {
  283 + [`${this.prefixCls}-disabled`]: this.disabled,
169 284 },
170   - default: 'bottom'
171   - },
172   - transfer: {
173   - type: Boolean,
174   - default: false
175   - },
176   - name: {
177   - type: String
178   - }
  285 + ];
179 286 },
180   - data () {
181   - return {
182   - val: _colorChange(this.value),
183   - currentValue: this.value,
184   - prefixCls: prefixCls,
185   - visible: false,
186   - disableCloseUnderTransfer: false, // transfer 模式下,点击Drop也会触发关闭
187   - recommendedColor: [
188   - '#2d8cf0',
189   - '#19be6b',
190   - '#ff9900',
191   - '#ed3f14',
192   - '#00b5ff',
193   - '#19c919',
194   - '#f9e31c',
195   - '#ea1a1a',
196   - '#9b1dea',
197   - '#00c2b1',
198   - '#ac7a33',
199   - '#1d35ea',
200   - '#8bc34a',
201   - '#f16b62',
202   - '#ea4ca3',
203   - '#0d94aa',
204   - '#febd79',
205   - '#5d4037',
206   - '#00bcd4',
207   - '#f06292',
208   - '#cddc39',
209   - '#607d8b',
210   - '#000000',
211   - '#ffffff'
212   - ]
213   - };
214   - },
215   - computed: {
216   - transition () {
217   - if (this.placement === 'bottom-start' || this.placement === 'bottom' || this.placement === 'bottom-end') {
218   - return 'slide-up';
219   - } else {
220   - return 'fade';
221   - }
222   - },
223   - saturationColors: {
224   - get () {
225   - return this.val;
  287 + inputClasses() {
  288 + return [
  289 + `${this.prefixCls}-input`,
  290 + `${this.inputPrefixCls}`,
  291 + `${this.inputPrefixCls}-${this.size}`,
  292 + {
  293 + [`${this.prefixCls}-focused`]: this.visible,
  294 + [`${this.prefixCls}-disabled`]: this.disabled,
226 295 },
227   - set (newVal) {
228   - this.val = newVal;
229   - this.$emit('on-active-change', this.formatColor);
  296 + ];
  297 + },
  298 + dropClasses() {
  299 + return [
  300 + `${this.transferPrefixCls}-no-max-height`,
  301 + {
  302 + [`${this.prefixCls}-transfer`]: this.transfer,
  303 + [`${this.prefixCls}-hide-drop`]: this.hideDropDown,
  304 + },
  305 + ];
  306 + },
  307 + displayedColorStyle() {
  308 + return {backgroundColor: toRGBAString(this.visible ? this.saturationColors.rgba : tinycolor(this.value).toRgb())};
  309 + },
  310 + formatColor() {
  311 + const {format, saturationColors} = this;
  312 +
  313 + if (format) {
  314 + if (format === 'hsl') {
  315 + return tinycolor(saturationColors.hsl).toHslString();
230 316 }
231   - },
232   - classes () {
233   - return [
234   - `${prefixCls}`,
235   - {
236   - [`${prefixCls}-transfer`]: this.transfer
237   - }
238   - ];
239   - },
240   - wrapClasses () {
241   - return [
242   - `${prefixCls}-rel`,
243   - `${prefixCls}-${this.size}`,
244   - `${inputPrefixCls}-wrapper`,
245   - `${inputPrefixCls}-wrapper-${this.size}`
246   - ];
247   - },
248   - inputClasses () {
249   - return [
250   - `${prefixCls}-input`,
251   - `${inputPrefixCls}`,
252   - `${inputPrefixCls}-${this.size}`,
253   - {
254   - [`${inputPrefixCls}-disabled`]: this.disabled
255   - }
256   - ];
257   - },
258   - displayedColor () {
259   - let color;
260   - if (this.visible) {
261   - const rgba = this.saturationColors.rgba;
262   - color = {
263   - r: rgba.r,
264   - g: rgba.g,
265   - b: rgba.b,
266   - a: rgba.a
267   - };
268   - } else {
269   - color = tinycolor(this.value).toRgb();
  317 +
  318 + if (format === 'hsv') {
  319 + return tinycolor(saturationColors.hsv).toHsvString();
270 320 }
271   - return `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`;
272   - },
273   - formatColor () {
274   - const value = this.saturationColors;
275   - const format = this.format;
276   - let color;
277 321  
278   - const rgba = `rgba(${value.rgba.r}, ${value.rgba.g}, ${value.rgba.b}, ${value.rgba.a})`;
279   - if (format) {
280   - if (format === 'hsl') {
281   - color = tinycolor(value.hsl).toHslString();
282   - } else if (format === 'hsv') {
283   - color = tinycolor(value.hsv).toHsvString();
284   - } else if (format === 'hex') {
285   - color = value.hex;
286   - } else if (format === 'rgb') {
287   - color = rgba;
288   - }
289   - } else if (this.alpha) {
290   - color = rgba;
291   - } else {
292   - color = value.hex;
  322 + if (format === 'hex') {
  323 + return saturationColors.hex;
293 324 }
294   - return color;
295   - }
296   - },
297   - watch: {
298   - value (newVal) {
299   - this.val = _colorChange(newVal);
300   - },
301   - visible (val) {
302   - this.val = _colorChange(this.value);
303   - if (val) {
304   - this.$refs.drop.update();
305   - } else {
306   - this.$refs.drop.destroy();
  325 +
  326 + if (format === 'rgb') {
  327 + return toRGBAString(saturationColors.rgba);
307 328 }
308   - this.$emit('on-open-change', Boolean(val));
  329 + } else if (this.alpha) {
  330 + return toRGBAString(saturationColors.rgba);
309 331 }
  332 +
  333 + return saturationColors.hex;
310 334 },
311   - methods: {
312   - // 开启 transfer 时,点击 Drop 即会关闭,这里不让其关闭
313   - handleTransferClick () {
314   - if (this.transfer) this.disableCloseUnderTransfer = true;
315   - },
316   - handleClose () {
317   - if (this.disableCloseUnderTransfer) {
318   - this.disableCloseUnderTransfer = false;
319   - return false;
  335 + },
  336 +
  337 + watch: {
  338 + value(newVal) {
  339 + this.val = changeColor(newVal);
  340 + },
  341 + visible(val) {
  342 + this.val = changeColor(this.value);
  343 + this.$refs.drop[val ? 'update' : 'destroy']();
  344 + this.$emit('on-open-change', Boolean(val));
  345 + },
  346 + },
  347 +
  348 + mounted() {
  349 + this.$on('on-escape-keydown', this.closer);
  350 + this.$on('on-dragging', this.setDragging);
  351 + },
  352 +
  353 + methods: {
  354 + setDragging(value) {
  355 + this.dragging = value;
  356 + },
  357 + handleClose(event) {
  358 + if (this.visible) {
  359 + if (this.dragging || event.type === 'mousedown') {
  360 + event.preventDefault();
  361 + return;
320 362 }
321   - this.visible = false;
322   - },
323   - toggleVisible () {
324   - this.visible = !this.visible;
325   - },
326   - childChange (data) {
327   - this.colorChange(data);
328   - },
329   - colorChange (data, oldHue) {
330   - this.oldHue = this.saturationColors.hsl.h;
331   - this.saturationColors = _colorChange(data, oldHue || this.oldHue);
332   - },
333   - isValidHex (hex) {
334   - return tinycolor(hex).isValid();
335   - },
336   - simpleCheckForValidColor (data) {
337   - const keysToCheck = ['r', 'g', 'b', 'a', 'h', 's', 'l', 'v'];
338   - let checked = 0;
339   - let passed = 0;
340 363  
341   - for (let i = 0; i < keysToCheck.length; i++) {
342   - const letter = keysToCheck[i];
343   - if (data[letter]) {
344   - checked++;
345   - if (!isNaN(data[letter])) {
346   - passed++;
347   - }
  364 + if (this.transfer) {
  365 + const {$el} = this.$refs.drop;
  366 + if ($el === event.target || $el.contains(event.target)) {
  367 + return;
348 368 }
349 369 }
350 370  
351   - if (checked === passed) {
352   - return data;
353   - }
354   - },
355   - handleSuccess () {
356   - const color = this.formatColor;
357   - this.currentValue = color;
358   - this.$emit('input', color);
359   - this.$emit('on-change', color);
360   - this.dispatch('FormItem', 'on-form-change', color);
361   - this.handleClose();
362   - },
363   - handleClear () {
364   - this.currentValue = '';
365   - this.$emit('input', '');
366   - this.$emit('on-change', '');
367   - this.dispatch('FormItem', 'on-form-change', '');
368   - this.handleClose();
369   - },
370   - handleSelectColor (color) {
371   - this.val = _colorChange(color);
  371 + this.closer(event);
  372 + return;
  373 + }
  374 +
  375 + this.visible = false;
  376 + },
  377 + toggleVisible() {
  378 + if (this.disabled) {
  379 + return;
  380 + }
  381 +
  382 + this.visible = !this.visible;
  383 + this.$refs.input.focus();
  384 + },
  385 + childChange(data) {
  386 + this.colorChange(data);
  387 + },
  388 + colorChange(data, oldHue) {
  389 + this.oldHue = this.saturationColors.hsl.h;
  390 + this.saturationColors = changeColor(data, oldHue || this.oldHue);
  391 + },
  392 + closer(event) {
  393 + if (event) {
  394 + event.preventDefault();
  395 + event.stopPropagation();
  396 + }
  397 +
  398 + this.visible = false;
  399 + this.$refs.input.focus();
  400 + },
  401 + handleButtons(event, value) {
  402 + this.currentValue = value;
  403 + this.$emit('input', value);
  404 + this.$emit('on-change', value);
  405 + this.dispatch('FormItem', 'on-form-change', value);
  406 + this.closer(event);
  407 + },
  408 + handleSuccess(event) {
  409 + this.handleButtons(event, this.formatColor);
  410 + this.$emit('on-pick-success');
  411 + },
  412 + handleClear(event) {
  413 + this.handleButtons(event, '');
  414 + this.$emit('on-pick-clear');
  415 + },
  416 + handleSelectColor(color) {
  417 + this.val = changeColor(color);
  418 + this.$emit('on-active-change', this.formatColor);
  419 + },
  420 + handleFirstTab(event) {
  421 + if (event.shiftKey) {
  422 + event.preventDefault();
  423 + event.stopPropagation();
  424 + this.$refs.ok.$el.focus();
372 425 }
373   - }
374   - };
  426 + },
  427 + handleLastTab(event) {
  428 + if (!event.shiftKey) {
  429 + event.preventDefault();
  430 + event.stopPropagation();
  431 + this.$refs.saturation.$el.focus();
  432 + }
  433 + },
  434 + onTab(event) {
  435 + if (this.visible) {
  436 + event.preventDefault();
  437 + }
  438 + },
  439 + onEscape(event) {
  440 + if (this.visible) {
  441 + this.closer(event);
  442 + }
  443 + },
  444 + onArrow(event) {
  445 + if (!this.visible) {
  446 + event.preventDefault();
  447 + event.stopPropagation();
  448 + this.visible = true;
  449 + }
  450 + },
  451 + },
  452 +};
375 453 </script>
... ...
src/components/color-picker/handleEscapeMixin.js 0 → 100644
  1 +export default {
  2 + methods: {
  3 + handleEscape(e) {
  4 + this.dispatch('ColorPicker', 'on-escape-keydown', e);
  5 + },
  6 + },
  7 +};
... ...
src/components/color-picker/hsaMixin.js 0 → 100644
  1 +import Emitter from '../../mixins/emitter';
  2 +import handleEscapeMixin from './handleEscapeMixin';
  3 +import {getTouches} from './utils';
  4 +
  5 +export default {
  6 + mixins: [Emitter, handleEscapeMixin],
  7 +
  8 + props: {
  9 + focused: {
  10 + type: Boolean,
  11 + default: false,
  12 + },
  13 + value: {
  14 + type: Object,
  15 + default: undefined,
  16 + },
  17 + },
  18 +
  19 + beforeDestroy() {
  20 + this.unbindEventListeners();
  21 + },
  22 +
  23 + created() {
  24 + if (this.focused) {
  25 + setTimeout(() => this.$el.focus(), 1);
  26 + }
  27 + },
  28 +
  29 + methods: {
  30 + handleLeft(e) {
  31 + this.handleSlide(e, this.left, 'left');
  32 + },
  33 + handleRight(e) {
  34 + this.handleSlide(e, this.right, 'right');
  35 + },
  36 + handleUp(e) {
  37 + this.handleSlide(e, this.up, 'up');
  38 + },
  39 + handleDown(e) {
  40 + this.handleSlide(e, this.down, 'down');
  41 + },
  42 + handleMouseDown(e) {
  43 + this.dispatch('ColorPicker', 'on-dragging', true);
  44 + this.handleChange(e, true);
  45 + window.addEventListener('mousemove', this.handleChange, false);
  46 + window.addEventListener('mouseup', this.handleMouseUp, false);
  47 + },
  48 + handleMouseUp() {
  49 + this.unbindEventListeners();
  50 + },
  51 + unbindEventListeners() {
  52 + window.removeEventListener('mousemove', this.handleChange);
  53 + window.removeEventListener('mouseup', this.handleMouseUp);
  54 + // This timeout is required so that the click handler for click-outside
  55 + // has the chance to run before the mouseup removes the dragging flag.
  56 + setTimeout(() => this.dispatch('ColorPicker', 'on-dragging', false), 1);
  57 + },
  58 + getLeft(e) {
  59 + const {container} = this.$refs;
  60 + const xOffset = container.getBoundingClientRect().left + window.pageXOffset;
  61 + const pageX = e.pageX || getTouches(e, 'PageX');
  62 +
  63 + return pageX - xOffset;
  64 + },
  65 + getTop(e) {
  66 + const {container} = this.$refs;
  67 + const yOffset = container.getBoundingClientRect().top + window.pageYOffset;
  68 + const pageY = e.pageY || getTouches(e, 'PageY');
  69 +
  70 + return pageY - yOffset;
  71 + },
  72 + },
  73 +};
... ...
src/components/color-picker/hue.vue
1 1 <template>
2   - <div class="ivu-color-picker-hue">
3   - <div class="ivu-color-picker-hue-container" ref="container"
4   - @mousedown="handleMouseDown"
5   - @touchmove="handleChange"
6   - @touchstart="handleChange">
7   - <div class="ivu-color-picker-hue-pointer" :style="{top: 0, left: pointerLeft}">
8   - <div class="ivu-color-picker-hue-picker"></div>
  2 + <div
  3 + :class="[prefixCls + '-hue']"
  4 + tabindex="0"
  5 + @click="$el.focus()"
  6 + @keydown.esc="handleEscape"
  7 + @keydown.left="handleLeft"
  8 + @keydown.right="handleRight"
  9 + @keydown.up="handleUp"
  10 + @keydown.down="handleDown"
  11 + >
  12 + <div
  13 + ref="container"
  14 + :class="[prefixCls + '-hue-container']"
  15 + @mousedown="handleMouseDown"
  16 + @touchmove="handleChange"
  17 + @touchstart="handleChange">
  18 + <div
  19 + :style="{top: 0, left: `${percent}%`}"
  20 + :class="[prefixCls + '-hue-pointer']">
  21 + <div :class="[prefixCls + '-hue-picker']"></div>
9 22 </div>
10 23 </div>
11 24 </div>
12 25 </template>
  26 +
13 27 <script>
14   - export default {
15   - name: 'Hue',
16   - props: {
17   - value: Object
18   - },
19   - data () {
20   - return {
21   - oldHue: 0,
22   - pullDirection: ''
23   - };
24   - },
25   - computed: {
26   - colors () {
27   - const h = this.value.hsl.h;
28   - if (h !== 0 && h - this.oldHue > 0) this.pullDirection = 'right';
29   - if (h !== 0 && h - this.oldHue < 0) this.pullDirection = 'left';
30   - this.oldHue = h;
  28 +import HASMixin from './hsaMixin';
  29 +import Prefixes from './prefixMixin';
  30 +import {clamp} from './utils';
  31 +
  32 +export default {
  33 + name: 'Hue',
  34 +
  35 + mixins: [HASMixin, Prefixes],
  36 +
  37 + data() {
  38 + const normalStep = 1 / 360 * 25;
  39 + const jumpStep = 20 * normalStep;
  40 +
  41 + return {
  42 + left: -normalStep,
  43 + right: normalStep,
  44 + up: jumpStep,
  45 + down: -jumpStep,
  46 + powerKey: 'shiftKey',
  47 + percent: clamp(this.value.hsl.h * 100 / 360, 0, 100),
  48 + };
  49 + },
  50 +
  51 + methods: {
  52 + change(percent) {
  53 + this.percent = clamp(percent, 0, 100);
31 54  
32   - return this.value;
33   - },
34   - pointerLeft () {
35   - if (this.colors.hsl.h === 0 && this.pullDirection === 'right') return '100%';
36   - return (this.colors.hsl.h * 100) / 360 + '%';
  55 + const {h, s, l, a} = this.value.hsl;
  56 + const newHue = clamp(percent / 100 * 360, 0, 360);
  57 +
  58 + if (h !== newHue) {
  59 + this.$emit('change', {h: newHue, s, l, a, source: 'hsl'});
37 60 }
38 61 },
39   - methods: {
40   - handleChange (e, skip) {
41   - !skip && e.preventDefault();
  62 + handleSlide(e, direction) {
  63 + e.preventDefault();
  64 + e.stopPropagation();
42 65  
43   - const container = this.$refs.container;
44   - const containerWidth = container.clientWidth;
  66 + if (e[this.powerKey]) {
  67 + this.change(direction < 0 ? 0 : 100);
  68 + return;
  69 + }
45 70  
46   - const xOffset = container.getBoundingClientRect().left + window.pageXOffset;
47   - const pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0);
48   - const left = pageX - xOffset;
  71 + this.change(this.percent + direction);
  72 + },
  73 + handleChange(e) {
  74 + e.preventDefault();
  75 + e.stopPropagation();
49 76  
50   - let h;
51   - let percent;
  77 + const left = this.getLeft(e);
52 78  
53   - if (left < 0) {
54   - h = 0;
55   - } else if (left > containerWidth) {
56   - h = 360;
57   - } else {
58   - percent = left * 100 / containerWidth;
59   - h = (360 * percent / 100);
60   - }
  79 + if (left < 0) {
  80 + this.change(0);
  81 + return;
  82 + }
61 83  
62   - if (this.colors.hsl.h !== h) {
63   - this.$emit('change', {
64   - h: h,
65   - s: this.colors.hsl.s,
66   - l: this.colors.hsl.l,
67   - a: this.colors.hsl.a,
68   - source: 'hsl'
69   - });
70   - }
71   - },
72   - handleMouseDown (e) {
73   - this.handleChange(e, true);
74   - window.addEventListener('mousemove', this.handleChange);
75   - window.addEventListener('mouseup', this.handleMouseUp);
76   - },
77   - handleMouseUp () {
78   - this.unbindEventListeners();
79   - },
80   - unbindEventListeners () {
81   - window.removeEventListener('mousemove', this.handleChange);
82   - window.removeEventListener('mouseup', this.handleMouseUp);
  84 + const {clientWidth} = this.$refs.container;
  85 +
  86 + if (left > clientWidth) {
  87 + this.change(100);
  88 + return;
83 89 }
84   - }
85   - };
86   -</script>
87 90 \ No newline at end of file
  91 +
  92 + this.change(left * 100 / clientWidth);
  93 + },
  94 + },
  95 +};
  96 +</script>
... ...
src/components/color-picker/index.js
1 1 import ColorPicker from './color-picker.vue';
2   -export default ColorPicker;
3 2 \ No newline at end of file
  3 +
  4 +export default ColorPicker;
... ...
src/components/color-picker/prefixMixin.js 0 → 100644
  1 +export default {
  2 + data() {
  3 + return {
  4 + prefixCls: 'ivu-color-picker',
  5 + inputPrefixCls: 'ivu-input',
  6 + iconPrefixCls: 'ivu-icon',
  7 + transferPrefixCls: 'ivu-transfer',
  8 + };
  9 + },
  10 +};
... ...
src/components/color-picker/recommend-colors.vue
1 1 <template>
2   - <div>
  2 + <div
  3 + ref="reference"
  4 + tabindex="0"
  5 + @click="handleClick"
  6 + @keydown.esc="handleEscape"
  7 + @keydown.enter="handleEnter"
  8 + @keydown.left="handleArrow($event, 'x', left)"
  9 + @keydown.right="handleArrow($event, 'x', right)"
  10 + @keydown.up="handleArrow($event, 'y', up)"
  11 + @keydown.down="handleArrow($event, 'y', down)"
  12 + @blur="blurColor"
  13 + @focus="focusColor"
  14 + >
3 15 <template v-for="(item, index) in list">
4   - <span @click="handleClick(index)"><em :style="{'background': item}"></em></span>
5   - <br v-if="(index + 1) % 12 === 0 && index !== 0 && (index + 1) !== list.length">
  16 + <div
  17 + :key="item + ':' + index"
  18 + :class="[prefixCls + '-picker-colors-wrapper']">
  19 + <div :data-color-id="index">
  20 + <div
  21 + :style="{background: item}"
  22 + :class="[prefixCls + '-picker-colors-wrapper-color']"
  23 + ></div>
  24 + <div
  25 + :ref="'color-circle-' + index"
  26 + :class="[prefixCls + '-picker-colors-wrapper-circle', hideClass]"></div>
  27 + </div>
  28 + </div>
  29 + <br v-if="lineBreak(list, index)">
6 30 </template>
7 31 </div>
8 32 </template>
  33 +
9 34 <script>
10   - export default {
11   - props: {
12   - list: Array
  35 +import Emitter from '../../mixins/emitter';
  36 +import HandleEscapeMixin from './handleEscapeMixin';
  37 +import Prefixes from './prefixMixin';
  38 +import {clamp} from './utils';
  39 +
  40 +export default {
  41 + name: 'RecommendedColors',
  42 +
  43 + mixins: [Emitter, HandleEscapeMixin, Prefixes],
  44 +
  45 + props: {
  46 + list: {
  47 + type: Array,
  48 + default: undefined,
13 49 },
14   - methods: {
15   - handleClick (index) {
16   - this.$emit('picker-color', this.list[index]);
  50 + },
  51 +
  52 + data() {
  53 + const columns = 12;
  54 + const rows = Math.ceil(this.list.length / columns);
  55 + const normalStep = 1;
  56 +
  57 + return {
  58 + left: -normalStep,
  59 + right: normalStep,
  60 + up: -normalStep,
  61 + down: normalStep,
  62 + powerKey: 'shiftKey',
  63 + grid: {x: 1, y: 1},
  64 + rows,
  65 + columns,
  66 + };
  67 + },
  68 +
  69 + computed: {
  70 + hideClass() {
  71 + return `${this.prefixCls}-hide`;
  72 + },
  73 + linearIndex() {
  74 + return this.getLinearIndex(this.grid);
  75 + },
  76 + currentCircle() {
  77 + return this.$refs[`color-circle-${this.linearIndex}`][0];
  78 + },
  79 + },
  80 +
  81 + methods: {
  82 + getLinearIndex(grid) {
  83 + return this.columns * (grid.y - 1) + grid.x - 1;
  84 + },
  85 + getMaxLimit(axis) {
  86 + return axis === 'x' ? this.columns : this.rows;
  87 + },
  88 + handleArrow(e, axis, direction) {
  89 + e.preventDefault();
  90 + e.stopPropagation();
  91 +
  92 + this.blurColor();
  93 +
  94 + const grid = {...this.grid};
  95 +
  96 + if (e[this.powerKey]) {
  97 + if (direction < 0) {
  98 + grid[axis] = 1;
  99 + } else {
  100 + grid[axis] = this.getMaxLimit(axis);
  101 + }
  102 + } else {
  103 + grid[axis] += direction;
  104 + }
  105 +
  106 + const index = this.getLinearIndex(grid);
  107 +
  108 + if (index >= 0 && index < this.list.length) {
  109 + this.grid[axis] = clamp(grid[axis], 1, this.getMaxLimit(axis));
  110 + }
  111 +
  112 + this.focusColor();
  113 + },
  114 + blurColor() {
  115 + this.currentCircle.classList.add(this.hideClass);
  116 + },
  117 + focusColor() {
  118 + this.currentCircle.classList.remove(this.hideClass);
  119 + },
  120 + handleEnter(e) {
  121 + this.handleClick(e, this.currentCircle);
  122 + },
  123 + handleClick(e, circle) {
  124 + e.preventDefault();
  125 + e.stopPropagation();
  126 +
  127 + this.$refs.reference.focus();
  128 +
  129 + const target = circle || e.target;
  130 + const colorId = target.dataset.colorId || target.parentElement.dataset.colorId;
  131 +
  132 + if (colorId) {
  133 + this.blurColor();
  134 + const id = Number(colorId) + 1;
  135 + this.grid.x = id % this.columns || this.columns;
  136 + this.grid.y = Math.ceil(id / this.columns);
  137 + this.focusColor();
  138 + this.$emit('picker-color', this.list[colorId]);
  139 + this.$emit('change', {hex: this.list[colorId], source: 'hex'});
17 140 }
18   - }
19   - };
20   -</script>
21 141 \ No newline at end of file
  142 + },
  143 + lineBreak(list, index) {
  144 + if (!index) {
  145 + return false;
  146 + }
  147 +
  148 + const nextIndex = index + 1;
  149 +
  150 + return nextIndex < list.length && nextIndex % this.columns === 0;
  151 + },
  152 + },
  153 +};
  154 +</script>
... ...
src/components/color-picker/saturation.vue
1 1 <template>
2   - <div class="ivu-color-picker-saturation-wrapper">
  2 + <div
  3 + :class="[prefixCls + '-saturation-wrapper']"
  4 + tabindex="0"
  5 + @keydown.esc="handleEscape"
  6 + @click="$el.focus()"
  7 + @keydown.left="handleLeft"
  8 + @keydown.right="handleRight"
  9 + @keydown.up="handleUp"
  10 + @keydown.down="handleDown"
  11 + >
3 12 <div
4   - class="ivu-color-picker-saturation"
5   - :style="{background: bgColor}"
6 13 ref="container"
  14 + :style="bgColorStyle"
  15 + :class="[prefixCls + '-saturation']"
7 16 @mousedown="handleMouseDown">
8   - <div class="ivu-color-picker-saturation--white"></div>
9   - <div class="ivu-color-picker-saturation--black"></div>
10   - <div class="ivu-color-picker-saturation-pointer" :style="{top: pointerTop, left: pointerLeft}">
11   - <div class="ivu-color-picker-saturation-circle"></div>
  17 + <div :class="[prefixCls + '-saturation--white']"></div>
  18 + <div :class="[prefixCls + '-saturation--black']"></div>
  19 + <div
  20 + :style="pointerStyle"
  21 + :class="[prefixCls + '-saturation-pointer']">
  22 + <div :class="[prefixCls + '-saturation-circle']"></div>
12 23 </div>
13 24 </div>
14 25 </div>
15 26 </template>
  27 +
16 28 <script>
17   - import throttle from 'lodash.throttle';
  29 +import HSAMixin from './hsaMixin';
  30 +import Prefixes from './prefixMixin';
  31 +import {clamp, getIncrement} from './utils';
  32 +
  33 +export default {
  34 + name: 'Saturation',
  35 +
  36 + mixins: [HSAMixin, Prefixes],
  37 +
  38 + data() {
  39 + const normalStep = 0.01;
  40 +
  41 + return {
  42 + left: -normalStep,
  43 + right: normalStep,
  44 + up: normalStep,
  45 + down: -normalStep,
  46 + multiplier: 10,
  47 + powerKey: 'shiftKey',
  48 + };
  49 + },
  50 +
  51 + computed: {
  52 + bgColorStyle() {
  53 + return {background: `hsl(${this.value.hsv.h}, 100%, 50%)`};
  54 + },
  55 + pointerStyle() {
  56 + return {top: `${-(this.value.hsv.v * 100) + 1 + 100}%`, left: `${this.value.hsv.s * 100}%`};
  57 + },
  58 + },
  59 +
  60 + methods: {
  61 + change(h, s, v, a) {
  62 + this.$emit('change', {h, s, v, a, source: 'hsva'});
  63 + },
  64 + handleSlide(e, direction, key) {
  65 + e.preventDefault();
  66 + e.stopPropagation();
  67 +
  68 + const isPowerKey = e[this.powerKey];
  69 + const increment = isPowerKey ? direction * this.multiplier : direction;
  70 + const {h, s, v, a} = this.value.hsv;
  71 + const saturation = clamp(s + getIncrement(key, ['left', 'right'], increment), 0, 1);
  72 + const bright = clamp(v + getIncrement(key, ['up', 'down'], increment), 0, 1);
  73 +
  74 + this.change(h, saturation, bright, a);
  75 + },
  76 + handleChange(e) {
  77 + e.preventDefault();
  78 + e.stopPropagation();
  79 +
  80 + const {clientWidth, clientHeight} = this.$refs.container;
  81 + const left = clamp(this.getLeft(e), 0, clientWidth);
  82 + const top = clamp(this.getTop(e), 0, clientHeight);
  83 + const saturation = left / clientWidth;
  84 + const bright = clamp(1 - top / clientHeight, 0, 1);
18 85  
19   - export default {
20   - name: 'Saturation',
21   - props: {
22   - value: Object
  86 + this.change(this.value.hsv.h, saturation, bright, this.value.hsv.a);
23 87 },
24   - data () {
25   - return {};
  88 + handleMouseDown(e) {
  89 + HSAMixin.methods.handleMouseDown.call(this, e);
  90 + window.addEventListener('mouseup', this.handleChange, false);
26 91 },
27   - computed: {
28   - colors () {
29   - return this.value;
30   - },
31   - bgColor () {
32   - return `hsl(${this.colors.hsv.h}, 100%, 50%)`;
33   - },
34   - pointerTop () {
35   - return (-(this.colors.hsv.v * 100) + 1) + 100 + '%';
36   - },
37   - pointerLeft () {
38   - return this.colors.hsv.s * 100 + '%';
39   - }
  92 + unbindEventListeners(e) {
  93 + HSAMixin.methods.unbindEventListeners.call(this, e);
  94 + window.removeEventListener('mouseup', this.handleChange);
40 95 },
41   - methods: {
42   - throttle: throttle((fn, data) => {fn(data);}, 20,
43   - {
44   - 'leading': true,
45   - 'trailing': false
46   - }),
47   - handleChange (e, skip) {
48   - !skip && e.preventDefault();
49   - const container = this.$refs.container;
50   - const containerWidth = container.clientWidth;
51   - const containerHeight = container.clientHeight;
52   - const xOffset = container.getBoundingClientRect().left + window.pageXOffset;
53   - const yOffset = container.getBoundingClientRect().top + window.pageYOffset;
54   - const pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0);
55   - const pageY = e.pageY || (e.touches ? e.touches[0].pageY : 0);
56   - let left = pageX - xOffset;
57   - let top = pageY - yOffset;
58   - if (left < 0) {
59   - left = 0;
60   - } else if (left > containerWidth) {
61   - left = containerWidth;
62   - } else if (top < 0) {
63   - top = 0;
64   - } else if (top > containerHeight) {
65   - top = containerHeight;
66   - }
67   - const saturation = left / containerWidth;
68   - let bright = -(top / containerHeight) + 1;
69   - bright = bright > 0 ? bright : 0;
70   - bright = bright > 1 ? 1 : bright;
71   - this.throttle(this.onChange, {
72   - h: this.colors.hsv.h,
73   - s: saturation,
74   - v: bright,
75   - a: this.colors.hsv.a,
76   - source: 'hsva'
77   - });
78   - },
79   - onChange (param) {
80   - this.$emit('change', param);
81   - },
82   - handleMouseDown () {
83   - // this.handleChange(e, true)
84   - window.addEventListener('mousemove', this.handleChange);
85   - window.addEventListener('mouseup', this.handleChange);
86   - window.addEventListener('mouseup', this.handleMouseUp);
87   - },
88   - handleMouseUp () {
89   - this.unbindEventListeners();
90   - },
91   - unbindEventListeners () {
92   - window.removeEventListener('mousemove', this.handleChange);
93   - window.removeEventListener('mouseup', this.handleChange);
94   - window.removeEventListener('mouseup', this.handleMouseUp);
95   - }
96   - }
97   - };
98   -</script>
99 96 \ No newline at end of file
  97 + },
  98 +};
  99 +</script>
... ...
src/components/color-picker/utils.js 0 → 100644
  1 +import tinycolor from 'tinycolor2';
  2 +import {oneOf} from '../../utils/assist';
  3 +
  4 +function setAlpha(data, alpha) {
  5 + const color = tinycolor(data);
  6 + const {_a} = color;
  7 +
  8 + if (_a === undefined || _a === null) {
  9 + color.setAlpha(alpha || 1);
  10 + }
  11 +
  12 + return color;
  13 +}
  14 +
  15 +function getColor(data, colorData) {
  16 + const alpha = colorData && colorData.a;
  17 +
  18 + if (colorData) {
  19 + // hsl is better than hex between conversions
  20 + if (colorData.hsl) {
  21 + return setAlpha(colorData.hsl, alpha);
  22 + }
  23 +
  24 + if (colorData.hex && colorData.hex.length > 0) {
  25 + return setAlpha(colorData.hex, alpha);
  26 + }
  27 + }
  28 +
  29 + return setAlpha(colorData, alpha);
  30 +}
  31 +
  32 +export function changeColor(data, oldHue) {
  33 + const colorData = data === '' ? '#2d8cf0' : data;
  34 + const color = getColor(data, colorData);
  35 + const hsl = color.toHsl();
  36 + const hsv = color.toHsv();
  37 +
  38 + if (hsl.s === 0) {
  39 + hsl.h = colorData.h || (colorData.hsl && colorData.hsl.h) || oldHue || 0;
  40 + hsv.h = hsl.h;
  41 + }
  42 +
  43 + // when the hsv.v is less than 0.0164 (base on test)
  44 + // because of possible loss of precision
  45 + // the result of hue and saturation would be miscalculated
  46 + if (hsv.v < 0.0164) {
  47 + hsv.h = colorData.h || (colorData.hsv && colorData.hsv.h) || 0;
  48 + hsv.s = colorData.s || (colorData.hsv && colorData.hsv.s) || 0;
  49 + }
  50 +
  51 + if (hsl.l < 0.01) {
  52 + hsl.h = colorData.h || (colorData.hsl && colorData.hsl.h) || 0;
  53 + hsl.s = colorData.s || (colorData.hsl && colorData.hsl.s) || 0;
  54 + }
  55 +
  56 + return {
  57 + hsl,
  58 + hex: color.toHexString().toUpperCase(),
  59 + rgba: color.toRgb(),
  60 + hsv,
  61 + oldHue: colorData.h || oldHue || hsl.h,
  62 + source: colorData.source,
  63 + a: colorData.a || color.getAlpha(),
  64 + };
  65 +}
  66 +
  67 +export function clamp(value, min, max) {
  68 + if (value < min) {
  69 + return min;
  70 + }
  71 +
  72 + if (value > max) {
  73 + return max;
  74 + }
  75 +
  76 + return value;
  77 +}
  78 +
  79 +export function getIncrement(key, keys, increment) {
  80 + return oneOf(key, keys) ? increment : 0;
  81 +}
  82 +
  83 +export function getTouches(e, prop) {
  84 + return e.touches ? e.touches[0][prop] : 0;
  85 +}
  86 +
  87 +export function toRGBAString(rgba) {
  88 + const {r, g, b, a} = rgba;
  89 +
  90 + return `rgba(${[r, g, b, a].join(',')})`;
  91 +}
  92 +
  93 +export function isValidHex(hex) {
  94 + return tinycolor(hex).isValid();
  95 +}
  96 +
  97 +function checkIteratee(data, counts, letter) {
  98 + let {checked, passed} = counts;
  99 + const value = data[letter];
  100 +
  101 + if (value) {
  102 + checked += 1;
  103 +
  104 + if (Number.isFinite(value)) {
  105 + passed += 1;
  106 + }
  107 + }
  108 +
  109 + return {checked, passed};
  110 +}
  111 +
  112 +const keysToCheck = ['r', 'g', 'b', 'a', 'h', 's', 'l', 'v'];
  113 +
  114 +export function simpleCheckForValidColor(data) {
  115 + const results = keysToCheck.reduce(checkIteratee.bind(null, data), {checked: 0, passed: 0});
  116 +
  117 + return results.checked === results.passed ? data : undefined;
  118 +}
... ...
src/styles/components/color-picker.less
1   -@color-picker-prefix-cls: ~"@{css-prefix}color-picker";
  1 +@color-picker-prefix-cls: ~'@{css-prefix}color-picker';
2 2  
3 3 .@{color-picker-prefix-cls} {
4 4 display: inline-block;
  5 + &-hide {
  6 + display: none;
  7 + &-drop {
  8 + visibility: hidden;
  9 + }
  10 + }
  11 + &-disabled {
  12 + background-color: @input-disabled-bg;
  13 + opacity: 1;
  14 + cursor: @cursor-disabled;
  15 + color: #ccc;
  16 + }
  17 + & > div:first-child:hover {
  18 + .ivu-input {
  19 + border-color: @input-hover-border-color;
  20 + }
  21 + }
  22 + & > div:first-child.@{color-picker-prefix-cls}-disabled:hover {
  23 + .ivu-input {
  24 + border-color: tint(@input-border-color, 20%);
  25 + }
  26 + }
5 27 & .@{select-dropdown-prefix-cls} {
6 28 padding: 0;
7 29 }
8   - &-rel{
  30 + &-focused {
  31 + box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
  32 + }
  33 + &-rel {
9 34 line-height: 0;
10 35 }
11   - &-color{
  36 + &-color {
12 37 width: 18px;
13 38 height: 18px;
14 39 background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
15 40 border-radius: 2px;
16 41 position: relative;
17 42 top: 2px;
18   - div{
  43 + div {
19 44 width: 100%;
20 45 height: 100%;
21   - box-shadow: inset 0 0 0 1px rgba(0,0,0,.15);
  46 + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15);
22 47 border-radius: 2px;
23 48 }
24   - &-empty{
  49 + &-empty {
25 50 background: #fff;
26 51 overflow: hidden;
27 52 text-align: center;
28   - i{
  53 + i {
29 54 font-size: 18px;
30 55 }
31 56 }
  57 + &-focused {
  58 + box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
  59 + }
32 60 }
33   - &-large &-color{
  61 + &-large &-color {
34 62 width: 20px;
35 63 height: 20px;
36 64 top: 1px;
37   - &-empty{
38   - i{
  65 + &-empty {
  66 + i {
39 67 font-size: 20px;
40 68 }
41 69 }
42 70 }
43   - &-small &-color{
  71 + &-small &-color {
44 72 width: 14px;
45 73 height: 14px;
46 74 top: 3px;
47   - &-empty{
48   - i{
  75 + &-empty {
  76 + i {
49 77 font-size: 14px;
50 78 }
51 79 }
52 80 }
53 81  
54   - &-picker{
55   - &-wrapper{
  82 + &-picker {
  83 + &-wrapper {
56 84 padding: 8px 8px 0;
57 85 }
58   - &-panel{
  86 + &-panel {
59 87 width: 240px;
60 88 margin: 0 auto;
61 89 box-sizing: initial;
62 90 position: relative;
63 91 }
64   - &-hue-slider, &-alpha-slider{
  92 + &-hue-slider,
  93 + &-alpha-slider {
65 94 height: 10px;
66 95 margin-top: 8px;
67 96 position: relative;
68 97 }
69   - &-colors{
  98 + &-colors {
70 99 margin-top: 8px;
71 100 overflow: hidden;
72   - span{
73   - display: inline-block;
  101 + outline: 0;
  102 + border: 1px solid @input-border-color;
  103 + transition: border @transition-time @ease-in-out, box-shadow @transition-time @ease-in-out;
  104 + &:hover {
  105 + border: 1px solid @input-hover-border-color;
  106 + }
  107 + &:focus {
  108 + box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
  109 + }
  110 + &-wrapper {
  111 + display: inline;
74 112 width: 20px;
75 113 height: 20px;
76 114 float: left;
77   - em{
  115 + position: relative;
  116 + &-color {
  117 + outline: 0;
78 118 display: block;
  119 + position: absolute;
79 120 width: 16px;
80 121 height: 16px;
81 122 margin: 2px;
82 123 cursor: pointer;
83 124 border-radius: 2px;
84   - box-shadow: inset 0 0 0 1px rgba(0,0,0,.15);
  125 + border: 1px solid @input-border-color;
  126 + transition: border @transition-time @ease-in-out, box-shadow @transition-time @ease-in-out;
  127 + &:hover {
  128 + border: 1px solid @input-hover-border-color;
  129 + }
  130 + &:focus {
  131 + box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
  132 + }
  133 + }
  134 + &-circle {
  135 + cursor: pointer;
  136 + top: 10px;
  137 + left: 10px;
  138 + position: absolute;
  139 + width: 4px;
  140 + height: 4px;
  141 + box-shadow: 0 0 0 1.5px #fff, inset 0 0 1px 1px rgba(0, 0, 0, 0.3), 0 0 1px 2px rgba(0, 0, 0, 0.4);
  142 + border-radius: 50%;
  143 + transform: translate(-2px, -2px);
85 144 }
86 145 }
87 146 }
88   - .@{picker-prefix-cls}-confirm{
  147 + .@{picker-prefix-cls}-confirm {
89 148 margin-top: 8px;
90 149 }
91 150 }
92 151  
93   - &-saturation{
94   - &-wrapper{
  152 + &-saturation {
  153 + &-wrapper {
95 154 width: 100%;
96 155 padding-bottom: 75%;
97 156 position: relative;
98 157 overflow: hidden;
  158 + outline: 0;
  159 + border: 1px solid @input-border-color;
  160 + box-shadow: @shadow-base;
  161 + transition: border @transition-time @ease-in-out, box-shadow @transition-time @ease-in-out;
  162 + &:hover {
  163 + border: 1px solid @input-hover-border-color;
  164 + }
  165 + &:focus {
  166 + box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
  167 + }
99 168 }
100   - &, &--white, &--black{
  169 + &,
  170 + &--white,
  171 + &--black {
101 172 cursor: pointer;
102 173 position: absolute;
103 174 top: 0;
... ... @@ -105,26 +176,26 @@
105 176 right: 0;
106 177 bottom: 0;
107 178 }
108   - &--white{
109   - background: linear-gradient(to right, #fff, rgba(255,255,255,0));
  179 + &--white {
  180 + background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
110 181 }
111   - &--black{
112   - background: linear-gradient(to top, #000, rgba(0,0,0,0));
  182 + &--black {
  183 + background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
113 184 }
114   - &-pointer{
  185 + &-pointer {
115 186 cursor: pointer;
116 187 position: absolute;
117 188 }
118   - &-circle{
  189 + &-circle {
119 190 width: 4px;
120 191 height: 4px;
121   - box-shadow: 0 0 0 1.5px #fff, inset 0 0 1px 1px rgba(0,0,0,.3), 0 0 1px 2px rgba(0,0,0,.4);
  192 + box-shadow: 0 0 0 1.5px #fff, inset 0 0 1px 1px rgba(0, 0, 0, 0.3), 0 0 1px 2px rgba(0, 0, 0, 0.4);
122 193 border-radius: 50%;
123 194 transform: translate(-2px, -2px);
124 195 }
125 196 }
126 197  
127   - &-hue{
  198 + &-hue {
128 199 position: absolute;
129 200 top: 0;
130 201 right: 0;
... ... @@ -132,35 +203,55 @@
132 203 left: 0;
133 204 border-radius: 2px;
134 205 background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
135   - &-container{
  206 + outline: 0;
  207 + border: 1px solid @input-border-color;
  208 + box-shadow: @shadow-base;
  209 + transition: border @transition-time @ease-in-out, box-shadow @transition-time @ease-in-out;
  210 + &:hover {
  211 + border: 1px solid @input-hover-border-color;
  212 + }
  213 + &:focus {
  214 + box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
  215 + }
  216 + &-container {
136 217 cursor: pointer;
137 218 margin: 0 2px;
138 219 position: relative;
139 220 height: 100%;
140 221 }
141   - &-pointer{
  222 + &-pointer {
142 223 z-index: 2;
143 224 position: absolute;
144 225 }
145   - &-picker{
  226 + &-picker {
146 227 cursor: pointer;
147 228 margin-top: 1px;
148 229 width: 4px;
149 230 border-radius: 1px;
150 231 height: 8px;
151   - box-shadow: 0 0 2px rgba(0, 0, 0, .6);
  232 + box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
152 233 background: #fff;
153 234 transform: translateX(-2px);
154 235 }
155 236 }
156 237  
157   - &-alpha{
  238 + &-alpha {
158 239 position: absolute;
159 240 top: 0;
160 241 right: 0;
161 242 bottom: 0;
162 243 left: 0;
163   - &-checkboard-wrap{
  244 + outline: 0;
  245 + border: 1px solid @input-border-color;
  246 + box-shadow: @shadow-base;
  247 + transition: border @transition-time @ease-in-out, box-shadow @transition-time @ease-in-out;
  248 + &:hover {
  249 + border: 1px solid @input-hover-border-color;
  250 + }
  251 + &:focus {
  252 + box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
  253 + }
  254 + &-checkboard-wrap {
164 255 position: absolute;
165 256 top: 0;
166 257 right: 0;
... ... @@ -169,7 +260,7 @@
169 260 overflow: hidden;
170 261 border-radius: 2px;
171 262 }
172   - &-checkerboard{
  263 + &-checkerboard {
173 264 position: absolute;
174 265 top: 0;
175 266 right: 0;
... ... @@ -177,7 +268,7 @@
177 268 left: 0;
178 269 background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
179 270 }
180   - &-gradient{
  271 + &-gradient {
181 272 position: absolute;
182 273 top: 0;
183 274 right: 0;
... ... @@ -185,32 +276,37 @@
185 276 left: 0;
186 277 border-radius: 2px;
187 278 }
188   - &-container{
  279 + &-container {
189 280 cursor: pointer;
190 281 position: relative;
191 282 z-index: 2;
192 283 height: 100%;
193 284 margin: 0 3px;
194 285 }
195   - &-pointer{
  286 + &-pointer {
196 287 z-index: 2;
197 288 position: absolute;
198 289 }
199   - &-picker{
  290 + &-picker {
200 291 cursor: pointer;
201 292 width: 4px;
202 293 border-radius: 1px;
203 294 height: 8px;
204   - box-shadow: 0 0 2px rgba(0, 0, 0, .6);
  295 + box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
205 296 background: #fff;
206 297 margin-top: 1px;
207 298 transform: translateX(-2px);
208 299 }
209 300 }
210 301  
211   - &-confirm{
  302 + &-confirm {
  303 + margin-top: 8px;
212 304 position: relative;
213   - &-color{
  305 + border-top: 1px solid @border-color-split;
  306 + text-align: right;
  307 + padding: 8px;
  308 + clear: both;
  309 + &-color {
214 310 position: absolute;
215 311 top: 11px;
216 312 left: 8px;
... ...
src/styles/mixins/input.less
... ... @@ -6,7 +6,6 @@
6 6 }
7 7  
8 8 .active(@color: @input-hover-border-color) {
9   - border-color: tint(@color, 20%);
10 9 outline: 0;
11 10 box-shadow: 0 0 0 2px fade(@color, 20%);
12 11 }
... ... @@ -266,4 +265,4 @@
266 265 &-append {
267 266 border-left: 0;
268 267 }
269   -}
270 268 \ No newline at end of file
  269 +}
... ...