rating notification

This commit is contained in:
Red Adaya 2024-11-20 08:57:04 +08:00
parent 416ba8d5da
commit d4ee83dcb7
6 changed files with 130 additions and 5 deletions

View File

@ -6,6 +6,7 @@ import { FloatingPortal, useFloating, useInteractions } from "@floating-ui/react
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { NotificationItem } from "./notificationitem"; import { NotificationItem } from "./notificationitem";
import { RatingBubble } from "./ratingbubble";
import { useNotification } from "./usenotification"; import { useNotification } from "./usenotification";
import clsx from "clsx"; import clsx from "clsx";
@ -16,6 +17,7 @@ const NotificationBubbles = () => {
notifications, notifications,
hoveredId, hoveredId,
hideNotification, hideNotification,
removeNotification,
copyNotification, copyNotification,
handleActionClick, handleActionClick,
formatTimestamp, formatTimestamp,
@ -60,6 +62,15 @@ const NotificationBubbles = () => {
> >
{notifications.map((notif) => { {notifications.map((notif) => {
if (notif.hidden) return null; if (notif.hidden) return null;
if (notif.componentType === "rating") {
return (
<RatingBubble
key={notif.id}
notification={notif}
onRemove={removeNotification}
></RatingBubble>
);
}
return ( return (
<NotificationItem <NotificationItem
key={notif.id} key={notif.id}

View File

@ -29,8 +29,8 @@ const NotificationItem = ({
onMouseEnter, onMouseEnter,
onMouseLeave, onMouseLeave,
}: NotificationItemProps) => { }: NotificationItemProps) => {
const { id, title, message, icon, type, timestamp, persistent, actions } = notification; const { id, title, message, icon, statusType, timestamp, persistent, actions } = notification;
const color = type === "error" ? "red" : type === "warning" ? "yellow" : "green"; const color = statusType === "error" ? "red" : statusType === "warning" ? "yellow" : "green";
const nIcon = icon ? icon : "bell"; const nIcon = icon ? icon : "bell";
const renderCloseButton = () => { const renderCloseButton = () => {

View File

@ -37,8 +37,8 @@ const NotificationPopover = () => {
setNotificationPopoverMode(!notificationPopoverMode); setNotificationPopoverMode(!notificationPopoverMode);
}, [notificationPopoverMode]); }, [notificationPopoverMode]);
const hasErrors = notifications.some((n) => n.type === "error"); const hasErrors = notifications.some((n) => n.statusType === "error");
const hasUpdate = notifications.some((n) => n.type === "update"); const hasUpdate = notifications.some((n) => n.statusType === "update");
const addOnClassNames = hasUpdate ? "solid green" : hasErrors ? "solid red" : "ghost grey"; const addOnClassNames = hasUpdate ? "solid green" : hasErrors ? "solid red" : "ghost grey";

View File

@ -0,0 +1,48 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
.notification-title {
font-size: 13px;
font-style: normal;
font-weight: 500;
line-height: 18px;
margin-bottom: 3px;
color: var(--success-color);
}
.notification-message {
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 18px;
opacity: 0.7;
}
.close-btn {
position: absolute;
top: 5px;
right: 5px;
}
.rating-bubble {
position: relative;
display: flex;
width: 380px;
padding: 16px 24px 16px 16px;
align-items: flex-start;
gap: 12px;
border-radius: 8px;
border: 0.5px solid rgba(255, 255, 255, 0.12);
background: #232323;
box-shadow: 0px 8px 32px 0px rgba(0, 0, 0, 0.25);
}
.notification-inner {
display: flex;
align-items: flex-start;
column-gap: 6px;
}
&.hovered {
background: #292929;
}

View File

@ -0,0 +1,65 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { Button } from "@/element/button";
import { makeIconClass } from "@/util/util";
import clsx from "clsx";
import { useState } from "react";
import "./ratingbubble.less";
interface RatingBubbleProps {
notification: NotificationType;
onRemove: (id: string) => void;
}
const RatingBubble = ({ notification, onRemove }: RatingBubbleProps) => {
const { id, title, message } = notification;
const [hoveredButtons, setHoveredButtons] = useState<{ [key: number]: boolean }>({});
const handleRatingClick = (rating: number) => {
console.log("rating clicked");
};
const handleMouseEnter = (buttonIndex: number) => {
setHoveredButtons((prev) => ({ ...prev, [buttonIndex]: true }));
};
const handleMouseLeave = (buttonIndex: number) => {
setHoveredButtons((prev) => ({ ...prev, [buttonIndex]: false }));
};
return (
<div className={clsx("rating-bubble")} title="Click to Copy Notification Message">
<Button
className="close-btn ghost grey vertical-padding-10"
onClick={(e) => {
e.stopPropagation();
onRemove(id);
}}
aria-label="Close"
>
<i className={clsx(makeIconClass("close", false))}></i>
</Button>
<div className="notification-inner">
<div className="notification-text">
{title && <div className={clsx("notification-title green")}>{title}</div>}
{message && <div className="notification-message">{message}</div>}
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((rating) => (
<Button
key={rating}
className={clsx(hoveredButtons[rating] ? "green" : "grey")}
onClick={() => handleRatingClick(rating)}
onMouseEnter={() => handleMouseEnter(rating)}
onMouseLeave={() => handleMouseLeave(rating)}
>
{rating}
</Button>
))}
</div>
</div>
</div>
);
};
export { RatingBubble };

View File

@ -320,7 +320,8 @@ declare global {
hidden?: boolean; hidden?: boolean;
actions?: NotificationActionType[]; actions?: NotificationActionType[];
persistent?: boolean; persistent?: boolean;
type?: "error" | "update" | "info" | "warning"; componentType?: "rating";
statusType?: "error" | "update" | "info" | "warning";
}; };
interface AbstractWshClient { interface AbstractWshClient {