color.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { Color } from '../../mol-util/color';
  8. import { ParamDefinition as PD } from '../../mol-util/param-definition';
  9. import { camelCaseToWords, stringToWords } from '../../mol-util/string';
  10. import * as React from 'react';
  11. import { _Props, _State } from '../base';
  12. import { ParamProps } from './parameters';
  13. import { TextInput } from './common';
  14. import { DefaultColorSwatch } from '../../mol-util/color/swatches';
  15. export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Color>, { isExpanded: boolean }> {
  16. state = { isExpanded: !!this.props.param.isExpanded }
  17. protected update(value: Color) {
  18. this.props.onChange({ param: this.props.param, name: this.props.name, value });
  19. }
  20. toggleExpanded = (e: React.MouseEvent<HTMLButtonElement>) => {
  21. this.setState({ isExpanded: !this.state.isExpanded });
  22. e.currentTarget.blur();
  23. }
  24. onClickSwatch = (e: React.MouseEvent<HTMLButtonElement>) => {
  25. const value = Color(+(e.currentTarget.getAttribute('data-color') || '0'));
  26. if (value !== this.props.value) {
  27. if (!this.props.param.isExpanded) this.setState({ isExpanded: false });
  28. this.update(value);
  29. }
  30. }
  31. onChangeText = (value: Color) => {
  32. if (value !== this.props.value) {
  33. this.update(value);
  34. }
  35. }
  36. swatch() {
  37. // const def = this.props.param.defaultValue;
  38. return <div className='msp-combined-color-swatch'>
  39. {/* <button title='Default Color' key={def} className='msp-form-control msp-btn' data-color={def} onClick={this.onClickSwatch} style={{ background: Color.toStyle(def) }}></button> */}
  40. {DefaultColorSwatch.map(c => <button key={c[1]} className='msp-form-control msp-btn' data-color={c[1]} onClick={this.onClickSwatch} style={{ background: Color.toStyle(c[1]) }}></button>)}
  41. </div>;
  42. }
  43. render() {
  44. const label = this.props.param.label || camelCaseToWords(this.props.name);
  45. return <>
  46. <div className='msp-control-row'>
  47. <span title={this.props.param.description}>{label}</span>
  48. <div>
  49. <button onClick={this.toggleExpanded} className='msp-combined-color-button' style={{ background: Color.toStyle(this.props.value) }}></button>
  50. </div>
  51. </div>
  52. {this.state.isExpanded && <div className='msp-control-offset'>
  53. {this.swatch()}
  54. <div className='msp-control-row'>
  55. <span>RGB</span>
  56. <div>
  57. <TextInput onChange={this.onChangeText} value={this.props.value}
  58. fromValue={formatColorRGB} toValue={getColorFromString} isValid={isValidColorString}
  59. className='msp-form-control' onEnter={this.props.onEnter} blurOnEnter={true} blurOnEscape={true}
  60. placeholder='e.g. 127 127 127' delayMs={250} />
  61. </div>
  62. </div>
  63. </div>}
  64. </>;
  65. }
  66. }
  67. function formatColorRGB(c: Color) {
  68. const [r, g, b] = Color.toRgb(c);
  69. return `${r} ${g} ${b}`;
  70. }
  71. function getColorFromString(s: string) {
  72. const cs = s.split(/\s+/g);
  73. return Color.fromRgb(+cs[0], +cs[1], +cs[2]);
  74. }
  75. function isValidColorString(s: string) {
  76. const cs = s.split(/\s+/g);
  77. if (cs.length !== 3 && !(cs.length === 4 && cs[3] === '')) return false;
  78. for (const c of cs) {
  79. if (c === '') continue;
  80. const n = +c;
  81. if ('' + n !== c) return false;
  82. if (n < 0 || n > 255) return false;
  83. }
  84. return true;
  85. }
  86. let _colors: React.ReactFragment | undefined = void 0;
  87. export function ColorOptions() {
  88. if (_colors) return _colors;
  89. _colors = <>{DefaultColorSwatch.map(v =>
  90. <option key={v[1]} value={v[1]} style={{ background: `${Color.toStyle(v[1])}` }} >
  91. {stringToWords(v[0])}
  92. </option>
  93. )}</>;
  94. return _colors;
  95. }
  96. const DefaultColorSwatchMap = (function () {
  97. const map = new Map<Color, string>()
  98. for (const v of DefaultColorSwatch) map.set(v[1], v[0])
  99. return map
  100. })()
  101. export function ColorValueOption(color: Color) {
  102. return !DefaultColorSwatchMap.has(color) ? <option key={Color.toHexString(color)} value={color} style={{ background: `${Color.toStyle(color)}` }} >
  103. {Color.toRgbString(color)}
  104. </option> : null
  105. }