You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractToolboxItem.tsx 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import React, { Component, ReactElement, ReactNode } from 'react';
  2. import { WithTranslation } from 'react-i18next';
  3. import { GestureResponderEvent } from 'react-native';
  4. import type { StyleType } from '../../styles/functions.any';
  5. import { TOOLTIP_POSITION } from '../../ui/constants.any';
  6. export type Styles = {
  7. /**
  8. * Style for the item's icon.
  9. */
  10. iconStyle?: StyleType;
  11. /**
  12. * Style for the item's label.
  13. */
  14. labelStyle?: StyleType;
  15. /**
  16. * Style for the item itself.
  17. */
  18. style?: StyleType;
  19. /**
  20. * Color for the item underlay (shows when clicked).
  21. */
  22. underlayColor?: string;
  23. };
  24. export interface IProps extends WithTranslation {
  25. /**
  26. * A succinct description of what the item does. Used by accessibility
  27. * tools and torture tests.
  28. */
  29. accessibilityLabel: string;
  30. /**
  31. * An extra class name to be added at the end of the element's class name
  32. * in order to enable custom styling.
  33. */
  34. customClass?: string;
  35. /**
  36. * Whether this item is disabled or not. When disabled, clicking an the item
  37. * has no effect, and it may reflect on its style.
  38. */
  39. disabled: boolean;
  40. /**
  41. * A React Element to display at the end of {@code ToolboxItem}.
  42. */
  43. elementAfter?: ReactNode;
  44. /**
  45. * The icon to render for this {@code ToolboxItem}.
  46. */
  47. icon: Function;
  48. /**
  49. * The text associated with this item. When `showLabel` is set to
  50. * {@code true}, it will be displayed alongside the icon.
  51. */
  52. label: string;
  53. labelProps: any;
  54. /**
  55. * On click handler.
  56. */
  57. onClick: (e?: React.MouseEvent<HTMLElement> | GestureResponderEvent) => void;
  58. /**
  59. * Whether to show the label or not.
  60. */
  61. showLabel: boolean;
  62. /**
  63. * Collection of styles for the item. Used only on native.
  64. */
  65. styles?: Styles;
  66. /**
  67. * True if the item is toggled, false otherwise.
  68. */
  69. toggled?: boolean;
  70. /**
  71. * The text to display in the tooltip. Used only on web.
  72. */
  73. tooltip?: string;
  74. /**
  75. * From which direction the tooltip should appear, relative to the
  76. * item. Used only on web.
  77. */
  78. tooltipPosition: TOOLTIP_POSITION;
  79. /**
  80. * Whether this item is visible or not.
  81. */
  82. visible: boolean;
  83. }
  84. /**
  85. * Abstract (base) class for an item in {@link Toolbox}. The item can be located
  86. * anywhere in the {@link Toolbox}, it will morph its shape to accommodate it.
  87. *
  88. * @abstract
  89. */
  90. export default class AbstractToolboxItem<P extends IProps> extends Component<P> {
  91. /**
  92. * Default values for {@code AbstractToolboxItem} component's properties.
  93. *
  94. * @static
  95. */
  96. static defaultProps = {
  97. disabled: false,
  98. label: '',
  99. showLabel: false,
  100. t: undefined,
  101. tooltip: '',
  102. tooltipPosition: 'top',
  103. visible: true
  104. };
  105. /**
  106. * Initializes a new {@code AbstractToolboxItem} instance.
  107. *
  108. * @param {Object} props - The React {@code Component} props to initialize
  109. * the new {@code AbstractToolboxItem} instance with.
  110. */
  111. constructor(props: P) {
  112. super(props);
  113. // Bind event handlers so they are only bound once per instance.
  114. this._onClick = this._onClick.bind(this);
  115. }
  116. /**
  117. * Helper property to get the item label. If a translation function was
  118. * provided then it will be translated using it.
  119. *
  120. * @protected
  121. * @returns {?string}
  122. */
  123. get label(): string | undefined {
  124. return this._maybeTranslateAttribute(this.props.label, this.props.labelProps);
  125. }
  126. /**
  127. * Helper property to get the item tooltip. If a translation function was
  128. * provided then it will be translated using it.
  129. *
  130. * @protected
  131. * @returns {?string}
  132. */
  133. get tooltip(): string | undefined {
  134. return this._maybeTranslateAttribute(this.props.tooltip ?? '');
  135. }
  136. /**
  137. * Helper property to get the item accessibilityLabel. If a translation
  138. * function was provided then it will be translated using it.
  139. *
  140. * @protected
  141. * @returns {?string}
  142. */
  143. get accessibilityLabel(): string {
  144. return this._maybeTranslateAttribute(this.props.accessibilityLabel);
  145. }
  146. /**
  147. * Utility function to translate the given string, if a translation
  148. * function is available.
  149. *
  150. * @param {string} text - What needs translating.
  151. * @param {string} textProps - Additional properties for translation text.
  152. * @private
  153. * @returns {string}
  154. */
  155. _maybeTranslateAttribute(text: string, textProps?: any) {
  156. const { t } = this.props;
  157. if (textProps) {
  158. return typeof t === 'function' ? t(text, textProps) : `${text} ${textProps}`;
  159. }
  160. return typeof t === 'function' ? t(text) : text;
  161. }
  162. /**
  163. * Handles clicking/pressing this {@code AbstractToolboxItem} by
  164. * forwarding the event to the {@code onClick} prop of this instance if any.
  165. *
  166. * @protected
  167. * @returns {void}
  168. */
  169. _onClick(...args: any) {
  170. const { disabled, onClick } = this.props;
  171. disabled || onClick?.(...args);
  172. }
  173. /**
  174. * Renders this {@code AbstractToolboxItem} (if it is {@code visible}). To
  175. * be implemented/overridden by extenders. The default implementation of
  176. * {@code AbstractToolboxItem} does nothing.
  177. *
  178. * @protected
  179. * @returns {ReactElement}
  180. */
  181. _renderItem(): ReactElement | null {
  182. // To be implemented by a subclass.
  183. return null;
  184. }
  185. /**
  186. * Implements React's {@link Component#render()}.
  187. *
  188. * @inheritdoc
  189. * @returns {ReactElement}
  190. */
  191. render() {
  192. return this.props.visible ? this._renderItem() : null;
  193. }
  194. }