busy-web/ember-date-time

View on GitHub
addon/utils/clock/svg.js

Summary

Maintainability
A
1 hr
Test Coverage
/**
 * @module Utils
 *
 */
import { isNone, isEmpty } from '@ember/utils';
import { assert } from '@ember/debug';
import { Snap, mina } from 'snapsvg';
import { numberToString } from './string';
import { getDate, isInBounds } from './data';
import Base from './base';

export default class SVG extends Base {
    /**
     * @constructor
     *
     */
    constructor(el, opts={}) {
        assert("el is required for class SVG", !isNone(el));
        super();

        this.setProps({ el, opts, snap: null });
    }

    createSnap() {
        this.__snap = new Snap(this.__el);
        return this;
    }

    get snap() {
        if (!this.__snap) {
            this.createSnap();
        }
        return this.__snap;
    }

    get face() {
        return this.snap.select(`.--svg-face`);
    }

    get pivot() {
        return this.snap.select(`.--svg-pivot`);
    }

    get selectRounder() {
        return this.__opts.selectRounder && this.__opts.selectRounder > 1 ? this.__opts.selectRounder : 1;
    }

    has(type, num) {
        assert(`num is required in svg.at, you pased type {${typeof num}} ${num}`, !isEmpty(num));
        if (typeof num === 'number') {
            num = numberToString(num);
        }
        return !isEmpty(this.snap.select(`.--svg-${type}-${num}`));
    }

    at(num) {
        assert(`num is required in svg.at, you pased type {${typeof num}} ${num}`, !isEmpty(num));
        if (typeof num === 'number') {
            num = numberToString(num);
        }

        let text = this.snap.select(`.--svg-text-${num}`);
        const arm = this.snap.select(`.--svg-arm-${num}`);
        const plot = this.snap.select(`.--svg-plot-${num}`);
        const path = this.snap.select(`.--svg-path-${num}`);

        assert(`"arm" was not found with number: ${num}`, !isEmpty(arm));
        assert(`"plot" was not found with number: ${num}`, !isEmpty(plot));
        assert(`"path" was not found with number: ${num}`, !isEmpty(path));

        return { path, arm, plot, text };
    }

    minMaxHandler(type, num, min, max, timestamp) {
        const date = getDate(type, num, timestamp);
        const bounds = isInBounds(date, min, max);
        if (bounds.isBefore || bounds.isAfter) {
            this.disablePlot(num);
        } else {
            this.enablePlot(num);
        }
    }

    clean(start, end) {
        for(let i=start; i<=end; i++) {
            if (i%this.selectRounder === 0) {
                const { text, arm, plot } = this.at(i);
                if (!isNone(text)) {
                    text.removeClass('selected');
                }
                arm.insertBefore(this.face);
                plot.insertBefore(this.face);
            }
        }
    }

    unselectPlot(value) {
        const { text, arm, plot } = this.at(value);
        let next, prev;
        if (this.has('plot', value - this.selectRounder)) {
            prev = this.at(value - this.selectRounder);
        } else {
            next = this.at(value + this.selectRounder);
        }

        if (!isNone(text)) {
            text.removeClass('selected');
            if (prev) {
                text.insertAfter(prev.path);
            } else {
                text.insertBefore(next.path);
            }
        }

        if (prev) {
            plot.insertAfter(prev.plot);
            arm.insertAfter(prev.plot);
        } else {
            arm.insertBefore(next.arm);
            plot.insertBefore(next.arm);
        }
    }

    selectPlot(value) {
        const { text, arm, plot } = this.at(value);
        if (!isNone(text) && !text.hasClass('selected')) {
            text.addClass('selected');
        }

        arm.appendTo(this.snap);
        plot.appendTo(this.snap);

        if (!isNone(text) && !text.hasClass('selected')) {
            const fill = (this.__opts.fillColor || 'white');
            text.animate({ fill }, 100, mina.easein).appendTo(this.snap);
        }
    }

    /**
        * enables an element that was disabled
        *
        * @private
        * @method enableClockNumber
        * @param target {instance} time-picker class reference
        * @param type {string} the type to set either minutes or hours
        * @param value {number} the integer value for the time
        */
    enablePlot(value) {
        const { text, path } = this.at(value);
        path.removeClass('disabled');
        if (!isNone(text)) {
            text.removeClass('disabled');
        }
    }

    /**
        * disables an element
        *
        * @private
        * @method disableClockNumber
        * @param target {instance} time-picker class reference
        * @param type {string} the type to set either minutes or hours
        * @param value {number} the integer value for the time
        */
    disablePlot(value) {
        const { text, path } = this.at(value);
        if (!path.hasClass('disabled')) {
            path.addClass('disabled');
        }

        if (!isNone(text) && !text.hasClass('disabled')) {
            text.addClass('disabled');
        }
    }
}