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

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