autoSelectText.tsx 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import {CSSProperties} from 'react';
  2. import * as React from 'react';
  3. import classNames from 'classnames';
  4. import {isRenderFunc} from 'app/utils/isRenderFunc';
  5. import {selectText} from 'app/utils/selectText';
  6. type ChildRenderProps = {
  7. doSelect: () => void;
  8. doMount: (el: HTMLElement) => void;
  9. };
  10. type ChildFunction = (props: ChildRenderProps) => React.ReactNode;
  11. type Props = {
  12. /**
  13. * Can be a `node` for a simple auto select div container.
  14. * When children is a render function, it is passed 2 functions:
  15. * - `doMount` - should be applied on parent element's `ref` whose
  16. * children is the text to be copied
  17. * - `doSelect` - selects text
  18. */
  19. children: React.ReactNode | ChildFunction;
  20. className?: string;
  21. style?: CSSProperties;
  22. };
  23. class AutoSelectText extends React.Component<Props> {
  24. private el: HTMLElement | undefined;
  25. selectText = () => {
  26. if (!this.el) {
  27. return;
  28. }
  29. selectText(this.el);
  30. };
  31. handleMount = (el: HTMLElement) => {
  32. this.el = el;
  33. };
  34. render() {
  35. const {children, className, ...props} = this.props;
  36. if (isRenderFunc<ChildFunction>(children)) {
  37. return children({
  38. doMount: this.handleMount,
  39. doSelect: this.selectText,
  40. });
  41. }
  42. // use an inner span here for the selection as otherwise the selectText
  43. // function will create a range that includes the entire part of the
  44. // div (including the div itself) which causes newlines to be selected
  45. // in chrome.
  46. return (
  47. <div
  48. {...props}
  49. onClick={this.selectText}
  50. className={classNames('auto-select-text', className)}
  51. >
  52. <span ref={this.handleMount}>{children}</span>
  53. </div>
  54. );
  55. }
  56. }
  57. export default AutoSelectText;