Notification modal refactor (#683)
This pull request refers to the Notification Center modal refactor, deleting permanently the PopupContext way of showing the popup, and utilizing the Popover component from ShadCN to display it.
This commit is contained in:
commit
d79f58cfb5
8 changed files with 144 additions and 130 deletions
38
src/frontend/package-lock.json
generated
38
src/frontend/package-lock.json
generated
|
|
@ -20,6 +20,7 @@
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"@radix-ui/react-menubar": "^1.0.3",
|
"@radix-ui/react-menubar": "^1.0.3",
|
||||||
|
"@radix-ui/react-popover": "^1.0.6",
|
||||||
"@radix-ui/react-progress": "^1.0.3",
|
"@radix-ui/react-progress": "^1.0.3",
|
||||||
"@radix-ui/react-separator": "^1.0.3",
|
"@radix-ui/react-separator": "^1.0.3",
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
|
|
@ -1463,6 +1464,43 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-popover": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-cZ4defGpkZ0qTRtlIBzJLSzL6ht7ofhhW4i1+pkemjV1IKXm0wgCRnee154qlV6r9Ttunmh2TNZhMfV2bavUyA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/primitive": "1.0.1",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.1",
|
||||||
|
"@radix-ui/react-context": "1.0.1",
|
||||||
|
"@radix-ui/react-dismissable-layer": "1.0.4",
|
||||||
|
"@radix-ui/react-focus-guards": "1.0.1",
|
||||||
|
"@radix-ui/react-focus-scope": "1.0.3",
|
||||||
|
"@radix-ui/react-id": "1.0.1",
|
||||||
|
"@radix-ui/react-popper": "1.1.2",
|
||||||
|
"@radix-ui/react-portal": "1.0.3",
|
||||||
|
"@radix-ui/react-presence": "1.0.1",
|
||||||
|
"@radix-ui/react-primitive": "1.0.3",
|
||||||
|
"@radix-ui/react-slot": "1.0.2",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.0.1",
|
||||||
|
"aria-hidden": "^1.1.1",
|
||||||
|
"react-remove-scroll": "2.5.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-popper": {
|
"node_modules/@radix-ui/react-popper": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"@radix-ui/react-menubar": "^1.0.3",
|
"@radix-ui/react-menubar": "^1.0.3",
|
||||||
|
"@radix-ui/react-popover": "^1.0.6",
|
||||||
"@radix-ui/react-progress": "^1.0.3",
|
"@radix-ui/react-progress": "^1.0.3",
|
||||||
"@radix-ui/react-separator": "^1.0.3",
|
"@radix-ui/react-separator": "^1.0.3",
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
|
|
|
||||||
|
|
@ -1,66 +1,72 @@
|
||||||
import { useContext, useRef } from "react";
|
import { useContext, useState } from "react";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "../../components/ui/popover";
|
||||||
import { alertContext } from "../../contexts/alertContext";
|
import { alertContext } from "../../contexts/alertContext";
|
||||||
import { PopUpContext } from "../../contexts/popUpContext";
|
|
||||||
import { AlertDropdownType } from "../../types/alerts";
|
import { AlertDropdownType } from "../../types/alerts";
|
||||||
import { useOnClickOutside } from "../hooks/useOnClickOutside";
|
|
||||||
import SingleAlert from "./components/singleAlertComponent";
|
import SingleAlert from "./components/singleAlertComponent";
|
||||||
|
|
||||||
export default function AlertDropdown({}: AlertDropdownType) {
|
export default function AlertDropdown({ children }: AlertDropdownType) {
|
||||||
const { closePopUp } = useContext(PopUpContext);
|
|
||||||
const componentRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
// Use the custom hook
|
|
||||||
useOnClickOutside(componentRef, () => {
|
|
||||||
closePopUp();
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
notificationList,
|
notificationList,
|
||||||
clearNotificationList,
|
clearNotificationList,
|
||||||
removeFromNotificationList,
|
removeFromNotificationList,
|
||||||
|
setNotificationCenter,
|
||||||
} = useContext(alertContext);
|
} = useContext(alertContext);
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Popover
|
||||||
ref={componentRef}
|
open={open}
|
||||||
className="z-10 flex h-[500px] w-[400px] flex-col overflow-hidden rounded-md bg-muted px-2 py-3 pb-4 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
|
onOpenChange={(k) => {
|
||||||
|
setOpen(k);
|
||||||
|
if (k) setNotificationCenter(false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="text-md flex flex-row justify-between pl-3 font-medium text-foreground">
|
<PopoverTrigger>{children}</PopoverTrigger>
|
||||||
Notifications
|
<PopoverContent className="flex h-[500px] w-[500px] flex-col">
|
||||||
<div className="flex gap-3 pr-3 ">
|
<div className="text-md flex flex-row justify-between pl-3 font-medium text-foreground">
|
||||||
<button
|
Notifications
|
||||||
className="text-foreground hover:text-status-red"
|
<div className="flex gap-3 pr-3 ">
|
||||||
onClick={() => {
|
<button
|
||||||
closePopUp();
|
className="text-foreground hover:text-status-red"
|
||||||
setTimeout(clearNotificationList, 100);
|
onClick={() => {
|
||||||
}}
|
setOpen(false);
|
||||||
>
|
setTimeout(clearNotificationList, 100);
|
||||||
<IconComponent name="Trash2" className="h-[1.1rem] w-[1.1rem]" />
|
}}
|
||||||
</button>
|
>
|
||||||
<button
|
<IconComponent name="Trash2" className="h-[1.1rem] w-[1.1rem]" />
|
||||||
className="text-foreground hover:text-status-red"
|
</button>
|
||||||
onClick={closePopUp}
|
<button
|
||||||
>
|
className="text-foreground hover:text-status-red"
|
||||||
<IconComponent name="X" className="h-5 w-5" />
|
onClick={() => {
|
||||||
</button>
|
setOpen(false);
|
||||||
</div>
|
}}
|
||||||
</div>
|
>
|
||||||
<div className="text-high-foreground mt-3 flex h-full w-full flex-col overflow-y-scroll scrollbar-hide">
|
<IconComponent name="X" className="h-5 w-5" />
|
||||||
{notificationList.length !== 0 ? (
|
</button>
|
||||||
notificationList.map((alertItem, index) => (
|
|
||||||
<SingleAlert
|
|
||||||
key={alertItem.id}
|
|
||||||
dropItem={alertItem}
|
|
||||||
removeAlert={removeFromNotificationList}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<div className="flex h-full w-full items-center justify-center pb-16 text-ring">
|
|
||||||
No new notifications
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
<div className="text-high-foreground mt-3 flex h-full w-full flex-col overflow-y-scroll scrollbar-hide">
|
||||||
</div>
|
{notificationList.length !== 0 ? (
|
||||||
|
notificationList.map((alertItem, index) => (
|
||||||
|
<SingleAlert
|
||||||
|
key={alertItem.id}
|
||||||
|
dropItem={alertItem}
|
||||||
|
removeAlert={removeFromNotificationList}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="flex h-full w-full items-center justify-center pb-16 text-ring">
|
||||||
|
No new notifications
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import { FaDiscord, FaGithub, FaTwitter } from "react-icons/fa";
|
import { FaDiscord, FaGithub, FaTwitter } from "react-icons/fa";
|
||||||
import { Link, useLocation, useParams } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom";
|
||||||
import AlertDropdown from "../../alerts/alertDropDown";
|
import AlertDropdown from "../../alerts/alertDropDown";
|
||||||
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
||||||
import { alertContext } from "../../contexts/alertContext";
|
import { alertContext } from "../../contexts/alertContext";
|
||||||
import { darkContext } from "../../contexts/darkContext";
|
import { darkContext } from "../../contexts/darkContext";
|
||||||
import { PopUpContext } from "../../contexts/popUpContext";
|
|
||||||
import { TabsContext } from "../../contexts/tabsContext";
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
import { typesContext } from "../../contexts/typesContext";
|
|
||||||
import { getRepoStars } from "../../controllers/API";
|
import { getRepoStars } from "../../controllers/API";
|
||||||
import IconComponent from "../genericIconComponent";
|
import IconComponent from "../genericIconComponent";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
|
@ -15,14 +13,9 @@ import { Separator } from "../ui/separator";
|
||||||
import MenuBar from "./components/menuBar";
|
import MenuBar from "./components/menuBar";
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
const { flows, addFlow, tabId } = useContext(TabsContext);
|
const { flows, tabId } = useContext(TabsContext);
|
||||||
const { openPopUp } = useContext(PopUpContext);
|
|
||||||
const { templates } = useContext(typesContext);
|
|
||||||
const { id } = useParams();
|
|
||||||
const AlertWidth = 384;
|
|
||||||
const { dark, setDark } = useContext(darkContext);
|
const { dark, setDark } = useContext(darkContext);
|
||||||
const { notificationCenter, setNotificationCenter, setErrorData } =
|
const { notificationCenter } = useContext(alertContext);
|
||||||
useContext(alertContext);
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const [stars, setStars] = useState(null);
|
const [stars, setStars] = useState(null);
|
||||||
|
|
@ -111,33 +104,18 @@ export default function Header() {
|
||||||
<IconComponent name="MoonIcon" className="side-bar-button-size" />
|
<IconComponent name="MoonIcon" className="side-bar-button-size" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<AlertDropdown>
|
||||||
className="extra-side-bar-save-disable relative"
|
<div className="extra-side-bar-save-disable relative">
|
||||||
onClick={(event: React.MouseEvent<HTMLElement>) => {
|
{notificationCenter && (
|
||||||
setNotificationCenter(false);
|
<div className="header-notifications"></div>
|
||||||
const { top, left } = (
|
)}
|
||||||
event.target as Element
|
<IconComponent
|
||||||
).getBoundingClientRect();
|
name="Bell"
|
||||||
openPopUp(
|
className="side-bar-button-size"
|
||||||
<>
|
aria-hidden="true"
|
||||||
<div
|
/>
|
||||||
className="absolute z-10"
|
</div>
|
||||||
style={{ top: top + 40, left: left - AlertWidth }}
|
</AlertDropdown>
|
||||||
>
|
|
||||||
<AlertDropdown />
|
|
||||||
</div>
|
|
||||||
<div className="header-notifications-box"></div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{notificationCenter && <div className="header-notifications"></div>}
|
|
||||||
<IconComponent
|
|
||||||
name="Bell"
|
|
||||||
className="side-bar-button-size"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
30
src/frontend/src/components/ui/popover.tsx
Normal file
30
src/frontend/src/components/ui/popover.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
||||||
|
import * as React from "react";
|
||||||
|
import { cn } from "../../utils/utils";
|
||||||
|
|
||||||
|
const Popover = PopoverPrimitive.Root;
|
||||||
|
|
||||||
|
const PopoverTrigger = PopoverPrimitive.Trigger;
|
||||||
|
|
||||||
|
const PopoverContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
||||||
|
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
||||||
|
<PopoverPrimitive.Portal>
|
||||||
|
<PopoverPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
align={align}
|
||||||
|
sideOffset={sideOffset}
|
||||||
|
className={cn(
|
||||||
|
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</PopoverPrimitive.Portal>
|
||||||
|
));
|
||||||
|
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
export { Popover, PopoverTrigger, PopoverContent };
|
||||||
|
|
@ -5,7 +5,6 @@ import { SSEProvider } from "./SSEContext";
|
||||||
import { AlertProvider } from "./alertContext";
|
import { AlertProvider } from "./alertContext";
|
||||||
import { DarkProvider } from "./darkContext";
|
import { DarkProvider } from "./darkContext";
|
||||||
import { LocationProvider } from "./locationContext";
|
import { LocationProvider } from "./locationContext";
|
||||||
import PopUpProvider from "./popUpContext";
|
|
||||||
import { TabsProvider } from "./tabsContext";
|
import { TabsProvider } from "./tabsContext";
|
||||||
import { TypesProvider } from "./typesContext";
|
import { TypesProvider } from "./typesContext";
|
||||||
import { UndoRedoProvider } from "./undoRedoContext";
|
import { UndoRedoProvider } from "./undoRedoContext";
|
||||||
|
|
@ -22,9 +21,7 @@ export default function ContextWrapper({ children }: { children: ReactNode }) {
|
||||||
<AlertProvider>
|
<AlertProvider>
|
||||||
<SSEProvider>
|
<SSEProvider>
|
||||||
<TabsProvider>
|
<TabsProvider>
|
||||||
<UndoRedoProvider>
|
<UndoRedoProvider>{children}</UndoRedoProvider>
|
||||||
<PopUpProvider>{children}</PopUpProvider>
|
|
||||||
</UndoRedoProvider>
|
|
||||||
</TabsProvider>
|
</TabsProvider>
|
||||||
</SSEProvider>
|
</SSEProvider>
|
||||||
</AlertProvider>
|
</AlertProvider>
|
||||||
|
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
import React, { createContext, useState } from "react";
|
|
||||||
|
|
||||||
// context to set JSX element on the DOM
|
|
||||||
export const PopUpContext = createContext({
|
|
||||||
openPopUp: (popUpElement: JSX.Element) => {},
|
|
||||||
closePopUp: () => {},
|
|
||||||
setCloseEdit: (value: string) => {},
|
|
||||||
closeEdit: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
interface PopUpProviderProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PopUpProvider = ({ children }: PopUpProviderProps) => {
|
|
||||||
const [popUpElements, setPopUpElements] = useState<JSX.Element[]>([]);
|
|
||||||
|
|
||||||
const openPopUp = (element: JSX.Element) => {
|
|
||||||
setPopUpElements((prevPopUps) => [element, ...prevPopUps]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const closePopUp = () => {
|
|
||||||
setPopUpElements((prevPopUps) => prevPopUps.slice(1));
|
|
||||||
};
|
|
||||||
|
|
||||||
const [closeEdit, setCloseEdit] = useState("");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PopUpContext.Provider
|
|
||||||
value={{ openPopUp, closePopUp, closeEdit, setCloseEdit }}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
{popUpElements[0]}
|
|
||||||
</PopUpContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PopUpProvider;
|
|
||||||
|
|
@ -19,7 +19,9 @@ export type SingleAlertComponentType = {
|
||||||
dropItem: AlertItemType;
|
dropItem: AlertItemType;
|
||||||
removeAlert: (index: string) => void;
|
removeAlert: (index: string) => void;
|
||||||
};
|
};
|
||||||
export type AlertDropdownType = {};
|
export type AlertDropdownType = {
|
||||||
|
children: JSX.Element;
|
||||||
|
};
|
||||||
export type AlertItemType = {
|
export type AlertItemType = {
|
||||||
type: "notice" | "error" | "success";
|
type: "notice" | "error" | "success";
|
||||||
title: string;
|
title: string;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue