fix: React Console Warnings and Accessibility Issues in Langflow Frontend (#6950)
* fix errors on console jsx * ✨ (NodeOutputfield/index.tsx): Refactor InspectButton component to use forwardRef for better performance and maintainability ♻️ (NodeOutputfield/index.tsx): Refactor AlertDropdown component to use forwardRef for better performance and maintainability * 📝 (dialog.tsx): Add VisuallyHidden component for accessibility and improve semantics in DialogContent component 🔧 (dialog.tsx): Update DialogContent component to conditionally include VisuallyHidden component for DialogTitle 🔧 (dialog.tsx): Export VisuallyHidden component from dialog.tsx ♻️ (textAnimation.tsx): Refactor TextEffectPerChar component to use TextEffect component with per="char" and as="span" properties for consistency * ✨ (CustomEdges/index.tsx): Destructure props to extract specific properties and pass the rest as domSafeProps for cleaner code ♻️ (singleAlertComponent/index.tsx): Remove unnecessary aria-hidden attribute from IconComponent to improve accessibility and reduce redundancy * ✨ (index.tsx): introduce ShortUniqueId library to generate unique keys for alert items in the dropdown list * ✨ (frontend): add support for displaying a list of items in the NoticeAlert component 📝 (frontend): update NoticeAlertType interface to include a list property for displaying multiple items in the alert * ✨ (styleUtils.ts): introduce new icon PencilRuler to the list of nodeIconsLucide for use in the frontend styling utilities.
This commit is contained in:
parent
47753d37d3
commit
08f886f507
11 changed files with 243 additions and 197 deletions
|
|
@ -56,11 +56,26 @@ export function DefaultEdge({
|
|||
targetY: targetYNew,
|
||||
});
|
||||
|
||||
const {
|
||||
animated,
|
||||
selectable,
|
||||
deletable,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
pathOptions,
|
||||
selected,
|
||||
...domSafeProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<BaseEdge
|
||||
path={targetHandleObject.output_types ? edgePathLoop : edgePath}
|
||||
strokeDasharray={targetHandleObject.output_types ? "5 5" : "0"}
|
||||
{...props}
|
||||
{...domSafeProps}
|
||||
data-animated={animated ? "true" : "false"}
|
||||
data-selectable={selectable ? "true" : "false"}
|
||||
data-deletable={deletable ? "true" : "false"}
|
||||
data-selected={selected ? "true" : "false"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,14 @@ import { ICON_STROKE_WIDTH } from "@/constants/constants";
|
|||
import { targetHandleType } from "@/types/flow";
|
||||
import { useUpdateNodeInternals } from "@xyflow/react";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { memo, useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import {
|
||||
forwardRef,
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from "react";
|
||||
import ForwardedIconComponent, {
|
||||
default as IconComponent,
|
||||
} from "../../../../components/common/genericIconComponent";
|
||||
|
|
@ -96,49 +103,56 @@ const HideShowButton = memo(
|
|||
);
|
||||
|
||||
const InspectButton = memo(
|
||||
({
|
||||
disabled,
|
||||
displayOutputPreview,
|
||||
unknownOutput,
|
||||
errorOutput,
|
||||
isToolMode,
|
||||
title,
|
||||
onClick,
|
||||
id,
|
||||
}: {
|
||||
disabled: boolean | undefined;
|
||||
displayOutputPreview: boolean;
|
||||
unknownOutput: boolean | undefined;
|
||||
errorOutput: boolean;
|
||||
isToolMode: boolean;
|
||||
title: string;
|
||||
onClick: () => void;
|
||||
id: string;
|
||||
}) => (
|
||||
<Button
|
||||
disabled={disabled}
|
||||
data-testid={`output-inspection-${title.toLowerCase()}-${id.toLowerCase()}`}
|
||||
unstyled
|
||||
onClick={onClick}
|
||||
>
|
||||
<IconComponent
|
||||
name="TextSearchIcon"
|
||||
strokeWidth={ICON_STROKE_WIDTH}
|
||||
className={cn(
|
||||
"icon-size",
|
||||
isToolMode
|
||||
? displayOutputPreview && !unknownOutput
|
||||
? "text-background hover:text-secondary-hover"
|
||||
: "cursor-not-allowed text-placeholder-foreground opacity-80"
|
||||
: displayOutputPreview && !unknownOutput
|
||||
? "text-foreground hover:text-primary-hover"
|
||||
: "cursor-not-allowed text-placeholder-foreground opacity-60",
|
||||
errorOutput ? "text-destructive" : "",
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
forwardRef(
|
||||
(
|
||||
{
|
||||
disabled,
|
||||
displayOutputPreview,
|
||||
unknownOutput,
|
||||
errorOutput,
|
||||
isToolMode,
|
||||
title,
|
||||
onClick,
|
||||
id,
|
||||
}: {
|
||||
disabled: boolean | undefined;
|
||||
displayOutputPreview: boolean;
|
||||
unknownOutput: boolean | undefined;
|
||||
errorOutput: boolean;
|
||||
isToolMode: boolean;
|
||||
title: string;
|
||||
onClick: () => void;
|
||||
id: string;
|
||||
},
|
||||
ref: React.ForwardedRef<HTMLButtonElement>,
|
||||
) => (
|
||||
<Button
|
||||
ref={ref}
|
||||
disabled={disabled}
|
||||
data-testid={`output-inspection-${title.toLowerCase()}-${id.toLowerCase()}`}
|
||||
unstyled
|
||||
onClick={onClick}
|
||||
>
|
||||
<IconComponent
|
||||
name="TextSearchIcon"
|
||||
strokeWidth={ICON_STROKE_WIDTH}
|
||||
className={cn(
|
||||
"icon-size",
|
||||
isToolMode
|
||||
? displayOutputPreview && !unknownOutput
|
||||
? "text-background hover:text-secondary-hover"
|
||||
: "cursor-not-allowed text-placeholder-foreground opacity-80"
|
||||
: displayOutputPreview && !unknownOutput
|
||||
? "text-foreground hover:text-primary-hover"
|
||||
: "cursor-not-allowed text-placeholder-foreground opacity-60",
|
||||
errorOutput ? "text-destructive" : "",
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
),
|
||||
),
|
||||
);
|
||||
InspectButton.displayName = "InspectButton";
|
||||
|
||||
const MemoizedOutputComponent = memo(OutputComponent);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,7 @@ export default function SingleAlert({
|
|||
key={dropItem.id}
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
<IconComponent
|
||||
name="XCircle"
|
||||
className="h-5 w-5 text-status-red"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<IconComponent name="XCircle" className="h-5 w-5 text-status-red" />
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<h3 className="text-sm font-medium text-error-foreground word-break-break-word">
|
||||
|
|
@ -80,11 +76,7 @@ export default function SingleAlert({
|
|||
className="inline-flex rounded-md p-1.5 text-status-red"
|
||||
>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<IconComponent
|
||||
name="X"
|
||||
className="h-4 w-4 text-error-foreground"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<IconComponent name="X" className="h-4 w-4 text-error-foreground" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -95,11 +87,7 @@ export default function SingleAlert({
|
|||
key={dropItem.id}
|
||||
>
|
||||
<div className="flex-shrink-0 cursor-help">
|
||||
<IconComponent
|
||||
name="Info"
|
||||
className="h-5 w-5 text-status-blue"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<IconComponent name="Info" className="h-5 w-5 text-status-blue" />
|
||||
</div>
|
||||
<div className="ml-3 flex-1 md:flex md:justify-between">
|
||||
<p className="text-sm font-medium text-info-foreground">
|
||||
|
|
@ -131,11 +119,7 @@ export default function SingleAlert({
|
|||
className="inline-flex rounded-md p-1.5 text-info-foreground"
|
||||
>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<IconComponent
|
||||
name="X"
|
||||
className="h-4 w-4 text-info-foreground"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<IconComponent name="X" className="h-4 w-4 text-info-foreground" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -149,7 +133,6 @@ export default function SingleAlert({
|
|||
<IconComponent
|
||||
name="CheckCircle2"
|
||||
className="h-5 w-5 text-status-green"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
|
|
@ -173,7 +156,6 @@ export default function SingleAlert({
|
|||
<IconComponent
|
||||
name="X"
|
||||
className="h-4 w-4 text-success-foreground"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Cross2Icon } from "@radix-ui/react-icons";
|
||||
import { useEffect, useState } from "react";
|
||||
import { forwardRef, useEffect, useState } from "react";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import IconComponent from "../../components/common/genericIconComponent";
|
||||
import {
|
||||
Popover,
|
||||
|
|
@ -11,85 +12,90 @@ import useAlertStore from "../../stores/alertStore";
|
|||
import { AlertDropdownType } from "../../types/alerts";
|
||||
import SingleAlert from "./components/singleAlertComponent";
|
||||
|
||||
export default function AlertDropdown({
|
||||
children,
|
||||
notificationRef,
|
||||
onClose,
|
||||
}: AlertDropdownType): JSX.Element {
|
||||
const notificationList = useAlertStore((state) => state.notificationList);
|
||||
const clearNotificationList = useAlertStore(
|
||||
(state) => state.clearNotificationList,
|
||||
);
|
||||
const removeFromNotificationList = useAlertStore(
|
||||
(state) => state.removeFromNotificationList,
|
||||
);
|
||||
const setNotificationCenter = useAlertStore(
|
||||
(state) => state.setNotificationCenter,
|
||||
);
|
||||
const AlertDropdown = forwardRef<HTMLDivElement, AlertDropdownType>(
|
||||
function AlertDropdown({ children, notificationRef, onClose }, ref) {
|
||||
const notificationList = useAlertStore((state) => state.notificationList);
|
||||
const clearNotificationList = useAlertStore(
|
||||
(state) => state.clearNotificationList,
|
||||
);
|
||||
const removeFromNotificationList = useAlertStore(
|
||||
(state) => state.removeFromNotificationList,
|
||||
);
|
||||
const setNotificationCenter = useAlertStore(
|
||||
(state) => state.setNotificationCenter,
|
||||
);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
onClose?.();
|
||||
}
|
||||
}, [open]);
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
onClose?.();
|
||||
}
|
||||
}, [open, onClose]);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
data-testid="notification-dropdown"
|
||||
open={open}
|
||||
onOpenChange={(target) => {
|
||||
setOpen(target);
|
||||
if (target) {
|
||||
setNotificationCenter(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
||||
<PopoverContent
|
||||
ref={notificationRef}
|
||||
data-testid="notification-dropdown-content"
|
||||
className="noflow nowheel nopan nodelete nodrag z-10 flex h-[500px] w-[500px] flex-col"
|
||||
const uid = new ShortUniqueId();
|
||||
|
||||
return (
|
||||
<Popover
|
||||
data-testid="notification-dropdown"
|
||||
open={open}
|
||||
onOpenChange={(target) => {
|
||||
setOpen(target);
|
||||
if (target) {
|
||||
setNotificationCenter(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="text-md flex flex-row justify-between pl-3 font-medium text-foreground">
|
||||
Notifications
|
||||
<div className="flex gap-3 pr-3">
|
||||
<button
|
||||
className="text-foreground hover:text-status-red"
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
setTimeout(clearNotificationList, 100);
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Trash2" className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
className="text-foreground opacity-70 hover:opacity-100"
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Cross2Icon className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-high-foreground mt-3 flex h-full w-full flex-col overflow-y-scroll scrollbar-hide">
|
||||
{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">
|
||||
{ZERO_NOTIFICATIONS}
|
||||
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
||||
<PopoverContent
|
||||
ref={ref}
|
||||
data-testid="notification-dropdown-content"
|
||||
className="noflow nowheel nopan nodelete nodrag z-10 flex h-[500px] w-[500px] flex-col"
|
||||
>
|
||||
<div
|
||||
ref={notificationRef}
|
||||
className="text-md flex flex-row justify-between pl-3 font-medium text-foreground"
|
||||
>
|
||||
Notifications
|
||||
<div className="flex gap-3 pr-3">
|
||||
<button
|
||||
className="text-foreground hover:text-status-red"
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
setTimeout(clearNotificationList, 100);
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Trash2" className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
className="text-foreground opacity-70 hover:opacity-100"
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Cross2Icon className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
<div className="text-high-foreground mt-3 flex h-full w-full flex-col overflow-y-scroll scrollbar-hide">
|
||||
{notificationList.length !== 0 ? (
|
||||
notificationList.map((alertItem) => (
|
||||
<SingleAlert
|
||||
key={uid.randomUUID(10)}
|
||||
dropItem={alertItem}
|
||||
removeAlert={removeFromNotificationList}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center pb-16 text-ring">
|
||||
{ZERO_NOTIFICATIONS}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default AlertDropdown;
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
import { CustomLink } from "@/customization/components/custom-link";
|
||||
import { Transition } from "@headlessui/react";
|
||||
import { useEffect, useState } from "react";
|
||||
import Markdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import IconComponent from "../../components/common/genericIconComponent";
|
||||
import { NoticeAlertType } from "../../types/alerts";
|
||||
|
||||
export default function NoticeAlert({
|
||||
title,
|
||||
link,
|
||||
list = [],
|
||||
id,
|
||||
link,
|
||||
removeAlert,
|
||||
}: NoticeAlertType): JSX.Element {
|
||||
const [show, setShow] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
setShow(false);
|
||||
// Wait for the leave transition before calling removeAlert
|
||||
if (show) {
|
||||
setTimeout(() => {
|
||||
removeAlert(id);
|
||||
}, 500); // match the duration of the leave transition
|
||||
}, 5000); // auto-dismiss alert after 5 seconds
|
||||
|
||||
return () => clearTimeout(timeoutId); // Cleanup timeout on component unmount or re-render
|
||||
}, [id, removeAlert]);
|
||||
setShow(false);
|
||||
setTimeout(() => {
|
||||
removeAlert(id);
|
||||
}, 500);
|
||||
}, 5000);
|
||||
}
|
||||
}, [id, removeAlert, show]);
|
||||
|
||||
const handleClick = () => {
|
||||
setShow(false);
|
||||
// Wait for the leave transition before calling removeAlert
|
||||
setTimeout(() => {
|
||||
removeAlert(id);
|
||||
}, 500); // Ensure the alert is removed after the animation
|
||||
}, 500);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -35,35 +35,65 @@ const DialogOverlay = React.forwardRef<
|
|||
));
|
||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
||||
|
||||
// Create a VisuallyHidden component for accessibility
|
||||
const VisuallyHidden = React.forwardRef<
|
||||
HTMLSpanElement,
|
||||
React.HTMLAttributes<HTMLSpanElement>
|
||||
>(({ children, ...props }, ref) => (
|
||||
<span
|
||||
ref={ref}
|
||||
className="absolute h-px w-px overflow-hidden whitespace-nowrap border-0 p-0"
|
||||
style={{ clip: "rect(0 0 0 0)", clipPath: "inset(50%)" }}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
));
|
||||
VisuallyHidden.displayName = "VisuallyHidden";
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed z-50 flex w-full max-w-lg flex-col gap-4 rounded-xl border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ShadTooltip
|
||||
styleClasses="z-50"
|
||||
content="Close"
|
||||
side="bottom"
|
||||
avoidCollisions
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
||||
hideTitle?: boolean;
|
||||
}
|
||||
>(({ className, children, hideTitle = false, ...props }, ref) => {
|
||||
// Check if DialogTitle is included in children
|
||||
const hasDialogTitle = React.Children.toArray(children).some(
|
||||
(child) => React.isValidElement(child) && child.type === DialogTitle,
|
||||
);
|
||||
|
||||
return (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed z-50 flex w-full max-w-lg flex-col gap-4 rounded-xl border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<DialogPrimitive.Close className="absolute right-2 top-2 flex h-8 w-8 items-center justify-center rounded-sm ring-offset-background transition-opacity hover:bg-secondary-hover hover:text-accent-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<Cross2Icon className="h-[18px] w-[18px]" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</ShadTooltip>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
));
|
||||
{!hasDialogTitle && (
|
||||
<VisuallyHidden>
|
||||
<DialogTitle>Dialog</DialogTitle>
|
||||
</VisuallyHidden>
|
||||
)}
|
||||
{children}
|
||||
<ShadTooltip
|
||||
styleClasses="z-50"
|
||||
content="Close"
|
||||
side="bottom"
|
||||
avoidCollisions
|
||||
>
|
||||
<DialogPrimitive.Close className="absolute right-2 top-2 flex h-8 w-8 items-center justify-center rounded-sm ring-offset-background transition-opacity hover:bg-secondary-hover hover:text-accent-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<Cross2Icon className="h-[18px] w-[18px]" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</ShadTooltip>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
);
|
||||
});
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
||||
|
||||
const DialogHeader = ({
|
||||
|
|
@ -126,4 +156,5 @@ export {
|
|||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
VisuallyHidden,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -111,11 +111,7 @@ const AnimationComponent: React.FC<{
|
|||
{segment}
|
||||
</motion.span>
|
||||
) : per === "word" ? (
|
||||
<motion.span
|
||||
aria-hidden="true"
|
||||
variants={variants}
|
||||
className="inline-block whitespace-pre"
|
||||
>
|
||||
<motion.span variants={variants} className="inline-block whitespace-pre">
|
||||
{segment}
|
||||
</motion.span>
|
||||
) : (
|
||||
|
|
@ -123,7 +119,6 @@ const AnimationComponent: React.FC<{
|
|||
{segment.split("").map((char, charIndex) => (
|
||||
<motion.span
|
||||
key={`char-${charIndex}`}
|
||||
aria-hidden="true"
|
||||
variants={variants}
|
||||
className="inline-block whitespace-pre"
|
||||
>
|
||||
|
|
@ -151,7 +146,7 @@ AnimationComponent.displayName = "AnimationComponent";
|
|||
export function TextEffect({
|
||||
children,
|
||||
per = "word",
|
||||
as = "p",
|
||||
as = "span",
|
||||
variants,
|
||||
className,
|
||||
preset,
|
||||
|
|
@ -224,7 +219,7 @@ export function TextEffect({
|
|||
|
||||
export function TextEffectPerChar({ children }: { children: string }) {
|
||||
return (
|
||||
<TextEffect per="char" preset="fade">
|
||||
<TextEffect per="char" preset="fade" as="span">
|
||||
{children}
|
||||
</TextEffect>
|
||||
);
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -9,6 +9,7 @@ export type NoticeAlertType = {
|
|||
link?: string;
|
||||
id: string;
|
||||
removeAlert: (id: string) => void;
|
||||
list?: Array<string>;
|
||||
};
|
||||
export type SuccessAlertType = {
|
||||
title: string;
|
||||
|
|
|
|||
|
|
@ -160,6 +160,7 @@ import {
|
|||
Pen,
|
||||
Pencil,
|
||||
PencilLine,
|
||||
PencilRuler,
|
||||
PieChart,
|
||||
Pin,
|
||||
Plane,
|
||||
|
|
@ -857,6 +858,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
UserMinus2,
|
||||
UserPlus2,
|
||||
Pencil,
|
||||
PencilRuler,
|
||||
ChevronsRight,
|
||||
ChevronsLeft,
|
||||
EyeOff,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue