@thecb/components 7.0.2-beta.5 → 7.0.2-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +129 -55
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.esm.js +129 -55
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/molecules/copyable/Copyable.js +99 -42
- package/src/components/molecules/copyable/index.d.ts +6 -5
- package/src/components/molecules/popover/Popover.js +32 -2
- package/src/components/molecules/popover/index.d.ts +3 -0
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
2
|
import ButtonWithAction from "../../atoms/button-with-action";
|
|
3
3
|
import ClipboardIcon from "../../atoms/icons/ClipboardIcon";
|
|
4
4
|
import Popover from "../popover";
|
|
@@ -6,12 +6,22 @@ import Stack from "../../atoms/layouts/Stack";
|
|
|
6
6
|
import Text from "../../atoms/text";
|
|
7
7
|
|
|
8
8
|
/*
|
|
9
|
-
This component will render `
|
|
10
|
-
When
|
|
11
|
-
|
|
9
|
+
This component will render `content` and a clipboard icon.
|
|
10
|
+
When hovered, a popover with content `initialPopoverContent` will be displayed.
|
|
11
|
+
to the left of the content.
|
|
12
|
+
|
|
13
|
+
When clicked, `content` will be copied to the clipboard, the popover content
|
|
14
|
+
will change to `copySuccessPopoverContent` for `copiedPopoverContentDuration`
|
|
15
|
+
milliseconds, and the `onCopy` callback will be executed.
|
|
16
|
+
|
|
17
|
+
If the content was unable to be copied to the clipboard, the popover will instead
|
|
18
|
+
display `copyErrorPopoverContent`
|
|
19
|
+
|
|
20
|
+
If you only want the copy to clipboard behaviour without a popover, pass the prop
|
|
21
|
+
`hasPopover={false}`
|
|
12
22
|
*/
|
|
13
23
|
|
|
14
|
-
const
|
|
24
|
+
const CopyableContent = ({ content, onClick }) => {
|
|
15
25
|
return (
|
|
16
26
|
<ButtonWithAction
|
|
17
27
|
data-testid="copyable-trigger"
|
|
@@ -21,7 +31,7 @@ const CopyableText = ({ text, onClick }) => {
|
|
|
21
31
|
extraStyles="padding: 0; margin: 0; min-height: initial; min-width: initial"
|
|
22
32
|
>
|
|
23
33
|
<Stack direction="row" childGap="0.25rem">
|
|
24
|
-
<Text>{
|
|
34
|
+
<Text>{content}</Text>
|
|
25
35
|
<ClipboardIcon />
|
|
26
36
|
</Stack>
|
|
27
37
|
</ButtonWithAction>
|
|
@@ -29,56 +39,103 @@ const CopyableText = ({ text, onClick }) => {
|
|
|
29
39
|
};
|
|
30
40
|
|
|
31
41
|
const Copyable = ({
|
|
32
|
-
|
|
42
|
+
content,
|
|
33
43
|
onCopy,
|
|
34
44
|
initialPopoverContent = "Click to copy to clipboard",
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
popoverMinWidth = "210px",
|
|
45
|
+
copySuccessPopoverContent = "Copied!",
|
|
46
|
+
copyErrorPopoverContent = "Unable to copy value",
|
|
38
47
|
copiedPopoverContentDuration = 1000,
|
|
48
|
+
hasPopover = true,
|
|
49
|
+
popoverMinWidth = "210px",
|
|
39
50
|
popoverID = 0,
|
|
40
|
-
|
|
51
|
+
popoverExtraStyles
|
|
41
52
|
}) => {
|
|
42
|
-
const popoverPosition = {
|
|
43
|
-
top: "-65px",
|
|
44
|
-
right: "auto",
|
|
45
|
-
bottom: "auto",
|
|
46
|
-
left: "50%",
|
|
47
|
-
};
|
|
48
|
-
const popoverArrowPosition = {
|
|
49
|
-
arrowTop: "auto",
|
|
50
|
-
arrowRight: "auto",
|
|
51
|
-
arrowBottom: "-8px",
|
|
52
|
-
arrowLeft: "calc(50% - 4px)", // Popover arrow is 8px wide. This places the middle of the arrow in the middle of the popover.
|
|
53
|
-
};
|
|
54
53
|
const [popoverContent, setPopoverContent] = useState(initialPopoverContent);
|
|
55
|
-
const [
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (
|
|
61
|
-
|
|
54
|
+
const [startTimer, setStartTimer] = useState(false);
|
|
55
|
+
const [attemptCopy, setAttemptCopy] = useState(false);
|
|
56
|
+
const [timeoutId, setTimeoutId] = useState(null);
|
|
57
|
+
|
|
58
|
+
const cleanup = () => {
|
|
59
|
+
if (timeoutId) {
|
|
60
|
+
// console.log(`Clearing timeout ${timeoutId}`);
|
|
61
|
+
clearTimeout(timeoutId);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (startTimer) {
|
|
67
|
+
if (timeoutId) {
|
|
68
|
+
// If there's an active timeout, clean it up.
|
|
69
|
+
cleanup();
|
|
70
|
+
}
|
|
71
|
+
// Start a timeout to restore popover content to the initial value.
|
|
72
|
+
// Record the ID of the timeout so it can be cleaned up later.
|
|
73
|
+
// console.log("Starting timeout");
|
|
74
|
+
setTimeoutId(
|
|
75
|
+
setTimeout(() => {
|
|
76
|
+
// console.log(`Timeout ${timeoutId} finished`);
|
|
77
|
+
setPopoverContent(initialPopoverContent);
|
|
78
|
+
}, copiedPopoverContentDuration)
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Set startTimer to false immediately after starting the timer,
|
|
82
|
+
// so subsequent clicks will start a new timer, refreshing the duration.
|
|
83
|
+
setStartTimer(false);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Return cleanup function so timeout will be cleared when component unmoumnts.
|
|
87
|
+
return cleanup;
|
|
88
|
+
}, [startTimer]);
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (attemptCopy) {
|
|
92
|
+
navigator.clipboard
|
|
93
|
+
.writeText(content)
|
|
94
|
+
.then(() => {
|
|
95
|
+
setPopoverContent(copySuccessPopoverContent);
|
|
96
|
+
onCopy?.();
|
|
97
|
+
setAttemptCopy(false);
|
|
98
|
+
})
|
|
99
|
+
.catch(error => {
|
|
100
|
+
console.error(error);
|
|
101
|
+
setPopoverContent(copyErrorPopoverContent);
|
|
102
|
+
setAttemptCopy(false);
|
|
103
|
+
});
|
|
62
104
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
);
|
|
105
|
+
}, [attemptCopy]);
|
|
106
|
+
|
|
107
|
+
const onClick = () => {
|
|
108
|
+
setStartTimer(true);
|
|
109
|
+
setAttemptCopy(true);
|
|
69
110
|
};
|
|
70
|
-
|
|
111
|
+
|
|
112
|
+
const popoverArrowWidth = "8px";
|
|
113
|
+
|
|
114
|
+
return hasPopover ? (
|
|
71
115
|
<Popover
|
|
72
116
|
minWidth={popoverMinWidth}
|
|
73
|
-
position={
|
|
74
|
-
|
|
117
|
+
position={{
|
|
118
|
+
top: "auto",
|
|
119
|
+
right: "auto",
|
|
120
|
+
bottom: "auto",
|
|
121
|
+
left: `calc(-${popoverArrowWidth} - 8px)`
|
|
122
|
+
}}
|
|
123
|
+
arrowPosition={{
|
|
124
|
+
arrowTop: "auto",
|
|
125
|
+
arrowRight: "-8px",
|
|
126
|
+
arrowBottom: `calc(50% - ${popoverArrowWidth})`,
|
|
127
|
+
arrowLeft: "auto"
|
|
128
|
+
}}
|
|
129
|
+
transform="translate(-100%, -75%)"
|
|
130
|
+
arrowDirection="right"
|
|
75
131
|
popoverID={popoverID}
|
|
76
|
-
|
|
77
|
-
|
|
132
|
+
buttonExtraStyles="margin: 0"
|
|
133
|
+
extraStyles={popoverExtraStyles}
|
|
134
|
+
triggerText={CopyableContent({ content, onClick })}
|
|
78
135
|
content={popoverContent}
|
|
79
136
|
></Popover>
|
|
80
137
|
) : (
|
|
81
|
-
|
|
138
|
+
CopyableContent({ content, onClick })
|
|
82
139
|
);
|
|
83
140
|
};
|
|
84
141
|
|
|
@@ -2,15 +2,16 @@ import React from "react";
|
|
|
2
2
|
import Expand from "../../../util/expand";
|
|
3
3
|
|
|
4
4
|
export interface CopyableProps {
|
|
5
|
-
|
|
5
|
+
content: string;
|
|
6
|
+
onCopy?: () => void;
|
|
6
7
|
initialPopoverContent?: string;
|
|
7
|
-
|
|
8
|
+
copySuccessPopoverContent?: string;
|
|
9
|
+
copyErrorPopoverContent?: string;
|
|
8
10
|
copiedPopoverContentDuration?: number;
|
|
9
|
-
|
|
11
|
+
hasPopover?: boolean;
|
|
10
12
|
popoverID?: number;
|
|
11
13
|
popoverMinWidth?: string;
|
|
12
|
-
|
|
13
|
-
extraStyles?: string;
|
|
14
|
+
popoverExtraStyles?: string;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export const Copyable: React.FC<Expand<CopyableProps> &
|
|
@@ -8,6 +8,20 @@ import { useOutsideClick } from "../../../util";
|
|
|
8
8
|
import { noop } from "../../../util/general";
|
|
9
9
|
import { fallbackValues } from "./Popover.theme";
|
|
10
10
|
|
|
11
|
+
const arrowBorder = (direction, width = "8px") => {
|
|
12
|
+
const angle = `${width} solid transparent`;
|
|
13
|
+
const straight = `${width} solid rgba(255, 255, 255, 0.85)`;
|
|
14
|
+
if (direction == "down") {
|
|
15
|
+
return `border-left: ${angle}; border-right: ${angle}; border-top: ${straight}`;
|
|
16
|
+
} else if (direction == "up") {
|
|
17
|
+
return `border-left: ${angle}; border-right: ${angle}; border-bottom: ${straight}`;
|
|
18
|
+
} else if (direction == "left") {
|
|
19
|
+
return `border-top: ${angle}; border-bottom: ${angle}; border-right: ${straight}`;
|
|
20
|
+
} else if (direction == "right") {
|
|
21
|
+
return `border-top: ${angle}; border-bottom: ${angle}; border-left: ${straight}`;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
11
25
|
const Popover = ({
|
|
12
26
|
themeValues,
|
|
13
27
|
triggerText = "",
|
|
@@ -23,7 +37,10 @@ const Popover = ({
|
|
|
23
37
|
maxWidth = "300px",
|
|
24
38
|
height = "auto",
|
|
25
39
|
position, // { top: string, right: string, bottom: string, left: string }
|
|
26
|
-
arrowPosition // { top: string, right: string, bottom: string, left: string }
|
|
40
|
+
arrowPosition, // { top: string, right: string, bottom: string, left: string }
|
|
41
|
+
arrowDirection = "down",
|
|
42
|
+
transform,
|
|
43
|
+
buttonExtraStyles
|
|
27
44
|
}) => {
|
|
28
45
|
const { hoverColor, activeColor, popoverTriggerColor } = themeValues;
|
|
29
46
|
const { top = "-110px", right = "auto", bottom = "auto", left = "-225px" } =
|
|
@@ -73,6 +90,7 @@ const Popover = ({
|
|
|
73
90
|
aria-describedby={`Disclosure${popoverID}`}
|
|
74
91
|
aria-controls={`Disclosed${popoverID}`}
|
|
75
92
|
ref={triggerRef}
|
|
93
|
+
extraStyles={buttonExtraStyles}
|
|
76
94
|
>
|
|
77
95
|
{hasIcon && (
|
|
78
96
|
<>
|
|
@@ -109,12 +127,24 @@ const Popover = ({
|
|
|
109
127
|
bottom: ${bottom};
|
|
110
128
|
left: ${left};
|
|
111
129
|
height: ${height};
|
|
130
|
+
transform: ${transform ? transform : "none"};
|
|
112
131
|
`}
|
|
113
132
|
>
|
|
114
133
|
<Paragraph>{content}</Paragraph>
|
|
115
134
|
<Box
|
|
116
135
|
padding="0"
|
|
117
|
-
extraStyles={`
|
|
136
|
+
extraStyles={`
|
|
137
|
+
position: absolute;
|
|
138
|
+
content: "";
|
|
139
|
+
width: 0;
|
|
140
|
+
height: 0;
|
|
141
|
+
${arrowBorder(arrowDirection, "8px")};
|
|
142
|
+
filter: drop-shadow(2px 8px 14px black);
|
|
143
|
+
bottom: ${arrowBottom};
|
|
144
|
+
right: ${arrowRight};
|
|
145
|
+
top: ${arrowTop};
|
|
146
|
+
left: ${arrowLeft};
|
|
147
|
+
`}
|
|
118
148
|
/>
|
|
119
149
|
</Box>
|
|
120
150
|
</Box>
|
|
@@ -20,6 +20,9 @@ export interface PopoverProps {
|
|
|
20
20
|
arrowBottom: string;
|
|
21
21
|
arrowLeft: string;
|
|
22
22
|
};
|
|
23
|
+
arrowDirection?: "left" | "right" | "top" | "bottom";
|
|
24
|
+
transform?: string;
|
|
25
|
+
disclosedExtraStyles?: string;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
export const Popover: React.FC<Expand<PopoverProps> &
|