diff --git a/components.json b/components.json
index 314321185..bddb07f05 100644
--- a/components.json
+++ b/components.json
@@ -59,5 +59,6 @@
"carousel-item": "./packages/carousel-item/index.js",
"collapse": "./packages/collapse/index.js",
"collapse-item": "./packages/collapse-item/index.js",
- "cascader": "./packages/cascader/index.js"
+ "cascader": "./packages/cascader/index.js",
+ "color-picker": "./packages/color-picker/index.js"
}
diff --git a/examples/docs/en-US/color-picker.md b/examples/docs/en-US/color-picker.md
new file mode 100644
index 000000000..cfa4ceb06
--- /dev/null
+++ b/examples/docs/en-US/color-picker.md
@@ -0,0 +1,100 @@
+
+
+
+
+## ColorPicker
+
+ColorPicker is a color picker component that is used to solve the need to select a color in certain scenes.
+
+### Color
+
+:::demo ColorPicker usage is similar to DatePicker and requires v-model to bind a variable in a Vue instance. The bind variable's data type needs to be a string.
+```html
+
+ Default value
+
+
+
+ Empty
+
+
+
+
+
+
+```
+:::
+
+### Color and alpha
+
+:::demo ColorPicker supports normal colors, also supports alpha-channel colors, through the show-alpha attribute to control whether to support the use of transparency.
+```html
+
+ Default value
+
+
+
+ Empty
+
+
+
+
+```
+:::
+
+### Attributes
+| Attribute | Description | Type | Accepted Values | Default |
+|---------- |-------- |---------- |------------- |-------- |
+| show-alpha | Whether to display the alpha slider. | Boolean | — | false |
+| color-format | Write the v-model's color format. In the case of show-alpha is true, the default value is rgb, otherwise hex. | string | hsl, hsv, hex, rgb | hex |
\ No newline at end of file
diff --git a/examples/docs/zh-CN/color-picker.md b/examples/docs/zh-CN/color-picker.md
new file mode 100644
index 000000000..6465dd3e7
--- /dev/null
+++ b/examples/docs/zh-CN/color-picker.md
@@ -0,0 +1,100 @@
+
+
+
+
+## ColorPicker
+
+ColorPicker 是一个颜色选择器,该组件是用来解决某些场景下需要选择颜色的需求。
+
+### 选择颜色
+
+:::demo ColorPicker 用法与 DatePicker 类似,需要使用 v-model 来与 Vue 实例中的一个变量进行双向绑定,绑定的变量需要是字符串类型。
+```html
+
+ 有默认值
+
+
+
+ 无默认值
+
+
+
+
+
+
+```
+:::
+
+### 选择颜色和透明度
+
+:::demo ColorPicker 支持普通颜色,也支持带 Alpha 通道的颜色,通过 show-alpha 属性即可控制是否支持透明度的使用。
+```html
+
+ 有默认值
+
+
+
+ 无默认值
+
+
+
+
+```
+:::
+
+### Attributes
+| 参数 | 说明 | 类型 | 可选值 | 默认值 |
+|---------- |-------- |---------- |------------- |-------- |
+| show-alpha | 是否显示透明度 Slider。 | Boolean | — | false |
+| color-format | 写入 v-model 的颜色的格式。在 show-alpha 为 true 的情况下,默认值为 rgb,否则为 hex。 | string | hsl, hsv, hex, rgb | hex |
\ No newline at end of file
diff --git a/examples/nav.config.json b/examples/nav.config.json
index dae15b449..140d006fe 100644
--- a/examples/nav.config.json
+++ b/examples/nav.config.json
@@ -219,6 +219,10 @@
{
"path": "/collapse",
"title": "Collapse 折叠面板"
+ },
+ {
+ "path": "/color-picker",
+ "title": "ColorPicker"
}
]
}
@@ -445,6 +449,10 @@
{
"path": "/collapse",
"title": "Collapse"
+ },
+ {
+ "path": "/color-picker",
+ "title": "ColorPicker"
}
]
}
diff --git a/examples/play/component.vue b/examples/play/component.vue
index d4711e944..4a257ab76 100644
--- a/examples/play/component.vue
+++ b/examples/play/component.vue
@@ -1,6 +1,11 @@
-
Test
+
Change Value
+
Value: {{ value }}
+
+
+
Value2: {{ value2 }}
+
@@ -13,7 +18,10 @@
},
data() {
- return {};
+ return {
+ value: '#bfcbd9',
+ value2: null
+ };
}
};
diff --git a/packages/color-picker/cooking.conf.js b/packages/color-picker/cooking.conf.js
new file mode 100644
index 000000000..e19746a23
--- /dev/null
+++ b/packages/color-picker/cooking.conf.js
@@ -0,0 +1,6 @@
+var cooking = require('cooking');
+var gen = require('../../build/gen-single-config');
+
+cooking.set(gen(__dirname, 'ElColorPicker'));
+
+module.exports = cooking.resolve();
diff --git a/packages/color-picker/index.js b/packages/color-picker/index.js
new file mode 100644
index 000000000..d79e21e08
--- /dev/null
+++ b/packages/color-picker/index.js
@@ -0,0 +1,8 @@
+import ColorPicker from './src/main';
+
+/* istanbul ignore next */
+ColorPicker.install = function(Vue) {
+ Vue.component(ColorPicker.name, ColorPicker);
+};
+
+export default ColorPicker;
diff --git a/packages/color-picker/package.json b/packages/color-picker/package.json
new file mode 100644
index 000000000..1da298ec2
--- /dev/null
+++ b/packages/color-picker/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "element-color-picker",
+ "version": "0.0.0",
+ "description": "A color-picker component for Vue.js.",
+ "keywords": [
+ "element",
+ "vue",
+ "component"
+ ],
+ "main": "./lib/index.js",
+ "repository": "https://github.com/ElemeFE/element/tree/master/packages/color-picker",
+ "author": "elemefe",
+ "license": "MIT",
+ "dependencies": {}
+}
diff --git a/packages/color-picker/src/color.js b/packages/color-picker/src/color.js
new file mode 100644
index 000000000..e4ea0911f
--- /dev/null
+++ b/packages/color-picker/src/color.js
@@ -0,0 +1,289 @@
+const hsv2hsl = function(hue, sat, val) {
+ return [
+ hue,
+ (sat * val / ((hue = (2 - sat) * val) < 1 ? hue : 2 - hue)) || 0,
+ hue / 2
+ ];
+};
+
+// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
+//
+const isOnePointZero = function(n) {
+ return typeof n === 'string' && n.indexOf('.') !== -1 && parseFloat(n) === 1;
+};
+
+const isPercentage = function(n) {
+ return typeof n === 'string' && n.indexOf('%') !== -1;
+};
+
+// Take input from [0, n] and return it as [0, 1]
+const bound01 = function(value, max) {
+ if (isOnePointZero(value)) value = '100%';
+
+ const processPercent = isPercentage(value);
+ value = Math.min(max, Math.max(0, parseFloat(value)));
+
+ // Automatically convert percentage into number
+ if (processPercent) {
+ value = parseInt(value * max, 10) / 100;
+ }
+
+ // Handle floating point rounding errors
+ if ((Math.abs(value - max) < 0.000001)) {
+ return 1;
+ }
+
+ // Convert into [0, 1] range if it isn't already
+ return (value % max) / parseFloat(max);
+};
+
+const INT_HEX_MAP = { 10: 'A', 11: 'B', 12: 'C', 13: 'D', 14: 'E', 15: 'F' };
+
+const toHex = function({ r, g, b }) {
+ const hexOne = function(value) {
+ value = Math.min(Math.round(value), 255);
+ const high = Math.floor(value / 16);
+ const low = value % 16;
+ return '' + (INT_HEX_MAP[high] || high) + (INT_HEX_MAP[low] || low);
+ };
+
+ if (isNaN(r) || isNaN(g) || isNaN(b)) return '';
+
+ return '#' + hexOne(r) + hexOne(g) + hexOne(b);
+};
+
+const HEX_INT_MAP = { A: 10, B: 11, C: 12, D: 13, E: 14, F: 15 };
+
+const parseHexChannel = function(hex) {
+ if (hex.length === 2) {
+ return (HEX_INT_MAP[hex[0].toUpperCase()] || +hex[0]) * 16 + (HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1]);
+ }
+
+ return HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1];
+};
+
+const hsl2hsv = function(hue, sat, light) {
+ sat *= light < 0.5 ? light : 1 - light;
+
+ return [ // [hue, saturation, value]
+ // Range should be between 0 - 1
+ hue, // Hue stays the same
+ 2 * sat / (light + sat), // Saturation
+ light + sat // Value
+ ];
+};
+
+// `rgbToHsv`
+// Converts an RGB color value to HSV
+// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
+// *Returns:* { h, s, v } in [0,1]
+const rgb2hsv = function(r, g, b) {
+ r = bound01(r, 255);
+ g = bound01(g, 255);
+ b = bound01(b, 255);
+
+ const max = Math.max(r, g, b);
+ const min = Math.min(r, g, b);
+ let h, s;
+ let v = max;
+
+ const d = max - min;
+ s = max === 0 ? 0 : d / max;
+
+ if (max === min) {
+ h = 0; // achromatic
+ } else {
+ switch (max) {
+ case r:
+ h = (g - b) / d + (g < b ? 6 : 0);
+ break;
+ case g:
+ h = (b - r) / d + 2;
+ break;
+ case b:
+ h = (r - g) / d + 4;
+ break;
+ }
+ h /= 6;
+ }
+
+ return { h: Math.round(h * 360), s: Math.round(s * 100), v: Math.round(v * 100) };
+};
+
+// `hsvToRgb`
+// Converts an HSV color value to RGB.
+// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
+// *Returns:* { r, g, b } in the set [0, 255]
+const hsv2rgb = function(h, s, v) {
+ h = bound01(h, 360) * 6;
+ s = bound01(s, 100);
+ v = bound01(v, 100);
+
+ const i = Math.floor(h);
+ const f = h - i;
+ const p = v * (1 - s);
+ const q = v * (1 - f * s);
+ const t = v * (1 - (1 - f) * s);
+ const mod = i % 6;
+ const r = [v, q, p, p, t, v][mod];
+ const g = [t, v, v, q, p, p][mod];
+ const b = [p, p, t, v, v, q][mod];
+
+ return {
+ r: Math.round(r * 255),
+ g: Math.round(g * 255),
+ b: Math.round(b * 255)
+ };
+};
+
+export default class Color {
+ constructor(options) {
+ this._hue = 0;
+ this._saturation = 100;
+ this._value = 100;
+ this._alpha = 100;
+
+ this.enableAlpha = false;
+ this.format = 'hex';
+ this.value = '';
+
+ options = options || {};
+
+ for (let option in options) {
+ if (options.hasOwnProperty(option)) {
+ this[option] = options[option];
+ }
+ }
+
+ this.doOnChange();
+ }
+
+ set(prop, value) {
+ if (arguments.length === 1 && typeof prop === 'object') {
+ for (let p in prop) {
+ if (prop.hasOwnProperty(p)) {
+ this.set(p, prop[p]);
+ }
+ }
+
+ return;
+ }
+
+ this['_' + prop] = value;
+ this.doOnChange();
+ }
+
+ get(prop) {
+ return this['_' + prop];
+ }
+
+ toRgb() {
+ return hsv2rgb(this._hue, this._saturation, this._value);
+ }
+
+ fromString(value) {
+ if (!value) {
+ this._hue = 0;
+ this._saturation = 100;
+ this._value = 100;
+
+ this.doOnChange();
+ return;
+ }
+
+ const fromHSV = (h, s, v) => {
+ this._hue = h;
+ this._saturation = s;
+ this._value = v;
+
+ this.doOnChange();
+ };
+
+ if (value.indexOf('hsl') !== -1) {
+ const parts = value.replace(/hsla|hsl|\(|\)/gm, '')
+ .split(/\s|,/g, '').filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
+
+ if (parts.length > 3) {
+ const { h, s, v } = hsl2hsv(parts[0], parts[1], parts[2]);
+ fromHSV(h, s, v);
+ }
+ if (parts.length === 4) {
+ this._alpha = Math.floor(parseFloat(parts[3]) * 100);
+ }
+ } else if (value.indexOf('hsv') !== -1) {
+ const parts = value.replace(/hsva|hsv|\(|\)/gm, '')
+ .split(/\s|,/g, '').filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
+
+ if (parts.length >= 3) {
+ fromHSV(parts[0], parts[1], parts[2]);
+ }
+
+ if (parts.length === 4) {
+ this._alpha = Math.floor(parseFloat(parts[3]) * 100);
+ }
+ } else if (value.indexOf('rgb') !== -1) {
+ const parts = value.replace(/rgba|rgb|\(|\)/gm, '')
+ .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
+
+ if (parts.length >= 3) {
+ const { h, s, v } = rgb2hsv(parts[0], parts[1], parts[2]);
+ fromHSV(h, s, v);
+ }
+
+ if (parts.length === 4) {
+ this._alpha = Math.floor(parseFloat(parts[3]) * 100);
+ }
+ } else if (value.indexOf('#') !== -1) {
+ const hex = value.replace('#', '').trim();
+ let r, g, b;
+
+ if (hex.length === 3) {
+ r = parseHexChannel(hex[0] + hex[0]);
+ g = parseHexChannel(hex[1] + hex[1]);
+ b = parseHexChannel(hex[2] + hex[2]);
+ } else if (hex.length === 6) {
+ r = parseHexChannel(hex.substring(0, 2));
+ g = parseHexChannel(hex.substring(2, 4));
+ b = parseHexChannel(hex.substring(4));
+ }
+
+ const { h, s, v } = rgb2hsv(r, g, b);
+ fromHSV(h, s, v);
+ }
+ }
+
+ doOnChange() {
+ const { _hue, _saturation, _value, _alpha, format } = this;
+
+ if (this.enableAlpha) {
+ switch (format) {
+ case 'hsl':
+ const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
+ this.value = `hsla(${ _hue }, ${ hsl[1] * 100 }%, ${ hsl[2] * 100 }%, ${ _alpha / 100})`;
+ break;
+ case 'hsv':
+ this.value = `hsva(${ _hue }, ${ _saturation }%, ${ _value }%, ${ _alpha / 100})`;
+ break;
+ default:
+ const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
+ this.value = `rgba(${r}, ${g}, ${b}, ${ _alpha / 100 })`;
+ }
+ } else {
+ switch (format) {
+ case 'hsl':
+ const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
+ this.value = `hsl(${ _hue }, ${ hsl[1] * 100 }%, ${ hsl[2] * 100 }%)`;
+ break;
+ case 'hsv':
+ this.value = `hsv(${ _hue }, ${ _saturation }%, ${ _value }%)`;
+ break;
+ case 'rgb':
+ const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
+ this.value = `rgb(${r}, ${g}, ${b})`;
+ break;
+ default:
+ this.value = toHex(hsv2rgb(_hue, _saturation, _value));
+ }
+ }
+ }
+};
diff --git a/packages/color-picker/src/components/alpha-slider.vue b/packages/color-picker/src/components/alpha-slider.vue
new file mode 100644
index 000000000..08944ef54
--- /dev/null
+++ b/packages/color-picker/src/components/alpha-slider.vue
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
diff --git a/packages/color-picker/src/components/hue-slider.vue b/packages/color-picker/src/components/hue-slider.vue
new file mode 100644
index 000000000..eafa095ef
--- /dev/null
+++ b/packages/color-picker/src/components/hue-slider.vue
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
diff --git a/packages/color-picker/src/components/picker-dropdown.vue b/packages/color-picker/src/components/picker-dropdown.vue
new file mode 100644
index 000000000..d39d0eeea
--- /dev/null
+++ b/packages/color-picker/src/components/picker-dropdown.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
diff --git a/packages/color-picker/src/components/sv-panel.vue b/packages/color-picker/src/components/sv-panel.vue
new file mode 100644
index 000000000..815180903
--- /dev/null
+++ b/packages/color-picker/src/components/sv-panel.vue
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
diff --git a/packages/color-picker/src/draggable.js b/packages/color-picker/src/draggable.js
new file mode 100644
index 000000000..14e8bfcac
--- /dev/null
+++ b/packages/color-picker/src/draggable.js
@@ -0,0 +1,34 @@
+let isDragging = false;
+
+export default function(element, options) {
+ const moveFn = function(event) {
+ if (options.drag) {
+ options.drag(event);
+ }
+ };
+ const upFn = function(event) {
+ document.removeEventListener('mousemove', moveFn);
+ document.removeEventListener('mouseup', upFn);
+ document.onselectstart = null;
+ document.ondragstart = null;
+
+ isDragging = false;
+
+ if (options.end) {
+ options.end(event);
+ }
+ };
+ element.addEventListener('mousedown', function(event) {
+ if (isDragging) return;
+ document.onselectstart = function() { return false; };
+ document.ondragstart = function() { return false; };
+
+ document.addEventListener('mousemove', moveFn);
+ document.addEventListener('mouseup', upFn);
+ isDragging = true;
+
+ if (options.start) {
+ options.start(event);
+ }
+ });
+}
diff --git a/packages/color-picker/src/main.vue b/packages/color-picker/src/main.vue
new file mode 100644
index 000000000..256f47dd4
--- /dev/null
+++ b/packages/color-picker/src/main.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
diff --git a/packages/theme-default/src/color-picker.css b/packages/theme-default/src/color-picker.css
new file mode 100644
index 000000000..af31a62b0
--- /dev/null
+++ b/packages/theme-default/src/color-picker.css
@@ -0,0 +1,250 @@
+@import "./common/var.css";
+
+@component-namespace el-color {
+ @component hue-slider {
+ position: relative;
+ box-sizing: border-box;
+ width: 240px;
+ height: 12px;
+ background-color: #f00;
+ padding: 0 2px;
+
+ @descendent bar {
+ position: relative;
+ background: linear-gradient(
+ to right, #f00 0%,
+ #ff0 17%, #0f0 33%,
+ #0ff 50%, #00f 67%,
+ #f0f 83%, #f00 100%);
+ height: 100%;
+ }
+
+ @descendent thumb {
+ position: absolute;
+ cursor: pointer;
+ box-sizing: border-box;
+ left: 0;
+ top: 0;
+ width: 4px;
+ height: 100%;
+ border-radius: 1px;
+ background: #fff;
+ border: 1px solid #f0f0f0;
+ box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
+ z-index: 1;
+ }
+
+ @when vertical {
+ width: 12px;
+ height: 180px;
+ padding: 2px 0;
+
+ .el-color-hue-slider__bar {
+ background: linear-gradient(
+ to bottom, #f00 0%,
+ #ff0 17%, #0f0 33%,
+ #0ff 50%, #00f 67%,
+ #f0f 83%, #f00 100%);
+ }
+
+ .el-color-hue-slider__thumb {
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 4px;
+ }
+ }
+ }
+
+ @component svpanel {
+ position: relative;
+ width: 240px;
+ height: 180px;
+
+ @descendent white, black {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ }
+
+ @descendent white {
+ background: linear-gradient(to right, #fff, rgba(255,255,255,0));
+ }
+
+ @descendent black {
+ background: linear-gradient(to top, #000, rgba(0,0,0,0));
+ }
+
+ @descendent cursor {
+ cursor: pointer;
+ position: absolute;
+
+ > div {
+ cursor: head;
+ width: 4px;
+ height: 4px;
+ 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);
+ border-radius: 50%;
+ transform: translate(-2px, -2px);
+ }
+ }
+ }
+
+ @component alpha-slider {
+ position: relative;
+ box-sizing: border-box;
+ width: 240px;
+ height: 12px;
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
+
+ @descendent bar {
+ position: relative;
+ background: linear-gradient(
+ to right, rgba(255, 255, 255, 0) 0%,
+ rgba(255, 255, 255, 1) 100%);
+ height: 100%;
+ }
+
+ @descendent thumb {
+ position: absolute;
+ cursor: pointer;
+ box-sizing: border-box;
+ left: 0;
+ top: 0;
+ width: 4px;
+ height: 100%;
+ border-radius: 1px;
+ background: #fff;
+ border: 1px solid #f0f0f0;
+ box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
+ z-index: 1;
+ }
+
+ @when vertical {
+ width: 20px;
+ height: 180px;
+
+ .el-color-alpha-slider__bar {
+ background: linear-gradient(
+ to bottom, rgba(255, 255, 255, 0) 0%,
+ rgba(255, 255, 255, 1) 100%);
+ }
+
+ .el-color-alpha-slider__thumb {
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 4px;
+ }
+ }
+ }
+
+ @component dropdown {
+ width: 260px;
+
+ @descendent main-wrapper {
+ margin-bottom: 6px;
+
+ &::after {
+ content: "";
+ display: table;
+ clear: both;
+ }
+ }
+
+ @descendent btns {
+ margin-top: 6px;
+ text-align: right;
+ }
+
+ @descendent btn {
+ border: 1px solid #dcdcdc;
+ color: #333;
+ line-height: 24px;
+ border-radius: 2px;
+ padding: 0 20px;
+ cursor: pointer;
+ background-color: transparent;
+ outline: none;
+ font-size: 12px;
+
+ &[disabled] {
+ color: #cccccc;
+ cursor: not-allowed;
+ }
+ }
+
+ @descendent link-btn {
+ cursor: pointer;
+ color: var(--color-primary);
+ text-decoration: none;
+ padding: 15px;
+ font-size: 12px;
+ }
+ }
+
+ @component picker {
+ display: inline-block;
+ position: relative;
+
+ @descendent trigger {
+ display: inline-block;
+ box-sizing: border-box;
+ height: 36px;
+ padding: 6px;
+ border: 1px solid #bfcbd9;
+ border-radius: 4px;
+ }
+
+ @descendent color {
+ position: relative;
+ display: inline-block;
+ box-sizing: border-box;
+ vertical-align: middle;
+ border: 1px solid #666;
+ width: 22px;
+ height: 22px;
+ text-align: center;
+
+ @when alpha {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
+ }
+ }
+
+ @descendent color-inner {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ }
+
+ @descendent empty {
+ font-size: 12px;
+ vertical-align: middle;
+ margin-top: -2px;
+ color: #666;
+ }
+
+ @descendent icon {
+ display: inline-block;
+ position: relative;
+ vertical-align: middle;
+ margin-left: 8px;
+ width: 12px;
+ color: #888;
+ }
+
+ @descendent panel {
+ position: absolute;
+ z-index: 10;
+ padding: 6px;
+ background-color: var(--color-white);
+ border: 1px solid var(--color-base-gray);
+ box-shadow: var(--dropdown-menu-box-shadow);
+ }
+ }
+}
diff --git a/packages/theme-default/src/index.css b/packages/theme-default/src/index.css
index 3b0319833..2c6b134ec 100644
--- a/packages/theme-default/src/index.css
+++ b/packages/theme-default/src/index.css
@@ -60,3 +60,4 @@
@import "./collapse.css";
@import "./collapse-item.css";
@import "./cascader.css";
+@import "./color-picker.css";
diff --git a/src/index.js b/src/index.js
index 91639305e..35b49d4c8 100644
--- a/src/index.js
+++ b/src/index.js
@@ -61,6 +61,7 @@ import CarouselItem from '../packages/carousel-item';
import Collapse from '../packages/collapse';
import CollapseItem from '../packages/collapse-item';
import Cascader from '../packages/cascader';
+import ColorPicker from '../packages/color-picker';
import locale from 'element-ui/src/locale';
const components = [
@@ -120,7 +121,8 @@ const components = [
CarouselItem,
Collapse,
CollapseItem,
- Cascader
+ Cascader,
+ ColorPicker
];
const install = function(Vue, opts = {}) {
@@ -214,5 +216,6 @@ module.exports = {
CarouselItem,
Collapse,
CollapseItem,
- Cascader
+ Cascader,
+ ColorPicker
};
diff --git a/src/locale/lang/bg.js b/src/locale/lang/bg.js
index 221683454..0ae46d74d 100644
--- a/src/locale/lang/bg.js
+++ b/src/locale/lang/bg.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Изчисти'
+ },
datepicker: {
now: 'Сега',
today: 'Днес',
diff --git a/src/locale/lang/da.js b/src/locale/lang/da.js
index b3d8de0a6..27b91c57b 100644
--- a/src/locale/lang/da.js
+++ b/src/locale/lang/da.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Ryd'
+ },
datepicker: {
now: 'Nu',
today: 'I dag',
diff --git a/src/locale/lang/de.js b/src/locale/lang/de.js
index bec8916e8..f5b92631c 100644
--- a/src/locale/lang/de.js
+++ b/src/locale/lang/de.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Leeren'
+ },
datepicker: {
now: 'Jetzt',
today: 'Heute',
diff --git a/src/locale/lang/el.js b/src/locale/lang/el.js
index 1f3f2bc83..335689fe0 100644
--- a/src/locale/lang/el.js
+++ b/src/locale/lang/el.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Καθαρισμός'
+ },
datepicker: {
now: 'Τώρα',
today: 'Σήμερα',
diff --git a/src/locale/lang/en.js b/src/locale/lang/en.js
index f7fc32a3b..904ad856f 100644
--- a/src/locale/lang/en.js
+++ b/src/locale/lang/en.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Clear'
+ },
datepicker: {
now: 'Now',
today: 'Today',
diff --git a/src/locale/lang/es.js b/src/locale/lang/es.js
index 26b804d6b..5508fedf8 100644
--- a/src/locale/lang/es.js
+++ b/src/locale/lang/es.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'Confirmar',
+ clear: 'Limpiar'
+ },
datepicker: {
now: 'Ahora',
today: 'Hoy',
diff --git a/src/locale/lang/fa.js b/src/locale/lang/fa.js
index 672f6f4d0..5433eef22 100644
--- a/src/locale/lang/fa.js
+++ b/src/locale/lang/fa.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'باشد',
+ clear: 'خذف'
+ },
datepicker: {
now: 'اکنون',
today: 'امروز',
diff --git a/src/locale/lang/fi.js b/src/locale/lang/fi.js
index bc57f2ce3..69d40a262 100644
--- a/src/locale/lang/fi.js
+++ b/src/locale/lang/fi.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Tyhjennä'
+ },
datepicker: {
now: 'Nyt',
today: 'Tänään',
diff --git a/src/locale/lang/fr.js b/src/locale/lang/fr.js
index 5e38a5feb..8bec7a4b2 100644
--- a/src/locale/lang/fr.js
+++ b/src/locale/lang/fr.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Effacer'
+ },
datepicker: {
now: 'Maintenant',
today: 'Auj.',
diff --git a/src/locale/lang/id.js b/src/locale/lang/id.js
index 4618bd2bd..4709e0216 100644
--- a/src/locale/lang/id.js
+++ b/src/locale/lang/id.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'YA',
+ clear: 'Kosongkan'
+ },
datepicker: {
now: 'Sekarang',
today: 'Hari ini',
diff --git a/src/locale/lang/it.js b/src/locale/lang/it.js
index 539a1d220..e577bd43d 100644
--- a/src/locale/lang/it.js
+++ b/src/locale/lang/it.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Pulisci'
+ },
datepicker: {
now: 'Ora',
today: 'Oggi',
diff --git a/src/locale/lang/ja.js b/src/locale/lang/ja.js
index d93a4d0d0..5d723aecd 100644
--- a/src/locale/lang/ja.js
+++ b/src/locale/lang/ja.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'はい',
+ clear: 'クリア'
+ },
datepicker: {
now: '現在',
today: '今日',
diff --git a/src/locale/lang/ko.js b/src/locale/lang/ko.js
index 7768b4247..b22ab8440 100644
--- a/src/locale/lang/ko.js
+++ b/src/locale/lang/ko.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: '확인',
+ clear: '초기화'
+ },
datepicker: {
now: '지금',
today: '오늘',
diff --git a/src/locale/lang/nb-NO.js b/src/locale/lang/nb-NO.js
index 1503459da..e185dd86e 100644
--- a/src/locale/lang/nb-NO.js
+++ b/src/locale/lang/nb-NO.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Tøm'
+ },
datepicker: {
now: 'Nå',
today: 'I dag',
diff --git a/src/locale/lang/nl.js b/src/locale/lang/nl.js
index 447a2657a..e79a12083 100644
--- a/src/locale/lang/nl.js
+++ b/src/locale/lang/nl.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'Bevestig',
+ clear: 'Legen'
+ },
datepicker: {
now: 'Nu',
today: 'Vandaag',
diff --git a/src/locale/lang/pl.js b/src/locale/lang/pl.js
index ca11e02f5..b8a9d9964 100644
--- a/src/locale/lang/pl.js
+++ b/src/locale/lang/pl.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Wyczyść'
+ },
datepicker: {
now: 'Teraz',
today: 'Dzisiaj',
diff --git a/src/locale/lang/pt-br.js b/src/locale/lang/pt-br.js
index 82a0277a7..0b6df083c 100644
--- a/src/locale/lang/pt-br.js
+++ b/src/locale/lang/pt-br.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'Confirmar',
+ clear: 'Limpar'
+ },
datepicker: {
now: 'Agora',
today: 'Hoje',
diff --git a/src/locale/lang/pt.js b/src/locale/lang/pt.js
index 8efcdcb4e..1695f0822 100644
--- a/src/locale/lang/pt.js
+++ b/src/locale/lang/pt.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'Confirmar',
+ clear: 'Limpar'
+ },
datepicker: {
now: 'Agora',
today: 'Hoje',
diff --git a/src/locale/lang/ru-RU.js b/src/locale/lang/ru-RU.js
index a91fb3836..8fe40a09b 100644
--- a/src/locale/lang/ru-RU.js
+++ b/src/locale/lang/ru-RU.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Очистить'
+ },
datepicker: {
now: 'Сейчас',
today: 'Сегодня',
diff --git a/src/locale/lang/sk.js b/src/locale/lang/sk.js
index 42bb95a66..ce79ce81d 100644
--- a/src/locale/lang/sk.js
+++ b/src/locale/lang/sk.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Zmazať'
+ },
datepicker: {
now: 'Teraz',
today: 'Dnes',
diff --git a/src/locale/lang/sv-SE.js b/src/locale/lang/sv-SE.js
index e53c90a42..1d5d4bf78 100644
--- a/src/locale/lang/sv-SE.js
+++ b/src/locale/lang/sv-SE.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Töm'
+ },
datepicker: {
now: 'Nu',
today: 'Idag',
diff --git a/src/locale/lang/th.js b/src/locale/lang/th.js
index 044f79c65..5860f6de3 100644
--- a/src/locale/lang/th.js
+++ b/src/locale/lang/th.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'ตกลง',
+ clear: 'ล้างข้อมูล'
+ },
datepicker: {
now: 'ตอนนี้',
today: 'วันนี้',
diff --git a/src/locale/lang/tr-TR.js b/src/locale/lang/tr-TR.js
index 55448e2a6..2adda5fdf 100644
--- a/src/locale/lang/tr-TR.js
+++ b/src/locale/lang/tr-TR.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Temizle'
+ },
datepicker: {
now: 'Şimdi',
today: 'Bugün',
diff --git a/src/locale/lang/vi.js b/src/locale/lang/vi.js
index b54f459ce..6bc46f94b 100644
--- a/src/locale/lang/vi.js
+++ b/src/locale/lang/vi.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: 'OK',
+ clear: 'Xóa'
+ },
datepicker: {
now: 'Hiện tại',
today: 'Hôm nay',
diff --git a/src/locale/lang/zh-CN.js b/src/locale/lang/zh-CN.js
index 5cc3050df..5056be251 100644
--- a/src/locale/lang/zh-CN.js
+++ b/src/locale/lang/zh-CN.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: '确定',
+ clear: '清空'
+ },
datepicker: {
now: '此刻',
today: '今天',
diff --git a/src/locale/lang/zh-TW.js b/src/locale/lang/zh-TW.js
index 27a33b2dc..f76958d3a 100644
--- a/src/locale/lang/zh-TW.js
+++ b/src/locale/lang/zh-TW.js
@@ -1,5 +1,9 @@
export default {
el: {
+ colorpicker: {
+ confirm: '確認',
+ clear: '清空'
+ },
datepicker: {
now: '現在',
today: '今天',
diff --git a/test/unit/specs/color-picker.spec.js b/test/unit/specs/color-picker.spec.js
new file mode 100644
index 000000000..ee8fa2e45
--- /dev/null
+++ b/test/unit/specs/color-picker.spec.js
@@ -0,0 +1,137 @@
+import { createTest, createVue, destroyVM } from '../util';
+import ColorPicker from 'packages/color-picker';
+
+describe('ColorPicker', () => {
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy(true);
+ destroyVM(vm);
+ const dropdown = document.querySelector('.el-color-dropdown');
+ if (dropdown && dropdown.parentNode) dropdown.parentNode.removeChild(dropdown);
+ });
+
+ it('should works', () => {
+ vm = createTest(ColorPicker, true);
+ expect(vm.$el).to.exist;
+ });
+
+ it('should show alpha slider when show-alpha=true', (done) => {
+ const vm = createVue({
+ template: `
+
+ `,
+
+ data() {
+ return {
+ color: null
+ };
+ }
+ }, true);
+
+ const trigger = vm.$el.querySelector('.el-color-picker__trigger');
+ trigger.click();
+
+ setTimeout(() => {
+ const alphaSlider = document.querySelector('.el-color-alpha-slider');
+ expect(alphaSlider).to.exist;
+ done();
+ }, ANIMATION_TIME);
+ });
+
+ it('should show color picker when click trigger', (done) => {
+ vm = createTest(ColorPicker, true);
+
+ const trigger = vm.$el.querySelector('.el-color-picker__trigger');
+ trigger.click();
+
+ vm.$nextTick(() => {
+ const dropdown = document.querySelector('.el-color-dropdown');
+ expect(dropdown).to.exist;
+ done();
+ });
+ });
+
+ const ANIMATION_TIME = 300;
+
+ it('should pick a color when confirm button click', (done) => {
+ const vm = createVue({
+ template: `
+
+ `,
+
+ data() {
+ return {
+ color: null
+ };
+ }
+ }, true);
+
+ const trigger = vm.$el.querySelector('.el-color-picker__trigger');
+ trigger.click();
+
+ setTimeout(() => {
+ const dropdown = document.querySelector('.el-color-dropdown__btn');
+ dropdown.click();
+ vm.$nextTick(() => {
+ expect(vm.color).to.equal('#FF0000');
+ done();
+ });
+ }, ANIMATION_TIME);
+ });
+
+ it('should init the right color when open', (done) => {
+ const vm = createVue({
+ template: `
+
+ `,
+
+ data() {
+ return {
+ color: '#0f0'
+ };
+ }
+ }, true);
+
+ const trigger = vm.$el.querySelector('.el-color-picker__trigger');
+ trigger.click();
+
+ setTimeout(() => {
+ const dropdown = document.querySelector('.el-color-dropdown__btn');
+ dropdown.click();
+ vm.$nextTick(() => {
+ const hueBar = document.querySelector('.el-color-hue-slider__thumb');
+ const top = parseInt(hueBar.style.top, 10);
+ expect(top > 10).to.be.true;
+ done();
+ });
+ }, ANIMATION_TIME);
+ });
+
+ it('should clear a color when clear button click', (done) => {
+ const vm = createVue({
+ template: `
+
+ `,
+
+ data() {
+ return {
+ color: '#f00'
+ };
+ }
+ }, true);
+
+ const trigger = vm.$el.querySelector('.el-color-picker__trigger');
+ trigger.click();
+
+ setTimeout(() => {
+ const clearBtn = document.querySelector('.el-color-dropdown__link-btn');
+ clearBtn.click();
+ setTimeout(() => {
+ expect(vm.color).to.equal(null);
+ done();
+ }, 30);
+ }, ANIMATION_TIME);
+ });
+});
+