import * as React from 'react'; export interface QueryPanelProps { elmtId: string; data: any; } export interface QueryPanelState { elmtId: string; dimensions; dimensionInputs; dimensionLevelInputs; dimensionPivotInputs; } /** * Parse Query Panel html, with provided id attr, into Query Panel data object * @param id */ export function getQueryPanelData(id: string): any { let data = { dimensions: [], dimensionInputs: [], dimensionLevelInputs: [], dimensionPivotInputs: [], }, elmt = document.getElementById(id), name, dimension, dimensionElmt, dimensionInputElmt, dimensionEmts = elmt.querySelectorAll('.mmui-dimension'), dimensionInputElmts = elmt.querySelectorAll('.mmui-dimension-input'); // parse the available dimensions html for (let i = 0; i < dimensionEmts.length; i++) { dimensionElmt = dimensionEmts[i]; dimension = { value: dimensionElmt.dataset.value, label: dimensionElmt.dataset.label, }; data.dimensions.push(dimension); } // parse the input field dimensions for (let i = 0; i < dimensionInputElmts.length; i++) { dimensionInputElmt = dimensionInputElmts[i]; name = dimensionInputElmt.name; dimension = { label: dimensionInputElmt.dataset.label, value: dimensionInputElmt.value, }; if (/_pivot$/.test(name)) { data.dimensionPivotInputs.push(dimension); } else if (/_level$/.test(name)) { data.dimensionLevelInputs.push(dimension); } else if (/^dim/.test(name)) { data.dimensionInputs.push(dimension); } } return data; } const DIMENSION_ARRAY_NAMES = [ 'dimensions', 'dimensionInputs', 'dimensionLevelInputs', 'dimensionPivotInputs', ]; /** * Query Panel Component * Provides the behavior for the Query Crafter query builder panel. */ export class MmuiQueryPanelComponent extends React.Component< QueryPanelProps, QueryPanelState > { constructor(props) { super(props); // init state const state = { elmtId: props.elmtId, dimensions: [], dimensionInputs: [], dimensionLevelInputs: [], dimensionPivotInputs: [], }; for (const daName of DIMENSION_ARRAY_NAMES) { state[daName] = props.data[daName]; } this.state = state; } componentDidUpdate(prevProps, prevState) { const daName = 'dimensions'; if (this.state[daName].length > prevState[daName].length) { this.orderDimensions(); } } /** * Default dimension order compare function. * Override in inheriting class to change. * @param a * @param b */ compareDimensionOrder = (a, b) => { return a.label.localeCompare(b.label); }; /** * Order the each of the dimension arrays according . */ orderDimensions() { const newState = {}, daName = 'dimensions', dimensionArray = [...this.state[daName]]; dimensionArray.sort(this.compareDimensionOrder); newState[daName] = dimensionArray; this.setState(newState); } /** * Find the name of the dimension array that contains the dimension with the provided value * @param dimensionValue - value of dimension to locate */ findDimensionArrayName(dimensionValue) { let dimension, dimensionArray, dimensionArrayName; for (const daName of DIMENSION_ARRAY_NAMES) { dimensionArray = this.state[daName]; for (dimension of dimensionArray) { if (dimension.value === dimensionValue) { dimensionArrayName = daName; break; } } } return dimensionArrayName; } /** * Move the dimension, with the provided dimensionValue, from its current array * to the dimension array with name specified by daNameTarget. * @param dimensionValue - value of dimension to be moved * @param daNameTarget - name of target dimension array */ moveDimension(dimensionValue, daNameTarget) { let daNameSrc, dimension, dimensionArraySrc, dimensionArraySrcNew = [], dimensionArrayTarget, dimensionArrayTargetNew = []; daNameSrc = this.findDimensionArrayName(dimensionValue); if (daNameSrc && daNameSrc !== daNameTarget) { dimensionArraySrc = this.state[daNameSrc]; for (const d of dimensionArraySrc) { if (d.value === dimensionValue) { dimension = d; } else { dimensionArraySrcNew.push(d); } } dimensionArrayTarget = this.state[daNameTarget]; for (const d of dimensionArrayTarget) { dimensionArrayTargetNew.push(d); } dimensionArrayTargetNew.push(dimension); const newState = {}; newState[daNameSrc] = dimensionArraySrcNew; newState[daNameTarget] = dimensionArrayTargetNew; this.setState(newState); } } /** * User has started to drag a dimension * @param evt */ onDimensionDragStart = (evt) => { const dimensionValue = evt.target.dataset.value; evt.dataTransfer.setData('dimensionValue', dimensionValue); }; /** * Dimension is dragged over target * @param evt */ onDimensionDragOver = (evt) => { evt.preventDefault(); /* * TODO: Finish below code to add list sorting. let elmtBox, elmtHalfBox, dimensionElmts, dimensionElmt, daName, dimensionValue = evt.dataTransfer.getData("dimensionValue"), index = -1 ; // console.log("dimensionValue", dimensionValue); let elmt = evt.target; while(elmt){ daName = elmt.dataset.name; if(daName){ break; } elmt = elmt.parentElement; } // console.log("daName", daName); if(daName){ dimensionElmts = elmt.querySelectorAll(".mmui-dimension"); // console.log(dimensionElmts); for (let i = 0; i < dimensionElmts.length; i++){ dimensionElmt = dimensionElmts[i]; elmtBox = dimensionElmt.getBoundingClientRect(); let elmtBoxTopQuarter = elmtBox.top + (elmtBox.height * 0.25); let elmtBoxBottomQuarter = elmtBox.bottom - (elmtBox.height * 0.25); elmtHalfBox = elmtBox.top + (elmtBox.height * 0.5); // console.log(evt.clientY, elmtBox.top); // if (elmtBox.top <= evt.clientY && evt.clientY < elmtBox.bottom){ // index = i; // break; // } if (elmtBox.top <= evt.clientY && evt.clientY < elmtBoxTopQuarter){ index = i; } else if (elmtBoxBottomQuarter <= evt.clientY && evt.clientY < elmtBox.bottom) { index = i + 1; } } let dimension, dimensionArray = this.state[daName], dimensionArrayNew = [] ; for(let i = 0; i < dimensionArray.length; i++){ dimension = dimensionArray[i]; if(dimension.isPlaceholder){ continue; } if (i === index) { dimensionArrayNew.push({ isPlaceholder: true }); } if(dimension.value !== dimensionValue){ dimensionArrayNew.push(dimension); } } if (index < 0){ dimensionArrayNew.push({ isPlaceholder: true }); } let newState = {}; newState[daName] = dimensionArrayNew; this.setState(newState); } */ }; /** * Dimension is dropped into target area * @param evt */ onDimensionDrop = (evt) => { evt.preventDefault(); const dimensionValue = evt.dataTransfer.getData('dimensionValue'), daNameSrc = evt.currentTarget.dataset.name; if (dimensionValue && daNameSrc) { this.moveDimension(dimensionValue, daNameSrc); } }; /** * Move dimension back to available dimentions * @param evt */ onDimensionReset = (evt) => { evt.preventDefault(); const dimensionValue = evt.target.dataset.value; this.moveDimension(dimensionValue, 'dimensions'); }; getEmptyState(msg) { return (

Drag items here to add {msg}.

); } /** * Render the Component */ render() { let dimensionElmts, dimensionInputElmts, dimensionLevelInputElmts, dimensionPivotInputElmts, dimensionFieldElmt, dimensionInputFieldElmt, dimensionLevelInputFieldElmt, dimensionPivotInputFieldElmt, hasDimensions = this.state.dimensions.length > 0, hasDimensionInputs = this.state.dimensionInputs.length > 0, hasDimensionLevelInputs = this.state.dimensionLevelInputs.length > 0, hasDimensionPivotInputs = this.state.dimensionPivotInputs.length > 0; // render dimension elements if (hasDimensions) { dimensionElmts = this.state.dimensions.map((dimension, index) => { const key = `dimension-${index}`; if (dimension.isPlaceholder) { return (
  •  
  • ); } else { return ( // @ts-ignore
  • {dimension.label}
  • ); } }); dimensionFieldElmt = ( ); } else { dimensionFieldElmt = this.getEmptyState('available dimensions'); } // render dimension element and hidden input if (hasDimensionInputs) { dimensionInputElmts = this.state.dimensionInputs.map( (dimension, index) => { const key = `dimension-input-${index}`; return ( // @ts-ignore
  • {dimension.label}
  • ); } ); dimensionInputFieldElmt = ( ); } else { dimensionInputFieldElmt = this.getEmptyState('dimensions'); } // render dimension level element and hidden input if (hasDimensionLevelInputs) { dimensionLevelInputElmts = this.state.dimensionLevelInputs.map( (dimension, index) => { const key = `dimension-level-input-${index}`; return ( // @ts-ignore
  • {dimension.label}
  • ); } ); dimensionLevelInputFieldElmt = ( ); } else { dimensionLevelInputFieldElmt = this.getEmptyState('dimension levels'); } // render dimension pivot element and hidden input if (hasDimensionPivotInputs) { dimensionPivotInputElmts = this.state.dimensionPivotInputs.map( (dimension, index) => { const key = `dimension-pivot-input-${index}`; return ( // @ts-ignore
  • {dimension.label}
  • ); } ); dimensionPivotInputFieldElmt = ( ); } else { dimensionPivotInputFieldElmt = this.getEmptyState('pivot columns'); } return (

    Available Dimensions

    {dimensionFieldElmt}

    Dimensions

    {dimensionInputFieldElmt}

    Dimension Levels

    {dimensionLevelInputFieldElmt}

    Pivot Columns

    {dimensionPivotInputFieldElmt}
    ); } }