Color keyboard control

This commit is contained in:
Graham Fairweather 2018-05-15 14:26:58 +02:00
parent b924d14da3
commit f2bcd4adaf
13 changed files with 1314 additions and 653 deletions

View file

@ -2,32 +2,134 @@
<div style="margin: 100px;"> <div style="margin: 100px;">
{{color}} {{color}}
<!--<Input placeholder="请输入..." size="large" style="width: 50px;"></Input>--> <!--<Input placeholder="请输入..." size="large" style="width: 50px;"></Input>-->
<color-picker @on-change="c1" @on-active-change="c2" v-model="color" placement="bottom-start" size="large"></color-picker> <color-picker
<Date-picker transfer type="date" placeholder="选择日期" style="width: 200px"></Date-picker> v-model="color"
<color-picker :transfer="true" ref="xxx" v-model="color" format="rgb" alpha :recommend="true"></color-picker> placement="bottom-start"
<color-picker v-model="color2" format="hsv" :alpha="true" :recommend="false"></color-picker> size="large"
@on-change="c1"
@on-active-change="c2"></color-picker>
<Date-picker
transfer
type="date"
placeholder="选择日期"
style="width: 200px"></Date-picker>
<color-picker
ref="xxx"
:transfer="true"
v-model="color"
:recommend="true"
format="rgb"
alpha
@on-change="onChange"
@on-active-change="onActiveChange"></color-picker>
<color-picker
v-model="color2"
:alpha="true"
:recommend="false"
format="hsv"></color-picker>
<!--<Date-picker type="date" placeholder="选择日期" style="width: 200px"></Date-picker>--> <!--<Date-picker type="date" placeholder="选择日期" style="width: 200px"></Date-picker>-->
<color-picker v-model="color" placement="bottom-start" size="small"></color-picker> <color-picker
<Date-picker type="date" placeholder="选择日期" size="small" style="width: 200px"></Date-picker> v-model="color"
placement="bottom-start"
size="small"></color-picker>
<Date-picker
type="date"
placeholder="选择日期"
size="small"
style="width: 200px"></Date-picker>
<color-picker
ref="yyy"
:colors="colors"
v-model="color"
transfer
format="rgb"
alpha></color-picker>
<Button @click="setColor">set color</Button> <Button @click="setColor">set color</Button>
<br><br><br><br> <br><br><br><br>
{{openState}} {{openState}}
<ColorPicker v-model="color7" :hue="false" @on-open-change="onOpenChange"></ColorPicker> <ColorPicker
v-model="color7"
:hue="false"
@on-open-change="onOpenChange"></ColorPicker>
<ColorPicker
v-model="color7"
:hue="false"
:hide-drop-down="hideDropDown"
transfer
@on-open-change="onOpenChange"></ColorPicker>
<br><br><br><br>
<ColorPicker
v-model="color7"
disabled></ColorPicker>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
props: {}, props: {},
data() { data() {
return { return {
color: 'rgba(12,34,255,.85)', color: 'rgba(12,34,255,.85)',
color2: '', color2: '',
color7: '#19be6b', color7: '#19be6b',
openState: false, openState: false,
colors: [
'#2d8cf0',
'#19be6b',
'#ff9900',
'#ed3f14',
'#00b5ff',
'#19c919',
'#f9e31c',
'#ea1a1a',
'#9b1dea',
'#00c2b1',
'#ac7a33',
'#1d35ea',
'#8bc34a',
'#f16b62',
'#ea4ca3',
'#0d94aa',
'#febd79',
'#5d4037',
'#00bcd4',
'#f06292',
'#cddc39',
'#607d8b',
'#000000',
'#ffffff',
'#2d8cf0',
'#19be6b',
'#ff9900',
'#ed3f14',
'#00b5ff',
'#19c919',
'#f9e31c',
'#ea1a1a',
'#9b1dea',
'#00c2b1',
'#ac7a33',
'#1d35ea',
'#8bc34a',
'#f16b62',
'#ea4ca3',
'#0d94aa',
'#febd79',
'#5d4037',
],
hideDropDown: false,
}; };
}, },
computed: {}, computed: {},
mounted() {
setInterval(this.toggleShowHide, 2000);
},
methods: { methods: {
setColor() { setColor() {
this.color = '#26bc77'; this.color = '#26bc77';
@ -40,7 +142,16 @@
}, },
onOpenChange(state) { onOpenChange(state) {
this.openState = state; this.openState = state;
} },
} onChange(d) {
console.log(d);
},
onActiveChange(d) {
console.log(d);
},
toggleShowHide() {
this.hideDropDown = !this.hideDropDown;
},
},
}; };
</script> </script>

View file

@ -1,77 +1,103 @@
<template> <template>
<div class="ivu-color-picker-alpha"> <div
<div class="ivu-color-picker-alpha-checkboard-wrap"> :class="[prefixCls + '-alpha']"
<div class="ivu-color-picker-alpha-checkerboard"></div> 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>
<div class="ivu-color-picker-alpha-gradient" :style="{background: gradientColor}"></div> <div
<div class="ivu-color-picker-alpha-container" ref="container" :style="gradientStyle"
:class="[prefixCls + '-alpha-gradient']"></div>
<div
ref="container"
:class="[prefixCls + '-alpha-container']"
@mousedown="handleMouseDown" @mousedown="handleMouseDown"
@touchmove="handleChange" @touchmove="handleChange"
@touchstart="handleChange"> @touchstart="handleChange">
<div class="ivu-color-picker-alpha-pointer" :style="{left: colors.a * 100 + '%'}"> <div
<div class="ivu-color-picker-alpha-picker"></div> :style="{top: 0, left: `${value.a * 100}%`}"
:class="[prefixCls + '-alpha-pointer']">
<div :class="[prefixCls + '-alpha-picker']"></div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import HSAMixin from './hsaMixin';
import Prefixes from './prefixMixin';
import {clamp, toRGBAString} from './utils';
export default { export default {
name: 'Alpha', name: 'Alpha',
props: {
value: Object, mixins: [HSAMixin, Prefixes],
onChange: Function
data() {
const normalStep = 1;
const jumpStep = 10;
return {
left: -normalStep,
right: normalStep,
up: jumpStep,
down: -jumpStep,
powerKey: 'shiftKey',
};
}, },
computed: { computed: {
colors () { gradientStyle() {
return this.value; 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%)`};
}, },
gradientColor () {
const rgba = this.colors.rgba;
const rgbStr = [rgba.r, rgba.g, rgba.b].join(',');
return 'linear-gradient(to right, rgba(' + rgbStr + ', 0) 0%, rgba(' + rgbStr + ', 1) 100%)';
}
}, },
methods: { methods: {
handleChange (e, skip) { change(newAlpha) {
!skip && e.preventDefault(); const {h, s, l} = this.value.hsl;
const container = this.$refs.container; const {a} = this.value;
const containerWidth = container.clientWidth;
const xOffset = container.getBoundingClientRect().left + window.pageXOffset; if (a !== newAlpha) {
const pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0); this.$emit('change', {h, s, l, a: newAlpha, source: 'rgba'});
const left = pageX - xOffset; }
},
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);
let a;
if (left < 0) { if (left < 0) {
a = 0; this.change(0);
} else if (left > containerWidth) { return;
a = 1;
} else {
a = Math.round(left * 100 / containerWidth) / 100;
} }
if (this.colors.a !== a) { const {clientWidth} = this.$refs.container;
this.$emit('change', {
h: this.colors.hsl.h, if (left > clientWidth) {
s: this.colors.hsl.s, this.change(1);
l: this.colors.hsl.l, return;
a: a,
source: 'rgba'
});
} }
this.change(Math.round(left * 100 / clientWidth) / 100);
}, },
handleMouseDown (e) {
this.handleChange(e, true);
window.addEventListener('mousemove', this.handleChange);
window.addEventListener('mouseup', this.handleMouseUp);
}, },
handleMouseUp () {
this.unbindEventListeners();
},
unbindEventListeners () {
window.removeEventListener('mousemove', this.handleChange);
window.removeEventListener('mouseup', this.handleMouseUp);
}
}
}; };
</script> </script>

View file

@ -1,37 +1,74 @@
<template> <template>
<div :class="classes" v-clickoutside="handleClose"> <div
<div ref="reference" @click="toggleVisible" :class="wrapClasses"> v-click-outside.capture="handleClose"
<input type="hidden" :name="name" :value="currentValue"> v-click-outside:mousedown.capture="handleClose"
<i class="ivu-icon ivu-icon-arrow-down-b ivu-input-icon ivu-input-icon-normal"></i> :class="classes">
<div :class="inputClasses"> <div
ref="reference"
:class="wrapClasses"
@click="toggleVisible">
<input
:name="name"
:value="currentValue"
type="hidden">
<i :class="arrowClasses"></i>
<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 :class="[prefixCls + '-color']">
<div :class="[prefixCls + '-color-empty']" v-show="value === '' && !visible"> <div
<i class="ivu-icon ivu-icon-ios-close-empty"></i> v-show="value === '' && !visible"
:class="[prefixCls + '-color-empty']">
<i :class="[iconPrefixCls, iconPrefixCls + '-ios-close-empty']"></i>
</div> </div>
<div v-show="value || visible" :style="{backgroundColor: displayedColor}"></div> <div
v-show="value || visible"
:style="displayedColorStyle"></div>
</div> </div>
</div> </div>
</div> </div>
<transition name="transition-drop"> <transition name="transition-drop">
<Drop <Drop
v-transfer-dom
v-show="visible" v-show="visible"
@click.native="handleTransferClick"
:class="{ [prefixCls + '-transfer']: transfer }"
class-name="ivu-transfer-no-max-height"
:placement="placement"
ref="drop" ref="drop"
:placement="placement"
:data-transfer="transfer" :data-transfer="transfer"
v-transfer-dom> :class="dropClasses"
<div :class="[prefixCls + '-picker']"> >
<transition name="fade">
<div
v-if="visible"
:class="[prefixCls + '-picker']">
<div :class="[prefixCls + '-picker-wrapper']"> <div :class="[prefixCls + '-picker-wrapper']">
<div :class="[prefixCls + '-picker-panel']"> <div :class="[prefixCls + '-picker-panel']">
<Saturation v-model="saturationColors" @change="childChange"></Saturation> <Saturation
ref="saturation"
v-model="saturationColors"
:focused="visible"
@change="childChange"
@keydown.native.tab="handleFirstTab"
></Saturation>
</div> </div>
<div v-if="hue" :class="[prefixCls + '-picker-hue-slider']"> <div
<Hue v-model="saturationColors" @change="childChange"></Hue> v-if="hue"
:class="[prefixCls + '-picker-hue-slider']">
<Hue
v-model="saturationColors"
@change="childChange"></Hue>
</div> </div>
<div v-if="alpha" :class="[prefixCls + '-picker-alpha-slider']"> <div
<Alpha v-model="saturationColors" @change="childChange"></Alpha> v-if="alpha"
:class="[prefixCls + '-picker-alpha-slider']">
<Alpha
v-model="saturationColors"
@change="childChange"></Alpha>
</div> </div>
<recommend-colors <recommend-colors
v-if="colors.length" v-if="colors.length"
@ -46,144 +83,138 @@
</div> </div>
<div :class="[prefixCls + '-confirm']"> <div :class="[prefixCls + '-confirm']">
<span :class="[prefixCls + '-confirm-color']">{{formatColor}}</span> <span :class="[prefixCls + '-confirm-color']">{{formatColor}}</span>
<Confirm @on-pick-success="handleSuccess" @on-pick-clear="handleClear"></Confirm> <i-button
ref="clear"
:tabindex="0"
size="small"
type="ghost"
@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>
</div> </div>
</transition>
</Drop> </Drop>
</transition> </transition>
</div> </div>
</template> </template>
<script> <script>
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import vClickOutside from 'v-click-outside-x/index';
import clickoutside from '../../directives/clickoutside';
import TransferDom from '../../directives/transfer-dom'; import TransferDom from '../../directives/transfer-dom';
import Drop from '../../components/select/dropdown.vue'; import Drop from '../../components/select/dropdown.vue';
import RecommendColors from './recommend-colors.vue'; import RecommendColors from './recommend-colors.vue';
import Confirm from '../date-picker/base/confirm.vue';
import Saturation from './saturation.vue'; import Saturation from './saturation.vue';
import Hue from './hue.vue'; import Hue from './hue.vue';
import Alpha from './alpha.vue'; import Alpha from './alpha.vue';
import Locale from '../../mixins/locale';
import {oneOf} from '../../utils/assist'; import {oneOf} from '../../utils/assist';
import Emitter from '../../mixins/emitter'; import Emitter from '../../mixins/emitter';
import Prefixes from './prefixMixin';
const prefixCls = 'ivu-color-picker'; import {changeColor, toRGBAString} from './utils';
const inputPrefixCls = 'ivu-input';
function _colorChange (data, oldHue) {
data = data === '' ? '#2d8cf0' : data;
const alpha = data && data.a;
let color;
// hsl is better than hex between conversions
if (data && data.hsl) {
color = tinycolor(data.hsl);
} else if (data && data.hex && data.hex.length > 0) {
color = tinycolor(data.hex);
} else {
color = tinycolor(data);
}
if (color && (color._a === undefined || color._a === null)) {
color.setAlpha(alpha || 1);
}
const hsl = color.toHsl();
const hsv = color.toHsv();
if (hsl.s === 0) {
hsv.h = hsl.h = data.h || (data.hsl && data.hsl.h) || oldHue || 0;
}
// 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 = data.h || (data.hsv && data.hsv.h) || 0;
hsv.s = data.s || (data.hsv && data.hsv.s) || 0;
}
if (hsl.l < 0.01) {
hsl.h = data.h || (data.hsl && data.hsl.h) || 0;
hsl.s = data.s || (data.hsl && data.hsl.s) || 0;
}
return {
hsl: hsl,
hex: color.toHexString().toUpperCase(),
rgba: color.toRgb(),
hsv: hsv,
oldHue: data.h || oldHue || hsl.h,
source: data.source,
a: data.a || color.getAlpha()
};
}
export default { export default {
name: 'ColorPicker', name: 'ColorPicker',
mixins: [ Emitter ],
components: { Drop, Confirm, RecommendColors, Saturation, Hue, Alpha }, components: {Drop, RecommendColors, Saturation, Hue, Alpha},
directives: { clickoutside, TransferDom },
directives: {clickOutside: vClickOutside.directive, TransferDom},
mixins: [Emitter, Locale, Prefixes],
props: { props: {
value: { value: {
type: String type: String,
default: undefined,
}, },
hue: { hue: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
alpha: { alpha: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
recommend: { recommend: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
format: { format: {
type: String,
validator(value) { validator(value) {
return oneOf(value, ['hsl', 'hsv', 'hex', 'rgb']); return oneOf(value, ['hsl', 'hsv', 'hex', 'rgb']);
} },
default: undefined,
}, },
colors: { colors: {
type: Array, type: Array,
default() { default() {
return []; return [];
} },
}, },
disabled: { disabled: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
size: { size: {
type: String,
validator(value) { validator(value) {
return oneOf(value, ['small', 'large', 'default']); return oneOf(value, ['small', 'large', 'default']);
}, },
default: 'default' default: 'default',
},
hideDropDown: {
type: Boolean,
default: false,
}, },
placement: { placement: {
type: String,
validator(value) { 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']); 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' default: 'bottom',
}, },
transfer: { transfer: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
name: { name: {
type: String type: String,
} default: undefined,
}, },
},
data() { data() {
return { return {
val: _colorChange(this.value), val: changeColor(this.value),
currentValue: this.value, currentValue: this.value,
prefixCls: prefixCls, dragging: false,
visible: false, visible: false,
disableCloseUnderTransfer: false, // transfer Drop
recommendedColor: [ recommendedColor: [
'#2d8cf0', '#2d8cf0',
'#19be6b', '#19be6b',
@ -208,17 +239,22 @@
'#cddc39', '#cddc39',
'#607d8b', '#607d8b',
'#000000', '#000000',
'#ffffff' '#ffffff',
] ],
}; };
}, },
computed: { computed: {
arrowClasses() {
return [
this.iconPrefixCls,
`${this.iconPrefixCls}-arrow-down-b`,
`${this.inputPrefixCls}-icon`,
`${this.inputPrefixCls}-icon-normal`,
];
},
transition() { transition() {
if (this.placement === 'bottom-start' || this.placement === 'bottom' || this.placement === 'bottom-end') { return oneOf(this.placement, ['bottom-start', 'bottom', 'bottom-end']) ? 'slide-up' : 'fade';
return 'slide-up';
} else {
return 'fade';
}
}, },
saturationColors: { saturationColors: {
get() { get() {
@ -227,149 +263,191 @@
set(newVal) { set(newVal) {
this.val = newVal; this.val = newVal;
this.$emit('on-active-change', this.formatColor); this.$emit('on-active-change', this.formatColor);
} },
}, },
classes() { classes() {
return [ return [
`${prefixCls}`, `${this.prefixCls}`,
{ {
[`${prefixCls}-transfer`]: this.transfer [`${this.prefixCls}-transfer`]: this.transfer,
} },
]; ];
}, },
wrapClasses() { wrapClasses() {
return [ return [
`${prefixCls}-rel`, `${this.prefixCls}-rel`,
`${prefixCls}-${this.size}`, `${this.prefixCls}-${this.size}`,
`${inputPrefixCls}-wrapper`, `${this.inputPrefixCls}-wrapper`,
`${inputPrefixCls}-wrapper-${this.size}` `${this.inputPrefixCls}-wrapper-${this.size}`,
{
[`${this.prefixCls}-disabled`]: this.disabled,
},
]; ];
}, },
inputClasses() { inputClasses() {
return [ return [
`${prefixCls}-input`, `${this.prefixCls}-input`,
`${inputPrefixCls}`, `${this.inputPrefixCls}`,
`${inputPrefixCls}-${this.size}`, `${this.inputPrefixCls}-${this.size}`,
{ {
[`${inputPrefixCls}-disabled`]: this.disabled [`${this.prefixCls}-focused`]: this.visible,
} [`${this.prefixCls}-disabled`]: this.disabled,
},
]; ];
}, },
displayedColor () { dropClasses() {
let color; return [
if (this.visible) { `${this.transferPrefixCls}-no-max-height`,
const rgba = this.saturationColors.rgba; {
color = { [`${this.prefixCls}-transfer`]: this.transfer,
r: rgba.r, [`${this.prefixCls}-hide-drop`]: this.hideDropDown,
g: rgba.g, },
b: rgba.b, ];
a: rgba.a },
}; displayedColorStyle() {
} else { return {backgroundColor: toRGBAString(this.visible ? this.saturationColors.rgba : tinycolor(this.value).toRgb())};
color = tinycolor(this.value).toRgb();
}
return `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`;
}, },
formatColor() { formatColor() {
const value = this.saturationColors; const {format, saturationColors} = this;
const format = this.format;
let color;
const rgba = `rgba(${value.rgba.r}, ${value.rgba.g}, ${value.rgba.b}, ${value.rgba.a})`;
if (format) { if (format) {
if (format === 'hsl') { if (format === 'hsl') {
color = tinycolor(value.hsl).toHslString(); return tinycolor(saturationColors.hsl).toHslString();
} else if (format === 'hsv') { }
color = tinycolor(value.hsv).toHsvString();
} else if (format === 'hex') { if (format === 'hsv') {
color = value.hex; return tinycolor(saturationColors.hsv).toHsvString();
} else if (format === 'rgb') { }
color = rgba;
if (format === 'hex') {
return saturationColors.hex;
}
if (format === 'rgb') {
return toRGBAString(saturationColors.rgba);
} }
} else if (this.alpha) { } else if (this.alpha) {
color = rgba; return toRGBAString(saturationColors.rgba);
} else {
color = value.hex;
}
return color;
} }
return saturationColors.hex;
}, },
},
watch: { watch: {
value(newVal) { value(newVal) {
this.val = _colorChange(newVal); this.val = changeColor(newVal);
}, },
visible(val) { visible(val) {
this.val = _colorChange(this.value); this.val = changeColor(this.value);
if (val) { this.$refs.drop[val ? 'update' : 'destroy']();
this.$refs.drop.update();
} else {
this.$refs.drop.destroy();
}
this.$emit('on-open-change', Boolean(val)); this.$emit('on-open-change', Boolean(val));
}
}, },
},
mounted() {
this.$on('on-escape-keydown', this.closer);
this.$on('on-dragging', this.setDragging);
},
methods: { methods: {
// transfer Drop setDragging(value) {
handleTransferClick () { this.dragging = value;
if (this.transfer) this.disableCloseUnderTransfer = true;
}, },
handleClose () { handleClose(event) {
if (this.disableCloseUnderTransfer) { if (this.visible) {
this.disableCloseUnderTransfer = false; if (this.dragging || event.type === 'mousedown') {
return false; 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; this.visible = false;
}, },
toggleVisible() { toggleVisible() {
if (this.disabled) {
return;
}
this.visible = !this.visible; this.visible = !this.visible;
this.$refs.input.focus();
}, },
childChange(data) { childChange(data) {
this.colorChange(data); this.colorChange(data);
}, },
colorChange(data, oldHue) { colorChange(data, oldHue) {
this.oldHue = this.saturationColors.hsl.h; this.oldHue = this.saturationColors.hsl.h;
this.saturationColors = _colorChange(data, oldHue || this.oldHue); this.saturationColors = changeColor(data, oldHue || this.oldHue);
}, },
isValidHex (hex) { closer(event) {
return tinycolor(hex).isValid(); if (event) {
}, event.preventDefault();
simpleCheckForValidColor (data) { event.stopPropagation();
const keysToCheck = ['r', 'g', 'b', 'a', 'h', 's', 'l', 'v'];
let checked = 0;
let passed = 0;
for (let i = 0; i < keysToCheck.length; i++) {
const letter = keysToCheck[i];
if (data[letter]) {
checked++;
if (!isNaN(data[letter])) {
passed++;
}
}
} }
if (checked === passed) { this.visible = false;
return data; this.$refs.input.focus();
}
}, },
handleSuccess () { handleButtons(event, value) {
const color = this.formatColor; this.currentValue = value;
this.currentValue = color; this.$emit('input', value);
this.$emit('input', color); this.$emit('on-change', value);
this.$emit('on-change', color); this.dispatch('FormItem', 'on-form-change', value);
this.dispatch('FormItem', 'on-form-change', color); this.closer(event);
this.handleClose();
}, },
handleClear () { handleSuccess(event) {
this.currentValue = ''; this.handleButtons(event, this.formatColor);
this.$emit('input', ''); this.$emit('on-pick-success');
this.$emit('on-change', ''); },
this.dispatch('FormItem', 'on-form-change', ''); handleClear(event) {
this.handleClose(); this.handleButtons(event, '');
this.$emit('on-pick-clear');
}, },
handleSelectColor(color) { handleSelectColor(color) {
this.val = _colorChange(color); this.val = changeColor(color);
this.$emit('on-active-change', this.formatColor);
},
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> </script>

View file

@ -0,0 +1,7 @@
export default {
methods: {
handleEscape(e) {
this.dispatch('ColorPicker', 'on-escape-keydown', e);
},
},
};

View file

@ -0,0 +1,73 @@
import Emitter from '../../mixins/emitter';
import handleEscapeMixin from './handleEscapeMixin';
import {getTouches} from './utils';
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);
},
handleMouseUp() {
this.unbindEventListeners();
},
unbindEventListeners() {
window.removeEventListener('mousemove', this.handleChange);
window.removeEventListener('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;
},
},
};

View file

@ -1,86 +1,95 @@
<template> <template>
<div class="ivu-color-picker-hue"> <div
<div class="ivu-color-picker-hue-container" ref="container" :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" @mousedown="handleMouseDown"
@touchmove="handleChange" @touchmove="handleChange"
@touchstart="handleChange"> @touchstart="handleChange">
<div class="ivu-color-picker-hue-pointer" :style="{top: 0, left: pointerLeft}"> <div
<div class="ivu-color-picker-hue-picker"></div> :style="{top: 0, left: `${percent}%`}"
:class="[prefixCls + '-hue-pointer']">
<div :class="[prefixCls + '-hue-picker']"></div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import HASMixin from './hsaMixin';
import Prefixes from './prefixMixin';
import {clamp} from './utils';
export default { export default {
name: 'Hue', name: 'Hue',
props: {
value: Object mixins: [HASMixin, Prefixes],
},
data() { data() {
const normalStep = 1 / 360 * 25;
const jumpStep = 20 * normalStep;
return { return {
oldHue: 0, left: -normalStep,
pullDirection: '' right: normalStep,
up: jumpStep,
down: -jumpStep,
powerKey: 'shiftKey',
percent: clamp(this.value.hsl.h * 100 / 360, 0, 100),
}; };
}, },
computed: {
colors () {
const h = this.value.hsl.h;
if (h !== 0 && h - this.oldHue > 0) this.pullDirection = 'right';
if (h !== 0 && h - this.oldHue < 0) this.pullDirection = 'left';
this.oldHue = h;
return this.value; methods: {
}, change(percent) {
pointerLeft () { this.percent = clamp(percent, 0, 100);
if (this.colors.hsl.h === 0 && this.pullDirection === 'right') return '100%';
return (this.colors.hsl.h * 100) / 360 + '%'; 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'});
} }
}, },
methods: { handleSlide(e, direction) {
handleChange (e, skip) { e.preventDefault();
!skip && e.preventDefault(); e.stopPropagation();
const container = this.$refs.container; if (e[this.powerKey]) {
const containerWidth = container.clientWidth; this.change(direction < 0 ? 0 : 100);
return;
}
const xOffset = container.getBoundingClientRect().left + window.pageXOffset; this.change(this.percent + direction);
const pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0); },
const left = pageX - xOffset; handleChange(e) {
e.preventDefault();
e.stopPropagation();
let h; const left = this.getLeft(e);
let percent;
if (left < 0) { if (left < 0) {
h = 0; this.change(0);
} else if (left > containerWidth) { return;
h = 360;
} else {
percent = left * 100 / containerWidth;
h = (360 * percent / 100);
} }
if (this.colors.hsl.h !== h) { const {clientWidth} = this.$refs.container;
this.$emit('change', {
h: h, if (left > clientWidth) {
s: this.colors.hsl.s, this.change(100);
l: this.colors.hsl.l, return;
a: this.colors.hsl.a,
source: 'hsl'
});
} }
this.change(left * 100 / clientWidth);
}, },
handleMouseDown (e) {
this.handleChange(e, true);
window.addEventListener('mousemove', this.handleChange);
window.addEventListener('mouseup', this.handleMouseUp);
}, },
handleMouseUp () {
this.unbindEventListeners();
},
unbindEventListeners () {
window.removeEventListener('mousemove', this.handleChange);
window.removeEventListener('mouseup', this.handleMouseUp);
}
}
}; };
</script> </script>

View file

@ -1,2 +1,3 @@
import ColorPicker from './color-picker.vue'; import ColorPicker from './color-picker.vue';
export default ColorPicker; export default ColorPicker;

View file

@ -0,0 +1,10 @@
export default {
data() {
return {
prefixCls: 'ivu-color-picker',
inputPrefixCls: 'ivu-input',
iconPrefixCls: 'ivu-icon',
transferPrefixCls: 'ivu-transfer',
};
},
};

View file

@ -1,20 +1,153 @@
<template> <template>
<div> <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"> <template v-for="(item, index) in list">
<span @click="handleClick(index)"><em :style="{'background': item}"></em></span> <div
<br v-if="(index + 1) % 12 === 0 && index !== 0 && (index + 1) !== list.length"> :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> </template>
</div> </div>
</template> </template>
<script> <script>
import Emitter from '../../mixins/emitter';
import HandleEscapeMixin from './handleEscapeMixin';
import Prefixes from './prefixMixin';
import {clamp} from './utils';
export default { export default {
name: 'RecommendedColors',
mixins: [Emitter, HandleEscapeMixin, Prefixes],
props: { props: {
list: Array 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: { methods: {
handleClick (index) { getLinearIndex(grid) {
this.$emit('picker-color', this.list[index]); 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> </script>

View file

@ -1,98 +1,98 @@
<template> <template>
<div class="ivu-color-picker-saturation-wrapper">
<div <div
class="ivu-color-picker-saturation" :class="[prefixCls + '-saturation-wrapper']"
:style="{background: bgColor}" tabindex="0"
@keydown.esc="handleEscape"
@click="$el.focus()"
@keydown.left="handleLeft"
@keydown.right="handleRight"
@keydown.up="handleUp"
@keydown.down="handleDown"
>
<div
ref="container" ref="container"
:style="bgColorStyle"
:class="[prefixCls + '-saturation']"
@mousedown="handleMouseDown"> @mousedown="handleMouseDown">
<div class="ivu-color-picker-saturation--white"></div> <div :class="[prefixCls + '-saturation--white']"></div>
<div class="ivu-color-picker-saturation--black"></div> <div :class="[prefixCls + '-saturation--black']"></div>
<div class="ivu-color-picker-saturation-pointer" :style="{top: pointerTop, left: pointerLeft}"> <div
<div class="ivu-color-picker-saturation-circle"></div> :style="pointerStyle"
:class="[prefixCls + '-saturation-pointer']">
<div :class="[prefixCls + '-saturation-circle']"></div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import throttle from 'lodash.throttle'; import HSAMixin from './hsaMixin';
import Prefixes from './prefixMixin';
import {clamp, getIncrement} from './utils';
export default { export default {
name: 'Saturation', name: 'Saturation',
props: {
value: Object mixins: [HSAMixin, Prefixes],
},
data() { data() {
return {}; const normalStep = 0.01;
return {
left: -normalStep,
right: normalStep,
up: normalStep,
down: -normalStep,
multiplier: 10,
powerKey: 'shiftKey',
};
}, },
computed: { computed: {
colors () { bgColorStyle() {
return this.value; return {background: `hsl(${this.value.hsv.h}, 100%, 50%)`};
}, },
bgColor () { pointerStyle() {
return `hsl(${this.colors.hsv.h}, 100%, 50%)`; return {top: `${-(this.value.hsv.v * 100) + 1 + 100}%`, left: `${this.value.hsv.s * 100}%`};
}, },
pointerTop () {
return (-(this.colors.hsv.v * 100) + 1) + 100 + '%';
},
pointerLeft () {
return this.colors.hsv.s * 100 + '%';
}
}, },
methods: { methods: {
throttle: throttle((fn, data) => {fn(data);}, 20, change(h, s, v, a) {
{ this.$emit('change', {h, s, v, a, source: 'hsva'});
'leading': true,
'trailing': false
}),
handleChange (e, skip) {
!skip && e.preventDefault();
const container = this.$refs.container;
const containerWidth = container.clientWidth;
const containerHeight = container.clientHeight;
const xOffset = container.getBoundingClientRect().left + window.pageXOffset;
const yOffset = container.getBoundingClientRect().top + window.pageYOffset;
const pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0);
const pageY = e.pageY || (e.touches ? e.touches[0].pageY : 0);
let left = pageX - xOffset;
let top = pageY - yOffset;
if (left < 0) {
left = 0;
} else if (left > containerWidth) {
left = containerWidth;
} else if (top < 0) {
top = 0;
} else if (top > containerHeight) {
top = containerHeight;
}
const saturation = left / containerWidth;
let bright = -(top / containerHeight) + 1;
bright = bright > 0 ? bright : 0;
bright = bright > 1 ? 1 : bright;
this.throttle(this.onChange, {
h: this.colors.hsv.h,
s: saturation,
v: bright,
a: this.colors.hsv.a,
source: 'hsva'
});
}, },
onChange (param) { handleSlide(e, direction, key) {
this.$emit('change', param); 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);
}, },
handleMouseDown () { handleChange(e) {
// this.handleChange(e, true) e.preventDefault();
window.addEventListener('mousemove', this.handleChange); e.stopPropagation();
window.addEventListener('mouseup', this.handleChange);
window.addEventListener('mouseup', this.handleMouseUp); 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);
}, },
handleMouseUp () { handleMouseDown(e) {
this.unbindEventListeners(); HSAMixin.methods.handleMouseDown.call(this, e);
window.addEventListener('mouseup', this.handleChange, false);
}, },
unbindEventListeners () { unbindEventListeners(e) {
window.removeEventListener('mousemove', this.handleChange); HSAMixin.methods.unbindEventListeners.call(this, e);
window.removeEventListener('mouseup', this.handleChange); window.removeEventListener('mouseup', this.handleChange);
window.removeEventListener('mouseup', this.handleMouseUp); },
} },
}
}; };
</script> </script>

View 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;
}

View file

@ -1,10 +1,35 @@
@color-picker-prefix-cls: ~"@{css-prefix}color-picker"; @color-picker-prefix-cls: ~'@{css-prefix}color-picker';
.@{color-picker-prefix-cls} { .@{color-picker-prefix-cls} {
display: inline-block; display: inline-block;
&-hide {
display: none;
&-drop {
visibility: hidden;
}
}
&-disabled {
background-color: @input-disabled-bg;
opacity: 1;
cursor: @cursor-disabled;
color: #ccc;
}
& > div:first-child:hover {
.ivu-input {
border-color: @input-hover-border-color;
}
}
& > div:first-child.@{color-picker-prefix-cls}-disabled:hover {
.ivu-input {
border-color: tint(@input-border-color, 20%);
}
}
& .@{select-dropdown-prefix-cls} { & .@{select-dropdown-prefix-cls} {
padding: 0; padding: 0;
} }
&-focused {
box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
}
&-rel { &-rel {
line-height: 0; line-height: 0;
} }
@ -18,7 +43,7 @@
div { div {
width: 100%; width: 100%;
height: 100%; height: 100%;
box-shadow: inset 0 0 0 1px rgba(0,0,0,.15); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15);
border-radius: 2px; border-radius: 2px;
} }
&-empty { &-empty {
@ -29,6 +54,9 @@
font-size: 18px; font-size: 18px;
} }
} }
&-focused {
box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
}
} }
&-large &-color { &-large &-color {
width: 20px; width: 20px;
@ -61,7 +89,8 @@
box-sizing: initial; box-sizing: initial;
position: relative; position: relative;
} }
&-hue-slider, &-alpha-slider{ &-hue-slider,
&-alpha-slider {
height: 10px; height: 10px;
margin-top: 8px; margin-top: 8px;
position: relative; position: relative;
@ -69,19 +98,49 @@
&-colors { &-colors {
margin-top: 8px; margin-top: 8px;
overflow: hidden; overflow: hidden;
span{ outline: 0;
display: inline-block; border: 1px solid @input-border-color;
transition: border @transition-time @ease-in-out, box-shadow @transition-time @ease-in-out;
&:hover {
border: 1px solid @input-hover-border-color;
}
&:focus {
box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
}
&-wrapper {
display: inline;
width: 20px; width: 20px;
height: 20px; height: 20px;
float: left; float: left;
em{ position: relative;
&-color {
outline: 0;
display: block; display: block;
position: absolute;
width: 16px; width: 16px;
height: 16px; height: 16px;
margin: 2px; margin: 2px;
cursor: pointer; cursor: pointer;
border-radius: 2px; border-radius: 2px;
box-shadow: inset 0 0 0 1px rgba(0,0,0,.15); border: 1px solid @input-border-color;
transition: border @transition-time @ease-in-out, box-shadow @transition-time @ease-in-out;
&:hover {
border: 1px solid @input-hover-border-color;
}
&:focus {
box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
}
}
&-circle {
cursor: pointer;
top: 10px;
left: 10px;
position: absolute;
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);
} }
} }
} }
@ -96,8 +155,20 @@
padding-bottom: 75%; padding-bottom: 75%;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
outline: 0;
border: 1px solid @input-border-color;
box-shadow: @shadow-base;
transition: border @transition-time @ease-in-out, box-shadow @transition-time @ease-in-out;
&:hover {
border: 1px solid @input-hover-border-color;
} }
&, &--white, &--black{ &:focus {
box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
}
}
&,
&--white,
&--black {
cursor: pointer; cursor: pointer;
position: absolute; position: absolute;
top: 0; top: 0;
@ -118,7 +189,7 @@
&-circle { &-circle {
width: 4px; width: 4px;
height: 4px; height: 4px;
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); 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%; border-radius: 50%;
transform: translate(-2px, -2px); transform: translate(-2px, -2px);
} }
@ -132,6 +203,16 @@
left: 0; left: 0;
border-radius: 2px; border-radius: 2px;
background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
outline: 0;
border: 1px solid @input-border-color;
box-shadow: @shadow-base;
transition: border @transition-time @ease-in-out, box-shadow @transition-time @ease-in-out;
&:hover {
border: 1px solid @input-hover-border-color;
}
&:focus {
box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
}
&-container { &-container {
cursor: pointer; cursor: pointer;
margin: 0 2px; margin: 0 2px;
@ -148,7 +229,7 @@
width: 4px; width: 4px;
border-radius: 1px; border-radius: 1px;
height: 8px; height: 8px;
box-shadow: 0 0 2px rgba(0, 0, 0, .6); box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
background: #fff; background: #fff;
transform: translateX(-2px); transform: translateX(-2px);
} }
@ -160,6 +241,16 @@
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
outline: 0;
border: 1px solid @input-border-color;
box-shadow: @shadow-base;
transition: border @transition-time @ease-in-out, box-shadow @transition-time @ease-in-out;
&:hover {
border: 1px solid @input-hover-border-color;
}
&:focus {
box-shadow: 0 0 0 2px fade(@input-hover-border-color, 20%);
}
&-checkboard-wrap { &-checkboard-wrap {
position: absolute; position: absolute;
top: 0; top: 0;
@ -201,7 +292,7 @@
width: 4px; width: 4px;
border-radius: 1px; border-radius: 1px;
height: 8px; height: 8px;
box-shadow: 0 0 2px rgba(0, 0, 0, .6); box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
background: #fff; background: #fff;
margin-top: 1px; margin-top: 1px;
transform: translateX(-2px); transform: translateX(-2px);
@ -209,7 +300,12 @@
} }
&-confirm { &-confirm {
margin-top: 8px;
position: relative; position: relative;
border-top: 1px solid @border-color-split;
text-align: right;
padding: 8px;
clear: both;
&-color { &-color {
position: absolute; position: absolute;
top: 11px; top: 11px;

View file

@ -6,7 +6,6 @@
} }
.active(@color: @input-hover-border-color) { .active(@color: @input-hover-border-color) {
border-color: tint(@color, 20%);
outline: 0; outline: 0;
box-shadow: 0 0 0 2px fade(@color, 20%); box-shadow: 0 0 0 2px fade(@color, 20%);
} }