Skip to content

Commit 958578c

Browse files
add notification component
1 parent fe784b7 commit 958578c

File tree

7 files changed

+84
-7
lines changed

7 files changed

+84
-7
lines changed

src/features/common/components/context-menu/context-menu.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import jwtWordmark from "@/public/img/jwt-wordmark.svg?raw";
1010
interface ContextMenuProps {
1111
dictionary: BrandDictionaryModel;
1212
position: { x: number; y: number } | null;
13+
setIsCopied: (value: React.SetStateAction<boolean>) => void;
1314
}
1415

15-
const ContextMenu: FC<ContextMenuProps> = ({ dictionary, position }) => {
16-
const [isCopied, setIsCopied] = useState(false);
16+
const ContextMenu: FC<ContextMenuProps> = ({ dictionary, position, setIsCopied }) => {
1717
if (!position) return null;
1818

1919
const handleIconCopy = async (svgString: string) => {
@@ -24,10 +24,6 @@ const ContextMenu: FC<ContextMenuProps> = ({ dictionary, position }) => {
2424
try {
2525
await navigator.clipboard.writeText(svgString);
2626
setIsCopied(true);
27-
28-
setTimeout(() => {
29-
setIsCopied(false);
30-
}, 2000);
3127
} catch (err) {
3228
console.error("Failed to copy SVG: ", err);
3329
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useEffect } from 'react';
2+
import { createPortal } from 'react-dom';
3+
import styles from './notification.module.scss';
4+
5+
interface NotificationProps {
6+
message: string;
7+
onClose: () => void;
8+
}
9+
10+
const Notification = ({ message, onClose }: NotificationProps) => {
11+
useEffect(() => {
12+
const timer = setTimeout(() => {
13+
onClose();
14+
}, 2000);
15+
16+
return () => clearTimeout(timer);
17+
}, [onClose]);
18+
19+
return createPortal(
20+
<div className={styles.container}>
21+
{message}
22+
</div>,
23+
document.body
24+
);
25+
};
26+
27+
export default Notification;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.container {
2+
position: fixed;
3+
top: 1rem;
4+
right: 1rem;
5+
transform: translateY(0);
6+
padding: 0.75rem 1rem;
7+
background-color: var(--color_bg_layer_alternate-bold);
8+
color: var(--color_fg_bold);
9+
border-radius: 0.75rem;
10+
font-size: 0.875rem;
11+
font-weight: 500;
12+
z-index: 9010;
13+
animation:
14+
fadeIn 0.3s,
15+
fadeOut 0.3s 1.7s;
16+
box-shadow:
17+
0 0 0 1px rgba(0, 0, 0, 0.12),
18+
0 2px 2px -1px rgba(0, 0, 0, 0.04),
19+
0 4px 4px -2px rgba(0, 0, 0, 0.04),
20+
0 8px 8px -4px rgba(0, 0, 0, 0.04),
21+
0 16px 16px -8px rgba(0, 0, 0, 0.04),
22+
0 32px 32px -16px rgba(0, 0, 0, 0.04),
23+
inset 0 -1px 1px -0.5px rgba(0, 0, 0, 0.04);
24+
}
25+
26+
@keyframes fadeIn {
27+
from {
28+
opacity: 0;
29+
transform: translateY(-100%);
30+
}
31+
to {
32+
opacity: 1;
33+
transform: translateY(0);
34+
}
35+
}
36+
37+
@keyframes fadeOut {
38+
from {
39+
opacity: 1;
40+
transform: translateY(0);
41+
}
42+
to {
43+
opacity: 0;
44+
transform: translateY(-100%);
45+
}
46+
}

src/features/common/components/site-brand/site-brand.component.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import clsx from "clsx";
77
import { JwtLogoComponent } from "../../assets/jwt-logo.component";
88
import { JwtWordmarkComponent } from "../../assets/jwt-wordmark.component";
99
import ContextMenu from "../context-menu/context-menu";
10+
import Notification from "../notification/notification.component";
1011

1112
interface SiteBrandComponentProps extends PropsWithChildren {
1213
path: string;
@@ -17,6 +18,7 @@ export const SiteBrandComponent: React.FC<SiteBrandComponentProps> = ({
1718
path,
1819
languageCode,
1920
}) => {
21+
const [isCopied, setIsCopied] = useState(false);
2022
const [isTooltipVisible, setIsTooltipVisible] = useState(false);
2123
const [menuPosition, setMenuPosition] = useState<{
2224
x: number;
@@ -65,7 +67,10 @@ export const SiteBrandComponent: React.FC<SiteBrandComponentProps> = ({
6567
<div className={styles.tooltip}>{brandDictionary.tooltip}</div>
6668
)}
6769
</Link>
68-
<ContextMenu dictionary={brandDictionary} position={menuPosition} />
70+
<ContextMenu dictionary={brandDictionary} position={menuPosition} setIsCopied={setIsCopied}/>
71+
{isCopied && (
72+
<Notification message={brandDictionary.alertMessage} onClose={() => setIsCopied(false)}/>
73+
)}
6974
</div>
7075
);
7176
};

src/features/localization/dictionaries/images/en.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BrandDictionaryModel } from "../../models/brand-dictionary.model";
22

33
export const enBrandDictionary: BrandDictionaryModel = {
4+
alertMessage: "SVG copied to clipboard",
45
tooltip: "Right-click or long-press for logo options",
56
menu: {
67
brand: {

src/features/localization/dictionaries/images/ja.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BrandDictionaryModel } from "../../models/brand-dictionary.model";
22

33
export const jaBrandDictionary: BrandDictionaryModel = {
4+
alertMessage: "SVG copied to clipboard",
45
tooltip: "Right-click or long-press for logo options",
56
menu: {
67
brand: {

src/features/localization/models/brand-dictionary.model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ interface BrandMenu {
2525

2626
export interface BrandDictionaryModel {
2727
tooltip: string;
28+
alertMessage: string;
2829
menu: BrandMenu;
2930
}

0 commit comments

Comments
 (0)