import {Feature} from './feature'; import Dom from '../dom'; import Arr from '../array'; import Str from '../string'; import Sort from '../sort'; import Event from '../event'; export class Dropdown extends Feature{ /** * Dropdown UI component * @param {Object} tf TableFilter instance */ constructor(tf){ super(tf, 'dropdown'); // Configuration object let f = tf.config(); this.enableSlcResetFilter = f.enable_slc_reset_filter===false ? false : true; //defines empty option text this.nonEmptyText = f.non_empty_text || '(Non empty)'; //sets select filling method: 'innerHTML' or 'createElement' this.slcFillingMethod = f.slc_filling_method || 'createElement'; //IE only, tooltip text appearing on select before it is populated this.activateSlcTooltip = f.activate_slc_tooltip || 'Click to activate'; //tooltip text appearing on multiple select this.multipleSlcTooltip = f.multiple_slc_tooltip || 'Use Ctrl key for multiple selections'; this.isCustom = null; this.opts = null; this.optsTxt = null; this.slcInnerHtml = null; } onSlcFocus(e) { let elm = Event.target(e); let tf = this.tf; tf.activeFilterId = elm.getAttribute('id'); tf.activeFlt = Dom.id(tf.activeFilterId); // select is populated when element has focus if(tf.loadFltOnDemand && elm.getAttribute('filled') === '0'){ let ct = elm.getAttribute('ct'); this.build(ct); } this.emitter.emit('filter-focus', tf, this); } onSlcChange() { if(this.tf.onSlcChange){ this.tf.filter(); } } /** * Initialize drop-down filter * @param {Number} colIndex Column index * @param {Boolean} isExternal External filter flag * @param {DOMElement} container Dom element containing the filter */ init(colIndex, isExternal, container){ let tf = this.tf; let col = tf.getFilterType(colIndex); let externalFltTgtId = isExternal ? tf.externalFltTgtIds[colIndex] : null; let slc = Dom.create(tf.fltTypeSlc, ['id', tf.prfxFlt+colIndex+'_'+tf.id], ['ct', colIndex], ['filled', '0'] ); if(col === tf.fltTypeMulti){ slc.multiple = tf.fltTypeMulti; slc.title = this.multipleSlcTooltip; } slc.className = Str.lower(col) === tf.fltTypeSlc ? tf.fltCssClass : tf.fltMultiCssClass; //filter is appended in container element if(externalFltTgtId){ Dom.id(externalFltTgtId).appendChild(slc); tf.externalFltEls.push(slc); } else { container.appendChild(slc); } tf.fltIds.push(slc.id); if(!tf.loadFltOnDemand){ this.build(colIndex); } else { //1st option is created here since build isn't invoked let opt0 = Dom.createOpt(tf.displayAllText, ''); slc.appendChild(opt0); } Event.add(slc, 'change', ()=> this.onSlcChange()); Event.add(slc, 'focus', (e)=> this.onSlcFocus(e)); this.emitter.on( ['build-select-filter'], (tf, colIndex, isLinked, isExternal)=> this.build(colIndex, isLinked, isExternal) ); this.emitter.on( ['select-options'], (tf, colIndex, values)=> this.selectOptions(colIndex, values) ); this.initialized = true; } /** * Build drop-down filter UI * @param {Number} colIndex Column index * @param {Boolean} isLinked Enable linked refresh behaviour * @param {Boolean} isExternal Render in external container * @param {String} extSlcId External container id */ build(colIndex, isLinked=false, isExternal=false, extSlcId=null){ let tf = this.tf; colIndex = parseInt(colIndex, 10); this.emitter.emit('before-populating-filter', tf, colIndex); this.opts = []; this.optsTxt = []; this.slcInnerHtml = ''; let slcId = tf.fltIds[colIndex]; if((!Dom.id(slcId) && !isExternal) || (!Dom.id(extSlcId) && isExternal)){ return; } let slc = !isExternal ? Dom.id(slcId) : Dom.id(extSlcId), rows = tf.tbl.rows, matchCase = tf.matchCase; //custom select test this.isCustom = tf.isCustomOptions(colIndex); //custom selects text let activeFlt; if(isLinked && tf.activeFilterId){ activeFlt = tf.activeFilterId.split('_')[0]; activeFlt = activeFlt.split(tf.prfxFlt)[1]; } let excludedOpts = null, filteredDataCol = null; if(isLinked && tf.disableExcludedOptions){ excludedOpts = []; filteredDataCol = []; } for(let k=tf.refRow; k' + lbl+''; } else { let opt; //fill select on demand if(tf.loadFltOnDemand && slcValue===this.opts[y] && tf.getFilterType(colIndex) === tf.fltTypeSlc){ opt = Dom.createOpt(lbl, val, true); } else { opt = Dom.createOpt(lbl, val, false); } if(isDisabled){ opt.disabled = true; } slc.appendChild(opt); } }// for y if(fillMethod === 'innerhtml'){ slc.innerHTML += this.slcInnerHtml; } slc.setAttribute('filled', '1'); } /** * Add drop-down header option * @param {Object} slc Select DOM element */ addFirstOption(slc){ let tf = this.tf, fillMethod = Str.lower(this.slcFillingMethod); if(fillMethod === 'innerhtml'){ this.slcInnerHtml += ''; } else { let opt0 = Dom.createOpt( (!this.enableSlcResetFilter ? '' : tf.displayAllText),''); if(!this.enableSlcResetFilter){ opt0.style.display = 'none'; } slc.appendChild(opt0); if(tf.enableEmptyOption){ let opt1 = Dom.createOpt(tf.emptyText, tf.emOperator); slc.appendChild(opt1); } if(tf.enableNonEmptyOption){ let opt2 = Dom.createOpt(tf.nonEmptyText, tf.nmOperator); slc.appendChild(opt2); } } return slc; } /** * Select filter options programmatically * @param {Number} colIndex Column index * @param {Array} values Array of option values to select */ selectOptions(colIndex, values=[]){ let tf = this.tf; if(tf.getFilterType(colIndex) !== tf.fltTypeMulti || values.length === 0){ return; } let slc = tf.getFilterElement(colIndex); [].forEach.call(slc.options, (option)=> { // Empty value means clear all selections and first option is the // clear all option if(values[0] === '' || option.value === ''){ option.selected = false; } if(option.value !== '' && Arr.has(values, option.value, true)){ option.selected = true; }//if }); } destroy(){ this.emitter.off( ['build-select-filter'], (colIndex, isLinked, isExternal)=> this.build(colIndex, isLinked, isExternal) ); this.emitter.off( ['select-options'], (tf, colIndex, values)=> this.selectOptions(colIndex, values) ); } }