Эх сурвалжийг харах

ColorPicker: add predefine colors (#10170)

* ColorPicker: add predefine colors(#8702)

* Add selected state of predefine color and test cases

* Fix the bugs mentioned in review
Harlan 7 жил өмнө
parent
commit
441669f081

+ 44 - 1
examples/docs/en-US/color-picker.md

@@ -5,7 +5,19 @@
         color1: '#409EFF',
         color2: null,
         color3: 'rgba(19, 206, 102, 0.8)',
-        color4: '#409EFF'
+        color4: '#409EFF',
+        color5: 'hsva(180, 65, 20, 0.5)',
+        predefineColors: [
+          'rgba(19, 206, 102, 0.18)',
+          'rgb(25, 159, 147)',
+          'hsv(250, 54, 98)',
+          'hsva(180, 65, 20, 0.5)',
+          'hsl(170, 32%, 87%)',
+          'hsla(45, 62%, 47%, 0.13)',
+          '#7486de',
+          '#45aa9477',
+          '#892345'
+        ]
       };
     },
     mounted() {
@@ -88,6 +100,36 @@ ColorPicker is a color selector supporting multiple color formats.
 ```
 :::
 
+### Predefine colors
+
+:::demo ColorPicker supports predefine colors
+```html
+<el-color-picker v-model="color5" show-alpha :predefine="predefineColors"></el-color-picker>
+
+<script>
+  export default {
+    data() {
+      return {
+        color5: 'hsva(180, 65, 20, 0.5)',
+        predefineColors: [
+          'rgba(19, 206, 102, 0.18)',
+          'rgb(25, 159, 147)',
+          'hsv(250, 54, 98)',
+          'hsva(180, 65, 20, 0.5)',
+          'hsl(170, 32%, 87%)',
+          'hsla(45, 62%, 47%, 0.13)',
+          '#7486de',
+          '#45aa9477',
+          '#892345'
+        ]
+      }
+    }
+  };
+</script>
+```
+:::
+
+
 ### Sizes
 
 :::demo
@@ -117,6 +159,7 @@ ColorPicker is a color selector supporting multiple color formats.
 | show-alpha | whether to display the alpha slider | boolean | — | false |
 | color-format | color format of v-model | string | hsl / hsv / hex / rgb | hex (when show-alpha is false)/ rgb (when show-alpha is true) |
 | popper-class | custom class name for ColorPicker's dropdown | string | — | — |
+| predefine | predefine some colors | array | — | — |
 
 ### Events
 | Event Name | Description | Parameters |

+ 43 - 1
examples/docs/es/color-picker.md

@@ -5,7 +5,19 @@
         color1: '#409EFF',
         color2: null,
         color3: 'rgba(19, 206, 102, 0.8)',
-        color4: '#409EFF'
+        color4: '#409EFF',
+        color5: 'hsva(180, 65, 20, 0.5)',
+        predefineColors: [
+          'rgba(19, 206, 102, 0.18)',
+          'rgb(25, 159, 147)',
+          'hsv(250, 54, 98)',
+          'hsva(180, 65, 20, 0.5)',
+          'hsl(170, 32%, 87%)',
+          'hsla(45, 62%, 47%, 0.13)',
+          '#7486de',
+          '#45aa9477',
+          '#892345'
+        ]
       };
     },
     mounted() {
@@ -88,6 +100,35 @@ ColorPicker es un selector de color que soporta varios formatos de color.
 ```
 :::
 
+### Colores predefinidos
+
+:::demo ColorPicker admite colores predefinidos
+```html
+<el-color-picker v-model="color5" show-alpha :predefine="predefineColors"></el-color-picker>
+
+<script>
+  export default {
+    data() {
+      return {
+        color5: 'hsva(180, 65, 20, 0.5)',
+        predefineColors: [
+          'rgba(19, 206, 102, 0.18)',
+          'rgb(25, 159, 147)',
+          'hsv(250, 54, 98)',
+          'hsva(180, 65, 20, 0.5)',
+          'hsl(170, 32%, 87%)',
+          'hsla(45, 62%, 47%, 0.13)',
+          '#7486de',
+          '#45aa9477',
+          '#892345'
+        ]
+      }
+    }
+  };
+</script>
+```
+:::
+
 ### Sizes
 
 :::demo
@@ -117,6 +158,7 @@ ColorPicker es un selector de color que soporta varios formatos de color.
 | show-alpha   | especifica si se muestra el control deslizante para el valor alpha | boolean | —                     | false                                    |
 | color-format | formato de color del `v-model`           | string  | hsl / hsv / hex / rgb | hex (si show-alpha es false)/ rgb (si show-alpha es true) |
 | popper-class | nombre de clase para el dropdown del ColorPicker | string  | —                     | —                                        |
+| predefine | colores predefinidos | array | — | — |
 
 ### Eventos
 | Nombre de Evento | Descripción                              | Parametros             |

+ 43 - 1
examples/docs/zh-CN/color-picker.md

@@ -5,7 +5,19 @@
         color1: '#409EFF',
         color2: null,
         color3: 'rgba(19, 206, 102, 0.8)',
-        color4: '#409EFF'
+        color4: '#409EFF',
+        color5: 'hsva(180, 65, 20, 0.5)',
+        predefineColors: [
+          'rgba(19, 206, 102, 0.18)',
+          'rgb(25, 159, 147)',
+          'hsv(250, 54, 98)',
+          'hsva(180, 65, 20, 0.5)',
+          'hsl(170, 32%, 87%)',
+          'hsla(45, 62%, 47%, 0.13)',
+          '#7486de',
+          '#45aa9477',
+          '#892345'
+        ]
       };
     },
     mounted() {
@@ -88,6 +100,35 @@
 ```
 :::
 
+### 预定义颜色
+
+:::demo ColorPicker 支持预定义颜色
+```html
+<el-color-picker v-model="color5" show-alpha :predefine="predefineColors"></el-color-picker>
+
+<script>
+  export default {
+    data() {
+      return {
+        color5: 'hsva(180, 65, 20, 0.5)',
+        predefineColors: [
+          'rgba(19, 206, 102, 0.18)',
+          'rgb(25, 159, 147)',
+          'hsv(250, 54, 98)',
+          'hsva(180, 65, 20, 0.5)',
+          'hsl(170, 32%, 87%)',
+          'hsla(45, 62%, 47%, 0.13)',
+          '#7486de',
+          '#45aa9477',
+          '#892345'
+        ]
+      }
+    }
+  };
+</script>
+```
+:::
+
 ### 不同尺寸
 
 :::demo
@@ -117,6 +158,7 @@
 | show-alpha | 是否支持透明度选择 | boolean | — | false |
 | color-format | 写入 v-model 的颜色的格式 | string | hsl / hsv / hex / rgb | hex(show-alpha 为 false)/ rgb(show-alpha 为 true) |
 | popper-class | ColorPicker 下拉框的类名 | string | — | — |
+| predefine | 预定义颜色 | array | — | — |
 
 ### Events
 | 事件名称      | 说明    | 回调参数      |

+ 21 - 2
packages/color-picker/src/color.js

@@ -215,6 +215,8 @@ export default class Color {
 
       if (parts.length === 4) {
         this._alpha = Math.floor(parseFloat(parts[3]) * 100);
+      } else if (parts.length === 3) {
+        this._alpha = 100;
       }
       if (parts.length >= 3) {
         const { h, s, v } = hsl2hsv(parts[0], parts[1], parts[2]);
@@ -226,6 +228,8 @@ export default class Color {
 
       if (parts.length === 4) {
         this._alpha = Math.floor(parseFloat(parts[3]) * 100);
+      } else if (parts.length === 3) {
+        this._alpha = 100;
       }
       if (parts.length >= 3) {
         fromHSV(parts[0], parts[1], parts[2]);
@@ -236,6 +240,8 @@ export default class Color {
 
       if (parts.length === 4) {
         this._alpha = Math.floor(parseFloat(parts[3]) * 100);
+      } else if (parts.length === 3) {
+        this._alpha = 100;
       }
       if (parts.length >= 3) {
         const { h, s, v } = rgb2hsv(parts[0], parts[1], parts[2]);
@@ -249,10 +255,16 @@ export default class Color {
         r = parseHexChannel(hex[0] + hex[0]);
         g = parseHexChannel(hex[1] + hex[1]);
         b = parseHexChannel(hex[2] + hex[2]);
-      } else if (hex.length === 6) {
+      } else if (hex.length === 6 || hex.length === 8) {
         r = parseHexChannel(hex.substring(0, 2));
         g = parseHexChannel(hex.substring(2, 4));
-        b = parseHexChannel(hex.substring(4));
+        b = parseHexChannel(hex.substring(4, 6));
+      }
+
+      if (hex.length === 8) {
+        this._alpha = Math.floor(parseHexChannel(hex.substring(6)) / 255 * 100);
+      } else if (hex.length === 3 || hex.length === 6) {
+        this._alpha = 100;
       }
 
       const { h, s, v } = rgb2hsv(r, g, b);
@@ -260,6 +272,13 @@ export default class Color {
     }
   }
 
+  compare(color) {
+    return Math.abs(color._hue - this._hue) < 2 &&
+      Math.abs(color._saturation - this._saturation) < 1 &&
+      Math.abs(color._value - this._value) < 1 &&
+      Math.abs(color._alpha - this._alpha) < 1;
+  }
+
   doOnChange() {
     const { _hue, _saturation, _value, _alpha, format } = this;
 

+ 6 - 2
packages/color-picker/src/components/picker-dropdown.vue

@@ -8,6 +8,7 @@
         <sv-panel ref="sl" :color="color"></sv-panel>
       </div>
       <alpha-slider v-if="showAlpha" ref="alpha" :color="color"></alpha-slider>
+      <predefine v-if="predefine" :color="color" :colors="predefine"></predefine>
       <div class="el-color-dropdown__btns">
         <span class="el-color-dropdown__value">
           <el-input
@@ -40,6 +41,7 @@
   import SvPanel from './sv-panel';
   import HueSlider from './hue-slider';
   import AlphaSlider from './alpha-slider';
+  import Predefine from './predefine';
   import Popper from 'element-ui/src/utils/vue-popper';
   import Locale from 'element-ui/src/mixins/locale';
   import ElInput from 'element-ui/packages/input';
@@ -55,14 +57,16 @@
       HueSlider,
       AlphaSlider,
       ElInput,
-      ElButton
+      ElButton,
+      Predefine
     },
 
     props: {
       color: {
         required: true
       },
-      showAlpha: Boolean
+      showAlpha: Boolean,
+      predefine: Array
     },
 
     data() {

+ 62 - 0
packages/color-picker/src/components/predefine.vue

@@ -0,0 +1,62 @@
+<template>
+  <div class="el-color-predefine">
+    预设:
+    <div class="el-color-predefine__colors">
+      <div class="el-color-predefine__color-selector"
+           :class="{selected: item.selected, 'is-alpha': item._alpha < 100}"
+           v-for="(item, index) in rgbaColors"
+           :key="colors[index]"
+           @click="handleSelect(index)">
+        <div :style="{'background-color': item.value}">
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import Color from '../color';
+
+  export default {
+    props: {
+      colors: { type: Array, required: true },
+      color: { required: true }
+    },
+    data() {
+      return {
+        rgbaColors: this.parseColors(this.colors, this.color),
+      }
+    },
+    methods: {
+      handleSelect(index) {
+        this.color.fromString(this.colors[index]);
+      },
+      parseColors(colors, color) {
+        return colors.map(value => {
+          const c = new Color();
+          c.enableAlpha = true;
+          c.format = 'rgba';
+          c.fromString(value);
+          c.selected = c.value === color.value;
+          return c;
+        });
+      }
+    },
+    watch: {
+      '$parent.currentColor'(val) {
+        const color = new Color();
+        color.fromString(val);
+
+        this.rgbaColors.forEach(item => {
+          item.selected = color.compare(item);
+        });
+      },
+      colors(newVal) {
+        this.rgbaColors = this.parseColors(newVal, this.color);
+      },
+      color(newVal) {
+        this.rgbaColors = this.parseColors(this.colors, newVal);
+      }
+    }
+  };
+</script>

+ 4 - 2
packages/color-picker/src/main.vue

@@ -24,7 +24,8 @@
        @pick="confirmValue"
        @clear="clearValue"
        :color="color"
-       :show-alpha="showAlpha">
+       :show-alpha="showAlpha"
+       :predefine="predefine">
     </picker-dropdown>
   </div>
 </template>
@@ -43,7 +44,8 @@
       colorFormat: String,
       disabled: Boolean,
       size: String,
-      popperClass: String
+      popperClass: String,
+      predefine: Array
     },
 
     inject: {

+ 34 - 0
packages/theme-chalk/src/color-picker.scss

@@ -1,6 +1,40 @@
 @import "mixins/mixins";
 @import "common/var";
 
+@include b(color-predefine) {
+  display: flex;
+  font-size: 12px;
+  margin-top: 8px;
+
+  @include e(colors) {
+    display: flex;
+    flex: 1;
+    flex-wrap: wrap;
+  }
+
+  @include e(color-selector) {
+    margin: 0 0 6px 6px;
+    width: 24px;
+    height: 24px;
+    border: 1px #8c939d solid;
+    border-radius: 2px;
+    cursor: pointer;
+
+    &.selected {
+      border: 3px $--color-primary solid;
+    }
+
+    > div {
+      display: flex;
+      height: 100%;
+    }
+
+    @include when(alpha) {
+      background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
+    }
+  }
+}
+
 @include b(color-hue-slider) {
   position: relative;
   box-sizing: border-box;

+ 94 - 0
test/unit/specs/color-picker.spec.js

@@ -265,4 +265,98 @@ describe('ColorPicker', () => {
       });
     }, ANIMATION_TIME);
   });
+
+  it('should change color to the selected color', done => {
+    const vm = createVue({
+      template: `
+        <el-color-picker v-model="color" show-alpha :predefine="colors"></el-color-picker>
+      `,
+      data() {
+        return {
+          color: 'hsva(180, 65, 20, 0.5)',
+          colors: [
+            'rgba(19, 206, 102, 0.18)',
+            'rgb(25, 159, 147)',
+            'hsv(250, 54, 98)',
+            'hsva(180, 65, 20, 0.5)',
+            'hsl(170, 32%, 87%)',
+            'hsla(45, 62%, 47%, 0.13)',
+            '#7486de',
+            '#45aa9477',
+            '#892345'
+          ]
+        };
+      }
+    }, true);
+
+    const trigger = vm.$el.querySelector('.el-color-picker__trigger');
+    trigger.click();
+
+    setTimeout(() => {
+      expect(document.querySelectorAll('.el-color-predefine__color-selector').length === 9).to.be.true;
+      const selector = document.querySelector('.el-color-predefine__color-selector:nth-child(4)');
+      selector.click();
+      vm.$nextTick(() => {
+        const picker = vm.$children[0];
+        expect(picker.color._hue === 180).to.be.true;
+        expect(picker.color._saturation === 65).to.be.true;
+        expect(picker.color._value === 20).to.be.true;
+        expect(picker.color._alpha === 50).to.be.true;
+
+        const selector2 = document.querySelector('.el-color-predefine__color-selector:nth-child(3)');
+        selector2.click();
+
+        vm.$nextTick(() => {
+          expect(picker.color._hue === 250).to.be.true;
+          expect(picker.color._saturation === 54).to.be.true;
+          expect(picker.color._value === 98).to.be.true;
+          expect(picker.color._alpha === 100).to.be.true;
+          done();
+        });
+      });
+    });
+  });
+
+  it('should change selected state of predefined color', done => {
+    const vm = createVue({
+      template: `
+        <el-color-picker v-model="color" show-alpha :predefine="colors"></el-color-picker>
+      `,
+      data() {
+        return {
+          color: 'hsva(180, 65, 20, 0.5)',
+          colors: [
+            'rgba(19, 206, 102, 0.18)',
+            'rgb(25, 159, 147)',
+            'hsv(250, 54, 98)',
+            'hsva(180, 65, 20, 0.5)',
+            'hsl(170, 32%, 87%)',
+            'hsla(45, 62%, 47%, 0.13)',
+            '#7486de',
+            '#45aa9477',
+            '#892345'
+          ]
+        };
+      }
+    }, true);
+
+    const trigger = vm.$el.querySelector('.el-color-picker__trigger');
+    trigger.click();
+
+    setTimeout(() => {
+      const selector = document.querySelector('.el-color-predefine__color-selector:nth-child(4)');
+      selector.click();
+      vm.$nextTick(() => {
+        expect(selector.classList.contains('selected')).to.be.true;
+
+        const hueBar = document.querySelector('.el-color-hue-slider');
+        hueBar.__vue__.handleClick({ target: null, clientX: 0, clientY: 1000 });
+
+        vm.$nextTick(() => {
+          expect(selector.classList.contains('selected')).to.be.false;
+          done();
+        });
+      });
+    });
+  });
 });