UNPKG

samagra-x-enketo-core

Version:

Extensible Enketo form engine

163 lines (140 loc) 5.54 kB
import RangeWidget from '../../widget/range/range-widget'; import { isNumber } from '../../js/utils'; import support from '../../js/support'; import events from '../../js/event'; /** * @augments RangeWidget */ class AnalogScaleWidget extends RangeWidget { /** * @type {string} */ static get selector() { return '.or-appearance-analog-scale input[type="number"]'; } _init() { super._init(); if ( this.props.vertical ) { this.question.classList.add( 'or-appearance-vertical' ); } this.question.classList.add( 'or-analog-scale-initialized' ); this._renderLabels(); this._setResizeListener(); } /** * @return {string} HTML string */ _getHtmlStr() { const html = `<div class="widget analog-scale-widget"> ${super._getHtmlStr()} </div>`; return html; } _updateMercury() {} /** * (re-)Renders the widget labels based on the current content of .question-label.active */ _renderLabels() { const fragment = document.createRange().createContextualFragment( '<div class="label-content widget"></div>' ); const wrapper = fragment.querySelector( '.label-content' ); this.question.querySelectorAll( '.question-label, .or-hint, .or-required-msg, [class*="or-constraint"]' ) .forEach( el => wrapper.append( el ) ); this.question.prepend( fragment ); this.labelContent = this.question.querySelector( '.label-content' ); this._updateLabels(); } /** * Updates labels */ _updateLabels() { if ( !this.question.classList.contains( 'or-analog-scale-initialized' ) ) { return; } const labelEl = this.labelContent.querySelector( '.question-label.active:not(.widget)' ); const labels = labelEl.innerHTML.split( /\|/ ).map( label => label.trim() ); const existingLabel = this.labelContent.querySelector( '.question-label.widget' ); if ( existingLabel ) { existingLabel.remove(); } const labelFragment = document.createRange().createContextualFragment( `<span class="question-label widget active">${labels[ 0 ]}</span>` ); labelEl.after( labelFragment ); const existingMaxLabel = this.widget.querySelector( '.max-label' ); if ( existingMaxLabel ) { existingMaxLabel.remove(); } const maxLabel = document.createRange().createContextualFragment( `<div class="max-label">${labels[ 1 ]}</div>` ); this.widget.prepend( maxLabel ); const existingMinLabel = this.widget.querySelector( '.min-label' ); if ( existingMinLabel ) { existingMinLabel.remove(); } const minLabel = document.createRange().createContextualFragment( `<div class="min-label">${labels[ 2 ]}</div>` ); this.widget.append( minLabel ); const showValue = this.labelContent.querySelector( '.show-value' ); if ( showValue ) { showValue.remove(); } if ( labels[ 3 ] ) { const showValueBox = document.createRange().createContextualFragment( `<div class="widget show-value"> <div class="show-value__box">${labels[ 3 ]}<span class="show-value__value">${this.value}</span></div> <div>` ); this.labelContent.append( showValueBox ); this.current = this.labelContent.querySelector( '.show-value__value' ); } } /** * Stretch the question to full page height. * Doing this with pure css flexbox using "flex-direction: column" interferes with the Grid theme * because that theme relies on flexbox with "flex-direction: row". */ _setResizeListener() { if ( this.props.vertical ) { // Will only be triggered if question by itself constitutes a page. // It will not be triggered if question is contained inside a group with fieldlist appearance. this.question.addEventListener( events.PageFlip().type, this._stretchHeight.bind( this ) ); } } /** * Stretches height */ _stretchHeight() { this.question.style[ 'min-height' ] = 'auto'; } /** * Updates with labels */ update() { super.update(); this._updateLabels(); } /** * @type {object} */ get props() { const props = this._props; props.touch = support.touch; props.vertical = !props.appearances.includes( 'horizontal' ); props.ticks = !props.appearances.includes( 'no-ticks' ); props.showScale = props.appearances.includes( 'show-scale' ) && props.vertical && props.ticks; const min = isNumber( this.element.getAttribute( 'min' ) ) ? this.element.getAttribute( 'min' ) : 0; const max = isNumber( this.element.getAttribute( 'max' ) ) ? this.element.getAttribute( 'max' ) : 100; const step = isNumber( this.element.getAttribute( 'step' ) ) ? this.element.getAttribute( 'step' ) : ( props.showScale ? 10 : 1 ); //( props.type === 'decimal' ? 0.1 : 1 ); props.min = Number( min ); props.max = Number( max ); props.step = Number( step ); props.maxTicks = 10; return props; } /** * @type {*} */ get value() { return super.value; } set value( value ) { super.value = value; } } export default AnalogScaleWidget;