recommend-colors.vue 4.32 KB
<template>
    <div
        ref="reference"
        tabindex="0"
        @click="handleClick"
        @keydown.esc="handleEscape"
        @keydown.enter="handleEnter"
        @keydown.left="handleArrow($event, 'x', left)"
        @keydown.right="handleArrow($event, 'x', right)"
        @keydown.up="handleArrow($event, 'y', up)"
        @keydown.down="handleArrow($event, 'y', down)"
        @blur="blurColor"
        @focus="focusColor"
    >
        <template v-for="(item, index) in list">
            <div
                :key="item + ':' + index"
                :class="[prefixCls + '-picker-colors-wrapper']">
                <div :data-color-id="index">
                    <div
                        :style="{background: item}"
                        :class="[prefixCls + '-picker-colors-wrapper-color']"
                    ></div>
                    <div
                        :ref="'color-circle-' + index"
                        :class="[prefixCls + '-picker-colors-wrapper-circle', hideClass]"></div>
                </div>
            </div>
            <br v-if="lineBreak(list, index)">
        </template>
    </div>
</template>

<script>
import Emitter from '../../mixins/emitter';
import HandleEscapeMixin from './handleEscapeMixin';
import Prefixes from './prefixMixin';
import {clamp} from './utils';

export default {
    name: 'RecommendedColors',

    mixins: [Emitter, HandleEscapeMixin, Prefixes],

    props: {
        list: {
            type: Array,
            default: undefined,
        },
    },

    data() {
        const columns = 12;
        const rows = Math.ceil(this.list.length / columns);
        const normalStep = 1;

        return {
            left: -normalStep,
            right: normalStep,
            up: -normalStep,
            down: normalStep,
            powerKey: 'shiftKey',
            grid: {x: 1, y: 1},
            rows,
            columns,
        };
    },

    computed: {
        hideClass() {
            return `${this.prefixCls}-hide`;
        },
        linearIndex() {
            return this.getLinearIndex(this.grid);
        },
        currentCircle() {
            return this.$refs[`color-circle-${this.linearIndex}`][0];
        },
    },

    methods: {
        getLinearIndex(grid) {
            return this.columns * (grid.y - 1) + grid.x - 1;
        },
        getMaxLimit(axis) {
            return axis === 'x' ? this.columns : this.rows;
        },
        handleArrow(e, axis, direction) {
            e.preventDefault();
            e.stopPropagation();

            this.blurColor();

            const grid = {...this.grid};

            if (e[this.powerKey]) {
                if (direction < 0) {
                    grid[axis] = 1;
                } else {
                    grid[axis] = this.getMaxLimit(axis);
                }
            } else {
                grid[axis] += direction;
            }

            const index = this.getLinearIndex(grid);

            if (index >= 0 && index < this.list.length) {
                this.grid[axis] = clamp(grid[axis], 1, this.getMaxLimit(axis));
            }

            this.focusColor();
        },
        blurColor() {
            this.currentCircle.classList.add(this.hideClass);
        },
        focusColor() {
            this.currentCircle.classList.remove(this.hideClass);
        },
        handleEnter(e) {
            this.handleClick(e, this.currentCircle);
        },
        handleClick(e, circle) {
            e.preventDefault();
            e.stopPropagation();

            this.$refs.reference.focus();

            const target = circle || e.target;
            const colorId = target.dataset.colorId || target.parentElement.dataset.colorId;

            if (colorId) {
                this.blurColor();
                const id = Number(colorId) + 1;
                this.grid.x = id % this.columns || this.columns;
                this.grid.y = Math.ceil(id / this.columns);
                this.focusColor();
                this.$emit('picker-color', this.list[colorId]);
                this.$emit('change', {hex: this.list[colorId], source: 'hex'});
            }
        },
        lineBreak(list, index) {
            if (!index) {
                return false;
            }

            const nextIndex = index + 1;

            return nextIndex < list.length && nextIndex % this.columns === 0;
        },
    },
};
</script>