CopyDropdown.jsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import React, {
  2. useState, useMemo, useCallback,
  3. } from 'react';
  4. import { pagePathUtils } from '@growi/core';
  5. import PropTypes from 'prop-types';
  6. import { CopyToClipboard } from 'react-copy-to-clipboard';
  7. import { useTranslation } from 'react-i18next';
  8. import {
  9. Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
  10. Tooltip,
  11. } from 'reactstrap';
  12. const { encodeSpaces } = pagePathUtils;
  13. /* eslint-disable react/prop-types */
  14. const DropdownItemContents = ({ title, contents }) => (
  15. <>
  16. <div className="h6 mt-1 mb-2"><strong>{title}</strong></div>
  17. <div className="card well mb-1 p-2">{contents}</div>
  18. </>
  19. );
  20. /* eslint-enable react/prop-types */
  21. const CopyDropdown = (props) => {
  22. const [dropdownOpen, setDropdownOpen] = useState(false);
  23. const [tooltipOpen, setTooltipOpen] = useState(false);
  24. const [isParamsAppended, setParamsAppended] = useState(!props.isShareLinkMode);
  25. /*
  26. * functions to construct labels and URLs
  27. */
  28. const getUriParams = useCallback(() => {
  29. if (!isParamsAppended || !dropdownOpen) {
  30. return '';
  31. }
  32. const {
  33. search, hash,
  34. } = window.location;
  35. return `${search}${hash}`;
  36. }, [isParamsAppended, dropdownOpen]);
  37. const pagePathWithParams = useMemo(() => {
  38. const { pagePath } = props;
  39. return decodeURI(`${pagePath}${getUriParams()}`);
  40. }, [props, getUriParams]);
  41. const pagePathUrl = useMemo(() => {
  42. const { origin } = window.location;
  43. return `${origin}${encodeSpaces(pagePathWithParams)}`;
  44. }, [pagePathWithParams]);
  45. const permalink = useMemo(() => {
  46. const { origin } = window.location;
  47. const { pageId, isShareLinkMode } = props;
  48. if (pageId == null) {
  49. return null;
  50. }
  51. if (isShareLinkMode) {
  52. return decodeURI(`${origin}/share/${pageId}`);
  53. }
  54. return encodeSpaces(decodeURI(`${origin}/${pageId}${getUriParams()}`));
  55. }, [props, getUriParams]);
  56. const markdownLink = useMemo(() => {
  57. const { pagePath } = props;
  58. const label = decodeURI(`${pagePath}${getUriParams()}`);
  59. // const permalink = generatePermalink();
  60. return `[${label}](${permalink})`;
  61. }, [props, getUriParams, permalink]);
  62. /**
  63. * control
  64. */
  65. const toggleDropdown = useCallback(() => {
  66. setDropdownOpen(!dropdownOpen);
  67. }, [dropdownOpen]);
  68. const toggleAppendParams = useCallback(() => {
  69. setParamsAppended(!isParamsAppended);
  70. }, [isParamsAppended]);
  71. const showToolTip = useCallback(() => {
  72. setTooltipOpen(true);
  73. setTimeout(() => {
  74. setTooltipOpen(false);
  75. }, 1000);
  76. }, []);
  77. /*
  78. * render
  79. */
  80. const { t } = useTranslation();
  81. const {
  82. dropdownToggleId, pageId, dropdownToggleClassName, children, isShareLinkMode,
  83. } = props;
  84. const customSwitchForParamsId = `customSwitchForParams_${dropdownToggleId}`;
  85. return (
  86. <>
  87. <Dropdown className="grw-copy-dropdown" isOpen={dropdownOpen} toggle={toggleDropdown}>
  88. <DropdownToggle
  89. caret
  90. className={dropdownToggleClassName}
  91. >
  92. <span id={dropdownToggleId}>{children}</span>
  93. </DropdownToggle>
  94. <DropdownMenu positionFixed modifiers={{ preventOverflow: { boundariesElement: undefined } }}>
  95. <div className="d-flex align-items-center justify-content-between">
  96. <DropdownItem header className="px-3">
  97. { t('copy_to_clipboard.Copy to clipboard') }
  98. </DropdownItem>
  99. { !isShareLinkMode && (
  100. <div className="px-3 custom-control custom-switch custom-switch-sm">
  101. <input
  102. type="checkbox"
  103. id={customSwitchForParamsId}
  104. className="custom-control-input"
  105. checked={isParamsAppended}
  106. onChange={toggleAppendParams}
  107. />
  108. <label className="custom-control-label small" htmlFor={customSwitchForParamsId}>Append params</label>
  109. </div>
  110. ) }
  111. </div>
  112. <DropdownItem divider className="my-0"></DropdownItem>
  113. {/* Page path */}
  114. <CopyToClipboard text={pagePathWithParams} onCopy={showToolTip}>
  115. <DropdownItem className="px-3">
  116. <DropdownItemContents title={t('copy_to_clipboard.Page path')} contents={pagePathWithParams} />
  117. </DropdownItem>
  118. </CopyToClipboard>
  119. <DropdownItem divider className="my-0"></DropdownItem>
  120. {/* Page path URL */}
  121. <CopyToClipboard text={pagePathUrl} onCopy={showToolTip}>
  122. <DropdownItem className="px-3">
  123. <DropdownItemContents title={t('copy_to_clipboard.Page URL')} contents={pagePathUrl} />
  124. </DropdownItem>
  125. </CopyToClipboard>
  126. <DropdownItem divider className="my-0"></DropdownItem>
  127. {/* Permanent Link */}
  128. { pageId && (
  129. <CopyToClipboard text={permalink} onCopy={showToolTip}>
  130. <DropdownItem className="px-3">
  131. <DropdownItemContents title={t('copy_to_clipboard.Permanent link')} contents={permalink} />
  132. </DropdownItem>
  133. </CopyToClipboard>
  134. )}
  135. <DropdownItem divider className="my-0"></DropdownItem>
  136. {/* Page path + Permanent Link */}
  137. { pageId && (
  138. <CopyToClipboard text={`${pagePathWithParams}\n${permalink}`} onCopy={showToolTip}>
  139. <DropdownItem className="px-3">
  140. <DropdownItemContents title={t('copy_to_clipboard.Page path and permanent link')} contents={<>{pagePathWithParams}<br />{permalink}</>} />
  141. </DropdownItem>
  142. </CopyToClipboard>
  143. )}
  144. <DropdownItem divider className="my-0"></DropdownItem>
  145. {/* Markdown Link */}
  146. { pageId && (
  147. <CopyToClipboard text={markdownLink} onCopy={showToolTip}>
  148. <DropdownItem className="px-3 text-wrap">
  149. <DropdownItemContents title={t('copy_to_clipboard.Markdown link')} contents={markdownLink} isContentsWrap />
  150. </DropdownItem>
  151. </CopyToClipboard>
  152. )}
  153. </DropdownMenu>
  154. </Dropdown>
  155. <Tooltip placement="bottom" isOpen={tooltipOpen} target={dropdownToggleId} fade={false}>
  156. copied!
  157. </Tooltip>
  158. </>
  159. );
  160. };
  161. CopyDropdown.propTypes = {
  162. children: PropTypes.node.isRequired,
  163. dropdownToggleId: PropTypes.string.isRequired,
  164. pagePath: PropTypes.string.isRequired,
  165. pageId: PropTypes.string,
  166. dropdownToggleClassName: PropTypes.string,
  167. isShareLinkMode: PropTypes.bool,
  168. };
  169. export default CopyDropdown;