rx/presenters

View on GitHub
views/mdc/assets/js/components/text-fields.js

Summary

Maintainability
A
55 mins
Test Coverage
import {MDCTextField} from '@material/textfield';
import {VBaseComponent, hookupComponents} from './base-component';
import {eventHandlerMixin} from './mixins/event-handler';
import {visibilityObserverMixin} from './mixins/visibility-observer';
import {dirtyableMixin} from './mixins/dirtyable';

const AFTER_INPUT_EVENT = 'after_input';
const AFTER_INPUT_TIMEOUT = 500; // ms

export function initTextFields(e) {
    console.debug('\tTextFields');
    hookupComponents(e, '.v-text-field', VTextField, MDCTextField);
}

export class VTextField extends dirtyableMixin(
    visibilityObserverMixin(
        eventHandlerMixin(VBaseComponent))) {
    constructor(element, mdcComponent) {
        super(element, mdcComponent);

        this.input = element.querySelector('input,textarea');
        this.input.vComponent = this;
        this.afterInputTimeout = null;
        this.helperDisplay = this.root.getElementById(`${element.id}-input-helper-text`);
        this.origHelperText = this.helperDisplay.innerHTML.trim();

        this.recalcWhenVisible(this);

        this.input.addEventListener('input', (event) => {
            clearTimeout(this.afterInputTimeout);
            this.afterInputTimeout = setTimeout(() => {
                this.element.dispatchEvent(new Event(AFTER_INPUT_EVENT, {composed: true}));
            }, AFTER_INPUT_TIMEOUT);
        });

        const caseType = element.dataset.case_type;
        if (caseType !== 'mixed') {
            this.input.addEventListener('keyup', (e) => {
                this.forceCase(caseType);
            });
        }
        this.originalValue = this.value();
    }

    // Called whenever a form is about to be submitted.
    // returns true on success
    // returns on failure return an error object that can be processed by VErrors:
    //    { email: ["email must be filled", "email must be from your domain"] }
    //    { :page: ["must be filled"] }
    validate(formData) {
        console.debug('TextField validate', formData);
        const isValid = this.input.checkValidity();
        if (isValid) {
            if (this.origHelperText !== '') {
                this.helperDisplay.innerHTML = this.origHelperText;
                this.helperDisplay.classList.remove('v-hidden', 'mdc-text-field-helper-text--validation-msg');
                this.element.classList.remove('mdc-text-field--invalid');
            }
            else {
                this.helperDisplay.classList.add('v-hidden');
            }
            return true;
        }

        const message = this.helperDisplay.dataset.validationError ?
            this.helperDisplay.dataset.validationError :
            this.input.validationMessage;

        const errorMessage = {};
        errorMessage[this.element.id] = [message];
        return errorMessage;
    }

    // Called to collect data for submission
    prepareSubmit(params) {
        const optionSelected = this.optionSelected();
        if (optionSelected) {
            const key = optionSelected.dataset.key;
            if (key) {
                const name = this.name();
                const id = name + '_id';
                params.push([id, key]);
                console.debug('TextField prepareSubmit added:' + id + '=' + key);
            }
        }
        params.push([this.name(), this.value()]);
    }

    optionSelected() {
        const dataList = this.element.querySelector('datalist');
        if (dataList) {
            const parentElement = this.input;
            // If we find the input inside our list, we submit the form
            for (const element of dataList.children) {
                if (element.value === parentElement.value) {
                    return element;
                }
            }
        }
        return null;
    }

    name() {
        return this.input.name;
    }

    value() {
        return this.input.value;
    }

    clear() {
        if (this.value() !== '') {
            this.setValue(null);
        }
    }

    reset() {
        this.input.value = this.originalValue;
    }

    setValue(value) {
        this.input.value = value;
    }

    isDirty() {
        return this.dirtyable
            && this.value().localeCompare(this.originalValue) !== 0;
    }

    onShow() {
        this.mdcComponent.layout();
    }

    preview(result, acceptsMimeTypes) {
        this.setValue(result);
    }

    // Return true if focus is able to be set, false otherwise
    focus() {
        if (this.isHidden() || this.input.disabled) return false;
        this.input.focus();
        return true;
    }

    isHidden() {
        const style = window.getComputedStyle(this.element);
        return (style.display === 'none');
    }

    forceCase(caseType){
        if (caseType === 'upper') {
            this.input.value = this.input.value.toUpperCase();
        } else if (caseType === 'lower') {
            this.input.value = this.input.value.toLowerCase();
        }
    }
}