OptionsSelector.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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"><i className={iconClassName}></i></span>
  127. Show active line
  128. </MenuItem>
  129. )
  130. }
  131. renderRealtimeMathJaxMenuItem() {
  132. if (!this.state.isMathJaxEnabled) {
  133. return;
  134. }
  135. const isEnabled = this.state.isMathJaxEnabled;
  136. const isActive = isEnabled && this.state.previewOptions.renderMathJaxInRealtime;
  137. const iconClasses = ['text-info']
  138. if (isActive) {
  139. iconClasses.push('ti-check')
  140. }
  141. const iconClassName = iconClasses.join(' ');
  142. return (
  143. <MenuItem onClick={this.onClickRenderMathJaxInRealtime}>
  144. <span className="icon-container"><i className={iconClassName}></i></span>
  145. MathJax Rendering
  146. </MenuItem>
  147. )
  148. }
  149. render() {
  150. return <span>{this.renderThemeSelector()} {this.renderConfigurationDropdown()}</span>
  151. }
  152. }
  153. export class EditorOptions {
  154. constructor(props) {
  155. this.theme = 'elegant';
  156. this.styleActiveLine = false;
  157. Object.assign(this, props);
  158. }
  159. }
  160. export class PreviewOptions {
  161. constructor(props) {
  162. this.renderMathJaxInRealtime = false;
  163. Object.assign(this, props);
  164. }
  165. }
  166. OptionsSelector.propTypes = {
  167. crowi: PropTypes.object.isRequired,
  168. editorOptions: PropTypes.instanceOf(EditorOptions),
  169. previewOptions: PropTypes.instanceOf(PreviewOptions),
  170. onChange: PropTypes.func,
  171. };