color.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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: false }
  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. onChangeSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
  25. const value = Color(parseInt(e.target.value));
  26. if (value !== this.props.value) {
  27. this.update(value);
  28. }
  29. }
  30. onClickSwatch = (e: React.MouseEvent<HTMLButtonElement>) => {
  31. const value = Color(+(e.currentTarget.getAttribute('data-color') || '0'));
  32. if (value !== this.props.value) {
  33. this.update(value);
  34. }
  35. }
  36. onChangeText = (value: Color) => {
  37. if (value !== this.props.value) {
  38. this.update(value);
  39. }
  40. }
  41. swatch() {
  42. // const def = this.props.param.defaultValue;
  43. return <div className='msp-combined-color-swatch'>
  44. {/* <button title='Default Color' key={def} className='msp-form-control msp-btn' data-color={def} onClick={this.onClickSwatch} style={{ background: Color.toStyle(def) }}></button> */}
  45. {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>)}
  46. </div>;
  47. }
  48. stripStyle(): React.CSSProperties {
  49. return {
  50. background: Color.toStyle(this.props.value),
  51. position: 'absolute',
  52. bottom: '0',
  53. height: '4px',
  54. right: '0',
  55. left: '0'
  56. };
  57. }
  58. render() {
  59. const label = this.props.param.label || camelCaseToWords(this.props.name);
  60. return <>
  61. <div className='msp-control-row'>
  62. <span title={this.props.param.description}>{label}</span>
  63. <div>
  64. <button onClick={this.toggleExpanded} className='msp-combined-color-button' style={{ background: Color.toStyle(this.props.value) }}></button>
  65. </div>
  66. </div>
  67. {this.state.isExpanded && <div className='msp-control-offset'>
  68. {this.swatch()}
  69. <div className='msp-control-row'>
  70. <span>RGB</span>
  71. <div>
  72. <TextInput onChange={this.onChangeText} value={this.props.value}
  73. fromValue={formatColorRGB} toValue={getColorFromString} isValid={isValidColorString}
  74. className='msp-form-control' onEnter={this.props.onEnter} blurOnEnter={true} blurOnEscape={true}
  75. placeholder='e.g. 127 127 127' delayMs={250} />
  76. </div>
  77. </div>
  78. </div>}
  79. </>;
  80. }
  81. }
  82. function formatColorRGB(c: Color) {
  83. const [r, g, b] = Color.toRgb(c);
  84. return `${r} ${g} ${b}`;
  85. }
  86. function getColorFromString(s: string) {
  87. const cs = s.split(/\s+/g);
  88. return Color.fromRgb(+cs[0], +cs[1], +cs[2]);
  89. }
  90. function isValidColorString(s: string) {
  91. const cs = s.split(/\s+/g);
  92. if (cs.length !== 3 && !(cs.length === 4 && cs[3] === '')) return false;
  93. for (const c of cs) {
  94. if (c === '') continue;
  95. const n = +c;
  96. if ('' + n !== c) return false;
  97. if (n < 0 || n > 255) return false;
  98. }
  99. return true;
  100. }
  101. let _colors: React.ReactFragment | undefined = void 0;
  102. export function ColorOptions() {
  103. if (_colors) return _colors;
  104. _colors = <>{DefaultColorSwatch.map(v =>
  105. <option key={v[1]} value={v[1]} style={{ background: `${Color.toStyle(v[1])}` }} >
  106. {stringToWords(v[0])}
  107. </option>
  108. )}</>;
  109. return _colors;
  110. }
  111. const DefaultColorSwatchMap = (function () {
  112. const map = new Map<Color, string>()
  113. for (const v of DefaultColorSwatch) map.set(v[1], v[0])
  114. return map
  115. })()
  116. export function ColorValueOption(color: Color) {
  117. return !DefaultColorSwatchMap.has(color) ? <option key={Color.toHexString(color)} value={color} style={{ background: `${Color.toStyle(color)}` }} >
  118. {Color.toRgbString(color)}
  119. </option> : null
  120. }