Added 'share button group' style + example on /styleguide (#3352)
* added 'share button group' style + example on /styleguide * turned share-button-group into a React component * removed redundant code * fixed linting errors * code improvement * Update source/js/components/share-button-group/share-button-group.jsx Co-Authored-By: Pomax <pomax@nihongoresources.com> * Update source/js/components/share-button-group/share-button-group.jsx Co-Authored-By: Pomax <pomax@nihongoresources.com> * Update source/js/components/share-button-group/share-button-group.jsx Co-Authored-By: Pomax <pomax@nihongoresources.com> * from <hr /> to <hr> * js/jsx improvements * code & language improvement * code improvement * added tooltip to 'copy link' button * updated tooltip text
This commit is contained in:
Родитель
cbf1dcedfc
Коммит
9308847bba
|
@ -307,6 +307,38 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="h1-heading mt-5">Share Button Group</h2>
|
||||
<hr class="hr-emphasis">
|
||||
|
||||
<h3 class="h2-heading mt-5">Full version</h3>
|
||||
<hr>
|
||||
|
||||
<div class="mb-5">
|
||||
<div class="share-button-group-wrapper"></div>
|
||||
</div>
|
||||
|
||||
<h3 class="h2-heading mt-5">Full Version & Stacked</h3>
|
||||
<hr>
|
||||
|
||||
<div class="mb-5">
|
||||
<div class="share-button-group-wrapper" data-layout="stacked"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<h3 class="h2-heading mt-5">Mini Version</h3>
|
||||
<hr>
|
||||
|
||||
<div class="mb-5">
|
||||
<div class="share-button-group-wrapper" data-version="mini"></div>
|
||||
</div>
|
||||
|
||||
<h3 class="h2-heading mt-5">Mini Version & Stacked</h3>
|
||||
<hr>
|
||||
|
||||
<div class="mb-5">
|
||||
<div class="share-button-group-wrapper" data-version="mini" data-layout="stacked"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path d="M13.605 3.287L9.252 7.86a1.75 1.75 0 0 1-2.54-.004l-4.277-4.53a.59.59 0 0 0-.041-.038c.06-.012.122-.017.185-.017h10.847c.06 0 .12.005.179.016zm.81.84c.007.047.01.095.01.144v7.333c0 .05-.003.098-.01.146l-3.717-3.717 3.717-3.906zm-.812 8.461c-.057.01-.117.016-.177.016H2.579c-.062 0-.123-.006-.182-.017l3.705-3.705a2.917 2.917 0 0 0 3.781-.014l3.72 3.72zM1.59 11.746a1.009 1.009 0 0 1-.01-.142V4.27c0-.048.003-.095.01-.14l3.699 3.916-3.699 3.699z" fill="#FFF" fill-rule="evenodd"/></svg>
|
После Ширина: | Высота: | Размер: 557 B |
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="a" d="M0 0h14v13.915H0z"/></defs><g transform="translate(1 1)" fill="#fff" fill-rule="evenodd"><mask id="b"><use xlink:href="#a"/></mask><path d="M14 7a7 7 0 1 0-8.094 6.915V9.023H4.13V7h1.777V5.458c0-1.755 1.045-2.724 2.644-2.724.766 0 1.567.137 1.567.137v1.723h-.882c-.87 0-1.141.54-1.141 1.093V7h1.941l-.31 2.023H8.094v4.892A7.002 7.002 0 0 0 14 7" mask="url(#b)"/></g></svg>
|
После Ширина: | Высота: | Размер: 500 B |
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g stroke="#FFF" stroke-width="2" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4l-8 8M6 12L2.005 8.005"/></g></svg>
|
После Ширина: | Высота: | Размер: 225 B |
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g stroke="#ffffff" stroke-width="1.5" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"><path d="M9.201 8.598a3 3 0 0 1-4.523.324l-1.799-1.8a3 3 0 0 1 4.24-4.24l1.033 1.025"/><path d="M6.802 7.399a3 3 0 0 1 4.523-.324l1.8 1.8a3 3 0 0 1-4.242 4.24l-1.025-1.026"/></g></svg>
|
После Ширина: | Высота: | Размер: 366 B |
|
@ -0,0 +1 @@
|
|||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path d="M13.353 5.896c0-.12 0-.24-.008-.359a5.683 5.683 0 0 0 1.408-1.438 5.689 5.689 0 0 1-1.62.437A2.799 2.799 0 0 0 14.372 3a5.71 5.71 0 0 1-1.792.674 2.856 2.856 0 0 0-3.992-.12 2.754 2.754 0 0 0-.817 2.654 8.057 8.057 0 0 1-5.815-2.902 2.754 2.754 0 0 0 .874 3.708 2.834 2.834 0 0 1-1.28-.348v.035c0 1.323.947 2.461 2.263 2.723a2.85 2.85 0 0 1-1.274.048 2.82 2.82 0 0 0 2.636 1.93A5.718 5.718 0 0 1 1 12.551a8.082 8.082 0 0 0 4.325 1.246c5.19.002 8.028-4.23 8.028-7.902z" fill="#ffffff" fill-rule="evenodd"/></svg>
|
После Ширина: | Высота: | Размер: 584 B |
|
@ -0,0 +1,162 @@
|
|||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import copyToClipboard from "../../copy-to-clipboard";
|
||||
|
||||
export default class ShareButtonGroup extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
linkCopied: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.whenLoaded) {
|
||||
this.props.whenLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
renderFacebookButton() {
|
||||
let label =
|
||||
this.props.version === `mini` ? (
|
||||
<span class="sr-only">Share on Facebook</span>
|
||||
) : (
|
||||
`Faceook`
|
||||
);
|
||||
|
||||
let link = this.props.link || ``;
|
||||
|
||||
return (
|
||||
<a
|
||||
class="btn btn-secondary btn-share facebook-share"
|
||||
href={`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(
|
||||
`https://${link}`
|
||||
)}`}
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
renderTwitterButton() {
|
||||
let shareText = this.props.shareText || ``;
|
||||
let label =
|
||||
this.props.version === `mini` ? (
|
||||
<span class="sr-only">Share on Twitter</span>
|
||||
) : (
|
||||
`Twitter`
|
||||
);
|
||||
|
||||
return (
|
||||
<a
|
||||
class="btn btn-secondary btn-share twitter-share"
|
||||
href={`https://twitter.com/intent/tweet?text=${encodeURIComponent(
|
||||
shareText
|
||||
)}`}
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
renderEmailButton() {
|
||||
let shareText = this.props.shareText || ``;
|
||||
let label =
|
||||
this.props.version === `mini` ? (
|
||||
<span class="sr-only">Share by email</span>
|
||||
) : (
|
||||
`Email`
|
||||
);
|
||||
|
||||
return (
|
||||
<a
|
||||
class="btn btn-secondary btn-share email-share"
|
||||
href={`mailto:?&body=${encodeURIComponent(shareText)}`}
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
handleLinkButtonClick(event) {
|
||||
event.preventDefault();
|
||||
|
||||
copyToClipboard(event.target, window.location.href);
|
||||
this.setState({
|
||||
linkCopied: true
|
||||
});
|
||||
}
|
||||
|
||||
renderLinkButton() {
|
||||
let tooltip = this.state.linkCopied
|
||||
? `Copied`
|
||||
: `Copy page URL to clipboard`;
|
||||
let label = this.state.linkCopied ? `Copied` : `Copy`;
|
||||
label =
|
||||
this.props.version === `mini` ? (
|
||||
<span class="sr-only">{label} page link</span>
|
||||
) : (
|
||||
label
|
||||
);
|
||||
|
||||
let classes = classNames(`btn btn-secondary btn-share link-share`, {
|
||||
copied: this.state.linkCopied
|
||||
});
|
||||
|
||||
return (
|
||||
<a
|
||||
class={classes}
|
||||
href="#"
|
||||
onClick={event => this.handleLinkButtonClick(event)}
|
||||
title={tooltip}
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
renderRectangleButtons() {
|
||||
let classes = classNames(`share-button-group rectangle`, {
|
||||
stacked: this.props.layout === `stacked`
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className="subgroup">
|
||||
{this.renderFacebookButton()}
|
||||
{this.renderTwitterButton()}
|
||||
</div>
|
||||
<div className="subgroup">
|
||||
{this.renderEmailButton()}
|
||||
{this.renderLinkButton()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderCircleButtons() {
|
||||
let classes = classNames(`share-button-group circle`, {
|
||||
stacked: this.props.layout === `stacked`
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
{this.renderFacebookButton()}
|
||||
{this.renderTwitterButton()}
|
||||
{this.renderEmailButton()}
|
||||
{this.renderLinkButton()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.props.version === `mini`
|
||||
? this.renderCircleButtons()
|
||||
: this.renderRectangleButtons()}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
.share-button-group {
|
||||
@mixin stacked-rectangle() {
|
||||
flex-direction: column;
|
||||
|
||||
.subgroup:not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.subgroup {
|
||||
flex: 0;
|
||||
}
|
||||
}
|
||||
|
||||
display: flex;
|
||||
|
||||
&.rectangle {
|
||||
@media (max-width: $bp-md) {
|
||||
@include stacked-rectangle();
|
||||
}
|
||||
|
||||
&.stacked {
|
||||
@include stacked-rectangle();
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 130px;
|
||||
|
||||
&::before {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.circle {
|
||||
&.stacked {
|
||||
flex-direction: column;
|
||||
|
||||
.btn:not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
padding: 0;
|
||||
|
||||
&::before {
|
||||
transform: translate(50%, 50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-right: 16px;
|
||||
|
||||
@include hover-focus-active {
|
||||
&::before {
|
||||
filter: brightness(1);
|
||||
}
|
||||
}
|
||||
|
||||
&::before {
|
||||
filter: brightness(0);
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: no-repeat 0 0 / contain;
|
||||
transition: filter 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
&.facebook-share {
|
||||
&::before {
|
||||
background-image: url(../_images/glyphs/social-share/facebook-share.svg);
|
||||
}
|
||||
}
|
||||
|
||||
&.twitter-share {
|
||||
&::before {
|
||||
background-image: url(../_images/glyphs/social-share/twitter-share.svg);
|
||||
}
|
||||
}
|
||||
|
||||
&.email-share {
|
||||
&::before {
|
||||
background-image: url(../_images/glyphs/social-share/email-share.svg);
|
||||
}
|
||||
}
|
||||
|
||||
&.link-share {
|
||||
&::before {
|
||||
background-image: url(../_images/glyphs/social-share/link-share.svg);
|
||||
}
|
||||
|
||||
&.copied {
|
||||
&::before {
|
||||
background-image: url(../_images/glyphs/social-share/link-share-copied.svg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
const copyToClipboard = (linkElement, textToCopy) => {
|
||||
let textArea = document.createElement(`textarea`);
|
||||
|
||||
textArea.setAttribute(`contenteditable`, true);
|
||||
textArea.setAttribute(`readonly`, false);
|
||||
|
||||
//
|
||||
// *** This styling is an extra step which is likely not required. ***
|
||||
//
|
||||
// Why is it here? To ensure:
|
||||
// 1. the element is able to have focus and selection.
|
||||
// 2. if element was to flash render it has minimal visual impact.
|
||||
// 3. less flakyness with selection and copying which **might** occur if
|
||||
// the textarea element is not visible.
|
||||
//
|
||||
// The likelihood is the element won't even render, not even a flash,
|
||||
// so some of these are just precautions. However in IE the element
|
||||
// is visible whilst the popup box asking the user for permission for
|
||||
// the web page to copy to the clipboard.
|
||||
//
|
||||
|
||||
// Place in top-left corner of screen regardless of scroll position.
|
||||
textArea.style.position = `fixed`;
|
||||
textArea.style.top = 0;
|
||||
textArea.style.left = 0;
|
||||
|
||||
// Ensure it has a small width and height. Setting to 1px / 1em
|
||||
// doesn't work as this gives a negative w/h on some browsers.
|
||||
textArea.style.width = `2em`;
|
||||
textArea.style.height = `2em`;
|
||||
|
||||
// We don't need padding, reducing the size if it does flash render.
|
||||
textArea.style.padding = 0;
|
||||
|
||||
// Clean up any borders.
|
||||
textArea.style.border = `none`;
|
||||
textArea.style.outline = `none`;
|
||||
textArea.style.boxShadow = `none`;
|
||||
|
||||
// Avoid flash of white box if rendered for any reason.
|
||||
textArea.style.background = `transparent`;
|
||||
|
||||
textArea.value = textToCopy;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
|
||||
// Simply running textArea.select() and document.execCommand(`copy`) won't work on iOS Safari
|
||||
// Below is the suggested solution to make copying and pasting working more cross-platform
|
||||
// For details see https://stackoverflow.com/a/34046084
|
||||
let range = document.createRange();
|
||||
let selection = window.getSelection();
|
||||
|
||||
range.selectNodeContents(textArea);
|
||||
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
|
||||
textArea.setSelectionRange(0, textArea.value.length);
|
||||
|
||||
try {
|
||||
document.execCommand(`copy`);
|
||||
|
||||
let target = linkElement;
|
||||
|
||||
if (target.dataset && target.dataset.successText) {
|
||||
let defaultText = target.innerText;
|
||||
|
||||
target.innerText = target.dataset.successText;
|
||||
target.classList.add(`copied`);
|
||||
|
||||
setTimeout(() => {
|
||||
target.innerText = defaultText;
|
||||
target.classList.remove(`copied`);
|
||||
}, 3000);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Copy failed.`);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
};
|
||||
|
||||
export default copyToClipboard;
|
|
@ -11,6 +11,7 @@ import MemberNotice from "./components/member-notice/member-notice.jsx";
|
|||
import MultipageNavMobile from "./components/multipage-nav-mobile/multipage-nav-mobile.jsx";
|
||||
import News from "./components/news/news.jsx";
|
||||
import PulseProjectList from "./components/pulse-project-list/pulse-project-list.jsx";
|
||||
import ShareButtonGroup from "./components/share-button-group/share-button-group.jsx";
|
||||
|
||||
import injectDonateModal from "./donate-modal/donate-modal.jsx";
|
||||
|
||||
|
@ -351,6 +352,25 @@ let main = {
|
|||
);
|
||||
});
|
||||
|
||||
// Share button group
|
||||
let shareButtonGroups = document.querySelectorAll(
|
||||
`.share-button-group-wrapper`
|
||||
);
|
||||
if (shareButtonGroups) {
|
||||
shareButtonGroups.forEach(element => {
|
||||
var props = element.dataset;
|
||||
|
||||
apps.push(
|
||||
new Promise(resolve => {
|
||||
ReactDOM.render(
|
||||
<ShareButtonGroup {...props} whenLoaded={() => resolve()} />,
|
||||
element
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
//Profile Directory Filter-Bar GA
|
||||
|
||||
const filters = document.querySelectorAll(
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
}
|
||||
|
||||
// append external link glyph
|
||||
.btn-secondary[href*="//"]:not([href*="foundation.mozilla.org"]) {
|
||||
.btn-secondary[href*="//"]:not([href*="foundation.mozilla.org"]):not(.btn-share) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ $bp-xl: #{map-get($grid-breakpoints, xl)}; // >= 1200px
|
|||
@import "../js/components/pulse-project-list/pulse-project-list";
|
||||
@import "../js/components/fellow-list/directory-filter-bar";
|
||||
@import "../js/components/petition/petition";
|
||||
@import "../js/components/share-button-group/share-button-group";
|
||||
|
||||
// Special react component
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче