import React from "react"; import BaseWidget, { WidgetProps } from "./BaseWidget"; import _ from "lodash"; import { EditorContext } from "../components/editorComponents/EditorContextProvider"; import { clearEvalPropertyCache } from "sagas/EvaluationsSaga"; import { ExecuteActionPayload } from "../constants/ActionConstants"; type DebouncedExecuteActionPayload = Omit< ExecuteActionPayload, "dynamicString" > & { dynamicString?: string; }; export interface WithMeta { updateWidgetMetaProperty: ( propertyName: string, propertyValue: any, actionExecution?: DebouncedExecuteActionPayload, ) => void; } const withMeta = (WrappedWidget: typeof BaseWidget) => { return class MetaHOC extends React.Component { static contextType = EditorContext; updatedProperties = new Map(); propertyTriggers = new Map(); debouncedHandleUpdateWidgetMetaProperty = _.debounce( this.handleUpdateWidgetMetaProperty.bind(this), 200, { leading: true, trailing: true, }, ); constructor(props: any) { super(props); const metaProperties = WrappedWidget.getMetaPropertiesMap(); this.state = _.fromPairs( Object.keys(metaProperties).map((metaProperty) => { return [metaProperty, this.props[metaProperty]]; }), ); } componentDidUpdate(prevProps: WidgetProps) { const metaProperties = WrappedWidget.getMetaPropertiesMap(); const defaultProperties = WrappedWidget.getDefaultPropertiesMap(); Object.keys(metaProperties).forEach((metaProperty) => { const defaultProperty = defaultProperties[metaProperty]; if ( !_.isEqual(prevProps[metaProperty], this.props[metaProperty]) && _.isEqual(this.props[defaultProperty], this.props[metaProperty]) ) { this.setState({ [metaProperty]: this.props[metaProperty] }); } }); } updateWidgetMetaProperty = ( propertyName: string, propertyValue: any, actionExecution?: DebouncedExecuteActionPayload, ): void => { this.updatedProperties.set(propertyName, true); if (actionExecution) { this.propertyTriggers.set(propertyName, actionExecution); } this.setState( { [propertyName]: propertyValue, }, () => { this.debouncedHandleUpdateWidgetMetaProperty(); }, ); }; handleUpdateWidgetMetaProperty() { const { updateWidgetMetaProperty, executeAction } = this.context; const { widgetId, widgetName } = this.props; // We have kept a map of all updated properties. After debouncing we will // go through these properties and update with the final value. This way // we will only update a certain property once per debounce interval. // Then we will execute any action associated with the trigger of // that value changing [...this.updatedProperties.keys()].forEach((propertyName) => { if (updateWidgetMetaProperty) { const propertyValue = this.state[propertyName]; clearEvalPropertyCache(`${widgetName}.${propertyName}`); updateWidgetMetaProperty(widgetId, propertyName, propertyValue); this.updatedProperties.delete(propertyName); } const debouncedPayload = this.propertyTriggers.get(propertyName); if ( debouncedPayload && debouncedPayload.dynamicString && executeAction ) { executeAction(debouncedPayload); this.propertyTriggers.delete(propertyName); } }); } updatedProps = () => { return { ...this.props, ...this.state, updateWidgetMetaProperty: this.updateWidgetMetaProperty, }; }; render() { return ; } }; }; export default withMeta;