update the master branch to the latest
This commit is contained in:
parent
67d534df27
commit
23a0ba9831
611 changed files with 122648 additions and 0 deletions
103
src/components/color-picker/alpha.vue
Normal file
103
src/components/color-picker/alpha.vue
Normal file
|
@ -0,0 +1,103 @@
|
|||
<template>
|
||||
<div
|
||||
:class="[prefixCls + '-alpha']"
|
||||
tabindex="0"
|
||||
@click="$el.focus()"
|
||||
@keydown.esc="handleEscape"
|
||||
@keydown.left="handleLeft"
|
||||
@keydown.right="handleRight"
|
||||
@keydown.up="handleUp"
|
||||
@keydown.down="handleDown"
|
||||
>
|
||||
<div :class="[prefixCls + '-alpha-checkboard-wrap']">
|
||||
<div :class="[prefixCls + '-alpha-checkerboard']"></div>
|
||||
</div>
|
||||
<div
|
||||
:style="gradientStyle"
|
||||
:class="[prefixCls + '-alpha-gradient']"></div>
|
||||
<div
|
||||
ref="container"
|
||||
:class="[prefixCls + '-alpha-container']"
|
||||
@mousedown="handleMouseDown"
|
||||
@touchmove="handleChange"
|
||||
@touchstart="handleChange">
|
||||
<div
|
||||
:style="{top: 0, left: `${value.a * 100}%`}"
|
||||
:class="[prefixCls + '-alpha-pointer']">
|
||||
<div :class="[prefixCls + '-alpha-picker']"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HSAMixin from './hsaMixin';
|
||||
import Prefixes from './prefixMixin';
|
||||
import {clamp, toRGBAString} from './utils';
|
||||
|
||||
export default {
|
||||
name: 'Alpha',
|
||||
|
||||
mixins: [HSAMixin, Prefixes],
|
||||
|
||||
data() {
|
||||
const normalStep = 1;
|
||||
const jumpStep = 10;
|
||||
|
||||
return {
|
||||
left: -normalStep,
|
||||
right: normalStep,
|
||||
up: jumpStep,
|
||||
down: -jumpStep,
|
||||
powerKey: 'shiftKey',
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
gradientStyle() {
|
||||
const {r, g, b} = this.value.rgba;
|
||||
const start = toRGBAString({r, g, b, a: 0});
|
||||
const finish = toRGBAString({r, g, b, a: 1});
|
||||
|
||||
return {background: `linear-gradient(to right, ${start} 0%, ${finish} 100%)`};
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
change(newAlpha) {
|
||||
const {h, s, l} = this.value.hsl;
|
||||
const {a} = this.value;
|
||||
|
||||
if (a !== newAlpha) {
|
||||
this.$emit('change', {h, s, l, a: newAlpha, source: 'rgba'});
|
||||
}
|
||||
},
|
||||
handleSlide(e, direction) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.change(clamp(e[this.powerKey] ? direction : Math.round(this.value.hsl.a * 100 + direction) / 100, 0, 1));
|
||||
},
|
||||
handleChange(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const left = this.getLeft(e);
|
||||
|
||||
if (left < 0) {
|
||||
this.change(0);
|
||||
return;
|
||||
}
|
||||
|
||||
const {clientWidth} = this.$refs.container;
|
||||
|
||||
if (left > clientWidth) {
|
||||
this.change(1);
|
||||
return;
|
||||
}
|
||||
|
||||
this.change(Math.round(left * 100 / clientWidth) / 100);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
512
src/components/color-picker/color-picker.vue
Normal file
512
src/components/color-picker/color-picker.vue
Normal file
|
@ -0,0 +1,512 @@
|
|||
<template>
|
||||
<div
|
||||
v-click-outside="handleClose"
|
||||
:class="classes">
|
||||
<div
|
||||
ref="reference"
|
||||
:class="wrapClasses"
|
||||
@click="toggleVisible">
|
||||
<input
|
||||
:name="name"
|
||||
:value="currentValue"
|
||||
type="hidden">
|
||||
<Icon :type="arrowType" :custom="customArrowType" :size="arrowSize" :class="arrowClasses"></Icon>
|
||||
<div
|
||||
ref="input"
|
||||
:tabindex="disabled ? undefined : 0"
|
||||
:class="inputClasses"
|
||||
@keydown.tab="onTab"
|
||||
@keydown.esc="onEscape"
|
||||
@keydown.up="onArrow"
|
||||
@keydown.down="onArrow"
|
||||
>
|
||||
<div :class="[prefixCls + '-color']">
|
||||
<div
|
||||
v-show="value === '' && !visible"
|
||||
:class="[prefixCls + '-color-empty']">
|
||||
<i :class="[iconPrefixCls, iconPrefixCls + '-ios-close']"></i>
|
||||
</div>
|
||||
<div
|
||||
v-show="value || visible"
|
||||
:style="displayedColorStyle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="transition-drop">
|
||||
<Drop
|
||||
v-transfer-dom
|
||||
v-show="visible"
|
||||
ref="drop"
|
||||
:placement="placement"
|
||||
:data-transfer="transfer"
|
||||
:transfer="transfer"
|
||||
:class="dropClasses"
|
||||
>
|
||||
<transition name="fade">
|
||||
<div
|
||||
v-if="visible"
|
||||
:class="[prefixCls + '-picker']">
|
||||
<div :class="[prefixCls + '-picker-wrapper']">
|
||||
<div :class="[prefixCls + '-picker-panel']">
|
||||
<Saturation
|
||||
ref="saturation"
|
||||
v-model="saturationColors"
|
||||
:focused="visible"
|
||||
@change="childChange"
|
||||
@keydown.native.tab="handleFirstTab"
|
||||
></Saturation>
|
||||
</div>
|
||||
<div
|
||||
v-if="hue"
|
||||
:class="[prefixCls + '-picker-hue-slider']">
|
||||
<Hue
|
||||
v-model="saturationColors"
|
||||
@change="childChange"></Hue>
|
||||
</div>
|
||||
<div
|
||||
v-if="alpha"
|
||||
:class="[prefixCls + '-picker-alpha-slider']">
|
||||
<Alpha
|
||||
v-model="saturationColors"
|
||||
@change="childChange"></Alpha>
|
||||
</div>
|
||||
<recommend-colors
|
||||
v-if="colors.length"
|
||||
:list="colors"
|
||||
:class="[prefixCls + '-picker-colors']"
|
||||
@picker-color="handleSelectColor"></recommend-colors>
|
||||
<recommend-colors
|
||||
v-if="!colors.length && recommend"
|
||||
:list="recommendedColor"
|
||||
:class="[prefixCls + '-picker-colors']"
|
||||
@picker-color="handleSelectColor"></recommend-colors>
|
||||
</div>
|
||||
<div :class="[prefixCls + '-confirm']">
|
||||
<span :class="confirmColorClasses">
|
||||
<template v-if="editable">
|
||||
<i-input :value="formatColor" size="small" @on-enter="handleEditColor" @on-blur="handleEditColor"></i-input>
|
||||
</template>
|
||||
<template v-else>{{formatColor}}</template>
|
||||
</span>
|
||||
<i-button
|
||||
ref="clear"
|
||||
:tabindex="0"
|
||||
size="small"
|
||||
@click.native="handleClear"
|
||||
@keydown.enter="handleClear"
|
||||
@keydown.native.esc="closer"
|
||||
>{{t('i.datepicker.clear')}}</i-button>
|
||||
<i-button
|
||||
ref="ok"
|
||||
:tabindex="0"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click.native="handleSuccess"
|
||||
@keydown.native.tab="handleLastTab"
|
||||
@keydown.enter="handleSuccess"
|
||||
@keydown.native.esc="closer"
|
||||
>{{t('i.datepicker.ok')}}</i-button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</Drop>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tinycolor from 'tinycolor2';
|
||||
import {directive as clickOutside} from 'v-click-outside-x';
|
||||
import TransferDom from '../../directives/transfer-dom';
|
||||
import Drop from '../../components/select/dropdown.vue';
|
||||
import RecommendColors from './recommend-colors.vue';
|
||||
import Saturation from './saturation.vue';
|
||||
import Hue from './hue.vue';
|
||||
import Alpha from './alpha.vue';
|
||||
import iInput from '../input/input.vue';
|
||||
import iButton from '../button/button.vue';
|
||||
import Icon from '../icon/icon.vue';
|
||||
import Locale from '../../mixins/locale';
|
||||
import {oneOf} from '../../utils/assist';
|
||||
import Emitter from '../../mixins/emitter';
|
||||
import Prefixes from './prefixMixin';
|
||||
import {changeColor, toRGBAString} from './utils';
|
||||
|
||||
export default {
|
||||
name: 'ColorPicker',
|
||||
|
||||
components: {Drop, RecommendColors, Saturation, Hue, Alpha, iInput, iButton, Icon},
|
||||
|
||||
directives: {clickOutside, TransferDom},
|
||||
|
||||
mixins: [Emitter, Locale, Prefixes],
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
hue: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
alpha: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
recommend: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
validator(value) {
|
||||
return oneOf(value, ['hsl', 'hsv', 'hex', 'rgb']);
|
||||
},
|
||||
default: undefined,
|
||||
},
|
||||
colors: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
size: {
|
||||
validator(value) {
|
||||
return oneOf(value, ['small', 'large', 'default']);
|
||||
},
|
||||
default () {
|
||||
return !this.$IVIEW || this.$IVIEW.size === '' ? 'default' : this.$IVIEW.size;
|
||||
}
|
||||
},
|
||||
hideDropDown: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
placement: {
|
||||
type: String,
|
||||
validator(value) {
|
||||
return oneOf(value, [
|
||||
'top',
|
||||
'top-start',
|
||||
'top-end',
|
||||
'bottom',
|
||||
'bottom-start',
|
||||
'bottom-end',
|
||||
'left',
|
||||
'left-start',
|
||||
'left-end',
|
||||
'right',
|
||||
'right-start',
|
||||
'right-end',
|
||||
]);
|
||||
},
|
||||
default: 'bottom',
|
||||
},
|
||||
transfer: {
|
||||
type: Boolean,
|
||||
default () {
|
||||
return !this.$IVIEW || this.$IVIEW.transfer === '' ? false : this.$IVIEW.transfer;
|
||||
}
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
val: changeColor(this.value),
|
||||
currentValue: this.value,
|
||||
dragging: false,
|
||||
visible: false,
|
||||
recommendedColor: [
|
||||
'#2d8cf0',
|
||||
'#19be6b',
|
||||
'#ff9900',
|
||||
'#ed4014',
|
||||
'#00b5ff',
|
||||
'#19c919',
|
||||
'#f9e31c',
|
||||
'#ea1a1a',
|
||||
'#9b1dea',
|
||||
'#00c2b1',
|
||||
'#ac7a33',
|
||||
'#1d35ea',
|
||||
'#8bc34a',
|
||||
'#f16b62',
|
||||
'#ea4ca3',
|
||||
'#0d94aa',
|
||||
'#febd79',
|
||||
'#5d4037',
|
||||
'#00bcd4',
|
||||
'#f06292',
|
||||
'#cddc39',
|
||||
'#607d8b',
|
||||
'#000000',
|
||||
'#ffffff',
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
arrowClasses() {
|
||||
return [
|
||||
`${this.inputPrefixCls}-icon`,
|
||||
`${this.inputPrefixCls}-icon-normal`,
|
||||
];
|
||||
},
|
||||
transition() {
|
||||
return oneOf(this.placement, ['bottom-start', 'bottom', 'bottom-end']) ? 'slide-up' : 'fade';
|
||||
},
|
||||
saturationColors: {
|
||||
get() {
|
||||
return this.val;
|
||||
},
|
||||
set(newVal) {
|
||||
this.val = newVal;
|
||||
this.$emit('on-active-change', this.formatColor);
|
||||
},
|
||||
},
|
||||
classes() {
|
||||
return [
|
||||
`${this.prefixCls}`,
|
||||
{
|
||||
[`${this.prefixCls}-transfer`]: this.transfer,
|
||||
},
|
||||
];
|
||||
},
|
||||
wrapClasses() {
|
||||
return [
|
||||
`${this.prefixCls}-rel`,
|
||||
`${this.prefixCls}-${this.size}`,
|
||||
`${this.inputPrefixCls}-wrapper`,
|
||||
`${this.inputPrefixCls}-wrapper-${this.size}`,
|
||||
{
|
||||
[`${this.prefixCls}-disabled`]: this.disabled,
|
||||
},
|
||||
];
|
||||
},
|
||||
inputClasses() {
|
||||
return [
|
||||
`${this.prefixCls}-input`,
|
||||
`${this.inputPrefixCls}`,
|
||||
`${this.inputPrefixCls}-${this.size}`,
|
||||
{
|
||||
[`${this.prefixCls}-focused`]: this.visible,
|
||||
[`${this.prefixCls}-disabled`]: this.disabled,
|
||||
},
|
||||
];
|
||||
},
|
||||
dropClasses() {
|
||||
return [
|
||||
`${this.transferPrefixCls}-no-max-height`,
|
||||
{
|
||||
[`${this.prefixCls}-transfer`]: this.transfer,
|
||||
[`${this.prefixCls}-hide-drop`]: this.hideDropDown,
|
||||
},
|
||||
];
|
||||
},
|
||||
displayedColorStyle() {
|
||||
return {backgroundColor: toRGBAString(this.visible ? this.saturationColors.rgba : tinycolor(this.value).toRgb())};
|
||||
},
|
||||
formatColor() {
|
||||
const {format, saturationColors} = this;
|
||||
|
||||
if (format) {
|
||||
if (format === 'hsl') {
|
||||
return tinycolor(saturationColors.hsl).toHslString();
|
||||
}
|
||||
|
||||
if (format === 'hsv') {
|
||||
return tinycolor(saturationColors.hsv).toHsvString();
|
||||
}
|
||||
|
||||
if (format === 'hex') {
|
||||
return saturationColors.hex;
|
||||
}
|
||||
|
||||
if (format === 'rgb') {
|
||||
return toRGBAString(saturationColors.rgba);
|
||||
}
|
||||
} else if (this.alpha) {
|
||||
return toRGBAString(saturationColors.rgba);
|
||||
}
|
||||
|
||||
return saturationColors.hex;
|
||||
},
|
||||
confirmColorClasses () {
|
||||
return [
|
||||
`${this.prefixCls}-confirm-color`,
|
||||
{
|
||||
[`${this.prefixCls}-confirm-color-editable`]: this.editable
|
||||
}
|
||||
];
|
||||
},
|
||||
// 3.4.0, global setting customArrow 有值时,arrow 赋值空
|
||||
arrowType () {
|
||||
let type = 'ios-arrow-down';
|
||||
|
||||
if (this.$IVIEW) {
|
||||
if (this.$IVIEW.colorPicker.customArrow) {
|
||||
type = '';
|
||||
} else if (this.$IVIEW.colorPicker.arrow) {
|
||||
type = this.$IVIEW.colorPicker.arrow;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
},
|
||||
// 3.4.0, global setting
|
||||
customArrowType () {
|
||||
let type = '';
|
||||
|
||||
if (this.$IVIEW) {
|
||||
if (this.$IVIEW.colorPicker.customArrow) {
|
||||
type = this.$IVIEW.colorPicker.customArrow;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
},
|
||||
// 3.4.0, global setting
|
||||
arrowSize () {
|
||||
let size = '';
|
||||
|
||||
if (this.$IVIEW) {
|
||||
if (this.$IVIEW.colorPicker.arrowSize) {
|
||||
size = this.$IVIEW.colorPicker.arrowSize;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.val = changeColor(newVal);
|
||||
},
|
||||
visible(val) {
|
||||
this.val = changeColor(this.value);
|
||||
this.$refs.drop[val ? 'update' : 'destroy']();
|
||||
this.$emit('on-open-change', Boolean(val));
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$on('on-escape-keydown', this.closer);
|
||||
this.$on('on-dragging', this.setDragging);
|
||||
},
|
||||
|
||||
methods: {
|
||||
setDragging(value) {
|
||||
this.dragging = value;
|
||||
},
|
||||
handleClose(event) {
|
||||
if (this.visible) {
|
||||
if (this.dragging || event.type === 'mousedown') {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.transfer) {
|
||||
const {$el} = this.$refs.drop;
|
||||
if ($el === event.target || $el.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.closer(event);
|
||||
return;
|
||||
}
|
||||
|
||||
this.visible = false;
|
||||
},
|
||||
toggleVisible() {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.visible = !this.visible;
|
||||
this.$refs.input.focus();
|
||||
},
|
||||
childChange(data) {
|
||||
this.colorChange(data);
|
||||
},
|
||||
colorChange(data, oldHue) {
|
||||
this.oldHue = this.saturationColors.hsl.h;
|
||||
this.saturationColors = changeColor(data, oldHue || this.oldHue);
|
||||
},
|
||||
closer(event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
this.visible = false;
|
||||
this.$refs.input.focus();
|
||||
},
|
||||
handleButtons(event, value) {
|
||||
this.currentValue = value;
|
||||
this.$emit('input', value);
|
||||
this.$emit('on-change', value);
|
||||
this.dispatch('FormItem', 'on-form-change', value);
|
||||
this.closer(event);
|
||||
},
|
||||
handleSuccess(event) {
|
||||
this.handleButtons(event, this.formatColor);
|
||||
this.$emit('on-pick-success');
|
||||
},
|
||||
handleClear(event) {
|
||||
this.handleButtons(event, '');
|
||||
this.$emit('on-pick-clear');
|
||||
},
|
||||
handleSelectColor(color) {
|
||||
this.val = changeColor(color);
|
||||
this.$emit('on-active-change', this.formatColor);
|
||||
},
|
||||
handleEditColor (event) {
|
||||
const value = event.target.value;
|
||||
this.handleSelectColor(value);
|
||||
},
|
||||
handleFirstTab(event) {
|
||||
if (event.shiftKey) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.$refs.ok.$el.focus();
|
||||
}
|
||||
},
|
||||
handleLastTab(event) {
|
||||
if (!event.shiftKey) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.$refs.saturation.$el.focus();
|
||||
}
|
||||
},
|
||||
onTab(event) {
|
||||
if (this.visible) {
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
onEscape(event) {
|
||||
if (this.visible) {
|
||||
this.closer(event);
|
||||
}
|
||||
},
|
||||
onArrow(event) {
|
||||
if (!this.visible) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.visible = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
7
src/components/color-picker/handleEscapeMixin.js
Normal file
7
src/components/color-picker/handleEscapeMixin.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
methods: {
|
||||
handleEscape(e) {
|
||||
this.dispatch('ColorPicker', 'on-escape-keydown', e);
|
||||
},
|
||||
},
|
||||
};
|
78
src/components/color-picker/hsaMixin.js
Normal file
78
src/components/color-picker/hsaMixin.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
import Emitter from '../../mixins/emitter';
|
||||
import handleEscapeMixin from './handleEscapeMixin';
|
||||
import {getTouches} from './utils';
|
||||
import { on, off } from '../../utils/dom';
|
||||
|
||||
export default {
|
||||
mixins: [Emitter, handleEscapeMixin],
|
||||
|
||||
props: {
|
||||
focused: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.unbindEventListeners();
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.focused) {
|
||||
setTimeout(() => this.$el.focus(), 1);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleLeft(e) {
|
||||
this.handleSlide(e, this.left, 'left');
|
||||
},
|
||||
handleRight(e) {
|
||||
this.handleSlide(e, this.right, 'right');
|
||||
},
|
||||
handleUp(e) {
|
||||
this.handleSlide(e, this.up, 'up');
|
||||
},
|
||||
handleDown(e) {
|
||||
this.handleSlide(e, this.down, 'down');
|
||||
},
|
||||
handleMouseDown(e) {
|
||||
this.dispatch('ColorPicker', 'on-dragging', true);
|
||||
this.handleChange(e, true);
|
||||
// window.addEventListener('mousemove', this.handleChange, false);
|
||||
// window.addEventListener('mouseup', this.handleMouseUp, false);
|
||||
on(window, 'mousemove', this.handleChange);
|
||||
on(window, 'mouseup', this.handleMouseUp);
|
||||
},
|
||||
handleMouseUp() {
|
||||
this.unbindEventListeners();
|
||||
},
|
||||
unbindEventListeners() {
|
||||
// window.removeEventListener('mousemove', this.handleChange);
|
||||
// window.removeEventListener('mouseup', this.handleMouseUp);
|
||||
off(window, 'mousemove', this.handleChange);
|
||||
off(window, 'mouseup', this.handleMouseUp);
|
||||
// This timeout is required so that the click handler for click-outside
|
||||
// has the chance to run before the mouseup removes the dragging flag.
|
||||
setTimeout(() => this.dispatch('ColorPicker', 'on-dragging', false), 1);
|
||||
},
|
||||
getLeft(e) {
|
||||
const {container} = this.$refs;
|
||||
const xOffset = container.getBoundingClientRect().left + window.pageXOffset;
|
||||
const pageX = e.pageX || getTouches(e, 'PageX');
|
||||
|
||||
return pageX - xOffset;
|
||||
},
|
||||
getTop(e) {
|
||||
const {container} = this.$refs;
|
||||
const yOffset = container.getBoundingClientRect().top + window.pageYOffset;
|
||||
const pageY = e.pageY || getTouches(e, 'PageY');
|
||||
|
||||
return pageY - yOffset;
|
||||
},
|
||||
},
|
||||
};
|
101
src/components/color-picker/hue.vue
Normal file
101
src/components/color-picker/hue.vue
Normal file
|
@ -0,0 +1,101 @@
|
|||
<template>
|
||||
<div
|
||||
:class="[prefixCls + '-hue']"
|
||||
tabindex="0"
|
||||
@click="$el.focus()"
|
||||
@keydown.esc="handleEscape"
|
||||
@keydown.left="handleLeft"
|
||||
@keydown.right="handleRight"
|
||||
@keydown.up="handleUp"
|
||||
@keydown.down="handleDown"
|
||||
>
|
||||
<div
|
||||
ref="container"
|
||||
:class="[prefixCls + '-hue-container']"
|
||||
@mousedown="handleMouseDown"
|
||||
@touchmove="handleChange"
|
||||
@touchstart="handleChange">
|
||||
<div
|
||||
:style="{top: 0, left: `${percent}%`}"
|
||||
:class="[prefixCls + '-hue-pointer']">
|
||||
<div :class="[prefixCls + '-hue-picker']"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HASMixin from './hsaMixin';
|
||||
import Prefixes from './prefixMixin';
|
||||
import {clamp} from './utils';
|
||||
|
||||
export default {
|
||||
name: 'Hue',
|
||||
|
||||
mixins: [HASMixin, Prefixes],
|
||||
|
||||
data() {
|
||||
const normalStep = 1 / 360 * 25;
|
||||
const jumpStep = 20 * normalStep;
|
||||
|
||||
return {
|
||||
left: -normalStep,
|
||||
right: normalStep,
|
||||
up: jumpStep,
|
||||
down: -jumpStep,
|
||||
powerKey: 'shiftKey',
|
||||
percent: clamp(this.value.hsl.h * 100 / 360, 0, 100),
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
value () {
|
||||
this.percent = clamp(this.value.hsl.h * 100 / 360, 0, 100);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
change(percent) {
|
||||
this.percent = clamp(percent, 0, 100);
|
||||
|
||||
const {h, s, l, a} = this.value.hsl;
|
||||
const newHue = clamp(percent / 100 * 360, 0, 360);
|
||||
|
||||
if (h !== newHue) {
|
||||
this.$emit('change', {h: newHue, s, l, a, source: 'hsl'});
|
||||
}
|
||||
},
|
||||
handleSlide(e, direction) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (e[this.powerKey]) {
|
||||
this.change(direction < 0 ? 0 : 100);
|
||||
return;
|
||||
}
|
||||
|
||||
this.change(this.percent + direction);
|
||||
},
|
||||
handleChange(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const left = this.getLeft(e);
|
||||
|
||||
if (left < 0) {
|
||||
this.change(0);
|
||||
return;
|
||||
}
|
||||
|
||||
const {clientWidth} = this.$refs.container;
|
||||
|
||||
if (left > clientWidth) {
|
||||
this.change(100);
|
||||
return;
|
||||
}
|
||||
|
||||
this.change(left * 100 / clientWidth);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
3
src/components/color-picker/index.js
Normal file
3
src/components/color-picker/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import ColorPicker from './color-picker.vue';
|
||||
|
||||
export default ColorPicker;
|
10
src/components/color-picker/prefixMixin.js
Normal file
10
src/components/color-picker/prefixMixin.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
export default {
|
||||
data() {
|
||||
return {
|
||||
prefixCls: 'ivu-color-picker',
|
||||
inputPrefixCls: 'ivu-input',
|
||||
iconPrefixCls: 'ivu-icon',
|
||||
transferPrefixCls: 'ivu-transfer',
|
||||
};
|
||||
},
|
||||
};
|
153
src/components/color-picker/recommend-colors.vue
Normal file
153
src/components/color-picker/recommend-colors.vue
Normal file
|
@ -0,0 +1,153 @@
|
|||
<template>
|
||||
<div
|
||||
ref="reference"
|
||||
tabindex="0"
|
||||
@click="handleClick"
|
||||
@keydown.esc="handleEscape"
|
||||
@keydown.enter="handleEnter"
|
||||
@keydown.left="handleArrow($event, 'x', left)"
|
||||
@keydown.right="handleArrow($event, 'x', right)"
|
||||
@keydown.up="handleArrow($event, 'y', up)"
|
||||
@keydown.down="handleArrow($event, 'y', down)"
|
||||
@blur="blurColor"
|
||||
@focus="focusColor"
|
||||
>
|
||||
<template v-for="(item, index) in list">
|
||||
<div
|
||||
:key="item + ':' + index"
|
||||
:class="[prefixCls + '-picker-colors-wrapper']">
|
||||
<div :data-color-id="index">
|
||||
<div
|
||||
:style="{background: item}"
|
||||
:class="[prefixCls + '-picker-colors-wrapper-color']"
|
||||
></div>
|
||||
<div
|
||||
:ref="'color-circle-' + index"
|
||||
:class="[prefixCls + '-picker-colors-wrapper-circle', hideClass]"></div>
|
||||
</div>
|
||||
</div>
|
||||
<br v-if="lineBreak(list, index)">
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Emitter from '../../mixins/emitter';
|
||||
import HandleEscapeMixin from './handleEscapeMixin';
|
||||
import Prefixes from './prefixMixin';
|
||||
import {clamp} from './utils';
|
||||
|
||||
export default {
|
||||
name: 'RecommendedColors',
|
||||
|
||||
mixins: [Emitter, HandleEscapeMixin, Prefixes],
|
||||
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
const columns = 12;
|
||||
const rows = Math.ceil(this.list.length / columns);
|
||||
const normalStep = 1;
|
||||
|
||||
return {
|
||||
left: -normalStep,
|
||||
right: normalStep,
|
||||
up: -normalStep,
|
||||
down: normalStep,
|
||||
powerKey: 'shiftKey',
|
||||
grid: {x: 1, y: 1},
|
||||
rows,
|
||||
columns,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
hideClass() {
|
||||
return `${this.prefixCls}-hide`;
|
||||
},
|
||||
linearIndex() {
|
||||
return this.getLinearIndex(this.grid);
|
||||
},
|
||||
currentCircle() {
|
||||
return this.$refs[`color-circle-${this.linearIndex}`][0];
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
getLinearIndex(grid) {
|
||||
return this.columns * (grid.y - 1) + grid.x - 1;
|
||||
},
|
||||
getMaxLimit(axis) {
|
||||
return axis === 'x' ? this.columns : this.rows;
|
||||
},
|
||||
handleArrow(e, axis, direction) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.blurColor();
|
||||
|
||||
const grid = {...this.grid};
|
||||
|
||||
if (e[this.powerKey]) {
|
||||
if (direction < 0) {
|
||||
grid[axis] = 1;
|
||||
} else {
|
||||
grid[axis] = this.getMaxLimit(axis);
|
||||
}
|
||||
} else {
|
||||
grid[axis] += direction;
|
||||
}
|
||||
|
||||
const index = this.getLinearIndex(grid);
|
||||
|
||||
if (index >= 0 && index < this.list.length) {
|
||||
this.grid[axis] = clamp(grid[axis], 1, this.getMaxLimit(axis));
|
||||
}
|
||||
|
||||
this.focusColor();
|
||||
},
|
||||
blurColor() {
|
||||
this.currentCircle.classList.add(this.hideClass);
|
||||
},
|
||||
focusColor() {
|
||||
this.currentCircle.classList.remove(this.hideClass);
|
||||
},
|
||||
handleEnter(e) {
|
||||
this.handleClick(e, this.currentCircle);
|
||||
},
|
||||
handleClick(e, circle) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.$refs.reference.focus();
|
||||
|
||||
const target = circle || e.target;
|
||||
const colorId = target.dataset.colorId || target.parentElement.dataset.colorId;
|
||||
|
||||
if (colorId) {
|
||||
this.blurColor();
|
||||
const id = Number(colorId) + 1;
|
||||
this.grid.x = id % this.columns || this.columns;
|
||||
this.grid.y = Math.ceil(id / this.columns);
|
||||
this.focusColor();
|
||||
this.$emit('picker-color', this.list[colorId]);
|
||||
this.$emit('change', {hex: this.list[colorId], source: 'hex'});
|
||||
}
|
||||
},
|
||||
lineBreak(list, index) {
|
||||
if (!index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const nextIndex = index + 1;
|
||||
|
||||
return nextIndex < list.length && nextIndex % this.columns === 0;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
101
src/components/color-picker/saturation.vue
Normal file
101
src/components/color-picker/saturation.vue
Normal file
|
@ -0,0 +1,101 @@
|
|||
<template>
|
||||
<div
|
||||
:class="[prefixCls + '-saturation-wrapper']"
|
||||
tabindex="0"
|
||||
@keydown.esc="handleEscape"
|
||||
@click="$el.focus()"
|
||||
@keydown.left="handleLeft"
|
||||
@keydown.right="handleRight"
|
||||
@keydown.up="handleUp"
|
||||
@keydown.down="handleDown"
|
||||
>
|
||||
<div
|
||||
ref="container"
|
||||
:style="bgColorStyle"
|
||||
:class="[prefixCls + '-saturation']"
|
||||
@mousedown="handleMouseDown">
|
||||
<div :class="[prefixCls + '-saturation--white']"></div>
|
||||
<div :class="[prefixCls + '-saturation--black']"></div>
|
||||
<div
|
||||
:style="pointerStyle"
|
||||
:class="[prefixCls + '-saturation-pointer']">
|
||||
<div :class="[prefixCls + '-saturation-circle']"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HSAMixin from './hsaMixin';
|
||||
import Prefixes from './prefixMixin';
|
||||
import {clamp, getIncrement} from './utils';
|
||||
import { on, off } from '../../utils/dom';
|
||||
|
||||
export default {
|
||||
name: 'Saturation',
|
||||
|
||||
mixins: [HSAMixin, Prefixes],
|
||||
|
||||
data() {
|
||||
const normalStep = 0.01;
|
||||
|
||||
return {
|
||||
left: -normalStep,
|
||||
right: normalStep,
|
||||
up: normalStep,
|
||||
down: -normalStep,
|
||||
multiplier: 10,
|
||||
powerKey: 'shiftKey',
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
bgColorStyle() {
|
||||
return {background: `hsl(${this.value.hsv.h}, 100%, 50%)`};
|
||||
},
|
||||
pointerStyle() {
|
||||
return {top: `${-(this.value.hsv.v * 100) + 1 + 100}%`, left: `${this.value.hsv.s * 100}%`};
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
change(h, s, v, a) {
|
||||
this.$emit('change', {h, s, v, a, source: 'hsva'});
|
||||
},
|
||||
handleSlide(e, direction, key) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const isPowerKey = e[this.powerKey];
|
||||
const increment = isPowerKey ? direction * this.multiplier : direction;
|
||||
const {h, s, v, a} = this.value.hsv;
|
||||
const saturation = clamp(s + getIncrement(key, ['left', 'right'], increment), 0, 1);
|
||||
const bright = clamp(v + getIncrement(key, ['up', 'down'], increment), 0, 1);
|
||||
|
||||
this.change(h, saturation, bright, a);
|
||||
},
|
||||
handleChange(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const {clientWidth, clientHeight} = this.$refs.container;
|
||||
const left = clamp(this.getLeft(e), 0, clientWidth);
|
||||
const top = clamp(this.getTop(e), 0, clientHeight);
|
||||
const saturation = left / clientWidth;
|
||||
const bright = clamp(1 - top / clientHeight, 0, 1);
|
||||
|
||||
this.change(this.value.hsv.h, saturation, bright, this.value.hsv.a);
|
||||
},
|
||||
handleMouseDown(e) {
|
||||
HSAMixin.methods.handleMouseDown.call(this, e);
|
||||
// window.addEventListener('mouseup', this.handleChange, false);
|
||||
on(window, 'mouseup', this.handleChange);
|
||||
},
|
||||
unbindEventListeners(e) {
|
||||
HSAMixin.methods.unbindEventListeners.call(this, e);
|
||||
// window.removeEventListener('mouseup', this.handleChange);
|
||||
off(window, 'mouseup', this.handleChange);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
118
src/components/color-picker/utils.js
Normal file
118
src/components/color-picker/utils.js
Normal file
|
@ -0,0 +1,118 @@
|
|||
import tinycolor from 'tinycolor2';
|
||||
import {oneOf} from '../../utils/assist';
|
||||
|
||||
function setAlpha(data, alpha) {
|
||||
const color = tinycolor(data);
|
||||
const {_a} = color;
|
||||
|
||||
if (_a === undefined || _a === null) {
|
||||
color.setAlpha(alpha || 1);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
function getColor(data, colorData) {
|
||||
const alpha = colorData && colorData.a;
|
||||
|
||||
if (colorData) {
|
||||
// hsl is better than hex between conversions
|
||||
if (colorData.hsl) {
|
||||
return setAlpha(colorData.hsl, alpha);
|
||||
}
|
||||
|
||||
if (colorData.hex && colorData.hex.length > 0) {
|
||||
return setAlpha(colorData.hex, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
return setAlpha(colorData, alpha);
|
||||
}
|
||||
|
||||
export function changeColor(data, oldHue) {
|
||||
const colorData = data === '' ? '#2d8cf0' : data;
|
||||
const color = getColor(data, colorData);
|
||||
const hsl = color.toHsl();
|
||||
const hsv = color.toHsv();
|
||||
|
||||
if (hsl.s === 0) {
|
||||
hsl.h = colorData.h || (colorData.hsl && colorData.hsl.h) || oldHue || 0;
|
||||
hsv.h = hsl.h;
|
||||
}
|
||||
|
||||
// when the hsv.v is less than 0.0164 (base on test)
|
||||
// because of possible loss of precision
|
||||
// the result of hue and saturation would be miscalculated
|
||||
if (hsv.v < 0.0164) {
|
||||
hsv.h = colorData.h || (colorData.hsv && colorData.hsv.h) || 0;
|
||||
hsv.s = colorData.s || (colorData.hsv && colorData.hsv.s) || 0;
|
||||
}
|
||||
|
||||
if (hsl.l < 0.01) {
|
||||
hsl.h = colorData.h || (colorData.hsl && colorData.hsl.h) || 0;
|
||||
hsl.s = colorData.s || (colorData.hsl && colorData.hsl.s) || 0;
|
||||
}
|
||||
|
||||
return {
|
||||
hsl,
|
||||
hex: color.toHexString().toUpperCase(),
|
||||
rgba: color.toRgb(),
|
||||
hsv,
|
||||
oldHue: colorData.h || oldHue || hsl.h,
|
||||
source: colorData.source,
|
||||
a: colorData.a || color.getAlpha(),
|
||||
};
|
||||
}
|
||||
|
||||
export function clamp(value, min, max) {
|
||||
if (value < min) {
|
||||
return min;
|
||||
}
|
||||
|
||||
if (value > max) {
|
||||
return max;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function getIncrement(key, keys, increment) {
|
||||
return oneOf(key, keys) ? increment : 0;
|
||||
}
|
||||
|
||||
export function getTouches(e, prop) {
|
||||
return e.touches ? e.touches[0][prop] : 0;
|
||||
}
|
||||
|
||||
export function toRGBAString(rgba) {
|
||||
const {r, g, b, a} = rgba;
|
||||
|
||||
return `rgba(${[r, g, b, a].join(',')})`;
|
||||
}
|
||||
|
||||
export function isValidHex(hex) {
|
||||
return tinycolor(hex).isValid();
|
||||
}
|
||||
|
||||
function checkIteratee(data, counts, letter) {
|
||||
let {checked, passed} = counts;
|
||||
const value = data[letter];
|
||||
|
||||
if (value) {
|
||||
checked += 1;
|
||||
|
||||
if (Number.isFinite(value)) {
|
||||
passed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return {checked, passed};
|
||||
}
|
||||
|
||||
const keysToCheck = ['r', 'g', 'b', 'a', 'h', 's', 'l', 'v'];
|
||||
|
||||
export function simpleCheckForValidColor(data) {
|
||||
const results = keysToCheck.reduce(checkIteratee.bind(null, data), {checked: 0, passed: 0});
|
||||
|
||||
return results.checked === results.passed ? data : undefined;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue