time-spinner.vue 8.75 KB
<template>
    <div :class="classes">
        <div :class="[prefixCls+ '-list']" ref="hours">
            <ul :class="[prefixCls + '-ul']">
                <li :class="getCellCls(item)" v-for="item in hoursList" v-show="!item.hide" @click="handleClick('hours', item)">{{ formatTime(item.text) }}</li>
            </ul>
        </div>
        <div :class="[prefixCls+ '-list']" ref="minutes">
            <ul :class="[prefixCls + '-ul']">
                <li :class="getCellCls(item)" v-for="item in minutesList" v-show="!item.hide" @click="handleClick('minutes', item)">{{ formatTime(item.text) }}</li>
            </ul>
        </div>
        <div :class="[prefixCls+ '-list']" v-show="showSeconds" ref="seconds">
            <ul :class="[prefixCls + '-ul']">
                <li :class="getCellCls(item)" v-for="item in secondsList" v-show="!item.hide" @click="handleClick('seconds', item)">{{ formatTime(item.text) }}</li>
            </ul>
        </div>
    </div>
</template>
<script>
    import Options from '../time-mixins';
    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: {
                type: [Number, String],
                default: NaN
            },
            minutes: {
                type: [Number, String],
                default: NaN
            },
            seconds: {
                type: [Number, String],
                default: NaN
            },
            showSeconds: {
                type: Boolean,
                default: true
            },
            steps: {
                type: Array,
                default: () => []
            }
        },
        data () {
            return {
                spinerSteps: [1, 1, 1].map((one, i) => Math.abs(this.steps[i]) || one),
                prefixCls: prefixCls,
                compiled: false,
                focusedColumn: -1, // which column inside the picker
                focusedTime: [0, 0, 0] // the values array into [hh, mm, ss]
            };
        },
        computed: {
            classes () {
                return [
                    `${prefixCls}`,
                    {
                        [`${prefixCls}-with-seconds`]: this.showSeconds
                    }
                ];
            },
            hoursList () {
                let hours = [];
                const step = this.spinerSteps[0];
                const focusedHour = this.focusedColumn === 0 && this.focusedTime[0];
                const hour_tmpl = {
                    text: 0,
                    selected: false,
                    disabled: false,
                    hide: false
                };

                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;
                        if (this.hideDisabledOptions) hour.hide = true;
                    }
                    if (this.hours === i) hour.selected = true;
                    hours.push(hour);
                }

                return hours;
            },
            minutesList () {
                let minutes = [];
                const step = this.spinerSteps[1];
                const focusedMinute = this.focusedColumn === 1 && this.focusedTime[1];
                const minute_tmpl = {
                    text: 0,
                    selected: false,
                    disabled: false,
                    hide: false
                };

                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;
                        if (this.hideDisabledOptions) minute.hide = true;
                    }
                    if (this.minutes === i) minute.selected = true;
                    minutes.push(minute);
                }
                return minutes;
            },
            secondsList () {
                let seconds = [];
                const step = this.spinerSteps[2];
                const focusedMinute = this.focusedColumn === 2 && this.focusedTime[2];
                const second_tmpl = {
                    text: 0,
                    selected: false,
                    disabled: false,
                    hide: false
                };

                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;
                        if (this.hideDisabledOptions) second.hide = true;
                    }
                    if (this.seconds === i) second.selected = true;
                    seconds.push(second);
                }

                return seconds;
            }
        },
        methods: {
            getCellCls (cell) {
                return [
                    `${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 = {[type]: cell.text};
                this.emitChange(data);
            },
            emitChange(changes){
                this.$emit('on-change', changes);
                this.$emit('on-pick-click');
            },
            scroll (type, index) {
                const from = this.$refs[type].scrollTop;
                const to = 24 * this.getScrollIndex(type, index);
                scrollTop(this.$refs[type], from, to, 500);
            },
            getScrollIndex (type, index) {
                const Type = firstUpperCase(type);
                const disabled = this[`disabled${Type}`];
                if (disabled.length && this.hideDisabledOptions) {
                    let _count = 0;
                    disabled.forEach(item => item <= index ? _count++ : '');
                    index -= _count;
                }
                return index;
            },
            updateScroll () {
                this.$nextTick(() => {
                    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: {
            hours (val) {
                if (!this.compiled) return;
                this.scroll('hours', this.hoursList.findIndex(obj => obj.text == val));
            },
            minutes (val) {
                if (!this.compiled) return;
                this.scroll('minutes', this.minutesList.findIndex(obj => obj.text == val));
            },
            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 () {
            this.$nextTick(() => this.compiled = true);
        }
    };
</script>