import {BPComponentProps, UiConfigRendererContextType} from "./BPComponent"; import {BPLabelledComponent, BPLabelledComponentState} from "./BPLabelledComponent"; import {equalsPrimitive, PrimitiveVal} from "uiconfig.js"; export type BPValueComponentState = BPLabelledComponentState & { value: TStateValue } export abstract class BPValueComponent, TStateValue> extends BPLabelledComponent { protected constructor(props: BPComponentProps, context: UiConfigRendererContextType, state: TState) { super(props, context, state); this.setValue = this.setValue.bind(this) } abstract convertValueToState(value: TValue, state: TState): TState; abstract convertStateToValue(state: TState): Promise; // this should be state.value ideally which is initialValue in parent component // a separate variable is made here so that it can be undefined in the beginning. private _lastValRef: TValue | undefined getUpdatedState(state: TState): TState { const val = this.context.methods.getValue(this.props.config, this._lastValRef, false) // this has to be false otherwise the children component will rerender everytime. like sliders if(this._lastValRef !== undefined && val !== undefined){ // changed from outside, so we need to update the children this.keyVersion++ } this._lastValRef = val // console.log('getUpdatedState', val); state = super.getUpdatedState(state) state = val !== undefined ? this.convertValueToState(val, state) : state return state } protected _previousLast = false // is this really required? doesNeedRefresh(state: TState, _last?: boolean): boolean { const needsRefresh = this._previousLast !== _last || !equals(this.state.value, state.value); this._previousLast = _last ?? true; // console.log('doesNeedRefresh', needsRefresh, this.state.value, state.value); return needsRefresh; } setState(state: TState, callback?: () => void) { // this.keyVersion++ // dont in getUpdatedState super.setState(state, callback); } async setValue(value: TStateValue, last?: boolean, refreshState = true) { return this.updateStateValue({...this.state, value}, last, refreshState) } forceOnChange: boolean = false async updateStateValue(state: TState, last?: boolean, refreshState = true) { if (!this.doesNeedRefresh(state, last)) { await this.setStatePromise(state) return } let val = await this.convertStateToValue(state) this._lastValRef = val await this.context.methods.setValue(this.props.config, val, {last}, this.forceOnChange) if(refreshState) await this.refreshConfigState(state) } } function equals(a: any, b: any){ return equalsPrimitive(a, b) }