OptionsSelector.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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. if (!this.state.isMathJaxEnabled) {
  116. return;
  117. }
  118. const isActive = this.state.editorOptions.styleActiveLine;
  119. const iconClasses = ['text-info']
  120. if (isActive) {
  121. iconClasses.push('ti-check')
  122. }
  123. const iconClassName = iconClasses.join(' ');
  124. return (
  125. <MenuItem onClick={this.onClickStyleActiveLine}>
  126. <span className="icon-container"></span>
  127. <span className="menuitem-label">Show active line</span>
  128. <i className={iconClassName}></i>
  129. </MenuItem>
  130. )
  131. }
  132. renderRealtimeMathJaxMenuItem() {
  133. if (!this.state.isMathJaxEnabled) {
  134. return;
  135. }
  136. const isEnabled = this.state.isMathJaxEnabled;
  137. const isActive = isEnabled && this.state.previewOptions.renderMathJaxInRealtime;
  138. const iconClasses = ['text-info']
  139. if (isActive) {
  140. iconClasses.push('ti-check')
  141. }
  142. const iconClassName = iconClasses.join(' ');
  143. return (
  144. <MenuItem onClick={this.onClickRenderMathJaxInRealtime}>
  145. <span className="icon-container"><img src="/images/icons/fx.svg" width="14px"></img></span>
  146. <span className="menuitem-label">MathJax Rendering</span>
  147. <i className={iconClassName}></i>
  148. </MenuItem>
  149. )
  150. }
  151. render() {
  152. return <span>
  153. <span className="m-l-5">{this.renderThemeSelector()}</span>
  154. <span className="m-l-5">{this.renderConfigurationDropdown()}</span>
  155. </span>
  156. }
  157. }
  158. export class EditorOptions {
  159. constructor(props) {
  160. this.theme = 'elegant';
  161. this.styleActiveLine = false;
  162. Object.assign(this, props);
  163. }
  164. }
  165. export class PreviewOptions {
  166. constructor(props) {
  167. this.renderMathJaxInRealtime = false;
  168. Object.assign(this, props);
  169. }
  170. }
  171. OptionsSelector.propTypes = {
  172. crowi: PropTypes.object.isRequired,
  173. editorOptions: PropTypes.instanceOf(EditorOptions),
  174. previewOptions: PropTypes.instanceOf(PreviewOptions),
  175. onChange: PropTypes.func,
  176. };