adds CopyToClipboard
This commit is contained in:
62
src/client/components/elements/CopyToClipboard/index.js
Normal file
62
src/client/components/elements/CopyToClipboard/index.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Copy from '../../icons/Copy';
|
||||
import Tooltip from '../Tooltip';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'copy-to-clipboard';
|
||||
|
||||
const CopyToClipboard = ({ value }) => {
|
||||
const ref = useRef(null);
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (copied && !hovered) {
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 1500);
|
||||
}
|
||||
}, [copied, hovered]);
|
||||
|
||||
return (
|
||||
<button
|
||||
onMouseEnter={() => {
|
||||
setHovered(true);
|
||||
setCopied(false);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setHovered(false);
|
||||
setCopied(false);
|
||||
}}
|
||||
type="button"
|
||||
className={baseClass}
|
||||
onClick={() => {
|
||||
if (ref && ref.current) {
|
||||
ref.current.select();
|
||||
ref.current.setSelectionRange(0, value.length + 1);
|
||||
document.execCommand('copy');
|
||||
|
||||
setCopied(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Copy />
|
||||
<Tooltip>
|
||||
{copied && 'Copied'}
|
||||
{!copied && 'Copy'}
|
||||
</Tooltip>
|
||||
<textarea
|
||||
value={value}
|
||||
ref={ref}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
CopyToClipboard.propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default CopyToClipboard;
|
||||
35
src/client/components/elements/CopyToClipboard/index.scss
Normal file
35
src/client/components/elements/CopyToClipboard/index.scss
Normal file
@@ -0,0 +1,35 @@
|
||||
@import '../../../scss/styles.scss';
|
||||
|
||||
.copy-to-clipboard {
|
||||
@extend %btn-reset;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
|
||||
textarea {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
height: 0px;
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.tooltip {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
transform: translate3d(-50%, -120%, 0);
|
||||
padding: 0 base(.4);
|
||||
color: white;
|
||||
line-height: $baseline;
|
||||
|
||||
27
src/client/components/icons/Copy/index.js
Normal file
27
src/client/components/icons/Copy/index.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const Copy = () => {
|
||||
return (
|
||||
<svg
|
||||
className="icon icon--copy"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 25 25"
|
||||
>
|
||||
<rect
|
||||
x="6.5"
|
||||
y="10"
|
||||
width="8"
|
||||
height="8"
|
||||
className="stroke"
|
||||
/>
|
||||
<path
|
||||
d="M10 9.98438V6.5H18V14.5H14"
|
||||
className="stroke"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Copy;
|
||||
12
src/client/components/icons/Copy/index.scss
Normal file
12
src/client/components/icons/Copy/index.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
@import '../../../scss/styles';
|
||||
|
||||
.icon--copy {
|
||||
height: $baseline;
|
||||
width: $baseline;
|
||||
|
||||
.stroke {
|
||||
fill: none;
|
||||
stroke: $color-dark-gray;
|
||||
stroke-width: $style-stroke-width-s;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import Form from '../../../forms/Form';
|
||||
import PreviewButton from '../../../elements/PreviewButton';
|
||||
import FormSubmit from '../../../forms/Submit';
|
||||
import RenderFields from '../../../forms/RenderFields';
|
||||
import CopyToClipboard from '../../../elements/CopyToClipboard';
|
||||
import * as fieldTypes from '../../../forms/field-types';
|
||||
|
||||
import './index.scss';
|
||||
@@ -34,6 +35,8 @@ const DefaultEditView = (props) => {
|
||||
preview,
|
||||
} = collection;
|
||||
|
||||
const apiURL = `${serverURL}${api}/${slug}/${id}`;
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<Form
|
||||
@@ -86,6 +89,22 @@ const DefaultEditView = (props) => {
|
||||
<PreviewButton generatePreviewURL={preview} />
|
||||
<FormSubmit>Save</FormSubmit>
|
||||
</div>
|
||||
{isEditing && (
|
||||
<div className={`${baseClass}__api-url`}>
|
||||
<span className={`${baseClass}__label`}>
|
||||
API URL
|
||||
{' '}
|
||||
<CopyToClipboard value={apiURL} />
|
||||
</span>
|
||||
<a
|
||||
href={apiURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{apiURL}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
<div className={`${baseClass}__sidebar-fields`}>
|
||||
<RenderFields
|
||||
filter={field => field.position === 'sidebar'}
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&__main {
|
||||
// max-width: base(20);
|
||||
}
|
||||
|
||||
&__header {
|
||||
h1 {
|
||||
word-break: break-all;
|
||||
@@ -44,7 +40,8 @@
|
||||
|
||||
&__document-actions,
|
||||
&__meta,
|
||||
&__sidebar-fields {
|
||||
&__sidebar-fields,
|
||||
&__api-url {
|
||||
padding-left: base(1.5);
|
||||
}
|
||||
|
||||
@@ -77,6 +74,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__api-url {
|
||||
margin-bottom: base(1.5);
|
||||
|
||||
a {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
&__sidebar-fields {
|
||||
margin-bottom: base(1.5);
|
||||
}
|
||||
@@ -120,7 +127,8 @@
|
||||
|
||||
&__document-actions,
|
||||
&__meta,
|
||||
&__sidebar-fields {
|
||||
&__sidebar-fields,
|
||||
&__api-url {
|
||||
padding-left: $baseline;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user