import {createRef} from "react"; import {BPComponentProps, UiConfigRendererContextType} from "./BPComponent"; import {Button, Icon, Popover} from "@blueprintjs/core"; import {BPValueComponent, BPValueComponentState} from "./BPValueComponent"; import {HexColorPicker} from "react-colorful"; import {FormGroupComponent} from "../components/FormGroupComponent"; import {getOrCall} from 'ts-browser-helpers' import type {Color, ColorRepresentation} from "three" import {InputGroup2} from "../lib"; export type BPColorComponentState = BPValueComponentState & { mode: 'number' | 'string' | 'Color', lastValue?: ColorRepresentation, srgbConvert: boolean } export class BPColorInputComponent extends BPValueComponent { private _inputRef = createRef() private _tempColor: Color constructor(props: BPComponentProps, context: UiConfigRendererContextType) { super(props, context, { label: 'Color', value: '#000000', mode: 'number', srgbConvert: false, }); if(!context.THREE?.Color) throw new Error('BPColorInputComponent requires THREE.Color to be available in context') if(!this._tempColor) this._tempColor = new context.THREE.Color() } convertValueToState(val: ColorRepresentation, state: BPColorComponentState): BPColorComponentState { if(!this._tempColor) this._tempColor = new this.context.THREE!.Color() let mode: BPColorComponentState['mode'] = state.mode const srgbConvert = getOrCall(this.props.config.srgb) === true if (typeof val === 'string') mode = 'string' else if (typeof val === 'number') mode = 'number'; else if (val.isColor) mode = 'Color' else console.error('not supported color', val) this._tempColor.set(val) if (srgbConvert) this._tempColor.convertLinearToSRGB() const value = '#' + this._tempColor.getHexString().toUpperCase() // console.log('color', mode, value, val) return {...state, mode, value, lastValue: val, srgbConvert} // default config.srgb = false } async convertStateToValue(state: BPColorComponentState): Promise { if(!this._tempColor) this._tempColor = new this.context.THREE!.Color() let val: ColorRepresentation = state.lastValue || 0 this._tempColor.set(state.value) if (state.srgbConvert) this._tempColor.convertSRGBToLinear() if (state.mode === 'number') { val = this._tempColor.getHex() } else if (state.mode === 'string') { val = '#' + this._tempColor.getHexString(); } else if (state.mode === 'Color') { val = ((state.lastValue as Color) ?? new this.context.THREE!.Color()).copy(this._tempColor) } return val; } forceOnChange: boolean = true doesNeedRefresh(state: BPColorComponentState, _?: boolean): boolean { const eq = !super.doesNeedRefresh(state); return ((state.value as any as Color)?.isColor && eq) ? true : !eq } private _mouseButtons = 0 componentDidMount() { super.componentDidMount(); document.addEventListener('mousemove', this._mouseMove) } private _mouseMove = (ev: MouseEvent) => { this._mouseButtons = ev.buttons if (!this._mouseButtons && this._hasMouseUp) this._mouseUp() } componentWillUnmount() { document.removeEventListener('mousemove', this._mouseMove) this._mouseButtons = 0 super.componentWillUnmount(); } private _hasMouseUp = false private _mouseUp = () => { document.removeEventListener('mouseup', this._mouseMove) if (!this._hasMouseUp) return this._hasMouseUp = false if(this._inputRef.current) { this.setValue(this._inputRef.current.value, true) } if (this._hasMouseUp) return document.addEventListener('mouseup', this._mouseUp) } async refreshConfigState(state?: BPColorComponentState): Promise { await super.refreshConfigState(state); if(this._inputRef.current) this._inputRef.current.value = this.state.value } protected flexBasis = "100%" render() { return !this.state.hidden ? ( { if(this.state.readOnly || this.state.disabled) return this._tempColor.set(c) const v = '#' + this._tempColor.getHexString().toUpperCase() this.setValue(v, false) this._inputRef.current!.value = v if (this._hasMouseUp) return this._hasMouseUp = true document.addEventListener('mouseup', this._mouseUp) } }/> } >