OptionsSelector.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import FormGroup from 'react-bootstrap/es/FormGroup';
  4. import FormControl from 'react-bootstrap/es/FormControl';
  5. import ControlLabel from 'react-bootstrap/es/ControlLabel';
  6. import Button from 'react-bootstrap/es/Button';
  7. import Dropdown from 'react-bootstrap/es/Dropdown';
  8. import MenuItem from 'react-bootstrap/es/MenuItem';
  9. import OverlayTrigger from 'react-bootstrap/es/OverlayTrigger';
  10. import Tooltip from 'react-bootstrap/es/Tooltip';
  11. export default class OptionsSelector extends React.Component {
  12. constructor(props) {
  13. super(props);
  14. const config = this.props.crowi.getConfig();
  15. const isMathJaxEnabled = !!config.env.MATHJAX;
  16. this.state = {
  17. editorOptions: this.props.editorOptions || new EditorOptions(),
  18. previewOptions: this.props.previewOptions || new PreviewOptions(),
  19. isCddMenuOpened: false,
  20. isMathJaxEnabled,
  21. }
  22. this.availableThemes = [
  23. 'elegant', 'neo', 'mdn-like', 'material', 'monokai', 'twilight'
  24. ]
  25. this.onChangeTheme = this.onChangeTheme.bind(this);
  26. this.onClickStyleActiveLine = this.onClickStyleActiveLine.bind(this);
  27. this.onClickRenderMathJaxInRealtime = this.onClickRenderMathJaxInRealtime.bind(this);
  28. this.onToggleConfigurationDropdown = this.onToggleConfigurationDropdown.bind(this);
  29. }
  30. componentDidMount() {
  31. this.init();
  32. }
  33. init() {
  34. this.themeSelectorInputEl.value = this.state.editorOptions.theme;
  35. }
  36. onChangeTheme() {
  37. const newValue = this.themeSelectorInputEl.value;
  38. const newOpts = Object.assign(this.state.editorOptions, {theme: newValue});
  39. this.setState({editorOptions: newOpts});
  40. // dispatch event
  41. this.dispatchOnChange();
  42. }
  43. onClickStyleActiveLine(event) {
  44. // keep dropdown opened
  45. this._cddForceOpen = true;
  46. const newValue = !this.state.editorOptions.styleActiveLine;
  47. const newOpts = Object.assign(this.state.editorOptions, {styleActiveLine: newValue});
  48. this.setState({editorOptions: newOpts});
  49. // dispatch event
  50. this.dispatchOnChange();
  51. }
  52. onClickRenderMathJaxInRealtime(event) {
  53. // keep dropdown opened
  54. this._cddForceOpen = true;
  55. const newValue = !this.state.previewOptions.renderMathJaxInRealtime;
  56. const newOpts = Object.assign(this.state.previewOptions, {renderMathJaxInRealtime: newValue});
  57. this.setState({previewOptions: newOpts});
  58. // dispatch event
  59. this.dispatchOnChange();
  60. }
  61. /*
  62. * see: https://github.com/react-bootstrap/react-bootstrap/issues/1490#issuecomment-207445759
  63. */
  64. onToggleConfigurationDropdown(newValue) {
  65. if (this._cddForceOpen) {
  66. this.setState({ isCddMenuOpened: true });
  67. this._cddForceOpen = false;
  68. }
  69. else {
  70. this.setState({ isCddMenuOpened: newValue });
  71. }
  72. }
  73. /**
  74. * dispatch onChange event
  75. */
  76. dispatchOnChange() {
  77. if (this.props.onChange != null) {
  78. this.props.onChange(this.state.editorOptions, this.state.previewOptions);
  79. }
  80. }
  81. renderThemeSelector() {
  82. const optionElems = this.availableThemes.map((theme) => {
  83. return <option key={theme} value={theme}>{theme}</option>;
  84. });
  85. const bsClassName = 'form-control-dummy'; // set form-control* to shrink width
  86. return (
  87. <FormGroup controlId="formControlsSelect">
  88. <ControlLabel>Theme:</ControlLabel>
  89. <FormControl componentClass="select" placeholder="select" bsClass={bsClassName} className="btn-group-sm selectpicker"
  90. onChange={this.onChangeTheme}
  91. inputRef={ el => this.themeSelectorInputEl=el }>
  92. {optionElems}
  93. </FormControl>
  94. </FormGroup>
  95. )
  96. }
  97. renderConfigurationDropdown() {
  98. return (
  99. <FormGroup controlId="formControlsSelect">
  100. <Dropdown dropup id="configurationDropdown" className="configuration-dropdown"
  101. open={this.state.isCddMenuOpened} onToggle={this.onToggleConfigurationDropdown}>
  102. <Dropdown.Toggle bsSize="sm">
  103. <i className="icon-settings"></i>
  104. </Dropdown.Toggle>
  105. <Dropdown.Menu>
  106. {this.renderActiveLineMenuItem()}
  107. {this.renderRealtimeMathJaxMenuItem()}
  108. {/* <MenuItem divider /> */}
  109. </Dropdown.Menu>
  110. </Dropdown>
  111. </FormGroup>
  112. )
  113. }
  114. renderActiveLineMenuItem() {
  115. const isActive = this.state.editorOptions.styleActiveLine;
  116. const iconClasses = ['text-info']
  117. if (isActive) {
  118. iconClasses.push('ti-check')
  119. }
  120. const iconClassName = iconClasses.join(' ');
  121. return (
  122. <MenuItem onClick={this.onClickStyleActiveLine}>
  123. <span className="icon-container"></span>
  124. <span className="menuitem-label">Show active line</span>
  125. <span className="icon-container"><i className={iconClassName}></i></span>
  126. </MenuItem>
  127. )
  128. }
  129. renderRealtimeMathJaxMenuItem() {
  130. if (!this.state.isMathJaxEnabled) {
  131. return;
  132. }
  133. const isEnabled = this.state.isMathJaxEnabled;
  134. const isActive = isEnabled && this.state.previewOptions.renderMathJaxInRealtime;
  135. const iconClasses = ['text-info']
  136. if (isActive) {
  137. iconClasses.push('ti-check')
  138. }
  139. const iconClassName = iconClasses.join(' ');
  140. return (
  141. <MenuItem onClick={this.onClickRenderMathJaxInRealtime}>
  142. <span className="icon-container"><img src="/images/icons/fx.svg" width="14px"></img></span>
  143. <span className="menuitem-label">MathJax Rendering</span>
  144. <i className={iconClassName}></i>
  145. </MenuItem>
  146. )
  147. }
  148. render() {
  149. return <span>
  150. <span className="m-l-5">{this.renderThemeSelector()}</span>
  151. <span className="m-l-5">{this.renderConfigurationDropdown()}</span>
  152. </span>
  153. }
  154. }
  155. export class EditorOptions {
  156. constructor(props) {
  157. this.theme = 'elegant';
  158. this.styleActiveLine = false;
  159. Object.assign(this, props);
  160. }
  161. }
  162. export class PreviewOptions {
  163. constructor(props) {
  164. this.renderMathJaxInRealtime = false;
  165. Object.assign(this, props);
  166. }
  167. }
  168. OptionsSelector.propTypes = {
  169. crowi: PropTypes.object.isRequired,
  170. editorOptions: PropTypes.instanceOf(EditorOptions),
  171. previewOptions: PropTypes.instanceOf(PreviewOptions),
  172. onChange: PropTypes.func,
  173. };