UNPKG

enketo-core

Version:

Extensible Enketo form engine

221 lines (190 loc) 6.1 kB
import $ from 'jquery'; import 'drag-drop-touch'; import sortable from 'html5sortable/dist/html5sortable.cjs'; import { t } from 'enketo/translator'; import Widget from '../../js/widget'; import support from '../../js/support'; import events from '../../js/event'; /** * @augments Widget */ class RankWidget extends Widget { /** * @type {string} */ static get selector() { return '.question input.rank'; } /** * @type {boolean} */ static get list() { return true; } _init() { const that = this; const loadedValue = this.originalInputValue; const startTextKey = support.touch ? 'rankwidget.tapstart' : 'rankwidget.clickstart'; this.itemSelector = 'label:not(.itemset-template)'; this.list = $(this.element) .next('.option-wrapper') .addClass('widget rank-widget')[0]; $(this.list) .toggleClass('rank-widget--empty', !loadedValue) .append(this.resetButtonHtml) .append( `<div class="rank-widget__overlay"><span class="rank-widget__overlay__content" data-i18n="${startTextKey}">${ support.touch ? t('rankwidget.tapstart') : t('rankwidget.clickstart') }</span></div>` ) .on('click', function () { if (!that.element.disabled) { this.classList.remove('rank-widget--empty'); that.originalInputValue = that.value; that.element.dispatchEvent(events.FakeFocus()); } }); this.list .querySelector('.btn-reset') .addEventListener('click', (evt) => { this._reset(); evt.stopPropagation(); }); this.element.classList.add('hide'); this.value = loadedValue; this.list.querySelectorAll(this.itemSelector).forEach((item) => { const handle = document.createElement('span'); handle.textContent = '::'; handle.className = 'handle'; item.append(handle); }); // Create the sortable drag-and-drop functionality sortable(this.list, { items: this.itemSelector, handle: '.handle', // hoverClass: 'rank-widget__item--hover', containerSerializer(container) { return { value: [].slice .call( container.node.querySelectorAll( `${that.itemSelector} input` ) ) .map((input) => input.value) .join(' '), }; }, })[0].addEventListener('sortupdate', () => { this.originalInputValue = this.value; this.element.dispatchEvent(events.FakeFocus()); }); if (this.props.readonly) { this.disable(); } } /** * Resets widget */ _reset() { this.originalInputValue = ''; } /** * @type {string} */ get value() { const result = sortable(this.list, 'serialize'); return result[0].container.value; } set value(value) { if (!value) { this._reset(); } else { const that = this; const values = value.split(' '); const items = [ ...this.list.querySelectorAll(`${this.itemSelector} input`), ]; // Basic error check if (values.length !== items.length) { throw new Error( 'Could not load rank widget value. Number of items mismatch.' ); } // Don't even attempt to rectify a mismatch between the value and the available items. items.sort((a, b) => { const aIndex = values.indexOf(a.value); const bIndex = values.indexOf(b.value); if (aIndex === -1 || bIndex === -1) { throw new Error( 'Could not load rank widget value. Mismatch in item values.' ); } return aIndex - bIndex; }); items.forEach((item) => { $(that.list) .find('.btn-reset') .before($(item.parentNode).detach()); }); } } /** * Disables widget */ disable() { $(this.element) .prop('disabled', true) .next('.widget') .find('input, button') .prop('disabled', true); sortable(this.list, 'disable'); } /** * Enables widget */ enable() { $(this.element) .prop('disabled', false) .next('.widget') .find('input, button') .prop('disabled', false); sortable(this.list, 'enable'); } /** * Updates widget */ update() { const { value } = this.element; // re-initalize sortable because the options may have changed sortable(this.list); if (value) { this.value = value; this.originalInputValue = value; } else { this._reset(); } } // Since we're overriding the setter we also have to overwrite the getter // https://stackoverflow.com/questions/28950760/override-a-setter-and-the-getter-must-also-be-overridden /** * @type {string} */ get originalInputValue() { return super.originalInputValue; } /** * This is the input that Enketo's engine listens on. * * @type {string} */ set originalInputValue(value) { super.originalInputValue = value; this.list.classList.toggle('rank-widget--empty', !value); } } export default RankWidget;