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.

CopyButton.js 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // @flow
  2. /* eslint-disable react/jsx-no-bind */
  3. import React, { useState } from 'react';
  4. import { Icon, IconCheck, IconCopy } from '../../base/icons';
  5. import { copyText } from '../util';
  6. type Props = {
  7. /**
  8. * Css class to apply on container.
  9. */
  10. className: string,
  11. /**
  12. * The displayed text.
  13. */
  14. displayedText: string,
  15. /**
  16. * The text that needs to be copied (might differ from the displayedText).
  17. */
  18. textToCopy: string,
  19. /**
  20. * The text displayed on mouse hover.
  21. */
  22. textOnHover: string,
  23. /**
  24. * The text displayed on copy success.
  25. */
  26. textOnCopySuccess: string,
  27. /**
  28. * The id of the button.
  29. */
  30. id?: string,
  31. };
  32. /**
  33. * Component meant to enable users to copy the conference URL.
  34. *
  35. * @returns {React$Element<any>}
  36. */
  37. function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnCopySuccess, id }: Props) {
  38. const [ isClicked, setIsClicked ] = useState(false);
  39. const [ isHovered, setIsHovered ] = useState(false);
  40. /**
  41. * Click handler for the element.
  42. *
  43. * @returns {void}
  44. */
  45. async function onClick() {
  46. setIsHovered(false);
  47. const isCopied = await copyText(textToCopy);
  48. if (isCopied) {
  49. setIsClicked(true);
  50. setTimeout(() => {
  51. setIsClicked(false);
  52. }, 2500);
  53. }
  54. }
  55. /**
  56. * Hover handler for the element.
  57. *
  58. * @returns {void}
  59. */
  60. function onHoverIn() {
  61. if (!isClicked) {
  62. setIsHovered(true);
  63. }
  64. }
  65. /**
  66. * Hover handler for the element.
  67. *
  68. * @returns {void}
  69. */
  70. function onHoverOut() {
  71. setIsHovered(false);
  72. }
  73. /**
  74. * KeyPress handler for accessibility.
  75. *
  76. * @param {React.KeyboardEventHandler<HTMLDivElement>} e - The key event to handle.
  77. *
  78. * @returns {void}
  79. */
  80. function onKeyPress(e) {
  81. if (onClick && (e.key === ' ' || e.key === 'Enter')) {
  82. e.preventDefault();
  83. onClick();
  84. }
  85. }
  86. /**
  87. * Renders the content of the link based on the state.
  88. *
  89. * @returns {React$Element<any>}
  90. */
  91. function renderContent() {
  92. if (isClicked) {
  93. return (
  94. <>
  95. <div className = 'copy-button-content selected'>
  96. <span role = { 'alert' }>{ textOnCopySuccess }</span>
  97. </div>
  98. <Icon src = { IconCheck } />
  99. </>
  100. );
  101. }
  102. return (
  103. <>
  104. <div className = 'copy-button-content'>
  105. {isHovered ? textOnHover : displayedText}
  106. </div>
  107. <Icon src = { IconCopy } />
  108. </>
  109. );
  110. }
  111. return (
  112. <div
  113. aria-label = { textOnHover }
  114. className = { `${className} copy-button${isClicked ? ' clicked' : ''}` }
  115. id = { id }
  116. onBlur = { onHoverOut }
  117. onClick = { onClick }
  118. onFocus = { onHoverIn }
  119. onKeyPress = { onKeyPress }
  120. onMouseOut = { onHoverOut }
  121. onMouseOver = { onHoverIn }
  122. role = 'button'
  123. tabIndex = { 0 }>
  124. { renderContent() }
  125. </div>
  126. );
  127. }
  128. CopyButton.defaultProps = {
  129. className: ''
  130. };
  131. export default CopyButton;