Merge branch '2.0' into anchor

This commit is contained in:
Lison 2018-06-20 11:00:05 +08:00 committed by GitHub
commit 754eedf571
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
156 changed files with 61134 additions and 18815 deletions

View file

@ -8,6 +8,7 @@
:placeholder="placeholder"
:size="size"
:placement="placement"
:value="currentValue"
filterable
remote
auto-complete
@ -124,7 +125,9 @@
},
watch: {
value (val) {
this.disableEmitChange = true;
if(this.currentValue !== val){
this.disableEmitChange = true;
}
this.currentValue = val;
},
currentValue (val) {
@ -144,21 +147,20 @@
},
handleChange (val) {
this.currentValue = val;
this.$refs.select.model = val;
this.$refs.input.blur();
this.$emit('on-select', val);
},
handleFocus () {
this.$refs.select.visible = true;
handleFocus (event) {
this.$emit('on-focus', event);
},
handleBlur () {
this.$refs.select.visible = false;
handleBlur (event) {
this.$emit('on-blur', event);
},
handleClear () {
if (!this.clearable) return;
this.currentValue = '';
this.$refs.select.model = '';
this.$refs.select.reset();
}
}
};
</script>
</script>

View file

@ -34,7 +34,7 @@ export default {
gpuAcceleration: false,
},
preventOverflow :{
boundariesElement: 'body'
boundariesElement: 'window'
}
}
};
@ -90,14 +90,14 @@ export default {
if (!options.modifiers.offset) {
options.modifiers.offset = {};
}
options.modifiers.offset = this.offset;
options.modifiers.offset.offset = this.offset;
options.onCreate =()=>{
this.$nextTick(this.updatePopper);
this.$emit('created', this);
};
this.popperJS = new Popper(reference, popper, options);
},
updatePopper() {
if (isServer) return;
@ -110,6 +110,10 @@ export default {
this.popperJS = null;
}
},
updated (){
this.$nextTick(()=>this.updatePopper());
},
beforeDestroy() {
if (isServer) return;
if (this.popperJS) {

View file

@ -1,6 +1,6 @@
<template>
<div :class="classes">
<button :class="arrowClasses" class="left" @click="arrowEvent(-1)">
<button type="button" :class="arrowClasses" class="left" @click="arrowEvent(-1)">
<Icon type="chevron-left"></Icon>
</button>
<div :class="[prefixCls + '-list']">
@ -10,7 +10,7 @@
<div :class="[prefixCls + '-track', showCopyTrack ? 'higher' : '']" :style="copyTrackStyles" ref="copyTrack" v-if="loop">
</div>
</div>
<button :class="arrowClasses" class="right" @click="arrowEvent(1)">
<button type="button" :class="arrowClasses" class="right" @click="arrowEvent(1)">
<Icon type="chevron-right"></Icon>
</button>
<ul :class="dotsClasses">
@ -18,7 +18,7 @@
<li :class="[n - 1 === currentIndex ? prefixCls + '-active' : '']"
@click="dotsEvent('click', n - 1)"
@mouseover="dotsEvent('hover', n - 1)">
<button :class="[radiusDot ? 'radius' : '']"></button>
<button type="button" :class="[radiusDot ? 'radius' : '']"></button>
</li>
</template>
</ul>

View file

@ -1,5 +1,5 @@
<template>
<div :class="classes" v-clickoutside="handleClose">
<div :class="classes" v-click-outside="handleClose">
<div :class="[prefixCls + '-rel']" @click="toggleOpen" ref="reference">
<input type="hidden" :name="name" :value="currentValue">
<slot>
@ -57,7 +57,7 @@
import Drop from '../select/dropdown.vue';
import Icon from '../icon/icon.vue';
import Caspanel from './caspanel.vue';
import clickoutside from '../../directives/clickoutside';
import {directive as clickOutside} from 'v-click-outside-x';
import TransferDom from '../../directives/transfer-dom';
import { oneOf } from '../../utils/assist';
import Emitter from '../../mixins/emitter';
@ -70,7 +70,7 @@
name: 'Cascader',
mixins: [ Emitter, Locale ],
components: { iInput, Drop, Icon, Caspanel },
directives: { clickoutside, TransferDom },
directives: { clickOutside, TransferDom },
props: {
data: {
type: Array,

View file

@ -36,15 +36,8 @@
this.$children.forEach((child, index) => {
const name = child.name || index.toString();
let isActive = false;
if (self.accordion) {
isActive = activeKey === name;
} else {
isActive = activeKey.indexOf(name) > -1;
}
child.isActive = isActive;
child.isActive = activeKey.indexOf(name) > -1;
child.index = index;
});
},

View file

@ -1,77 +1,103 @@
<template>
<div class="ivu-color-picker-alpha">
<div class="ivu-color-picker-alpha-checkboard-wrap">
<div class="ivu-color-picker-alpha-checkerboard"></div>
<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 class="ivu-color-picker-alpha-gradient" :style="{background: gradientColor}"></div>
<div class="ivu-color-picker-alpha-container" ref="container"
@mousedown="handleMouseDown"
@touchmove="handleChange"
@touchstart="handleChange">
<div class="ivu-color-picker-alpha-pointer" :style="{left: colors.a * 100 + '%'}">
<div class="ivu-color-picker-alpha-picker"></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>
export default {
name: 'Alpha',
props: {
value: Object,
onChange: Function
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%)`};
},
computed: {
colors () {
return this.value;
},
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: {
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'});
}
},
methods: {
handleChange (e, skip) {
!skip && e.preventDefault();
const container = this.$refs.container;
const containerWidth = container.clientWidth;
handleSlide(e, direction) {
e.preventDefault();
e.stopPropagation();
const xOffset = container.getBoundingClientRect().left + window.pageXOffset;
const pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0);
const left = pageX - xOffset;
this.change(clamp(e[this.powerKey] ? direction : Math.round(this.value.hsl.a * 100 + direction) / 100, 0, 1));
},
handleChange(e) {
e.preventDefault();
e.stopPropagation();
let a;
if (left < 0) {
a = 0;
} else if (left > containerWidth) {
a = 1;
} else {
a = Math.round(left * 100 / containerWidth) / 100;
}
const left = this.getLeft(e);
if (this.colors.a !== a) {
this.$emit('change', {
h: this.colors.hsl.h,
s: this.colors.hsl.s,
l: this.colors.hsl.l,
a: a,
source: 'rgba'
});
}
},
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);
if (left < 0) {
this.change(0);
return;
}
}
};
</script>
const {clientWidth} = this.$refs.container;
if (left > clientWidth) {
this.change(1);
return;
}
this.change(Math.round(left * 100 / clientWidth) / 100);
},
},
};
</script>

View file

@ -1,374 +1,453 @@
<template>
<div :class="classes" v-clickoutside="handleClose">
<div ref="reference" @click="toggleVisible" :class="wrapClasses">
<input type="hidden" :name="name" :value="currentValue">
<i class="ivu-icon ivu-icon-arrow-down-b ivu-input-icon ivu-input-icon-normal"></i>
<div :class="inputClasses">
<div
v-click-outside.capture="handleClose"
v-click-outside:mousedown.capture="handleClose"
:class="classes">
<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-empty']" v-show="value === '' && !visible">
<i class="ivu-icon ivu-icon-ios-close-empty"></i>
<div
v-show="value === '' && !visible"
:class="[prefixCls + '-color-empty']">
<i :class="[iconPrefixCls, iconPrefixCls + '-ios-close-empty']"></i>
</div>
<div v-show="value || visible" :style="{backgroundColor: displayedColor}"></div>
<div
v-show="value || visible"
:style="displayedColorStyle"></div>
</div>
</div>
</div>
<transition name="transition-drop">
<Drop
v-transfer-dom
v-show="visible"
@click.native="handleTransferClick"
:class="{ [prefixCls + '-transfer']: transfer }"
class-name="ivu-transfer-no-max-height"
:placement="placement"
ref="drop"
:placement="placement"
:data-transfer="transfer"
v-transfer-dom>
<div :class="[prefixCls + '-picker']">
<div :class="[prefixCls + '-picker-wrapper']">
<div :class="[prefixCls + '-picker-panel']">
<Saturation v-model="saturationColors" @change="childChange"></Saturation>
: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 v-if="hue" :class="[prefixCls + '-picker-hue-slider']">
<Hue v-model="saturationColors" @change="childChange"></Hue>
<div :class="[prefixCls + '-confirm']">
<span :class="[prefixCls + '-confirm-color']">{{formatColor}}</span>
<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 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="[prefixCls + '-confirm-color']">{{ formatColor }}</span>
<Confirm @on-pick-success="handleSuccess" @on-pick-clear="handleClear"></Confirm>
</div>
</div>
</transition>
</Drop>
</transition>
</div>
</template>
<script>
import tinycolor from 'tinycolor2';
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 Locale from '../../mixins/locale';
import {oneOf} from '../../utils/assist';
import Emitter from '../../mixins/emitter';
import Prefixes from './prefixMixin';
import {changeColor, toRGBAString} from './utils';
import clickoutside from '../../directives/clickoutside';
import TransferDom from '../../directives/transfer-dom';
export default {
name: 'ColorPicker',
import Drop from '../../components/select/dropdown.vue';
import RecommendColors from './recommend-colors.vue';
import Confirm from '../date-picker/base/confirm.vue';
import Saturation from './saturation.vue';
import Hue from './hue.vue';
import Alpha from './alpha.vue';
components: {Drop, RecommendColors, Saturation, Hue, Alpha},
import { oneOf } from '../../utils/assist';
import Emitter from '../../mixins/emitter';
directives: {clickOutside, TransferDom},
const prefixCls = 'ivu-color-picker';
const inputPrefixCls = 'ivu-input';
mixins: [Emitter, Locale, Prefixes],
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;
}
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: {
type: String,
validator(value) {
return oneOf(value, ['small', 'large', 'default']);
},
default: 'default',
},
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: false,
},
name: {
type: String,
default: undefined,
},
},
data() {
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()
val: changeColor(this.value),
currentValue: this.value,
dragging: false,
visible: false,
recommendedColor: [
'#2d8cf0',
'#19be6b',
'#ff9900',
'#ed3f14',
'#00b5ff',
'#19c919',
'#f9e31c',
'#ea1a1a',
'#9b1dea',
'#00c2b1',
'#ac7a33',
'#1d35ea',
'#8bc34a',
'#f16b62',
'#ea4ca3',
'#0d94aa',
'#febd79',
'#5d4037',
'#00bcd4',
'#f06292',
'#cddc39',
'#607d8b',
'#000000',
'#ffffff',
],
};
}
},
export default {
name: 'ColorPicker',
mixins: [ Emitter ],
components: { Drop, Confirm, RecommendColors, Saturation, Hue, Alpha },
directives: { clickoutside, TransferDom },
props: {
value: {
type: String
computed: {
arrowClasses() {
return [
this.iconPrefixCls,
`${this.iconPrefixCls}-arrow-down-b`,
`${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;
},
hue: {
type: Boolean,
default: true
set(newVal) {
this.val = newVal;
this.$emit('on-active-change', this.formatColor);
},
alpha: {
type: Boolean,
default: false
},
recommend: {
type: Boolean,
default: false
},
format: {
validator (value) {
return oneOf(value, ['hsl', 'hsv', 'hex', 'rgb']);
}
},
colors: {
type: Array,
default () {
return [];
}
},
disabled: {
type: Boolean,
default: false
},
size: {
validator (value) {
return oneOf(value, ['small', 'large', 'default']);
},
classes() {
return [
`${this.prefixCls}`,
{
[`${this.prefixCls}-transfer`]: this.transfer,
},
default: 'default'
},
placement: {
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']);
];
},
wrapClasses() {
return [
`${this.prefixCls}-rel`,
`${this.prefixCls}-${this.size}`,
`${this.inputPrefixCls}-wrapper`,
`${this.inputPrefixCls}-wrapper-${this.size}`,
{
[`${this.prefixCls}-disabled`]: this.disabled,
},
default: 'bottom'
},
transfer: {
type: Boolean,
default: false
},
name: {
type: String
}
];
},
data () {
return {
val: _colorChange(this.value),
currentValue: this.value,
prefixCls: prefixCls,
visible: false,
disableCloseUnderTransfer: false, // transfer Drop
recommendedColor: [
'#2d8cf0',
'#19be6b',
'#ff9900',
'#ed3f14',
'#00b5ff',
'#19c919',
'#f9e31c',
'#ea1a1a',
'#9b1dea',
'#00c2b1',
'#ac7a33',
'#1d35ea',
'#8bc34a',
'#f16b62',
'#ea4ca3',
'#0d94aa',
'#febd79',
'#5d4037',
'#00bcd4',
'#f06292',
'#cddc39',
'#607d8b',
'#000000',
'#ffffff'
]
};
},
computed: {
transition () {
if (this.placement === 'bottom-start' || this.placement === 'bottom' || this.placement === 'bottom-end') {
return 'slide-up';
} else {
return 'fade';
}
},
saturationColors: {
get () {
return this.val;
inputClasses() {
return [
`${this.prefixCls}-input`,
`${this.inputPrefixCls}`,
`${this.inputPrefixCls}-${this.size}`,
{
[`${this.prefixCls}-focused`]: this.visible,
[`${this.prefixCls}-disabled`]: this.disabled,
},
set (newVal) {
this.val = newVal;
this.$emit('on-active-change', this.formatColor);
}
},
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-transfer`]: this.transfer
}
];
},
wrapClasses () {
return [
`${prefixCls}-rel`,
`${prefixCls}-${this.size}`,
`${inputPrefixCls}-wrapper`,
`${inputPrefixCls}-wrapper-${this.size}`
];
},
inputClasses () {
return [
`${prefixCls}-input`,
`${inputPrefixCls}`,
`${inputPrefixCls}-${this.size}`,
{
[`${inputPrefixCls}-disabled`]: this.disabled
}
];
},
displayedColor () {
let color;
if (this.visible) {
const rgba = this.saturationColors.rgba;
color = {
r: rgba.r,
g: rgba.g,
b: rgba.b,
a: rgba.a
};
} else {
color = tinycolor(this.value).toRgb();
}
return `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`;
},
formatColor () {
const value = this.saturationColors;
const format = this.format;
let color;
];
},
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;
const rgba = `rgba(${value.rgba.r}, ${value.rgba.g}, ${value.rgba.b}, ${value.rgba.a})`;
if (format) {
if (format === 'hsl') {
color = tinycolor(value.hsl).toHslString();
} else if (format === 'hsv') {
color = tinycolor(value.hsv).toHsvString();
} else if (format === 'hex') {
color = value.hex;
} else if (format === 'rgb') {
color = rgba;
}
} else if (this.alpha) {
color = rgba;
} else {
color = value.hex;
if (format) {
if (format === 'hsl') {
return tinycolor(saturationColors.hsl).toHslString();
}
return color;
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;
},
},
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);
},
handleFirstTab(event) {
if (event.shiftKey) {
event.preventDefault();
event.stopPropagation();
this.$refs.ok.$el.focus();
}
},
watch: {
value (newVal) {
this.val = _colorChange(newVal);
},
visible (val) {
this.val = _colorChange(this.value);
if (val) {
this.$refs.drop.update();
} else {
this.$refs.drop.destroy();
}
handleLastTab(event) {
if (!event.shiftKey) {
event.preventDefault();
event.stopPropagation();
this.$refs.saturation.$el.focus();
}
},
methods: {
// transfer Drop
handleTransferClick () {
if (this.transfer) this.disableCloseUnderTransfer = true;
},
handleClose () {
if (this.disableCloseUnderTransfer) {
this.disableCloseUnderTransfer = false;
return false;
}
this.visible = false;
},
toggleVisible () {
this.visible = !this.visible;
},
childChange (data) {
this.colorChange(data);
},
colorChange (data, oldHue) {
this.oldHue = this.saturationColors.hsl.h;
this.saturationColors = _colorChange(data, oldHue || this.oldHue);
},
isValidHex (hex) {
return tinycolor(hex).isValid();
},
simpleCheckForValidColor (data) {
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) {
return data;
}
},
handleSuccess () {
const color = this.formatColor;
this.currentValue = color;
this.$emit('input', color);
this.$emit('on-change', color);
this.dispatch('FormItem', 'on-form-change', color);
this.handleClose();
},
handleClear () {
this.currentValue = '';
this.$emit('input', '');
this.$emit('on-change', '');
this.dispatch('FormItem', 'on-form-change', '');
this.handleClose();
},
handleSelectColor (color) {
this.val = _colorChange(color);
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>

View file

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

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

View file

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

View file

@ -1,2 +1,3 @@
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>
<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">
<span @click="handleClick(index)"><em :style="{'background': item}"></em></span>
<br v-if="(index + 1) % 12 === 0 && index !== 0 && (index + 1) !== list.length">
<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>
export default {
props: {
list: Array
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,
},
methods: {
handleClick (index) {
this.$emit('picker-color', this.list[index]);
},
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;
}
}
};
</script>
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>

View file

@ -1,98 +1,101 @@
<template>
<div class="ivu-color-picker-saturation-wrapper">
<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
class="ivu-color-picker-saturation"
:style="{background: bgColor}"
ref="container"
:style="bgColorStyle"
:class="[prefixCls + '-saturation']"
@mousedown="handleMouseDown">
<div class="ivu-color-picker-saturation--white"></div>
<div class="ivu-color-picker-saturation--black"></div>
<div class="ivu-color-picker-saturation-pointer" :style="{top: pointerTop, left: pointerLeft}">
<div class="ivu-color-picker-saturation-circle"></div>
<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 throttle from 'lodash.throttle';
export default {
name: 'Saturation',
props: {
value: Object
<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%)`};
},
data () {
return {};
pointerStyle() {
return {top: `${-(this.value.hsv.v * 100) + 1 + 100}%`, left: `${this.value.hsv.s * 100}%`};
},
computed: {
colors () {
return this.value;
},
bgColor () {
return `hsl(${this.colors.hsv.h}, 100%, 50%)`;
},
pointerTop () {
return (-(this.colors.hsv.v * 100) + 1) + 100 + '%';
},
pointerLeft () {
return this.colors.hsv.s * 100 + '%';
}
},
methods: {
change(h, s, v, a) {
this.$emit('change', {h, s, v, a, source: 'hsva'});
},
methods: {
throttle: throttle((fn, data) => {fn(data);}, 20,
{
'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) {
this.$emit('change', param);
},
handleMouseDown () {
// this.handleChange(e, true)
window.addEventListener('mousemove', this.handleChange);
window.addEventListener('mouseup', this.handleChange);
window.addEventListener('mouseup', this.handleMouseUp);
},
handleMouseUp () {
this.unbindEventListeners();
},
unbindEventListeners () {
window.removeEventListener('mousemove', this.handleChange);
window.removeEventListener('mouseup', this.handleChange);
window.removeEventListener('mouseup', this.handleMouseUp);
}
}
};
</script>
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>

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,37 +1,47 @@
<template>
<div :class="[prefixCls + '-confirm']">
<span :class="timeClasses" v-if="showTime" @click="handleToggleTime">
<template v-if="isTime">{{ t('i.datepicker.selectDate') }}</template>
<template v-else>{{ t('i.datepicker.selectTime') }}</template>
</span>
<i-button size="small" type="text" @click.native="handleClear">{{ t('i.datepicker.clear') }}</i-button>
<i-button size="small" type="primary" @click.native="handleSuccess">{{ t('i.datepicker.ok') }}</i-button>
<div :class="[prefixCls + '-confirm']" @keydown.tab.capture="handleTab">
<i-button :class="timeClasses" size="small" type="text" :disabled="timeDisabled" v-if="showTime" @click="handleToggleTime">
{{labels.time}}
</i-button>
<i-button size="small" type="ghost" @click.native="handleClear" @keydown.enter.native="handleClear">
{{labels.clear}}
</i-button>
<i-button size="small" type="primary" @click.native="handleSuccess" @keydown.enter.native="handleSuccess">
{{labels.ok}}
</i-button>
</div>
</template>
<script>
import iButton from '../../button/button.vue';
import Locale from '../../../mixins/locale';
import Emitter from '../../../mixins/emitter';
const prefixCls = 'ivu-picker';
export default {
mixins: [ Locale ],
components: { iButton },
mixins: [Locale, Emitter],
components: {iButton},
props: {
showTime: false,
isTime: false,
timeDisabled: false
},
data () {
data() {
return {
prefixCls: prefixCls
};
},
computed: {
timeClasses () {
return {
[`${prefixCls}-confirm-time-disabled`]: this.timeDisabled
};
return `${prefixCls}-confirm-time`;
},
labels(){
const labels = ['time', 'clear', 'ok'];
const values = [(this.isTime ? 'selectDate' : 'selectTime'), 'clear', 'ok'];
return labels.reduce((obj, key, i) => {
obj[key] = this.t('i.datepicker.' + values[i]);
return obj;
}, {});
}
},
methods: {
@ -44,6 +54,17 @@
handleToggleTime () {
if (this.timeDisabled) return;
this.$emit('on-pick-toggle-time');
this.dispatch('CalendarPicker', 'focus-input');
},
handleTab(e) {
const tabbables = [...this.$el.children];
const expectedFocus = tabbables[e.shiftKey ? 'shift' : 'pop']();
if (document.activeElement === expectedFocus) {
e.preventDefault();
e.stopPropagation();
this.dispatch('CalendarPicker', 'focus-input');
}
}
}
};

View file

@ -7,9 +7,9 @@
</div>
<span
:class="getCellCls(cell)"
v-for="(cell, i) in readCells"
v-for="(cell, i) in cells"
:key="String(cell.date) + i"
@click="handleClick(cell)"
@click="handleClick(cell, $event)"
@mouseenter="handleMouseMove(cell)"
>
<em>{{ cell.desc }}</em>
@ -61,7 +61,7 @@
const weekDays = translatedDays.splice(weekStartDay, 7 - weekStartDay).concat(translatedDays.splice(0, weekStartDay));
return this.showWeekNumbers ? [''].concat(weekDays) : weekDays;
},
readCells () {
cells () {
const tableYear = this.tableDate.getFullYear();
const tableMonth = this.tableDate.getMonth();
const today = clearHours(new Date()); // timestamp of today
@ -99,7 +99,9 @@
[`${prefixCls}-cell-prev-month`]: cell.type === 'prevMonth',
[`${prefixCls}-cell-next-month`]: cell.type === 'nextMonth',
[`${prefixCls}-cell-week-label`]: cell.type === 'weekLabel',
[`${prefixCls}-cell-range`]: cell.range && !cell.start && !cell.end
[`${prefixCls}-cell-range`]: cell.range && !cell.start && !cell.end,
[`${prefixCls}-focused`]: clearHours(cell.date) === clearHours(this.focusedDate)
}
];
},

View file

@ -2,6 +2,7 @@
import {clearHours} from '../util';
export default {
name: 'PanelTable',
props: {
tableDate: {
type: Date,
@ -26,7 +27,10 @@ export default {
selecting: false
})
},
focusedDate: {
type: Date,
required: true,
}
},
computed: {
dates(){

View file

@ -38,14 +38,16 @@
const tableYear = this.tableDate.getFullYear();
const selectedDays = this.dates.filter(Boolean).map(date => clearHours(new Date(date.getFullYear(), date.getMonth(), 1)));
const focusedDate = clearHours(new Date(this.focusedDate.getFullYear(), this.focusedDate.getMonth(), 1));
for (let i = 0; i < 12; i++) {
const cell = deepCopy(cell_tmpl);
cell.date = new Date(tableYear, i, 1);
cell.text = this.tCell(i + 1);
const time = clearHours(cell.date);
const day = clearHours(cell.date);
cell.disabled = typeof this.disabledDate === 'function' && this.disabledDate(cell.date) && this.selectionMode === 'month';
cell.selected = selectedDays.includes(time);
cell.selected = selectedDays.includes(day);
cell.focused = day === focusedDate;
cells.push(cell);
}
@ -59,6 +61,7 @@
{
[`${prefixCls}-cell-selected`]: cell.selected,
[`${prefixCls}-cell-disabled`]: cell.disabled,
[`${prefixCls}-cell-focused`]: cell.focused,
[`${prefixCls}-cell-range`]: cell.range && !cell.start && !cell.end
}
];

View file

@ -22,8 +22,10 @@
import { deepCopy, scrollTop, firstUpperCase } from '../../../utils/assist';
const prefixCls = 'ivu-time-picker-cells';
const timeParts = ['hours', 'minutes', 'seconds'];
export default {
name: 'TimeSpinner',
mixins: [Options],
props: {
hours: {
@ -51,7 +53,9 @@
return {
spinerSteps: [1, 1, 1].map((one, i) => Math.abs(this.steps[i]) || one),
prefixCls: prefixCls,
compiled: false
compiled: false,
focusedColumn: -1, // which column inside the picker
focusedTime: [0, 0, 0] // the values array into [hh, mm, ss]
};
},
computed: {
@ -66,6 +70,7 @@
hoursList () {
let hours = [];
const step = this.spinerSteps[0];
const focusedHour = this.focusedColumn === 0 && this.focusedTime[0];
const hour_tmpl = {
text: 0,
selected: false,
@ -76,6 +81,7 @@
for (let i = 0; i < 24; i += step) {
const hour = deepCopy(hour_tmpl);
hour.text = i;
hour.focused = i === focusedHour;
if (this.disabledHours.length && this.disabledHours.indexOf(i) > -1) {
hour.disabled = true;
@ -90,6 +96,7 @@
minutesList () {
let minutes = [];
const step = this.spinerSteps[1];
const focusedMinute = this.focusedColumn === 1 && this.focusedTime[1];
const minute_tmpl = {
text: 0,
selected: false,
@ -100,6 +107,7 @@
for (let i = 0; i < 60; i += step) {
const minute = deepCopy(minute_tmpl);
minute.text = i;
minute.focused = i === focusedMinute;
if (this.disabledMinutes.length && this.disabledMinutes.indexOf(i) > -1) {
minute.disabled = true;
@ -113,6 +121,7 @@
secondsList () {
let seconds = [];
const step = this.spinerSteps[2];
const focusedMinute = this.focusedColumn === 2 && this.focusedTime[2];
const second_tmpl = {
text: 0,
selected: false,
@ -123,6 +132,7 @@
for (let i = 0; i < 60; i += step) {
const second = deepCopy(second_tmpl);
second.text = i;
second.focused = i === focusedMinute;
if (this.disabledSeconds.length && this.disabledSeconds.indexOf(i) > -1) {
second.disabled = true;
@ -141,15 +151,32 @@
`${prefixCls}-cell`,
{
[`${prefixCls}-cell-selected`]: cell.selected,
[`${prefixCls}-cell-focused`]: cell.focused,
[`${prefixCls}-cell-disabled`]: cell.disabled
}
];
},
chooseValue(values){
const changes = timeParts.reduce((obj, part, i) => {
const value = values[i];
if (this[part] === value) return obj;
return {
...obj,
[part]: value
};
}, {});
if (Object.keys(changes).length > 0) {
this.emitChange(changes);
}
},
handleClick (type, cell) {
if (cell.disabled) return;
const data = {};
data[type] = cell.text;
this.$emit('on-change', data);
const data = {[type]: cell.text};
this.emitChange(data);
},
emitChange(changes){
this.$emit('on-change', changes);
this.$emit('on-pick-click');
},
scroll (type, index) {
@ -168,15 +195,19 @@
return index;
},
updateScroll () {
const times = ['hours', 'minutes', 'seconds'];
this.$nextTick(() => {
times.forEach(type => {
timeParts.forEach(type => {
this.$refs[type].scrollTop = 24 * this[`${type}List`].findIndex(obj => obj.text == this[type]);
});
});
},
formatTime (text) {
return text < 10 ? '0' + text : text;
},
updateFocusedTime(col, time) {
this.focusedColumn = col;
this.focusedTime = time.slice();
}
},
watch: {
@ -191,6 +222,13 @@
seconds (val) {
if (!this.compiled) return;
this.scroll('seconds', this.secondsList.findIndex(obj => obj.text == val));
},
focusedTime(updated, old){
timeParts.forEach((part, i) => {
if (updated[i] === old[i] || typeof updated[i] === 'undefined') return;
const valueIndex = this[`${part}List`].findIndex(obj => obj.text === updated[i]);
this.scroll(part, valueIndex);
});
}
},
mounted () {

View file

@ -39,13 +39,15 @@
};
const selectedDays = this.dates.filter(Boolean).map(date => clearHours(new Date(date.getFullYear(), 0, 1)));
const focusedDate = clearHours(new Date(this.focusedDate.getFullYear(), 0, 1));
for (let i = 0; i < 10; i++) {
const cell = deepCopy(cell_tmpl);
cell.date = new Date(this.startYear + i, 0, 1);
cell.disabled = typeof this.disabledDate === 'function' && this.disabledDate(cell.date) && this.selectionMode === 'year';
const time = clearHours(cell.date);
cell.selected = selectedDays.includes(time);
const day = clearHours(cell.date);
cell.selected = selectedDays.includes(day);
cell.focused = day === focusedDate;
cells.push(cell);
}
@ -59,6 +61,7 @@
{
[`${prefixCls}-cell-selected`]: cell.selected,
[`${prefixCls}-cell-disabled`]: cell.disabled,
[`${prefixCls}-cell-focused`]: cell.focused,
[`${prefixCls}-cell-range`]: cell.range && !cell.start && !cell.end
}
];

View file

@ -46,6 +46,10 @@ export default {
pickerType: {
type: String,
require: true
},
focusedDate: {
type: Date,
required: true,
}
},
computed: {

View file

@ -6,7 +6,7 @@
v-for="shortcut in shortcuts"
@click="handleShortcutClick(shortcut)">{{ shortcut.text }}</div>
</div>
<div :class="[prefixCls + '-body']">
<div :class="panelBodyClasses">
<div :class="[prefixCls + '-content', prefixCls + '-content-left']" v-show="!isTime">
<div :class="[datePrefixCls + '-header']" v-show="currentView !== 'time'">
<span
@ -41,6 +41,8 @@
:range-state="rangeState"
:show-week-numbers="showWeekNumbers"
:value="preSelecting.left ? [dates[0]] : dates"
:focused-date="focusedDate"
@on-change-range="handleChangeRange"
@on-pick="panelPickerHandlers.left"
@on-pick-click="handlePickClick"
@ -80,6 +82,8 @@
:disabled-date="disabledDate"
:show-week-numbers="showWeekNumbers"
:value="preSelecting.right ? [dates[dates.length - 1]] : dates"
:focused-date="focusedDate"
@on-change-range="handleChangeRange"
@on-pick="panelPickerHandlers.right"
@on-pick-click="handlePickClick"></component>
@ -157,7 +161,7 @@
leftPickerTable: `${this.selectionMode}-table`,
rightPickerTable: `${this.selectionMode}-table`,
leftPanelDate: leftPanelDate,
rightPanelDate: new Date(leftPanelDate.getFullYear(), leftPanelDate.getMonth() + 1, leftPanelDate.getDate())
rightPanelDate: new Date(leftPanelDate.getFullYear(), leftPanelDate.getMonth() + 1, 1)
};
},
computed: {
@ -171,6 +175,15 @@
}
];
},
panelBodyClasses(){
return [
prefixCls + '-body',
{
[prefixCls + '-body-time']: this.showTime,
[prefixCls + '-body-date']: !this.showTime,
}
];
},
leftDatePanelLabel(){
return this.panelLabelConfig('left');
},
@ -215,10 +228,7 @@
// set panels positioning
const leftPanelDate = this.startDate || this.dates[0] || new Date();
this.leftPanelDate = leftPanelDate;
const rightPanelDate = new Date(leftPanelDate.getFullYear(), leftPanelDate.getMonth() + 1, leftPanelDate.getDate());
this.rightPanelDate = this.splitPanels ? new Date(Math.max(this.dates[1], rightPanelDate)) : rightPanelDate;
this.setPanelDates(this.startDate || this.dates[0] || new Date());
},
currentView(currentView){
const leftMonth = this.leftPanelDate.getMonth();
@ -237,6 +247,9 @@
},
selectionMode(type){
this.currentView = type || 'range';
},
focusedDate(date){
this.setPanelDates(date || new Date());
}
},
methods: {
@ -245,6 +258,11 @@
this.leftPickerTable = `${this.currentView}-table`;
this.rightPickerTable = `${this.currentView}-table`;
},
setPanelDates(leftPanelDate){
this.leftPanelDate = leftPanelDate;
const rightPanelDate = new Date(leftPanelDate.getFullYear(), leftPanelDate.getMonth() + 1, leftPanelDate.getDate());
this.rightPanelDate = this.splitPanels ? new Date(Math.max(this.dates[1], rightPanelDate)) : rightPanelDate;
},
panelLabelConfig (direction) {
const locale = this.t('i.locale');
const datePanelLabel = this.t('i.datepicker.datePanelLabel');
@ -275,11 +293,12 @@
nextMonth(panel){
this.changePanelDate(panel, 'Month', 1);
},
changePanelDate(panel, type, increment){
changePanelDate(panel, type, increment, updateOtherPanel = true){
const current = new Date(this[`${panel}PanelDate`]);
current[`set${type}`](current[`get${type}`]() + increment);
this[`${panel}PanelDate`] = current;
if (!updateOtherPanel) return;
if (this.splitPanels){
// change other panel if dates overlap
@ -312,12 +331,11 @@
if (!this.splitPanels){
const otherPanel = panel === 'left' ? 'right' : 'left';
const type = currentViewType === 'year-table' ? 'FullYear' : 'Month';
this[`${otherPanel}PanelDate`] = value;
this.changePanelDate(otherPanel, type, 1);
this.changePanelDate(otherPanel, 'Month', 1, false);
}
},
handleRangePick (val) {
handleRangePick (val, type) {
if (this.rangeState.selecting || this.currentView === 'time'){
if (this.currentView === 'time'){
this.dates = val;
@ -330,7 +348,7 @@
selecting: false
};
}
this.handleConfirm(false);
this.handleConfirm(false, type || 'date');
} else {
this.rangeState = {
from: val,

View file

@ -39,6 +39,8 @@
:value="dates"
:selection-mode="selectionMode"
:disabled-date="disabledDate"
:focused-date="focusedDate"
@on-pick="panelPickerHandlers"
@on-pick-click="handlePickClick"
></component>
@ -51,6 +53,8 @@
:format="format"
:time-disabled="timeDisabled"
:disabled-date="disabledDate"
:focused-date="focusedDate"
v-bind="timePickerOptions"
@on-pick="handlePick"
@on-pick-click="handlePickClick"
@ -150,7 +154,6 @@
},
currentView (currentView) {
this.$emit('on-selection-mode-change', currentView);
this.pickertable = this.getTableType(currentView);
if (this.currentView === 'time') {
this.$nextTick(() => {
@ -162,6 +165,13 @@
selectionMode(type){
this.currentView = type;
this.pickerTable = this.getTableType(type);
},
focusedDate(date){
const isDifferentYear = date.getFullYear() !== this.panelDate.getFullYear();
const isDifferentMonth = isDifferentYear || date.getMonth() !== this.panelDate.getMonth();
if (isDifferentYear || isDifferentMonth){
this.panelDate = date;
}
}
},
methods: {
@ -188,14 +198,14 @@
else this.pickerTable = this.getTableType(this.currentView);
},
handlePick (value) {
handlePick (value, type) {
const {selectionMode, panelDate} = this;
if (selectionMode === 'year') value = new Date(value.getFullYear(), 0, 1);
else if (selectionMode === 'month') value = new Date(panelDate.getFullYear(), value.getMonth(), 1);
else value = new Date(value);
this.dates = [value];
this.$emit('on-pick', value);
this.$emit('on-pick', value, false, type || selectionMode);
},
},
};

View file

@ -142,7 +142,7 @@
// judge endTime > startTime?
if (dateEnd < dateStart) dateEnd = dateStart;
if (emit) this.$emit('on-pick', [dateStart, dateEnd], true);
if (emit) this.$emit('on-pick', [dateStart, dateEnd], 'time');
},
handleStartChange (date) {
this.handleChange(date, {});

View file

@ -135,7 +135,7 @@
type => newDate[`set${capitalize(type)}`](date[type])
);
if (emit) this.$emit('on-pick', newDate, true);
if (emit) this.$emit('on-pick', newDate, 'time');
},
},
mounted () {

View file

@ -44,8 +44,8 @@ export default {
this.handleConfirm();
// if (this.showTime) this.$refs.timePicker.handleClear();
},
handleConfirm(visible) {
this.$emit('on-pick', this.dates, visible);
handleConfirm(visible, type) {
this.$emit('on-pick', this.dates, visible, type || this.type);
},
onToggleVisibility(open){
const {timeSpinner, timeSpinnerEnd} = this.$refs;

View file

@ -1,5 +1,9 @@
<template>
<div :class="[prefixCls]" v-clickoutside="handleClose">
<div
:class="wrapperClasses"
v-click-outside:mousedown.capture="handleClose"
v-click-outside.capture="handleClose"
>
<div ref="reference" :class="[prefixCls + '-rel']">
<slot>
<i-input
@ -12,10 +16,14 @@
:placeholder="placeholder"
:value="visualValue"
:name="name"
ref="input"
@on-input-change="handleInputChange"
@on-focus="handleFocus"
@on-blur="handleBlur"
@on-click="handleIconClick"
@click.native="handleFocus"
@keydown.native="handleKeydown"
@mouseenter.native="handleInputMouseenter"
@mouseleave.native="handleInputMouseleave"
@ -48,6 +56,7 @@
:show-week-numbers="showWeekNumbers"
:picker-type="type"
:multiple="multiple"
:focused-date="focusedDate"
:time-picker-options="timePickerOptions"
@ -69,21 +78,49 @@
import iInput from '../../components/input/input.vue';
import Drop from '../../components/select/dropdown.vue';
import clickoutside from '../../directives/clickoutside';
import {directive as clickOutside} from 'v-click-outside-x';
import TransferDom from '../../directives/transfer-dom';
import { oneOf } from '../../utils/assist';
import { DEFAULT_FORMATS, RANGE_SEPARATOR, TYPE_VALUE_RESOLVER_MAP } from './util';
import { DEFAULT_FORMATS, RANGE_SEPARATOR, TYPE_VALUE_RESOLVER_MAP, getDayCountOfMonth } from './util';
import {findComponentsDownward} from '../../utils/assist';
import Emitter from '../../mixins/emitter';
const prefixCls = 'ivu-date-picker';
const pickerPrefixCls = 'ivu-picker';
const isEmptyArray = val => val.reduce((isEmpty, str) => isEmpty && !str || (typeof str === 'string' && str.trim() === ''), true);
const keyValueMapper = {
40: 'up',
39: 'right',
38: 'down',
37: 'left',
};
const mapPossibleValues = (key, horizontal, vertical) => {
if (key === 'left') return horizontal * -1;
if (key === 'right') return horizontal * 1;
if (key === 'up') return vertical * 1;
if (key === 'down') return vertical * -1;
};
const pulseElement = (el) => {
const pulseClass = 'ivu-date-picker-btn-pulse';
el.classList.add(pulseClass);
setTimeout(() => el.classList.remove(pulseClass), 200);
};
const extractTime = date => {
if (!date) return [0, 0, 0];
return [
date.getHours(), date.getMinutes(), date.getSeconds()
];
};
export default {
name: 'CalendarPicker',
mixins: [ Emitter ],
components: { iInput, Drop },
directives: { clickoutside, TransferDom },
directives: { clickOutside, TransferDom },
props: {
format: {
type: String
@ -172,6 +209,7 @@
const isRange = this.type.includes('range');
const emptyArray = isRange ? [null, null] : [null];
const initialValue = isEmptyArray((isRange ? this.value : [this.value]) || []) ? emptyArray : this.parseDate(this.value);
const focusedTime = initialValue.map(extractTime);
return {
prefixCls: prefixCls,
@ -181,10 +219,24 @@
disableClickOutSide: false, // fixed when click a date,trigger clickoutside to close picker
disableCloseUnderTransfer: false, // transfer Drop,
selectionMode: this.onSelectionModeChange(this.type),
forceInputRerender: 1
forceInputRerender: 1,
isFocused: false,
focusedDate: initialValue[0] || this.startDate || new Date(),
focusedTime: {
column: 0, // which column inside the picker
picker: 0, // which picker
time: focusedTime, // the values array into [hh, mm, ss],
active: false
},
internalFocus: false,
};
},
computed: {
wrapperClasses(){
return [prefixCls, {
[prefixCls + '-focused']: this.isFocused
}];
},
publicVModelValue(){
if (this.multiple){
return this.internalValue.slice();
@ -232,32 +284,254 @@
handleTransferClick () {
if (this.transfer) this.disableCloseUnderTransfer = true;
},
handleClose () {
handleClose (e) {
if (this.disableCloseUnderTransfer) {
this.disableCloseUnderTransfer = false;
return false;
}
if (this.open !== null) return;
this.visible = false;
if (e && e.type === 'mousedown' && this.visible) {
e.preventDefault();
e.stopPropagation();
return;
}
if (this.visible) {
const pickerPanel = this.$refs.pickerPanel && this.$refs.pickerPanel.$el;
if (e && pickerPanel && pickerPanel.contains(e.target)) return; // its a click inside own component, lets ignore it.
this.visible = false;
e && e.preventDefault();
e && e.stopPropagation();
return;
}
this.isFocused = false;
this.disableClickOutSide = false;
},
handleFocus () {
handleFocus (e) {
if (this.readonly) return;
this.isFocused = true;
if (e && e.type === 'focus') return; // just focus, don't open yet
this.visible = true;
this.$refs.pickerPanel.onToggleVisibility(true);
},
handleBlur () {
this.visible = false;
handleBlur (e) {
if (this.internalFocus){
this.internalFocus = false;
return;
}
if (this.visible) {
e.preventDefault();
return;
}
this.isFocused = false;
this.onSelectionModeChange(this.type);
this.internalValue = this.internalValue.slice(); // trigger panel watchers to reset views
this.reset();
this.$refs.pickerPanel.onToggleVisibility(false);
},
handleKeydown(e){
const keyCode = e.keyCode;
// handle "tab" key
if (keyCode === 9){
if (this.visible){
e.stopPropagation();
e.preventDefault();
if (this.isConfirm){
const selector = `.${pickerPrefixCls}-confirm > *`;
const tabbable = this.$refs.drop.$el.querySelectorAll(selector);
this.internalFocus = true;
const element = [...tabbable][e.shiftKey ? 'pop' : 'shift']();
element.focus();
} else {
this.handleClose();
}
} else {
this.focused = false;
}
}
// open the panel
const arrows = [37, 38, 39, 40];
if (!this.visible && arrows.includes(keyCode)){
this.visible = true;
return;
}
// close on "esc" key
if (keyCode === 27){
if (this.visible) {
e.stopPropagation();
this.handleClose();
}
}
// select date, "Enter" key
if (keyCode === 13){
const timePickers = findComponentsDownward(this, 'TimeSpinner');
if (timePickers.length > 0){
const columnsPerPicker = timePickers[0].showSeconds ? 3 : 2;
const pickerIndex = Math.floor(this.focusedTime.column / columnsPerPicker);
const value = this.focusedTime.time[pickerIndex];
timePickers[pickerIndex].chooseValue(value);
return;
}
if (this.type.match(/range/)){
this.$refs.pickerPanel.handleRangePick(this.focusedDate, 'date');
} else {
const panels = findComponentsDownward(this, 'PanelTable');
const compareDate = (d) => {
const sliceIndex = ['year', 'month', 'date'].indexOf((this.type)) + 1;
return [d.getFullYear(), d.getMonth(), d.getDate()].slice(0, sliceIndex).join('-');
};
const dateIsValid = panels.find(({cells}) => {
return cells.find(({date, disabled}) => compareDate(date) === compareDate(this.focusedDate) && !disabled);
});
if (dateIsValid) this.onPick(this.focusedDate, false, 'date');
}
}
if (!arrows.includes(keyCode)) return; // ignore rest of keys
// navigate times and dates
if (this.focusedTime.active) e.preventDefault(); // to prevent cursor from moving
this.navigateDatePanel(keyValueMapper[keyCode], e.shiftKey);
},
reset(){
this.$refs.pickerPanel.reset && this.$refs.pickerPanel.reset();
},
navigateTimePanel(direction){
this.focusedTime.active = true;
const horizontal = direction.match(/left|right/);
const vertical = direction.match(/up|down/);
const timePickers = findComponentsDownward(this, 'TimeSpinner');
const maxNrOfColumns = (timePickers[0].showSeconds ? 3 : 2) * timePickers.length;
const column = (currentColumn => {
const incremented = currentColumn + (horizontal ? (direction === 'left' ? -1 : 1) : 0);
return (incremented + maxNrOfColumns) % maxNrOfColumns;
})(this.focusedTime.column);
const columnsPerPicker = maxNrOfColumns / timePickers.length;
const pickerIndex = Math.floor(column / columnsPerPicker);
const col = column % columnsPerPicker;
if (horizontal){
const time = this.internalValue.map(extractTime);
this.focusedTime = {
...this.focusedTime,
column: column,
time: time
};
timePickers.forEach((instance, i) => {
if (i === pickerIndex) instance.updateFocusedTime(col, time[pickerIndex]);
else instance.updateFocusedTime(-1, instance.focusedTime);
});
}
if (vertical){
const increment = direction === 'up' ? 1 : -1;
const timeParts = ['hours', 'minutes', 'seconds'];
const pickerPossibleValues = timePickers[pickerIndex][`${timeParts[col]}List`];
const nextIndex = pickerPossibleValues.findIndex(({text}) => this.focusedTime.time[pickerIndex][col] === text) + increment;
const nextValue = pickerPossibleValues[nextIndex % pickerPossibleValues.length].text;
const times = this.focusedTime.time.map((time, i) => {
if (i !== pickerIndex) return time;
time[col] = nextValue;
return time;
});
this.focusedTime = {
...this.focusedTime,
time: times
};
timePickers.forEach((instance, i) => {
if (i === pickerIndex) instance.updateFocusedTime(col, times[i]);
else instance.updateFocusedTime(-1, instance.focusedTime);
});
}
},
navigateDatePanel(direction, shift){
const timePickers = findComponentsDownward(this, 'TimeSpinner');
if (timePickers.length > 0) {
// we are in TimePicker mode
this.navigateTimePanel(direction, shift, timePickers);
return;
}
if (shift){
if (this.type === 'year'){
this.focusedDate = new Date(
this.focusedDate.getFullYear() + mapPossibleValues(direction, 0, 10),
this.focusedDate.getMonth(),
this.focusedDate.getDate()
);
} else {
this.focusedDate = new Date(
this.focusedDate.getFullYear() + mapPossibleValues(direction, 0, 1),
this.focusedDate.getMonth() + mapPossibleValues(direction, 1, 0),
this.focusedDate.getDate()
);
}
const position = direction.match(/left|down/) ? 'prev' : 'next';
const double = direction.match(/up|down/) ? '-double' : '';
// pulse button
const button = this.$refs.drop.$el.querySelector(`.ivu-date-picker-${position}-btn-arrow${double}`);
if (button) pulseElement(button);
return;
}
const initialDate = this.focusedDate || (this.internalValue && this.internalValue[0]) || new Date();
const focusedDate = new Date(initialDate);
if (this.type.match(/^date/)){
const lastOfMonth = getDayCountOfMonth(initialDate.getFullYear(), initialDate.getMonth());
const startDay = initialDate.getDate();
const nextDay = focusedDate.getDate() + mapPossibleValues(direction, 1, 7);
if (nextDay < 1) {
if (direction.match(/left|right/)) {
focusedDate.setMonth(focusedDate.getMonth() + 1);
focusedDate.setDate(nextDay);
} else {
focusedDate.setDate(startDay + Math.floor((lastOfMonth - startDay) / 7) * 7);
}
} else if (nextDay > lastOfMonth){
if (direction.match(/left|right/)) {
focusedDate.setMonth(focusedDate.getMonth() - 1);
focusedDate.setDate(nextDay);
} else {
focusedDate.setDate(startDay % 7);
}
} else {
focusedDate.setDate(nextDay);
}
}
if (this.type.match(/^month/)) {
focusedDate.setMonth(focusedDate.getMonth() + mapPossibleValues(direction, 1, 3));
}
if (this.type.match(/^year/)) {
focusedDate.setFullYear(focusedDate.getFullYear() + mapPossibleValues(direction, 1, 3));
}
this.focusedDate = focusedDate;
},
handleInputChange (event) {
const isArrayValue = this.type.includes('range') || this.multiple;
const oldValue = this.visualValue;
@ -272,7 +546,7 @@
const isValidDate = newDate.reduce((valid, date) => valid && date instanceof Date, true);
if (newValue !== oldValue && !isDisabled && isValidDate) {
this.emitChange();
this.emitChange(this.type);
this.internalValue = newDate;
} else {
this.forceInputRerender++;
@ -299,7 +573,7 @@
this.internalValue = this.internalValue.map(() => null);
this.$emit('on-clear');
this.dispatch('FormItem', 'on-form-change', '');
this.emitChange();
this.emitChange(this.type);
this.reset();
setTimeout(
@ -307,9 +581,9 @@
500 // delay to improve dropdown close visual effect
);
},
emitChange () {
emitChange (type) {
this.$nextTick(() => {
this.$emit('on-change', this.publicStringValue);
this.$emit('on-change', this.publicStringValue, type);
this.dispatch('FormItem', 'on-form-change', this.publicStringValue);
});
},
@ -366,7 +640,7 @@
return formatter(value, this.format || format);
}
},
onPick(dates, visible = false) {
onPick(dates, visible = false, type) {
if (this.multiple){
const pickedTimeStamp = dates.getTime();
const indexOfPickedDate = this.internalValue.findIndex(date => date && date.getTime() === pickedTimeStamp);
@ -377,29 +651,36 @@
this.internalValue = Array.isArray(dates) ? dates : [dates];
}
if (this.internalValue[0]) this.focusedDate = this.internalValue[0];
this.focusedTime = {
...this.focusedTime,
time: this.internalValue.map(extractTime)
};
if (!this.isConfirm) this.onSelectionModeChange(this.type); // reset the selectionMode
if (!this.isConfirm) this.visible = visible;
this.emitChange();
this.emitChange(type);
},
onPickSuccess(){
this.visible = false;
this.$emit('on-ok');
this.focus();
this.reset();
},
focus() {
this.$refs.input && this.$refs.input.focus();
}
},
watch: {
visible (state) {
if (state === false){
this.$refs.drop.destroy();
const input = this.$el.querySelector('input');
if (input) input.blur();
}
this.$refs.drop.update();
this.$emit('on-open-change', state);
},
value(val) {
this.internalValue = this.parseDate(val);
},
open (val) {
this.visible = val === true;
@ -421,6 +702,9 @@
this.$emit('input', this.publicVModelValue); // to update v-model
}
if (this.open !== null) this.visible = this.open;
// to handle focus from confirm buttons
this.$on('focus-input', () => this.focus());
}
};
</script>

View file

@ -5,6 +5,7 @@ import RangeDatePickerPanel from '../panel/Date/date-range.vue';
import { oneOf } from '../../../utils/assist';
export default {
name: 'CalendarPicker',
mixins: [Picker],
props: {
type: {

View file

@ -3,7 +3,7 @@ import TimePickerPanel from '../panel/Time/time.vue';
import RangeTimePickerPanel from '../panel/Time/time-range.vue';
import Options from '../time-mixins';
import { oneOf } from '../../../utils/assist';
import { findComponentsDownward, oneOf } from '../../../utils/assist';
export default {
mixins: [Picker, Options],
@ -30,4 +30,14 @@ export default {
};
}
},
watch: {
visible(visible){
if (visible) {
this.$nextTick(() => {
const spinners = findComponentsDownward(this, 'TimeSpinner');
spinners.forEach(instance => instance.updateScroll());
});
}
}
}
};

View file

@ -226,7 +226,15 @@ export const TYPE_VALUE_RESOLVER_MAP = {
formatter: (value, format) => {
return value.filter(Boolean).map(date => formatDate(date, format)).join(',');
},
parser: (text, format) => text.split(',').map(string => parseDate(string.trim(), format))
parser: (value, format) => {
const values = typeof value === 'string' ? value.split(',') : value;
return values.map(value => {
if (value instanceof Date) return value;
if (typeof value === 'string') value = value.trim();
else if (typeof value !== 'number' && !value) value = '';
return parseDate(value, format);
});
}
},
number: {
formatter(value) {

View file

@ -1,7 +1,7 @@
<template>
<div
:class="[prefixCls]"
v-clickoutside="onClickoutside"
v-click-outside="onClickoutside"
@mouseenter="handleMouseenter"
@mouseleave="handleMouseleave">
<div :class="[prefixCls + '-rel']" ref="reference" @click="handleClick"><slot></slot></div>
@ -20,7 +20,7 @@
</template>
<script>
import Drop from '../select/dropdown.vue';
import clickoutside from '../../directives/clickoutside';
import {directive as clickOutside} from 'v-click-outside-x';
import TransferDom from '../../directives/transfer-dom';
import { oneOf, findComponentUpward } from '../../utils/assist';
@ -28,7 +28,7 @@
export default {
name: 'Dropdown',
directives: { clickoutside, TransferDom },
directives: { clickOutside, TransferDom },
components: { Drop },
props: {
trigger: {

View file

@ -4,7 +4,7 @@
</div>
</template>
<script>
import { oneOf, findComponentsDownward } from '../../utils/assist';
import { oneOf, findComponentDownward, findBrothersComponents } from '../../utils/assist';
const prefixCls = 'ivu-row';
@ -58,7 +58,10 @@
},
methods: {
updateGutter (val) {
const Cols = findComponentsDownward(this, 'iCol');
// Col Row Col
// const Cols = findComponentsDownward(this, 'iCol');
const Col = findComponentDownward(this, 'iCol');
const Cols = findBrothersComponents(Col, 'iCol', false);
if (Cols.length) {
Cols.forEach((child) => {
if (val !== 0) {

View file

@ -1,5 +1,5 @@
<template>
<i :class="classes" :style="styles"></i>
<i :class="classes" :style="styles" @click="handleClick"></i>
</template>
<script>
const prefixCls = 'ivu-icon';
@ -28,6 +28,11 @@
return style;
}
},
methods: {
handleClick (event) {
this.$emit('click', event);
}
}
};
</script>

View file

@ -26,10 +26,12 @@
@blur="blur"
@keydown.stop="keyDown"
@input="change"
@mouseup="preventDefault"
@change="change"
:readonly="readonly || !editable"
:name="name"
:value="formatterValue">
:value="formatterValue"
:placeholder="placeholder">
</div>
</div>
</template>
@ -119,7 +121,11 @@
},
parser: {
type: Function
}
},
placeholder: {
type: String,
default: ''
},
},
data () {
return {
@ -175,6 +181,7 @@
},
precisionValue () {
// can not display 1.0
if(!this.currentValue) return this.currentValue;
return this.precision ? this.currentValue.toFixed(this.precision) : this.currentValue;
},
formatterValue () {
@ -241,7 +248,7 @@
},
setValue (val) {
// step precision
if (!isNaN(this.precision)) val = Number(Number(val).toFixed(this.precision));
if (val && !isNaN(this.precision)) val = Number(Number(val).toFixed(this.precision));
this.$nextTick(() => {
this.currentValue = val;
@ -250,9 +257,9 @@
this.dispatch('FormItem', 'on-form-change', val);
});
},
focus () {
focus (event) {
this.focused = true;
this.$emit('on-focus');
this.$emit('on-focus', event);
},
blur () {
this.focused = false;

View file

@ -2,7 +2,7 @@
<ul :class="classes" :style="styles"><slot></slot></ul>
</template>
<script>
import { oneOf, findComponentsDownward, findBrothersComponents } from '../../utils/assist';
import { oneOf, findComponentsDownward, findComponentsUpward } from '../../utils/assist';
import Emitter from '../../mixins/emitter';
const prefixCls = 'ivu-menu';
@ -79,23 +79,44 @@
updateOpenKeys (name) {
let names = [...this.openedNames];
const index = names.indexOf(name);
if (this.accordion) findComponentsDownward(this, 'Submenu').forEach(item => {
item.opened = false;
});
if (index >= 0) {
names.splice(index, 1);
let currentSubmenu = null;
findComponentsDownward(this, 'Submenu').forEach(item => {
if (item.name === name) {
currentSubmenu = item;
item.opened = false;
}
});
findComponentsUpward(currentSubmenu, 'Submenu').forEach(item => {
item.opened = true;
});
findComponentsDownward(currentSubmenu, 'Submenu').forEach(item => {
item.opened = false;
});
} else {
if (this.accordion) {
let currentSubmenu = null;
findComponentsDownward(this, 'Submenu').forEach(item => {
if (item.name === name) currentSubmenu = item;
if (item.name === name) {
currentSubmenu = item;
item.opened = true;
}
});
findBrothersComponents(currentSubmenu, 'Submenu').forEach(item => {
let i = names.indexOf(item.name);
if (i >= 0) names.splice(i, 1);
findComponentsUpward(currentSubmenu, 'Submenu').forEach(item => {
item.opened = true;
});
} else {
findComponentsDownward(this, 'Submenu').forEach(item => {
if (item.name === name) item.opened = true;
});
names.push(name);
}
}
this.openedNames = names;
this.$emit('on-open-change', this.openedNames);
let openedNames = findComponentsDownward(this, 'Submenu').filter(item => item.opened).map(item => item.name);
this.openedNames = [...openedNames];
this.$emit('on-open-change', openedNames);
},
updateOpened () {
const items = findComponentsDownward(this, 'Submenu');

View file

@ -16,4 +16,4 @@ export default {
return this.menu.mode;
}
}
};
};

View file

@ -13,6 +13,10 @@ export default {
this.scrollBarWidth = getScrollBarSize();
}
},
checkMaskInVisible () {
let masks = document.getElementsByClassName('ivu-modal-mask') || [];
return Array.from(masks).every(m => m.style.display === 'none' || m.classList.contains('fade-leave-to'));
},
setScrollBar () {
if (this.bodyIsOverflowing && this.scrollBarWidth !== undefined) {
document.body.style.paddingRight = `${this.scrollBarWidth}px`;
@ -27,8 +31,10 @@ export default {
document.body.style.overflow = 'hidden';
},
removeScrollEffect() {
document.body.style.overflow = '';
this.resetScrollBar();
if (this.checkMaskInVisible()) {
document.body.style.overflow = '';
this.resetScrollBar();
}
}
}
};
};

View file

@ -50,7 +50,7 @@ function notice (type, options) {
if (type == 'normal') {
withIcon = false;
content = `
<div class="${prefixCls}-custom-content ${prefixCls}-with-normal${with_desc}">
<div class="${prefixCls}-custom-content ${prefixCls}-with-normal ${with_desc}">
<div class="${prefixCls}-title">${title}</div>
<div class="${prefixCls}-desc">${desc}</div>
</div>
@ -59,7 +59,7 @@ function notice (type, options) {
const iconType = iconTypes[type];
withIcon = true;
content = `
<div class="${prefixCls}-custom-content ${prefixCls}-with-icon ${prefixCls}-with-${type}${with_desc}">
<div class="${prefixCls}-custom-content ${prefixCls}-with-icon ${prefixCls}-with-${type} ${with_desc}">
<span class="${prefixCls}-icon ${prefixCls}-icon-${type}">
<i class="${iconPrefixCls} ${iconPrefixCls}-${iconType}"></i>
</span>
@ -122,4 +122,4 @@ export default {
noticeInstance = null;
instance.destroy('ivu-notice');
}
};
};

View file

@ -3,7 +3,7 @@
:class="classes"
@mouseenter="handleMouseenter"
@mouseleave="handleMouseleave"
v-clickoutside="handleClose">
v-click-outside="handleClose">
<div
:class="[prefixCls + '-rel']"
ref="reference"
@ -49,7 +49,7 @@
<script>
import Popper from '../base/popper';
import iButton from '../button/button.vue';
import clickoutside from '../../directives/clickoutside';
import {directive as clickOutside} from 'v-click-outside-x';
import TransferDom from '../../directives/transfer-dom';
import { oneOf } from '../../utils/assist';
import Locale from '../../mixins/locale';
@ -59,7 +59,7 @@
export default {
name: 'Poptip',
mixins: [ Popper, Locale ],
directives: { clickoutside, TransferDom },
directives: { clickOutside, TransferDom },
components: { iButton },
props: {
trigger: {

View file

@ -67,7 +67,7 @@
this.childrens = findComponentsDownward(this, 'Radio');
if (this.childrens) {
this.childrens.forEach(child => {
child.currentValue = this.value === child.label;
child.currentValue = this.currentValue === child.label;
child.group = true;
});
}
@ -82,8 +82,10 @@
},
watch: {
value () {
this.currentValue = this.value;
this.updateValue();
if(this.currentValue !== this.value){
this.currentValue = this.value;
this.updateValue();
}
}
}
};

View file

@ -11,8 +11,7 @@
@change="change"
@focus="onFocus"
@blur="onBlur">
</span>
<slot>{{ label }}</slot>
</span><slot>{{ label }}</slot>
</label>
</template>
<script>

View file

@ -45,6 +45,10 @@
},
name: {
type: String
},
clearable: {
type: Boolean,
default: false
}
},
data () {
@ -123,8 +127,13 @@
},
handleClick (value) {
if (this.disabled) return;
// value++;
//value++;
if (this.isHalf) value -= 0.5;
if(this.clearable && Math.abs(value - this.currentValue) < 0.01) {
value = 0;
}
this.currentValue = value;
this.$emit('input', value);
this.$emit('on-change', value);

View file

@ -49,7 +49,7 @@
gpuAcceleration: false
},
preventOverflow :{
boundariesElement: 'body'
boundariesElement: 'window'
}
},
onCreate:()=>{
@ -79,8 +79,16 @@
}
},
resetTransformOrigin() {
let placement = this.popper.popper.getAttribute('x-placement').split('-')[0];
this.popper.popper.style.transformOrigin = placement==='bottom'?'center top':'center bottom';
// Select
if (!this.popper) return;
let x_placement = this.popper.popper.getAttribute('x-placement');
let placementStart = x_placement.split('-')[0];
let placementEnd = x_placement.split('-')[1];
const leftOrRight = x_placement === 'left' || x_placement === 'right';
if(!leftOrRight){
this.popper.popper.style.transformOrigin = placementStart==='bottom' || ( placementStart !== 'top' && placementEnd === 'start') ? 'center top' : 'center bottom';
}
}
},
created () {

View file

@ -0,0 +1,29 @@
<script>
const returnArrayFn = () => [];
export default {
props: {
options: {
type: Array,
default: returnArrayFn
},
slotOptions: {
type: Array,
default: returnArrayFn
},
slotUpdateHook: {
type: Function,
default: () => {}
},
},
functional: true,
render(h, {props, parent}){
// to detect changes in the $slot children/options we do this hack
// so we can trigger the parents computed properties and have everything reactive
// although $slot.default is not
if (props.slotOptions !== parent.$slots.default) props.slotUpdateHook();
return props.options;
}
};
</script>

View file

@ -1,5 +1,11 @@
<template>
<li :class="classes" @click.stop="select" @mouseout.stop="blur" v-show="!hidden"><slot>{{ showLabel }}</slot></li>
<li
:class="classes"
@click.stop="select"
@touchend.stop="select"
@mousedown.prevent
@touchstart.prevent
><slot>{{ showLabel }}</slot></li>
</template>
<script>
import Emitter from '../../mixins/emitter';
@ -22,15 +28,19 @@
disabled: {
type: Boolean,
default: false
},
selected: {
type: Boolean,
default: false
},
isFocused: {
type: Boolean,
default: false
}
},
data () {
return {
selected: false,
index: 0, // for up and down to focus
isFocus: false,
hidden: false, // for search
searchLabel: '', // the value is slot,only for search
searchLabel: '', // the slot value (textContent)
autoComplete: false
};
},
@ -41,53 +51,34 @@
{
[`${prefixCls}-disabled`]: this.disabled,
[`${prefixCls}-selected`]: this.selected && !this.autoComplete,
[`${prefixCls}-focus`]: this.isFocus
[`${prefixCls}-focus`]: this.isFocused
}
];
},
showLabel () {
return (this.label) ? this.label : this.value;
},
optionLabel(){
return this.label || (this.$el && this.$el.textContent);
}
},
methods: {
select () {
if (this.disabled) {
return false;
}
if (this.disabled) return false;
this.dispatch('iSelect', 'on-select-selected', this.value);
this.dispatch('iSelect', 'on-select-selected', {
value: this.value,
label: this.optionLabel,
});
this.$emit('on-select-selected', {
value: this.value,
label: this.optionLabel,
});
},
blur () {
this.isFocus = false;
},
queryChange (val) {
const parsedQuery = val.replace(/(\^|\(|\)|\[|\]|\$|\*|\+|\.|\?|\\|\{|\}|\|)/g, '\\$1');
this.hidden = !new RegExp(parsedQuery, 'i').test(this.searchLabel);
},
// 使 key SearchLabel #1865
updateSearchLabel () {
this.searchLabel = this.$el.textContent;
},
onSelectClose(){
this.isFocus = false;
},
onQueryChange(val){
this.queryChange(val);
}
},
mounted () {
this.updateSearchLabel();
this.dispatch('iSelect', 'append');
this.$on('on-select-close', this.onSelectClose);
this.$on('on-query-change',this.onQueryChange);
const Select = findComponentUpward(this, 'iSelect');
if (Select) this.autoComplete = Select.autoComplete;
},
beforeDestroy () {
this.dispatch('iSelect', 'remove');
this.$off('on-select-close', this.onSelectClose);
this.$off('on-query-change',this.onQueryChange);
}
};
</script>

View file

@ -0,0 +1,200 @@
<template>
<div @click="onHeaderClick">
<div class="ivu-tag ivu-tag-checked" v-for="item in selectedMultiple">
<span class="ivu-tag-text">{{ item.label }}</span>
<Icon type="ios-close-empty" @click.native.stop="removeTag(item)"></Icon>
</div>
<span
:class="singleDisplayClasses"
v-show="singleDisplayValue"
>{{ singleDisplayValue }}</span>
<input
:id="inputElementId"
type="text"
v-if="filterable"
v-model="query"
:disabled="disabled"
:class="[prefixCls + '-input']"
:placeholder="showPlaceholder ? localePlaceholder : ''"
:style="inputStyle"
autocomplete="off"
spellcheck="false"
@keydown="resetInputState"
@keydown.delete="handleInputDelete"
@focus="onInputFocus"
@blur="onInputFocus"
ref="input">
<Icon type="ios-close" :class="[prefixCls + '-arrow']" v-if="resetSelect" @click.native.stop="onClear"></Icon>
<Icon type="arrow-down-b" :class="[prefixCls + '-arrow']" v-if="!resetSelect && !remote && !disabled"></Icon>
</div>
</template>
<script>
import Icon from '../icon';
import Emitter from '../../mixins/emitter';
import Locale from '../../mixins/locale';
const prefixCls = 'ivu-select';
export default {
name: 'iSelectHead',
mixins: [ Emitter, Locale ],
components: { Icon },
props: {
disabled: {
type: Boolean,
default: false
},
filterable: {
type: Boolean,
default: false
},
multiple: {
type: Boolean,
default: false
},
remote: {
type: Boolean,
default: false
},
initialLabel: {
type: [String, Number, Array],
},
values: {
type: Array,
default: () => []
},
clearable: {
type: [Function, Boolean],
default: false,
},
inputElementId: {
type: String
},
placeholder: {
type: String
},
queryProp: {
type: String,
default: ''
}
},
data () {
return {
prefixCls: prefixCls,
query: '',
inputLength: 20,
remoteInitialLabel: this.initialLabel,
preventRemoteCall: false,
};
},
computed: {
singleDisplayClasses(){
const {filterable, multiple, showPlaceholder} = this;
return [{
[prefixCls + '-placeholder']: showPlaceholder && !filterable,
[prefixCls + '-selected-value']: !showPlaceholder && !multiple && !filterable,
}];
},
singleDisplayValue(){
if ((this.multiple && this.values.length > 0) || this.filterable) return '';
return `${this.selectedSingle}` || this.localePlaceholder;
},
showPlaceholder () {
let status = false;
if (!this.multiple) {
const value = this.values[0];
if (typeof value === 'undefined' || String(value).trim() === ''){
status = !this.remoteInitialLabel;
}
} else {
if (!this.values.length > 0) {
status = true;
}
}
return status;
},
resetSelect(){
return !this.showPlaceholder && this.clearable;
},
inputStyle () {
let style = {};
if (this.multiple) {
if (this.showPlaceholder) {
style.width = '100%';
} else {
style.width = `${this.inputLength}px`;
}
}
return style;
},
localePlaceholder () {
if (this.placeholder === undefined) {
return this.t('i.select.placeholder');
} else {
return this.placeholder;
}
},
selectedSingle(){
const selected = this.values[0];
return selected ? selected.label : (this.remoteInitialLabel || '');
},
selectedMultiple(){
return this.multiple ? this.values : [];
}
},
methods: {
onInputFocus(e){
this.$emit(e.type === 'focus' ? 'on-input-focus' : 'on-input-blur');
},
removeTag (value) {
if (this.disabled) return false;
this.dispatch('iSelect', 'on-select-selected', value);
},
resetInputState () {
this.inputLength = this.$refs.input.value.length * 12 + 20;
},
handleInputDelete () {
if (this.multiple && this.selectedMultiple.length && this.query === '') {
this.removeTag(this.selectedMultiple[this.selectedMultiple.length - 1]);
}
},
onHeaderClick(e){
if (this.filterable && e.target === this.$el){
this.$refs.input.focus();
}
},
onClear(){
this.$emit('on-clear');
}
},
watch: {
values ([value]) {
if (!this.filterable) return;
this.preventRemoteCall = true;
if (this.multiple){
this.query = '';
this.preventRemoteCall = false; // this should be after the query change setter above
return;
}
// #982
if (typeof value === 'undefined' || value === '' || value === null) this.query = '';
else this.query = value.label;
this.$nextTick(() => this.preventRemoteCall = false); // this should be after the query change setter above
},
query (val) {
if (this.preventRemoteCall) {
this.preventRemoteCall = false;
return;
}
this.$emit('on-query-change', val);
},
queryProp(query){
if (query !== this.query) this.query = query;
},
}
};
</script>

File diff suppressed because it is too large Load diff

View file

@ -332,8 +332,8 @@
changeButtonPosition (newPos, forceType) {
const type = forceType || this.pointerDown;
const index = type === 'min' ? 0 : 1;
if (type === 'min') newPos = this.checkLimits([newPos, this.maxPosition])[0];
else newPos = this.checkLimits([this.minPosition, newPos])[1];
if (type === 'min') newPos = this.checkLimits([newPos, this.max])[0];
else newPos = this.checkLimits([this.min, newPos])[1];
const modulus = this.handleDecimal(newPos,this.step);
const value = this.currentValue;

View file

@ -44,7 +44,9 @@ Spin.newInstance = properties => {
spin.visible = false;
setTimeout(function() {
spin.$parent.$destroy();
document.body.removeChild(document.getElementsByClassName('ivu-spin-fullscreen')[0]);
if (document.getElementsByClassName('ivu-spin-fullscreen')[0] !== undefined) {
document.body.removeChild(document.getElementsByClassName('ivu-spin-fullscreen')[0]);
}
cb();
}, 500);
},
@ -52,4 +54,4 @@ Spin.newInstance = properties => {
};
};
export default Spin;
export default Spin;

View file

@ -34,6 +34,7 @@
<span :class="[prefixCls + '-filter']">
<i class="ivu-icon ivu-icon-funnel" :class="{on: getColumn(rowIndex, index)._isFiltered}"></i>
</span>
<div slot="content" :class="[prefixCls + '-filter-list']" v-if="getColumn(rowIndex, index)._filterMultiple">
<div :class="[prefixCls + '-filter-list-item']">
<checkbox-group v-model="getColumn(rowIndex, index)._filterChecked">
@ -133,8 +134,8 @@
},
scrollBarCellClass(){
let hasRightFixed = false;
for(var i in this.headRows){
for(var j in this.headRows[i]){
for(let i in this.headRows){
for(let j in this.headRows[i]){
if(this.headRows[i][j].fixed === 'right') {
hasRightFixed=true;
break;
@ -205,7 +206,13 @@
// _ isGroup
getColumn (rowIndex, index) {
const isGroup = this.columnRows.length > 1;
return isGroup ? this.columns[rowIndex] : this.headRows[rowIndex][index];
if (isGroup) {
const id = this.headRows[rowIndex][index].__id;
return this.columns.filter(item => item.__id === id)[0];
} else {
return this.headRows[rowIndex][index];
}
}
}
};

View file

@ -103,7 +103,7 @@
import ExportCsv from './export-csv';
import Locale from '../../mixins/locale';
import elementResizeDetectorMaker from 'element-resize-detector';
import { getAllColumns, convertToRows, convertColumnOrder } from './util';
import { getAllColumns, convertToRows, convertColumnOrder, getRandomStr } from './util';
const prefixCls = 'ivu-table';
@ -178,6 +178,7 @@
}
},
data () {
const colsWithId = this.makeColumnsId(this.columns);
return {
ready: false,
tableWidth: 0,
@ -186,13 +187,11 @@
compiledUids: [],
objData: this.makeObjData(), // checkbox or highlight-row
rebuildData: [], // for sort or filter
cloneColumns: this.makeColumns(),
minWidthColumns:[],
maxWidthColumns:[],
columnRows: this.makeColumnRows(false),
leftFixedColumnRows: this.makeColumnRows('left'),
rightFixedColumnRows: this.makeColumnRows('right'),
allColumns: getAllColumns(this.columns), // for multiple table-head, get columns that have no children
cloneColumns: this.makeColumns(colsWithId),
columnRows: this.makeColumnRows(false, colsWithId),
leftFixedColumnRows: this.makeColumnRows('left', colsWithId),
rightFixedColumnRows: this.makeColumnRows('right', colsWithId),
allColumns: getAllColumns(colsWithId), // for multiple table-head, get columns that have no children
showSlotHeader: true,
showSlotFooter: true,
bodyHeight: 0,
@ -349,74 +348,43 @@
//let tableWidth = parseInt(getStyle(this.$el, 'width')) - 1;
let tableWidth = this.$el.offsetWidth - 1;
let columnsWidth = {};
let sumMinWidth = 0;
let hasWidthColumns = [];
let noWidthColumns = [];
let minWidthColumns = this.minWidthColumns;
let maxWidthColumns = this.maxWidthColumns;
let maxWidthColumns = [];
let noMaxWidthColumns = [];
this.cloneColumns.forEach((col) => {
if (col.width) {
hasWidthColumns.push(col);
}
else{
noWidthColumns.push(col);
if (col.minWidth) {
sumMinWidth += col.minWidth;
}
if (col.maxWidth) {
maxWidthColumns.push(col);
}
else {
noMaxWidthColumns.push(col);
}
}
col._width = null;
});
let unUsableWidth = hasWidthColumns.map(cell => cell.width).reduce((a, b) => a + b, 0);
let usableWidth = tableWidth - unUsableWidth - (this.showVerticalScrollBar?this.scrollBarWidth:0);
let usableWidth = tableWidth - unUsableWidth - sumMinWidth - (this.showVerticalScrollBar?this.scrollBarWidth:0) - 1;
let usableLength = noWidthColumns.length;
let columnWidth = 0;
if(usableWidth > 0 && usableLength > 0){
columnWidth = parseInt(usableWidth / usableLength);
}
for (let i = 0; i < maxWidthColumns.length; i++) {
if(columnWidth > maxWidthColumns[i].maxWidth){
maxWidthColumns[i]._width = maxWidthColumns[i].maxWidth;
usableWidth -= maxWidthColumns[i].maxWidth;
usableLength--;
if (usableWidth>0) {
if (usableLength === 0) {
columnWidth = 0;
}
else {
columnWidth = parseInt(usableWidth / usableLength);
}
}
else{
columnWidth = 0;
}
}
}
for (let i = 0; i < minWidthColumns.length; i++) {
if(columnWidth < minWidthColumns[i].minWidth && !minWidthColumns[i].width){
if(!minWidthColumns[i]._width){
minWidthColumns[i]._width = minWidthColumns[i].minWidth;
usableWidth -= minWidthColumns[i].minWidth;
usableLength--;
if (usableWidth>0) {
if (usableLength === 0) {
columnWidth = 0;
}
else {
columnWidth = parseInt(usableWidth / usableLength);
}
}
else{
columnWidth = 0;
}
}
}
}
for (let i = 0; i < this.cloneColumns.length; i++) {
const column = this.cloneColumns[i];
let width = columnWidth;
let width = columnWidth + (column.minWidth?column.minWidth:0);
if(column.width){
width = column.width;
}
@ -424,17 +392,18 @@
if (column._width) {
width = column._width;
}
else if (column.minWidth > width){
width = column.minWidth;
}
else if (column.maxWidth < width){
width = column.maxWidth;
}
else {
if (column.minWidth > width){
width = column.minWidth;
}
else if (column.maxWidth < width){
width = column.maxWidth;
}
if (usableWidth>0) {
if (usableLength > 1) {
usableLength--;
usableWidth -= width;
usableWidth -= width - (column.minWidth?column.minWidth:0);
usableLength--;
if (usableLength > 0) {
columnWidth = parseInt(usableWidth / usableLength);
}
else {
@ -447,15 +416,38 @@
}
}
this.cloneColumns[i]._width = width;
column._width = width;
columnsWidth[column._index] = {
width: width
};
}
//this.tableWidth = this.cloneColumns.map(cell => cell._width).reduce((a, b) => a + b, 0);
this.tableWidth = this.cloneColumns.map(cell => cell._width).reduce((a, b) => a + b, 0) + (this.showVerticalScrollBar?this.scrollBarWidth:0);
if(usableWidth>0) {
usableLength = noMaxWidthColumns.length;
columnWidth = parseInt(usableWidth / usableLength);
for (let i = 0; i < noMaxWidthColumns.length; i++) {
const column = noMaxWidthColumns[i];
let width = column._width + columnWidth;
if (usableLength > 1) {
usableLength--;
usableWidth -= columnWidth;
columnWidth = parseInt(usableWidth / usableLength);
}
else {
columnWidth = 0;
}
column._width = width;
columnsWidth[column._index] = {
width: width
};
}
}
this.tableWidth = this.cloneColumns.map(cell => cell._width).reduce((a, b) => a + b, 0) + (this.showVerticalScrollBar?this.scrollBarWidth:0) + 1;
this.columnsWidth = columnsWidth;
this.fixedHeader();
},
@ -565,19 +557,18 @@
const headerHeight = parseInt(getStyle(this.$refs.header, 'height')) || 0;
const footerHeight = parseInt(getStyle(this.$refs.footer, 'height')) || 0;
this.bodyHeight = this.height - titleHeight - headerHeight - footerHeight;
this.fixedBody();
this.$nextTick(()=>this.fixedBody());
});
} else {
this.bodyHeight = 0;
this.fixedBody();
this.$nextTick(()=>this.fixedBody());
}
},
fixedBody (){
if (this.$refs.header) {
this.headerWidth = this.$refs.header.children[0].offsetWidth;
this.headerHeight = this.$refs.header.children[0].offsetHeight;
this.showHorizontalScrollBar = this.headerWidth>this.$refs.header.offsetWidth;
//this.showHorizontalScrollBar = this.headerWidth>this.$refs.header.offsetWidth;
}
if (!this.$refs.tbody || !this.data || this.data.length === 0) {
@ -589,10 +580,7 @@
let bodyContentHeight = bodyContentEl.offsetHeight;
let bodyHeight = bodyEl.offsetHeight;
if (!this.$refs.header) {
this.showHorizontalScrollBar = bodyEl.offsetWidth < bodyContentEl.offsetWidth + (this.showVerticalScrollBar?this.scrollBarWidth:0);
}
this.showHorizontalScrollBar = bodyEl.offsetWidth < bodyContentEl.offsetWidth + (this.showVerticalScrollBar?this.scrollBarWidth:0);
this.showVerticalScrollBar = this.bodyHeight? bodyHeight - (this.showHorizontalScrollBar?this.scrollBarWidth:0) < bodyContentHeight : false;
if(this.showVerticalScrollBar){
@ -828,9 +816,17 @@
});
return data;
},
makeColumns () {
// id便
makeColumnsId (columns) {
return columns.map(item => {
if ('children' in item) item.children = this.makeColumnsId(item.children);
item.__id = getRandomStr(6);
return item;
});
},
makeColumns (cols) {
// data this.allColumns undefined
let columns = deepCopy(getAllColumns(this.columns));
let columns = deepCopy(getAllColumns(cols));
let left = [];
let right = [];
let center = [];
@ -869,27 +865,8 @@
return left.concat(center).concat(right);
},
// create a multiple table-head
makeColumnRows (fixedType) {
return convertToRows(this.columns, fixedType);
},
setMinMaxColumnRows (){
let minWidthColumns=[];
let maxWidthColumns=[];
this.cloneColumns.forEach((col) => {
if (!col.width) {
if(col.minWidth){
minWidthColumns.push(col);
}
if(col.maxWidth){
maxWidthColumns.push(col);
}
}
});
minWidthColumns.sort((a,b)=>a.minWidth > b.minWidth);
maxWidthColumns.sort((a,b)=>a.maxWidth < b.maxWidth);
this.minWidthColumns = minWidthColumns;
this.maxWidthColumns = maxWidthColumns;
makeColumnRows (fixedType, cols) {
return convertToRows(cols, fixedType);
},
exportCsv (params) {
if (params.filename) {
@ -927,7 +904,6 @@
},
mounted () {
this.handleResize();
this.setMinMaxColumnRows();
this.$nextTick(() => this.ready = true);
on(window, 'resize', this.handleResize);
@ -964,13 +940,13 @@
columns: {
handler () {
// todo
this.allColumns = getAllColumns(this.columns);
this.cloneColumns = this.makeColumns();
this.setMinMaxColumnRows();
const colsWithId = this.makeColumnsId(this.columns);
this.allColumns = getAllColumns(colsWithId);
this.cloneColumns = this.makeColumns(colsWithId);
this.columnRows = this.makeColumnRows(false);
this.leftFixedColumnRows = this.makeColumnRows('left');
this.rightFixedColumnRows = this.makeColumnRows('right');
this.columnRows = this.makeColumnRows(false, colsWithId);
this.leftFixedColumnRows = this.makeColumnRows('left', colsWithId);
this.rightFixedColumnRows = this.makeColumnRows('right', colsWithId);
this.rebuildData = this.makeDataWithSortAndFilter();
this.handleResize();
},

View file

@ -78,4 +78,16 @@ const convertToRows = (columns, fixedType = false) => {
return rows;
};
export {convertToRows};
export {convertToRows};
const getRandomStr = function (len = 32) {
const $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
const maxPos = $chars.length;
let str = '';
for (let i = 0; i < len; i++) {
str += $chars.charAt(Math.floor(Math.random() * maxPos));
}
return str;
};
export {getRandomStr};

View file

@ -2,7 +2,13 @@
<div :class="classes">
<div :class="[prefixCls + '-bar']">
<div :class="[prefixCls + '-nav-right']" v-if="showSlot"><slot name="extra"></slot></div>
<div :class="[prefixCls + '-nav-container']">
<div
:class="[prefixCls + '-nav-container']"
tabindex="0"
ref="navContainer"
@keydown="handleTabKeyNavigation"
@keydown.space.prevent="handleTabKeyboardSelect(false)"
>
<div ref="navWrap" :class="[prefixCls + '-nav-wrap', scrollable ? prefixCls + '-nav-scrollable' : '']">
<span :class="[prefixCls + '-nav-prev', scrollable ? '' : prefixCls + '-nav-scroll-disabled']" @click="scrollPrev"><Icon type="chevron-left"></Icon></span>
<span :class="[prefixCls + '-nav-next', scrollable ? '' : prefixCls + '-nav-scroll-disabled']" @click="scrollNext"><Icon type="chevron-right"></Icon></span>
@ -20,7 +26,7 @@
</div>
</div>
</div>
<div :class="contentClasses" :style="contentStyle"><slot></slot></div>
<div :class="contentClasses" :style="contentStyle" ref="panes"><slot></slot></div>
</div>
</template>
<script>
@ -31,6 +37,28 @@
import elementResizeDetectorMaker from 'element-resize-detector';
const prefixCls = 'ivu-tabs';
const transitionTime = 300; // from CSS
const getNextTab = (list, activeKey, direction, countDisabledAlso) => {
const currentIndex = list.findIndex(tab => tab.name === activeKey);
const nextIndex = (currentIndex + direction + list.length) % list.length;
const nextTab = list[nextIndex];
if (nextTab.disabled) return getNextTab(list, nextTab.name, direction, countDisabledAlso);
else return nextTab;
};
const focusFirst = (element, root) => {
try {element.focus();}
catch(err) {} // eslint-disable-line no-empty
if (document.activeElement == element && element !== root) return true;
const candidates = element.children;
for (let candidate of candidates) {
if (focusFirst(candidate, root)) return true;
}
return false;
};
export default {
name: 'Tabs',
@ -56,6 +84,10 @@
type: Boolean,
default: true
},
captureFocus: {
type: Boolean,
default: false
},
closable: {
type: Boolean,
default: false
@ -68,11 +100,13 @@
barWidth: 0,
barOffset: 0,
activeKey: this.value,
focusedKey: this.value,
showSlot: false,
navStyle: {
transform: ''
},
scrollable: false
scrollable: false,
transitioning: false,
};
},
computed: {
@ -103,7 +137,7 @@
];
},
contentStyle () {
const x = this.navList.findIndex((nav) => nav.name === this.activeKey);
const x = this.getTabIndex(this.activeKey);
const p = x === 0 ? '0%' : `-${x}00%`;
let style = {};
@ -116,10 +150,10 @@
},
barStyle () {
let style = {
display: 'none',
visibility: 'hidden',
width: `${this.barWidth}px`
};
if (this.type === 'line') style.display = 'block';
if (this.type === 'line') style.visibility = 'visible';
if (this.animated) {
style.transform = `translate3d(${this.barOffset}px, 0px, 0px)`;
} else {
@ -154,7 +188,7 @@
},
updateBar () {
this.$nextTick(() => {
const index = this.navList.findIndex((nav) => nav.name === this.activeKey);
const index = this.getTabIndex(this.activeKey);
if (!this.$refs.nav) return; // #2100
const prevTabs = this.$refs.nav.querySelectorAll(`.${prefixCls}-tab`);
const tab = prevTabs[index];
@ -183,17 +217,35 @@
`${prefixCls}-tab`,
{
[`${prefixCls}-tab-disabled`]: item.disabled,
[`${prefixCls}-tab-active`]: item.name === this.activeKey
[`${prefixCls}-tab-active`]: item.name === this.activeKey,
[`${prefixCls}-tab-focused`]: item.name === this.focusedKey,
}
];
},
handleChange (index) {
if (this.transitioning) return;
this.transitioning = true;
setTimeout(() => this.transitioning = false, transitionTime);
const nav = this.navList[index];
if (nav.disabled) return;
this.activeKey = nav.name;
this.$emit('input', nav.name);
this.$emit('on-click', nav.name);
},
handleTabKeyNavigation(e){
if (e.keyCode !== 37 && e.keyCode !== 39) return;
const direction = e.keyCode === 39 ? 1 : -1;
const nextTab = getNextTab(this.navList, this.focusedKey, direction);
this.focusedKey = nextTab.name;
},
handleTabKeyboardSelect(init = false){
if (init) return;
const focused = this.focusedKey || 0;
const index = this.getTabIndex(focused);
this.handleChange(index);
},
handleRemove (index) {
const tabs = this.getTabs();
const tab = tabs[index];
@ -262,6 +314,9 @@
? Number(navStyle.transform.match(/translateX\(-(\d+(\.\d+)*)px\)/)[1])
: 0;
},
getTabIndex(name){
return this.navList.findIndex(nav => nav.name === name);
},
setOffset(value) {
this.navStyle.transform = `translateX(-${value}px)`;
},
@ -320,19 +375,37 @@
parentNode = parentNode.parentNode;
}
return false;
},
updateVisibility(index){
[...this.$refs.panes.children].forEach((el, i) => {
if (index === i) {
[...el.children].forEach(child => child.style.visibility = 'visible');
if (this.captureFocus) setTimeout(() => focusFirst(el, el), transitionTime);
} else {
setTimeout(() => {
[...el.children].forEach(child => child.style.visibility = 'hidden');
}, transitionTime);
}
});
}
},
watch: {
value (val) {
this.activeKey = val;
this.focusedKey = val;
},
activeKey () {
activeKey (val) {
this.focusedKey = val;
this.updateBar();
this.updateStatus();
this.broadcast('Table', 'on-visible-change', true);
this.$nextTick(() => {
this.scrollToActiveTab();
});
// update visibility
const nextIndex = Math.max(this.getTabIndex(this.focusedKey), 0);
this.updateVisibility(nextIndex);
}
},
mounted () {
@ -351,6 +424,9 @@
this.mutationObserver.observe(hiddenParentNode, { attributes: true, childList: true, characterData: true, attributeFilter: ['style'] });
}
this.handleTabKeyboardSelect(true);
this.updateVisibility(this.getTabIndex(this.activeKey));
},
beforeDestroy() {
this.observer.removeListener(this.$refs.navWrap, this.handleResize);

View file

@ -1,11 +1,16 @@
<template>
<transition name="fade">
<transition name="fade" v-if="fade">
<div :class="classes" @click.stop="check" :style="wraperStyles">
<span :class="dotClasses" v-if="showDot" :style="bgColorStyle"></span>
<span :class="textClasses" :style="textColorStyle"><slot></slot></span>
<Icon v-if="closable" :class="iconClass" :color="lineColor" type="ios-close-empty" @click.native.stop="close"></Icon>
</div>
</transition>
<div v-else :class="classes" @click.stop="check" :style="wraperStyles">
<span :class="dotClasses" v-if="showDot" :style="bgColorStyle"></span>
<span :class="textClasses" :style="textColorStyle"><slot></slot></span>
<Icon v-if="closable" :class="iconClass" :color="lineColor" type="ios-close-empty" @click.native.stop="close"></Icon>
</div>
</template>
<script>
import Icon from '../icon';
@ -39,6 +44,10 @@
},
name: {
type: [String, Number]
},
fade: {
type: Boolean,
default: true
}
},
data () {

View file

@ -0,0 +1,2 @@
import Time from './time.vue';
export default Time;

View file

@ -0,0 +1,83 @@
/**
* @param {Number} timeStamp 判断时间戳格式是否是毫秒
* @returns {Boolean}
*/
// const isMillisecond = timeStamp => {
// const timeStr = String(timeStamp)
// return timeStr.length > 10
// }
/**
* @param {Number} timeStamp 传入的时间戳
* @param {Number} currentTime 当前时间时间戳
* @returns {Boolean} 传入的时间戳是否早于当前时间戳
*/
const isEarly = (timeStamp, currentTime) => {
return timeStamp < currentTime;
};
/**
* @param {Number} num 数值
* @returns {String} 处理后的字符串
* @description 如果传入的数值小于10即位数只有1位则在前面补充0
*/
const getHandledValue = num => {
return num < 10 ? '0' + num : num;
};
/**
* @param {Number} timeStamp 传入的时间戳
* @param {Number} startType 要返回的时间字符串的格式类型传入'year'则返回年开头的完整时间
*/
const getDate = (timeStamp, startType) => {
const d = new Date(timeStamp * 1000);
const year = d.getFullYear();
const month = getHandledValue(d.getMonth() + 1);
const date = getHandledValue(d.getDate());
const hours = getHandledValue(d.getHours());
const minutes = getHandledValue(d.getMinutes());
const second = getHandledValue(d.getSeconds());
let resStr = '';
if (startType === 'year') resStr = year + '-' + month + '-' + date + ' ' + hours + ':' + minutes + ':' + second;
else resStr = month + '-' + date + ' ' + hours + ':' + minutes;
return resStr;
};
/**
* @param {String|Number} timeStamp 时间戳
* @returns {String} 相对时间字符串
*/
export const getRelativeTime = timeStamp => {
// 判断当前传入的时间戳是秒格式还是毫秒
const IS_MILLISECOND = true;
// 如果是毫秒格式则转为秒格式
if (IS_MILLISECOND) Math.floor(timeStamp /= 1000);
// 传入的时间戳可以是数值或字符串类型,这里统一转为数值类型
timeStamp = Number(timeStamp);
// 获取当前时间时间戳
const currentTime = Math.floor(Date.parse(new Date()) / 1000);
// 判断传入时间戳是否早于当前时间戳
const IS_EARLY = isEarly(timeStamp, currentTime);
// 获取两个时间戳差值
let diff = currentTime - timeStamp;
// 如果IS_EARLY为false则差值取反
if (!IS_EARLY) diff = -diff;
let resStr = '';
const dirStr = IS_EARLY ? '前' : '后';
// 少于等于59秒
if (diff <= 59) resStr = diff + '秒' + dirStr;
// 多于59秒少于等于59分钟59秒
else if (diff > 59 && diff <= 3599) resStr = Math.floor(diff / 60) + '分钟' + dirStr;
// 多于59分钟59秒少于等于23小时59分钟59秒
else if (diff > 3599 && diff <= 86399) resStr = Math.floor(diff / 3600) + '小时' + dirStr;
// 多于23小时59分钟59秒少于等于29天59分钟59秒
else if (diff > 86399 && diff <= 2623859) resStr = Math.floor(diff / 86400) + '天' + dirStr;
// 多于29天59分钟59秒少于364天23小时59分钟59秒且传入的时间戳早于当前
else if (diff > 2623859 && diff <= 31567859 && IS_EARLY) resStr = getDate(timeStamp);
else resStr = getDate(timeStamp, 'year');
return resStr;
};
export default function (timestamp) {
return getRelativeTime(timestamp);
}

View file

@ -0,0 +1,95 @@
<template>
<span :class="classes" @click="handleClick">{{ date }}</span>
</template>
<script>
import Vue from 'vue';
const isServer = Vue.prototype.$isServer;
import { oneOf } from '../../utils/assist';
import Time from './time';
const prefixCls = 'ivu-time';
export default {
name: 'Time',
props: {
time: {
type: [Number, Date],
required: true
},
type: {
type: String,
validator (value) {
return oneOf(value, ['relative', 'date', 'datetime']);
},
default: 'relative'
},
hash: {
type: String,
default: ''
},
interval: {
type: Number,
default: 60
}
},
data () {
return {
date: ''
};
},
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-with-hash`]: this.hash
}
];
}
},
methods: {
handleClick () {
if (this.hash !== '') window.location.hash = this.hash;
},
setTime () {
const type = typeof this.time;
let time;
if (type === 'number') {
const timestamp = this.time.toString().length > 10 ? this.time : this.time * 1000;
time = (new Date(timestamp)).getTime();
} else if (type === 'object') {
time = this.time.getTime();
}
if (this.type === 'relative') {
this.date = Time(time);
} else {
const date = new Date(this.time);
const year = date.getFullYear();
const month = (date.getMonth() + 1) < 10 ? '0' + (date.getMonth() + 1) : (date.getMonth() + 1);
const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
const hour = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
const minute = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
const second = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
if (this.type === 'datetime') {
this.date = `${year}-${month}-${day} ${hour}:${minute}:${second}`;
} else if (this.type === 'date') {
this.date = `${year}-${month}-${day}`;
}
}
}
},
mounted () {
this.setTime();
if (isServer) return;
this.timer = setInterval(() => {
this.setTime();
}, 1000 * this.interval);
},
beforeDestroy () {
if (this.timer) clearInterval(this.timer);
}
};
</script>