diff --git a/css/buttons/copy.scss b/css/buttons/copy.scss new file mode 100644 index 000000000..9596abcc3 --- /dev/null +++ b/css/buttons/copy.scss @@ -0,0 +1,37 @@ +.copy-button { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 8px 8px 16px; + margin-top: 8px; + width: calc(100% - 24px); + height: 24px; + + background: #0376DA; + border-radius: 4px; + cursor: pointer; + + &:hover { + background: #278ADF; + font-weight: 600; + } + + &-content { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 292px; + + &.selected { + font-weight: 600; + } + } + + &.clicked { + background: #31B76A; + } + + & > div > svg > path { + fill: #fff; + } +} \ No newline at end of file diff --git a/css/main.scss b/css/main.scss index 826fb9932..1257937f2 100644 --- a/css/main.scss +++ b/css/main.scss @@ -33,6 +33,7 @@ $flagsImagePath: "../images/"; @import 'inlay'; @import 'reload_overlay/reload_overlay'; @import 'mini_toolbox'; +@import 'buttons/copy.scss'; @import 'modals/desktop-picker/desktop-picker'; @import 'modals/device-selection/device-selection'; @import 'modals/dialog'; diff --git a/css/modals/invite/_invite_more.scss b/css/modals/invite/_invite_more.scss index 824ed7f6b..73160928a 100644 --- a/css/modals/invite/_invite_more.scss +++ b/css/modals/invite/_invite_more.scss @@ -67,44 +67,6 @@ } } - &.copy-link { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 8px 8px 16px; - margin-top: 8px; - width: calc(100% - 24px); - height: 24px; - - background: #0376DA; - border-radius: 4px; - cursor: pointer; - - &:hover { - background: #278ADF; - font-weight: 600; - } - - &-text { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 292px; - - &.selected { - font-weight: 600; - } - } - - &.clicked { - background: #31B76A; - } - - & > div > svg > path { - fill: #fff; - } - } - &.separator { margin: 24px 0 24px -20px; padding: 0 20px; diff --git a/react/features/base/buttons/CopyButton.js b/react/features/base/buttons/CopyButton.js new file mode 100644 index 000000000..a99ba7cc5 --- /dev/null +++ b/react/features/base/buttons/CopyButton.js @@ -0,0 +1,125 @@ +// @flow + +import React, { useState } from 'react'; + +import { translate } from '../../base/i18n'; +import { Icon, IconCheck, IconCopy } from '../../base/icons'; +import { copyText } from '../../base/util'; + + +type Props = { + + /** + * Css class to apply on container + */ + className: string, + + /** + * The displayed text + */ + displayedText: string, + + /** + * The text that needs to be copied (might differ from the displayedText) + */ + textToCopy: string, + + /** + * The text displayed on mouse hover + */ + textOnHover: string, + + /** + * The text displayed on copy success + */ + textOnCopySuccess: string +}; + +/** + * Component meant to enable users to copy the conference URL. + * + * @returns {React$Element} + */ +function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnCopySuccess }: Props) { + const [ isClicked, setIsClicked ] = useState(false); + const [ isHovered, setIsHovered ] = useState(false); + + /** + * Click handler for the element. + * + * @returns {void} + */ + function onClick() { + setIsHovered(false); + if (copyText(textToCopy)) { + setIsClicked(true); + + setTimeout(() => { + setIsClicked(false); + }, 2500); + } + } + + /** + * Hover handler for the element. + * + * @returns {void} + */ + function onHoverIn() { + if (!isClicked) { + setIsHovered(true); + } + } + + /** + * Hover handler for the element. + * + * @returns {void} + */ + function onHoverOut() { + setIsHovered(false); + } + + /** + * Renders the content of the link based on the state. + * + * @returns {React$Element} + */ + function renderContent() { + if (isClicked) { + return ( + <> +
+ {textOnCopySuccess} +
+ + + ); + } + + return ( + <> +
+ {isHovered ? textOnHover : displayedText} +
+ + + ); + } + + return ( +
+ { renderContent() } +
+ ); +} + +CopyButton.defaultProps = { + className: '' +}; + +export default translate(CopyButton); diff --git a/react/features/invite/components/add-people-dialog/web/CopyMeetingLinkSection.js b/react/features/invite/components/add-people-dialog/web/CopyMeetingLinkSection.js index 6dc327d80..4f07ddc0e 100644 --- a/react/features/invite/components/add-people-dialog/web/CopyMeetingLinkSection.js +++ b/react/features/invite/components/add-people-dialog/web/CopyMeetingLinkSection.js @@ -1,10 +1,10 @@ // @flow -import React, { useState } from 'react'; +import React from 'react'; +import CopyButton from '../../../../base/buttons/CopyButton'; import { translate } from '../../../../base/i18n'; -import { Icon, IconCheck, IconCopy } from '../../../../base/icons'; -import { copyText, getDecodedURI } from '../../../../base/util'; +import { getDecodedURI } from '../../../../base/util'; type Props = { @@ -26,84 +26,15 @@ type Props = { * @returns {React$Element} */ function CopyMeetingLinkSection({ t, url }: Props) { - const [ isClicked, setIsClicked ] = useState(false); - const [ isHovered, setIsHovered ] = useState(false); - - /** - * Click handler for the element. - * - * @returns {void} - */ - function onClick() { - setIsHovered(false); - if (copyText(url)) { - setIsClicked(true); - - setTimeout(() => { - setIsClicked(false); - }, 2500); - } - } - - /** - * Hover handler for the element. - * - * @returns {void} - */ - function onHoverIn() { - if (!isClicked) { - setIsHovered(true); - } - } - - /** - * Hover handler for the element. - * - * @returns {void} - */ - function onHoverOut() { - setIsHovered(false); - } - - /** - * Renders the content of the link based on the state. - * - * @returns {React$Element} - */ - function renderLinkContent() { - if (isClicked) { - return ( - <> -
- {t('addPeople.linkCopied')} -
- - - ); - } - - const displayUrl = getDecodedURI(url); - - return ( - <> -
- {isHovered ? t('addPeople.copyLink') : displayUrl} -
- - - ); - } - return ( <> {t('addPeople.shareLink')} -
- { renderLinkContent() } -
+ ); }