|
@@ -1,92 +1,70 @@
|
|
|
-import React from 'react';
|
|
|
|
|
|
|
+import React, {
|
|
|
|
|
+ useState, useMemo, useCallback,
|
|
|
|
|
+} from 'react';
|
|
|
import PropTypes from 'prop-types';
|
|
import PropTypes from 'prop-types';
|
|
|
|
|
|
|
|
import { withTranslation } from 'react-i18next';
|
|
import { withTranslation } from 'react-i18next';
|
|
|
|
|
|
|
|
import {
|
|
import {
|
|
|
- UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem,
|
|
|
|
|
|
|
+ Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
|
|
|
Tooltip,
|
|
Tooltip,
|
|
|
} from 'reactstrap';
|
|
} from 'reactstrap';
|
|
|
|
|
|
|
|
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
|
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
|
|
|
|
|
|
|
-class CopyDropdown extends React.Component {
|
|
|
|
|
-
|
|
|
|
|
- constructor(props) {
|
|
|
|
|
- super(props);
|
|
|
|
|
|
|
+function encodeSpaces(str) {
|
|
|
|
|
+ if (str == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- this.state = {
|
|
|
|
|
- tooltipOpen: false,
|
|
|
|
|
- isParamsAppended: true,
|
|
|
|
|
- pagePathWithParams: '',
|
|
|
|
|
- pagePathUrl: '',
|
|
|
|
|
- permalink: '',
|
|
|
|
|
- markdownLink: '',
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ // Encode SPACE and IDEOGRAPHIC SPACE
|
|
|
|
|
+ return str.replace(/ /g, '%20').replace(/\u3000/g, '%E3%80%80');
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- this.id = (Math.random() * 1000).toString();
|
|
|
|
|
|
|
|
|
|
- this.showToolTip = this.showToolTip.bind(this);
|
|
|
|
|
- this.generateItemContents = this.generateItemContents.bind(this);
|
|
|
|
|
- this.generatePagePathWithParams = this.generatePagePathWithParams.bind(this);
|
|
|
|
|
- this.generatePagePathUrl = this.generatePagePathUrl.bind(this);
|
|
|
|
|
- this.generatePermalink = this.generatePermalink.bind(this);
|
|
|
|
|
- this.generateMarkdownLink = this.generateMarkdownLink.bind(this);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+/* eslint-disable react/prop-types */
|
|
|
|
|
+const DropdownItemContents = ({ title, contents }) => (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <div className="h6 mt-1 mb-2"><strong>{title}</strong></div>
|
|
|
|
|
+ <div className="card well mb-1 p-2">{contents}</div>
|
|
|
|
|
+ </>
|
|
|
|
|
+);
|
|
|
|
|
+/* eslint-enable react/prop-types */
|
|
|
|
|
|
|
|
- showToolTip() {
|
|
|
|
|
- this.setState({ tooltipOpen: true });
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- this.setState({ tooltipOpen: false });
|
|
|
|
|
- }, 1000);
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- get uriParams() {
|
|
|
|
|
- const { isParamsAppended } = this.state;
|
|
|
|
|
|
|
+const CopyDropdown = (props) => {
|
|
|
|
|
+ const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
|
|
|
+ const [tooltipOpen, setTooltipOpen] = useState(false);
|
|
|
|
|
+ const [isParamsAppended, setParamsAppended] = useState(!props.isShareLinkMode);
|
|
|
|
|
|
|
|
- if (!isParamsAppended) {
|
|
|
|
|
|
|
+ /*
|
|
|
|
|
+ * functions to construct labels and URLs
|
|
|
|
|
+ */
|
|
|
|
|
+ const getUriParams = useCallback(() => {
|
|
|
|
|
+ if (!isParamsAppended || !dropdownOpen) {
|
|
|
return '';
|
|
return '';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const {
|
|
const {
|
|
|
search, hash,
|
|
search, hash,
|
|
|
} = window.location;
|
|
} = window.location;
|
|
|
- return `${search}${hash}`;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- encodeSpaces(str) {
|
|
|
|
|
- if (str == null) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- // Encode SPACE and IDEOGRAPHIC SPACE
|
|
|
|
|
- return str.replace(/ /g, '%20').replace(/\u3000/g, '%E3%80%80');
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- generateItemContents() {
|
|
|
|
|
- const pagePathWithParams = this.generatePagePathWithParams();
|
|
|
|
|
- const pagePathUrl = this.generatePagePathUrl();
|
|
|
|
|
- const permalink = this.generatePermalink();
|
|
|
|
|
- const markdownLink = this.generateMarkdownLink();
|
|
|
|
|
-
|
|
|
|
|
- this.setState({
|
|
|
|
|
- pagePathWithParams, pagePathUrl, permalink, markdownLink,
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return `${search}${hash}`;
|
|
|
|
|
+ }, [isParamsAppended, dropdownOpen]);
|
|
|
|
|
|
|
|
- generatePagePathWithParams() {
|
|
|
|
|
- const { pagePath } = this.props;
|
|
|
|
|
- return decodeURI(`${pagePath}${this.uriParams}`);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const pagePathWithParams = useMemo(() => {
|
|
|
|
|
+ const { pagePath } = props;
|
|
|
|
|
+ return decodeURI(`${pagePath}${getUriParams()}`);
|
|
|
|
|
+ }, [props, getUriParams]);
|
|
|
|
|
|
|
|
- generatePagePathUrl() {
|
|
|
|
|
|
|
+ const pagePathUrl = useMemo(() => {
|
|
|
const { origin } = window.location;
|
|
const { origin } = window.location;
|
|
|
- return `${origin}${this.encodeSpaces(this.generatePagePathWithParams())}`;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return `${origin}${encodeSpaces(pagePathWithParams)}`;
|
|
|
|
|
+ }, [pagePathWithParams]);
|
|
|
|
|
|
|
|
- generatePermalink() {
|
|
|
|
|
|
|
+ const permalink = useMemo(() => {
|
|
|
const { origin } = window.location;
|
|
const { origin } = window.location;
|
|
|
- const { pageId, isShareLinkMode } = this.props;
|
|
|
|
|
|
|
+ const { pageId, isShareLinkMode } = props;
|
|
|
|
|
|
|
|
if (pageId == null) {
|
|
if (pageId == null) {
|
|
|
return null;
|
|
return null;
|
|
@@ -95,140 +73,146 @@ class CopyDropdown extends React.Component {
|
|
|
return decodeURI(`${origin}/share/${pageId}`);
|
|
return decodeURI(`${origin}/share/${pageId}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return this.encodeSpaces(decodeURI(`${origin}/${pageId}${this.uriParams}`));
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return encodeSpaces(decodeURI(`${origin}/${pageId}${getUriParams()}`));
|
|
|
|
|
+ }, [props, getUriParams]);
|
|
|
|
|
|
|
|
- generateMarkdownLink() {
|
|
|
|
|
- const { pagePath } = this.props;
|
|
|
|
|
|
|
+ const markdownLink = useMemo(() => {
|
|
|
|
|
+ const { pagePath } = props;
|
|
|
|
|
|
|
|
- const label = decodeURI(`${pagePath}${this.uriParams}`);
|
|
|
|
|
- const permalink = this.generatePermalink();
|
|
|
|
|
|
|
+ const label = decodeURI(`${pagePath}${getUriParams()}`);
|
|
|
|
|
+ // const permalink = generatePermalink();
|
|
|
|
|
|
|
|
return `[${label}](${permalink})`;
|
|
return `[${label}](${permalink})`;
|
|
|
- }
|
|
|
|
|
|
|
+ }, [props, getUriParams, permalink]);
|
|
|
|
|
|
|
|
- DropdownItemContents = ({ title, contents }) => (
|
|
|
|
|
- <>
|
|
|
|
|
- <div className="h6 mt-1 mb-2"><strong>{title}</strong></div>
|
|
|
|
|
- <div className="card well mb-1 p-2">{contents}</div>
|
|
|
|
|
- </>
|
|
|
|
|
- );
|
|
|
|
|
|
|
|
|
|
- render() {
|
|
|
|
|
- const {
|
|
|
|
|
- t, pageId, isShareLinkMode,
|
|
|
|
|
- } = this.props;
|
|
|
|
|
- const {
|
|
|
|
|
- isParamsAppended, pagePathWithParams, pagePathUrl, permalink, markdownLink,
|
|
|
|
|
- } = this.state;
|
|
|
|
|
-
|
|
|
|
|
- const copyTarget = isShareLinkMode ? `copyShareLink${pageId}` : 'copyPagePathDropdown';
|
|
|
|
|
- const dropdownToggleStyle = isShareLinkMode ? 'btn btn-secondary' : 'd-block text-muted bg-transparent btn-copy border-0';
|
|
|
|
|
-
|
|
|
|
|
- const { id, DropdownItemContents } = this;
|
|
|
|
|
-
|
|
|
|
|
- const customSwitchForParamsId = `customSwitchForParams_${id}`;
|
|
|
|
|
-
|
|
|
|
|
- return (
|
|
|
|
|
- <>
|
|
|
|
|
- <UncontrolledDropdown id={copyTarget} className="grw-copy-dropdown">
|
|
|
|
|
- <DropdownToggle
|
|
|
|
|
- caret
|
|
|
|
|
- className={dropdownToggleStyle}
|
|
|
|
|
- style={this.props.buttonStyle}
|
|
|
|
|
- onClick={this.generateItemContents}
|
|
|
|
|
- >
|
|
|
|
|
- { isShareLinkMode ? (
|
|
|
|
|
- <>Copy Link</>
|
|
|
|
|
- ) : (<i className="ti-clipboard"></i>)}
|
|
|
|
|
- </DropdownToggle>
|
|
|
|
|
-
|
|
|
|
|
- <DropdownMenu positionFixed modifiers={{ preventOverflow: { boundariesElement: null } }}>
|
|
|
|
|
-
|
|
|
|
|
- <div className="d-flex align-items-center justify-content-between">
|
|
|
|
|
- <DropdownItem header className="px-3">
|
|
|
|
|
- { t('copy_to_clipboard.Copy to clipboard') }
|
|
|
|
|
- </DropdownItem>
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * control
|
|
|
|
|
+ */
|
|
|
|
|
+ const toggleDropdown = useCallback(() => {
|
|
|
|
|
+ setDropdownOpen(!dropdownOpen);
|
|
|
|
|
+ }, [dropdownOpen]);
|
|
|
|
|
+
|
|
|
|
|
+ const toggleAppendParams = useCallback(() => {
|
|
|
|
|
+ setParamsAppended(!isParamsAppended);
|
|
|
|
|
+ }, [isParamsAppended]);
|
|
|
|
|
+
|
|
|
|
|
+ const showToolTip = useCallback(() => {
|
|
|
|
|
+ setTooltipOpen(true);
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ setTooltipOpen(false);
|
|
|
|
|
+ }, 1000);
|
|
|
|
|
+ }, []);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /*
|
|
|
|
|
+ * render
|
|
|
|
|
+ */
|
|
|
|
|
+ const {
|
|
|
|
|
+ t, dropdownToggleId, pageId, dropdownToggleClassName, children, isShareLinkMode,
|
|
|
|
|
+ } = props;
|
|
|
|
|
+
|
|
|
|
|
+ const customSwitchForParamsId = `customSwitchForParams_${dropdownToggleId}`;
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <Dropdown className="grw-copy-dropdown" isOpen={dropdownOpen} toggle={toggleDropdown}>
|
|
|
|
|
+ <DropdownToggle
|
|
|
|
|
+ caret
|
|
|
|
|
+ className={dropdownToggleClassName}
|
|
|
|
|
+ >
|
|
|
|
|
+ <span id={dropdownToggleId}>{children}</span>
|
|
|
|
|
+ </DropdownToggle>
|
|
|
|
|
+
|
|
|
|
|
+ <DropdownMenu positionFixed modifiers={{ preventOverflow: { boundariesElement: null } }}>
|
|
|
|
|
+
|
|
|
|
|
+ <div className="d-flex align-items-center justify-content-between">
|
|
|
|
|
+ <DropdownItem header className="px-3">
|
|
|
|
|
+ { t('copy_to_clipboard.Copy to clipboard') }
|
|
|
|
|
+ </DropdownItem>
|
|
|
|
|
+ { !isShareLinkMode && (
|
|
|
<div className="px-3 custom-control custom-switch custom-switch-sm">
|
|
<div className="px-3 custom-control custom-switch custom-switch-sm">
|
|
|
<input
|
|
<input
|
|
|
type="checkbox"
|
|
type="checkbox"
|
|
|
id={customSwitchForParamsId}
|
|
id={customSwitchForParamsId}
|
|
|
className="custom-control-input"
|
|
className="custom-control-input"
|
|
|
checked={isParamsAppended}
|
|
checked={isParamsAppended}
|
|
|
- onChange={e => this.setState({ isParamsAppended: !isParamsAppended })}
|
|
|
|
|
|
|
+ onChange={toggleAppendParams}
|
|
|
/>
|
|
/>
|
|
|
<label className="custom-control-label small" htmlFor={customSwitchForParamsId}>Append params</label>
|
|
<label className="custom-control-label small" htmlFor={customSwitchForParamsId}>Append params</label>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
|
+ ) }
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <DropdownItem divider className="my-0"></DropdownItem>
|
|
|
|
|
+
|
|
|
|
|
+ {/* Page path */}
|
|
|
|
|
+ <CopyToClipboard text={pagePathWithParams} onCopy={showToolTip}>
|
|
|
|
|
+ <DropdownItem className="px-3">
|
|
|
|
|
+ <DropdownItemContents title={t('copy_to_clipboard.Page path')} contents={pagePathWithParams} />
|
|
|
|
|
+ </DropdownItem>
|
|
|
|
|
+ </CopyToClipboard>
|
|
|
|
|
+
|
|
|
|
|
+ <DropdownItem divider className="my-0"></DropdownItem>
|
|
|
|
|
+
|
|
|
|
|
+ {/* Page path URL */}
|
|
|
|
|
+ <CopyToClipboard text={pagePathUrl} onCopy={showToolTip}>
|
|
|
|
|
+ <DropdownItem className="px-3">
|
|
|
|
|
+ <DropdownItemContents title={t('copy_to_clipboard.Page URL')} contents={pagePathUrl} />
|
|
|
|
|
+ </DropdownItem>
|
|
|
|
|
+ </CopyToClipboard>
|
|
|
|
|
+ <DropdownItem divider className="my-0"></DropdownItem>
|
|
|
|
|
+
|
|
|
|
|
+ {/* Permanent Link */}
|
|
|
|
|
+ { pageId && (
|
|
|
|
|
+ <CopyToClipboard text={permalink} onCopy={showToolTip}>
|
|
|
|
|
+ <DropdownItem className="px-3">
|
|
|
|
|
+ <DropdownItemContents title={t('copy_to_clipboard.Permanent link')} contents={permalink} />
|
|
|
|
|
+ </DropdownItem>
|
|
|
|
|
+ </CopyToClipboard>
|
|
|
|
|
+ )}
|
|
|
|
|
|
|
|
- <DropdownItem divider className="my-0"></DropdownItem>
|
|
|
|
|
|
|
+ <DropdownItem divider className="my-0"></DropdownItem>
|
|
|
|
|
|
|
|
- {/* Page path */}
|
|
|
|
|
- <CopyToClipboard text={pagePathWithParams} onCopy={this.showToolTip}>
|
|
|
|
|
|
|
+ {/* Page path + Permanent Link */}
|
|
|
|
|
+ { pageId && (
|
|
|
|
|
+ <CopyToClipboard text={`${pagePathWithParams}\n${permalink}`} onCopy={showToolTip}>
|
|
|
<DropdownItem className="px-3">
|
|
<DropdownItem className="px-3">
|
|
|
- <DropdownItemContents title={t('copy_to_clipboard.Page path')} contents={pagePathWithParams} />
|
|
|
|
|
|
|
+ <DropdownItemContents title={t('copy_to_clipboard.Page path and permanent link')} contents={<>{pagePathWithParams}<br />{permalink}</>} />
|
|
|
</DropdownItem>
|
|
</DropdownItem>
|
|
|
</CopyToClipboard>
|
|
</CopyToClipboard>
|
|
|
|
|
+ )}
|
|
|
|
|
|
|
|
- <DropdownItem divider className="my-0"></DropdownItem>
|
|
|
|
|
|
|
+ <DropdownItem divider className="my-0"></DropdownItem>
|
|
|
|
|
|
|
|
- {/* Page path URL */}
|
|
|
|
|
- <CopyToClipboard text={pagePathUrl} onCopy={this.showToolTip}>
|
|
|
|
|
- <DropdownItem className="px-3">
|
|
|
|
|
- <DropdownItemContents title={t('copy_to_clipboard.Page URL')} contents={pagePathUrl} />
|
|
|
|
|
|
|
+ {/* Markdown Link */}
|
|
|
|
|
+ { pageId && (
|
|
|
|
|
+ <CopyToClipboard text={markdownLink} onCopy={showToolTip}>
|
|
|
|
|
+ <DropdownItem className="px-3 text-wrap">
|
|
|
|
|
+ <DropdownItemContents title={t('copy_to_clipboard.Markdown link')} contents={markdownLink} isContentsWrap />
|
|
|
</DropdownItem>
|
|
</DropdownItem>
|
|
|
</CopyToClipboard>
|
|
</CopyToClipboard>
|
|
|
- <DropdownItem divider className="my-0"></DropdownItem>
|
|
|
|
|
-
|
|
|
|
|
- {/* Permanent Link */}
|
|
|
|
|
- { pageId && (
|
|
|
|
|
- <CopyToClipboard text={permalink} onCopy={this.showToolTip}>
|
|
|
|
|
- <DropdownItem className="px-3">
|
|
|
|
|
- <DropdownItemContents title={t('copy_to_clipboard.Permanent link')} contents={permalink} />
|
|
|
|
|
- </DropdownItem>
|
|
|
|
|
- </CopyToClipboard>
|
|
|
|
|
- )}
|
|
|
|
|
-
|
|
|
|
|
- <DropdownItem divider className="my-0"></DropdownItem>
|
|
|
|
|
-
|
|
|
|
|
- {/* Page path + Permanent Link */}
|
|
|
|
|
- { pageId && (
|
|
|
|
|
- <CopyToClipboard text={`${pagePathWithParams}\n${permalink}`} onCopy={this.showToolTip}>
|
|
|
|
|
- <DropdownItem className="px-3">
|
|
|
|
|
- <DropdownItemContents title={t('copy_to_clipboard.Page path and permanent link')} contents={<>{pagePathWithParams}<br />{permalink}</>} />
|
|
|
|
|
- </DropdownItem>
|
|
|
|
|
- </CopyToClipboard>
|
|
|
|
|
- )}
|
|
|
|
|
-
|
|
|
|
|
- <DropdownItem divider className="my-0"></DropdownItem>
|
|
|
|
|
-
|
|
|
|
|
- {/* Markdown Link */}
|
|
|
|
|
- { pageId && (
|
|
|
|
|
- <CopyToClipboard text={markdownLink} onCopy={this.showToolTip}>
|
|
|
|
|
- <DropdownItem className="px-3 text-wrap">
|
|
|
|
|
- <DropdownItemContents title={t('copy_to_clipboard.Markdown link')} contents={markdownLink} isContentsWrap />
|
|
|
|
|
- </DropdownItem>
|
|
|
|
|
- </CopyToClipboard>
|
|
|
|
|
- )}
|
|
|
|
|
- </DropdownMenu>
|
|
|
|
|
-
|
|
|
|
|
- </UncontrolledDropdown>
|
|
|
|
|
-
|
|
|
|
|
- <Tooltip placement="bottom" isOpen={this.state.tooltipOpen} target={copyTarget} fade={false}>
|
|
|
|
|
- copied!
|
|
|
|
|
- </Tooltip>
|
|
|
|
|
- </>
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ )}
|
|
|
|
|
+ </DropdownMenu>
|
|
|
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ </Dropdown>
|
|
|
|
|
+
|
|
|
|
|
+ <Tooltip placement="bottom" isOpen={tooltipOpen} target={dropdownToggleId} fade={false}>
|
|
|
|
|
+ copied!
|
|
|
|
|
+ </Tooltip>
|
|
|
|
|
+ </>
|
|
|
|
|
+ );
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
CopyDropdown.propTypes = {
|
|
CopyDropdown.propTypes = {
|
|
|
t: PropTypes.func.isRequired, // i18next
|
|
t: PropTypes.func.isRequired, // i18next
|
|
|
|
|
|
|
|
|
|
+ children: PropTypes.node.isRequired,
|
|
|
|
|
+ dropdownToggleId: PropTypes.string.isRequired,
|
|
|
pagePath: PropTypes.string.isRequired,
|
|
pagePath: PropTypes.string.isRequired,
|
|
|
|
|
+
|
|
|
pageId: PropTypes.string,
|
|
pageId: PropTypes.string,
|
|
|
- buttonStyle: PropTypes.object,
|
|
|
|
|
|
|
+ dropdownToggleClassName: PropTypes.string,
|
|
|
isShareLinkMode: PropTypes.bool,
|
|
isShareLinkMode: PropTypes.bool,
|
|
|
};
|
|
};
|
|
|
|
|
|