選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

AbstractButton.js 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. // @flow
  2. import React, { Component } from 'react';
  3. import { NOTIFY_CLICK_MODE } from '../../../toolbox/constants';
  4. import { combineStyles } from '../../styles';
  5. import type { Styles } from './AbstractToolboxItem';
  6. import ToolboxItem from './ToolboxItem';
  7. export type Props = {|
  8. /**
  9. * Function to be called after the click handler has been processed.
  10. */
  11. afterClick: ?Function,
  12. /**
  13. * The button's key.
  14. */
  15. buttonKey?: string,
  16. /**
  17. * An extra class name to be added at the end of the element's class name
  18. * in order to enable custom styling.
  19. */
  20. customClass?: string,
  21. /**
  22. * Extra styles which will be applied in conjunction with `styles` or
  23. * `toggledStyles` when the button is disabled;.
  24. */
  25. disabledStyles: ?Styles,
  26. /**
  27. * External handler for click action.
  28. */
  29. handleClick?: Function,
  30. /**
  31. * Notify mode for `toolbarButtonClicked` event -
  32. * whether to only notify or to also prevent button click routine.
  33. */
  34. notifyMode?: string,
  35. /**
  36. * Whether to show the label or not.
  37. */
  38. showLabel: boolean,
  39. /**
  40. * Collection of styles for the button.
  41. */
  42. styles: ?Styles,
  43. /**
  44. * Collection of styles for the button, when in toggled state.
  45. */
  46. toggledStyles: ?Styles,
  47. /**
  48. * From which direction the tooltip should appear, relative to the button.
  49. */
  50. tooltipPosition: string,
  51. /**
  52. * Whether this button is visible or not.
  53. */
  54. visible: boolean
  55. |};
  56. declare var APP: Object;
  57. /**
  58. * Default style for disabled buttons.
  59. */
  60. export const defaultDisabledButtonStyles = {
  61. iconStyle: {
  62. opacity: 0.5
  63. },
  64. labelStyle: {
  65. opacity: 0.5
  66. },
  67. style: undefined,
  68. underlayColor: undefined
  69. };
  70. /**
  71. * An abstract implementation of a button.
  72. */
  73. export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
  74. static defaultProps = {
  75. afterClick: undefined,
  76. disabledStyles: defaultDisabledButtonStyles,
  77. showLabel: false,
  78. styles: undefined,
  79. toggledStyles: undefined,
  80. tooltipPosition: 'top',
  81. visible: true
  82. };
  83. /**
  84. * A succinct description of what the button does. Used by accessibility
  85. * tools and torture tests.
  86. *
  87. * @abstract
  88. */
  89. accessibilityLabel: string;
  90. /**
  91. * The icon of this button.
  92. *
  93. * @abstract
  94. */
  95. icon: Object;
  96. /**
  97. * The text associated with this button. When `showLabel` is set to
  98. * {@code true}, it will be displayed alongside the icon.
  99. *
  100. * @abstract
  101. */
  102. label: string;
  103. /**
  104. * The label for this button, when toggled.
  105. */
  106. toggledLabel: string;
  107. /**
  108. * The icon of this button, when toggled.
  109. *
  110. * @abstract
  111. */
  112. toggledIcon: Object;
  113. /**
  114. * The text to display in the tooltip. Used only on web.
  115. *
  116. * @abstract
  117. */
  118. tooltip: ?string;
  119. /**
  120. * Initializes a new {@code AbstractButton} instance.
  121. *
  122. * @param {Props} props - The React {@code Component} props to initialize
  123. * the new {@code AbstractButton} instance with.
  124. */
  125. constructor(props: P) {
  126. super(props);
  127. // Bind event handlers so they are only bound once per instance.
  128. this._onClick = this._onClick.bind(this);
  129. }
  130. /**
  131. * Helper function to be implemented by subclasses, which should be used
  132. * to handle a key being down.
  133. *
  134. * @protected
  135. * @returns {void}
  136. */
  137. _onKeyDown() {
  138. // To be implemented by subclass.
  139. }
  140. /**
  141. * Helper function to be implemented by subclasses, which should be used
  142. * to handle the button being clicked / pressed.
  143. *
  144. * @protected
  145. * @returns {void}
  146. */
  147. _handleClick() {
  148. // To be implemented by subclass.
  149. }
  150. /**
  151. * Helper function to be implemented by subclasses, which may return a
  152. * new React Element to be appended at the end of the button.
  153. *
  154. * @protected
  155. * @returns {ReactElement|null}
  156. */
  157. _getElementAfter() {
  158. return null;
  159. }
  160. /**
  161. * Gets the current icon, taking the toggled state into account. If no
  162. * toggled icon is provided, the regular icon will also be used in the
  163. * toggled state.
  164. *
  165. * @private
  166. * @returns {string}
  167. */
  168. _getIcon() {
  169. return (
  170. this._isToggled() ? this.toggledIcon : this.icon
  171. ) || this.icon;
  172. }
  173. /**
  174. * Gets the current label, taking the toggled state into account. If no
  175. * toggled label is provided, the regular label will also be used in the
  176. * toggled state.
  177. *
  178. * @private
  179. * @returns {string}
  180. */
  181. _getLabel() {
  182. return (this._isToggled() ? this.toggledLabel : this.label)
  183. || this.label;
  184. }
  185. /**
  186. * Gets the current styles, taking the toggled state into account. If no
  187. * toggled styles are provided, the regular styles will also be used in the
  188. * toggled state.
  189. *
  190. * @private
  191. * @returns {?Styles}
  192. */
  193. _getStyles(): ?Styles {
  194. const { disabledStyles, styles, toggledStyles } = this.props;
  195. const buttonStyles
  196. = (this._isToggled() ? toggledStyles : styles) || styles;
  197. if (this._isDisabled() && buttonStyles && disabledStyles) {
  198. return {
  199. iconStyle: combineStyles(
  200. buttonStyles.iconStyle, disabledStyles.iconStyle),
  201. labelStyle: combineStyles(
  202. buttonStyles.labelStyle, disabledStyles.labelStyle),
  203. style: combineStyles(
  204. buttonStyles.style, disabledStyles.style),
  205. underlayColor:
  206. disabledStyles.underlayColor || buttonStyles.underlayColor
  207. };
  208. }
  209. return buttonStyles;
  210. }
  211. /**
  212. * Get the tooltip to display when hovering over the button.
  213. *
  214. * @private
  215. * @returns {string}
  216. */
  217. _getTooltip() {
  218. return this.tooltip || '';
  219. }
  220. /**
  221. * Helper function to be implemented by subclasses, which must return a
  222. * boolean value indicating if this button is disabled or not.
  223. *
  224. * @protected
  225. * @returns {boolean}
  226. */
  227. _isDisabled() {
  228. return false;
  229. }
  230. /**
  231. * Helper function to be implemented by subclasses, which must return a
  232. * {@code boolean} value indicating if this button is toggled or not or
  233. * undefined if the button is not toggleable.
  234. *
  235. * @protected
  236. * @returns {?boolean}
  237. */
  238. _isToggled() {
  239. return undefined;
  240. }
  241. _onClick: (*) => void;
  242. /**
  243. * Handles clicking / pressing the button.
  244. *
  245. * @param {Object} e - Event.
  246. * @private
  247. * @returns {void}
  248. */
  249. _onClick(e) {
  250. const { afterClick, handleClick, notifyMode, buttonKey } = this.props;
  251. if (typeof APP !== 'undefined' && notifyMode) {
  252. APP.API.notifyToolbarButtonClicked(
  253. buttonKey, notifyMode === NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY
  254. );
  255. }
  256. if (notifyMode !== NOTIFY_CLICK_MODE.PREVENT_AND_NOTIFY) {
  257. if (handleClick) {
  258. handleClick();
  259. }
  260. this._handleClick();
  261. }
  262. afterClick && afterClick(e);
  263. // blur after click to release focus from button to allow PTT.
  264. e?.currentTarget?.blur && e.currentTarget.blur();
  265. }
  266. /**
  267. * Implements React's {@link Component#render()}.
  268. *
  269. * @inheritdoc
  270. * @returns {React$Node}
  271. */
  272. render(): React$Node {
  273. const props = {
  274. ...this.props,
  275. accessibilityLabel: this.accessibilityLabel,
  276. elementAfter: this._getElementAfter(),
  277. icon: this._getIcon(),
  278. label: this._getLabel(),
  279. styles: this._getStyles(),
  280. toggled: this._isToggled(),
  281. tooltip: this._getTooltip()
  282. };
  283. return (
  284. <ToolboxItem
  285. disabled = { this._isDisabled() }
  286. onClick = { this._onClick }
  287. onKeyDown = { this._onKeyDown }
  288. { ...props } />
  289. );
  290. }
  291. }