color.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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, Button, ControlRow } from './common';
  14. import { DefaultColorSwatch } from '../../mol-util/color/swatches';
  15. export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Color>, { isExpanded: boolean, lightness: number }> {
  16. state = {
  17. isExpanded: !!this.props.param.isExpanded,
  18. lightness: 0
  19. };
  20. protected update(value: Color) {
  21. this.props.onChange({ param: this.props.param, name: this.props.name, value });
  22. }
  23. toggleExpanded = (e: React.MouseEvent<HTMLButtonElement>) => {
  24. this.setState({ isExpanded: !this.state.isExpanded });
  25. e.currentTarget.blur();
  26. };
  27. onClickSwatch = (e: React.MouseEvent<HTMLButtonElement>) => {
  28. const value = Color(+(e.currentTarget.getAttribute('data-color') || '0'));
  29. if (value !== this.props.value) {
  30. if (!this.props.param.isExpanded) this.setState({ isExpanded: false });
  31. this.update(value);
  32. }
  33. };
  34. onR = (v: number) => {
  35. const [, g, b] = Color.toRgb(this.props.value);
  36. const value = Color.fromRgb(v, g, b);
  37. if (value !== this.props.value) this.update(value);
  38. };
  39. onG = (v: number) => {
  40. const [r, , b] = Color.toRgb(this.props.value);
  41. const value = Color.fromRgb(r, v, b);
  42. if (value !== this.props.value) this.update(value);
  43. };
  44. onB = (v: number) => {
  45. const [r, g] = Color.toRgb(this.props.value);
  46. const value = Color.fromRgb(r, g, v);
  47. if (value !== this.props.value) this.update(value);
  48. };
  49. onLighten = () => {
  50. this.update(Color.lighten(this.props.value, 0.1));
  51. };
  52. onDarken = () => {
  53. this.update(Color.darken(this.props.value, 0.1));
  54. };
  55. swatch() {
  56. return <div className='msp-combined-color-swatch'>
  57. {DefaultColorSwatch.map(c => <Button key={c[1]} inline data-color={c[1]} onClick={this.onClickSwatch} style={{ background: Color.toStyle(c[1]) }} />)}
  58. </div>;
  59. }
  60. render() {
  61. const label = this.props.param.label || camelCaseToWords(this.props.name);
  62. const [r, g, b] = Color.toRgb(this.props.value);
  63. return <>
  64. <ControlRow title={this.props.param.description}
  65. label={label}
  66. control={<Button onClick={this.toggleExpanded} inline className='msp-combined-color-button' style={{ background: Color.toStyle(this.props.value) }} />} />
  67. {this.state.isExpanded && <div className='msp-control-offset'>
  68. {this.swatch()}
  69. <ControlRow label='RGB' className='msp-control-label-short' control={<div style={{ display: 'flex', textAlignLast: 'center', left: '80px' }}>
  70. <TextInput onChange={this.onR} numeric value={r} delayMs={250} style={{ order: 1, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control' onEnter={this.props.onEnter} blurOnEnter={true} blurOnEscape={true} />
  71. <TextInput onChange={this.onG} numeric value={g} delayMs={250} style={{ order: 2, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control' onEnter={this.props.onEnter} blurOnEnter={true} blurOnEscape={true} />
  72. <TextInput onChange={this.onB} numeric value={b} delayMs={250} style={{ order: 3, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control' onEnter={this.props.onEnter} blurOnEnter={true} blurOnEscape={true} />
  73. </div>}/>
  74. <div style={{ display: 'flex', textAlignLast: 'center' }}>
  75. <Button onClick={this.onLighten} style={{ order: 1, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control'>Lighten</Button>
  76. <Button onClick={this.onDarken} style={{ order: 1, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control'>Darken</Button>
  77. </div>
  78. </div>}
  79. </>;
  80. }
  81. }
  82. let _colors: any = void 0;
  83. export function ColorOptions() {
  84. if (_colors) return _colors;
  85. _colors = <>{DefaultColorSwatch.map(v =>
  86. <option key={v[1]} value={v[1]} style={{ background: `${Color.toStyle(v[1])}` }} >
  87. {stringToWords(v[0])}
  88. </option>
  89. )}</>;
  90. return _colors;
  91. }
  92. const DefaultColorSwatchMap = (function () {
  93. const map = new Map<Color, string>();
  94. for (const v of DefaultColorSwatch) map.set(v[1], v[0]);
  95. return map;
  96. })();
  97. export function ColorValueOption(color: Color) {
  98. return !DefaultColorSwatchMap.has(color) ? <option key={Color.toHexString(color)} value={color} style={{ background: `${Color.toStyle(color)}` }} >
  99. {Color.toRgbString(color)}
  100. </option> : null;
  101. }