import * as React from 'react'; /** * Component modes */ export const COMPONENT_MODE_INITIAL = 'initial'; export const COMPONENT_MODE_LOADING = 'loading'; export const COMPONENT_MODE_ERROR = 'error'; export const COMPONENT_MODE_EMPTY = 'empty'; export const COMPONENT_MODE_OK = 'ok'; export const COMPONENT_MODE_CUSTOM = 'custom'; export interface iSetToStore { (path: any, newState: any): void; } export interface iMergeToStore { (path: any, newState: any): void; } export interface iSetComponentValue { (name: string, key: string | Array, value: any): void; } export interface Props { state?: any; setComponentValue?: iSetComponentValue; } export interface State {} /** * React Component Base class with loadingImage and helper methods to store/extract data from either Redux store. * @deprecated Use MmuiCommonComponent */ export class CommonComponent

extends React.Component< P, S > { name; loadingImage; constructor(props: P) { super(props); // Note: need to pass staticUrl when initiating the store for each case // @ts-ignore this.loadingImage = this.props.staticUrl + 'images/mm-loading.gif'; } getState() { // only get this component's state.component, defined by the name attribute, which needs to be unique // @ts-ignore return this.props.state.component[this.name]; } // get particular state.data getData(key) { // @ts-ignore return this.props.state.data[key]; } getFilterFormObj() { // @ts-ignore return this.props.state.filterObj; } setMode(mode) { // like 'loading'=> spinning image, 'error' 'ok' 'empty', constant defined at the top // use `bindActionCreators` to bind action function `setComponentValue` to props // @ts-ignore this.props.setComponentValue(this.name, 'mode', mode); } } /** * React Chart component with common chart member variable. */ export class ChartComponent

extends CommonComponent< P, S > { chart; chartId; chartSelector; constructor(props: P) { super(props); } } /** * React Table Component with empty row and error row render helper methods. * @deprecated Use MmuiTableComponent */ export class TableComponent

extends CommonComponent< P, S > { constructor(props: P) { super(props); } createEmptyRow(cols) { return ( No data available ); } createErrorRow(cols) { return ( Unfortunately an error occurred. ); } } export interface MmuiProps { elmtId?: string; // html element id of the Component name?: string; // if useStore is true, this indicates where the state is read/written to/from the store store?: any; // Redux store setToStore?: iSetToStore; // function to set objects into the store mergeToStore?: iMergeToStore; // function to merge values into the store useStore?: boolean; // should the Component use its local state or the injected Redux store payload?: any; // data payload to power the content render of the Component // initializeState: if true, the Component State will be initialized with the result returned by getInitialComponentState, // override the getInitialComponentState in your class that inherits from MmuiCommonComponent initializeState?: boolean; staticUrl?: string; // Url to static resources } export interface MmuiState { mode?: any; // component mode constant i.e. COMPONENT_MODE_* payload?: any; // data payload to power the content render of the Component } /** * React Component Base class with loadingImage and helper methods to store/extract data from either Redux store or local state. */ export class MmuiCommonComponent< P extends MmuiProps, S extends MmuiState > extends React.Component { protected id; protected static id_counter = 0; name: string; loadingImage: string; // flag to determine if you want to use the Components local state or Redux store injected via the props useStore: boolean; /** * Generate a unique id for this component instance */ protected getId(): number { if (this.id === undefined) { this.id = MmuiCommonComponent.id_counter; MmuiCommonComponent.id_counter++; } return this.id; } constructor(props) { super(props); this.name = props.name; this.useStore = false; if (this.props.useStore) { this.useStore = true; } if (this.props.staticUrl) { this.loadingImage = this.props.staticUrl + 'images/mm-loading.gif'; } else if (this.props.store && this.props.store.staticUrl) { this.loadingImage = this.props.store.staticUrl + 'images/mm-loading.gif'; } if (this.props.initializeState) { const state = this.getInitialComponentState(); this.setComponentState(state); } } /** * A method to return the Component's initial state. * Will be called in constructor if this.props.initializeState is true. * Override in a child class that inherits from this class. * @return this component's initial state. */ getInitialComponentState(): any { return {}; } /** * Get the Component's state * @param fromStore - overrides the useStore property */ getComponentState(fromStore = false) { if (fromStore || this.useStore) { return this.props.store.component[this.name]; } return this.state; } /** * Get the Component's state * @param fromStore - overrides the useStore property * * @deprecated Use getComponentState instead */ getState(fromStore = false) { return this.getComponentState(fromStore); } /** * Set a value into the Component's state * @param newState - new component state * @param toStore - overrides the useStore property */ setComponentState(newState: any, toStore = false) { // newState.updatedAt = new Date(); if (toStore || this.useStore) { const path = ['component', this.name]; this.props.setToStore(path, newState); } else { // TODO: Do not set the state diurectly. // eslint-disable-next-line react/no-direct-mutation-state this.state = newState; } } /** * Set a value into the Component's state * @param updateState - component state update * @param toStore - overrides the useStore property */ mergeComponentState(mergeState: any, toStore = false) { // newState.updatedAt = new Date(); if (toStore || this.useStore) { const path = ['component', this.name]; this.props.mergeToStore(path, mergeState); } else { this.setState(mergeState); } } /** * Set a value into the Component's state * @param key - key to set value at * @param value - value to set * @param toStore - overrides the useStore property */ setComponentStateValue(key: string, value: any, toStore = false) { const newState: any = {}; newState[key] = value; newState.updatedAt = new Date(); if (toStore || this.useStore) { const path = ['component', this.name]; this.props.mergeToStore(path, newState); } else { this.setState(newState); } } /** * Set a value into the Component's state * @param key - key to set value at * @param value - value to set * @param toStore - overrides the useStore property * * @deprecated Use setComponentStateValue instead */ setStateValue(key: string, value: string, toStore = false) { this.setComponentStateValue(key, value, toStore); } /** * Set a new mode value into the Component's state. * @param mode - component mode constant i.e. COMPONENT_MODE_* * @param toStore - overrides the useStore property */ setOperation(operation: string, toStore = false) { this.setComponentStateValue('operation', operation, toStore); } /** * Get payload data from state or store. * @param fromStore - overrides the useStore property * @param dKey - optional key to index into the Redux store's data object */ getPayloadData(fromStore = false, dKey?: string) { let payload, dataKey = this.name; if (dKey) { dataKey = dKey; } if (fromStore || this.useStore) { return this.props.store.data[dataKey]; } payload = this.props.payload; if (payload === undefined || payload === null) { payload = this.state.payload; } return payload; } } /** * React Table Component with empty row and error row render helper methods. */ export class MmuiTableComponent< P extends MmuiProps, S extends MmuiState > extends MmuiCommonComponent { constructor(props: MmuiProps) { super(props); } createEmptyRow(cols) { return ( No data available ); } createErrorRow(cols) { return ( Unfortunately an error occurred. ); } } /** * React Component to Overlay a parent Component with a mode indicator. * make sure when initiating the ComponentModeWindow instance, provide attributes: mode, hasEmptyMode, hasErrorMode, loadingGraphicUrl */ export interface ComponentModeWindowProps { mode?: any; hasEmptyMode?: any; hasErrorMode?: any; loadingGraphicUrl?: any; reportIssueUrl?: string; customIconClass?: string; customMessage?: string; } /** * React Component to Overlay MmuiChartComponent and MmuiCraftedTableComponent to output a html message when mode is not OK (loading, error, empty) */ export class ComponentModeWindow extends React.Component< ComponentModeWindowProps, State > { constructor(props: ComponentModeWindowProps) { super(props); } render() { let content = null; let reportIssueUrlContent = null; if (this.props.reportIssueUrl) { reportIssueUrlContent = (

Report Issue
); } if (this.props.mode === COMPONENT_MODE_CUSTOM) { content = (

{this.props.customMessage}

); } else if (this.props.mode === COMPONENT_MODE_LOADING) { content = (
{'Loading'} Loading...
); } else if ( this.props.hasEmptyMode && this.props.mode === COMPONENT_MODE_EMPTY ) { content = (

No data available based on your selections

Try modifying any conflicting selections or reducing selections.

); } else if ( this.props.hasErrorMode && this.props.mode === COMPONENT_MODE_ERROR ) { content = (

Something went wrong on our end

Try again with some changes.

{reportIssueUrlContent}
); } return content; } }