feat: upgrade to tailwindcss v4
Some checks are pending
Deploy React Webpage / build (push) Waiting to run
Deploy React Webpage / deploy (push) Blocked by required conditions

This commit is contained in:
Shinwoo PARK 2025-03-11 20:30:21 +09:00
parent b5edb5d03e
commit c0b58d1737
24 changed files with 2141 additions and 2062 deletions

View File

@ -45,9 +45,10 @@
"lefthook": "^1.6.18",
"postcss": "^8.4.38",
"tailwind-scrollbar": "^3.1.0",
"tailwindcss": "^3.4.4",
"tailwindcss": "^4.0.12",
"typescript": "^5.4.5",
"vite": "^5.3.0",
"vite-plugin-dynamic-import": "^1.5.0"
}
},
"packageManager": "yarn@4.4.0+sha512.91d93b445d9284e7ed52931369bc89a663414e5582d00eea45c67ddc459a2582919eece27c412d6ffd1bd0793ff35399381cb229326b961798ce4f4cc60ddfdb"
}

View File

@ -33,7 +33,7 @@ function SideNav() {
function DocsLayout() {
return (
<div className="flex-grow grid grid-cols-1 md:grid-cols-[12rem_1fr] lg:grid-cols-[12rem_1fr_10rem] w-full max-w-5xl mx-auto">
<div className="grow grid grid-cols-1 md:grid-cols-[12rem_1fr] lg:grid-cols-[12rem_1fr_10rem] w-full max-w-5xl mx-auto">
<SideNav />
<Outlet />
</div>

View File

@ -3,7 +3,7 @@ import { Link } from "react-router-dom";
function Home() {
return (
<main className="flex-grow h-full flex flex-col p-4 justify-center items-center">
<main className="grow h-full flex flex-col p-4 justify-center items-center">
<section className="h-full flex flex-col justify-center items-center text-center gap-8">
<header className="flex flex-col justify-center items-center gap-2">
<h1 className="text-4xl font-bold">

View File

@ -150,7 +150,7 @@ function TopNav() {
</svg>
</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerOverlay className="z-99">
<DrawerContent className="w-[300px] overflow-auto">
<DrawerClose className="absolute top-4 right-4">
<Button

View File

@ -16,7 +16,7 @@ export const Bottom = () => {
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerOverlay className="z-99">
<DrawerContent position="bottom">
<DrawerHeader>
<h1 className="text-2xl font-bold">Drawer</h1>

View File

@ -16,7 +16,7 @@ export const Left = () => {
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerOverlay className="z-99">
<DrawerContent
position="left"
className="max-w-[320px]"

View File

@ -16,7 +16,7 @@ export const Right = () => {
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerOverlay className="z-99">
<DrawerContent
position="right"
className="max-w-[320px]"

View File

@ -16,7 +16,7 @@ export const Top = () => {
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerOverlay className="z-99">
<DrawerContent position="top">
<DrawerHeader>
<h1 className="text-2xl font-bold">Drawer</h1>

View File

@ -33,7 +33,7 @@ export function DrawerDemo() {
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerOverlay className="z-99">
<DrawerContent
position={DrawerContentProps.position}
maxSize={DrawerContentProps.maxSize}

View File

@ -67,7 +67,7 @@ export function PopoverDemo() {
d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"
/>
</svg>
<span className="flex-grow text-left">Dashboard</span>
<span className="grow text-left">Dashboard</span>
</Button>
<Button
preset="ghost"
@ -85,7 +85,7 @@ export function PopoverDemo() {
d="m17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5M4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4z"
/>
</svg>
<span className="flex-grow text-left">Log out</span>
<span className="grow text-left">Log out</span>
</Button>
</PopoverContent>
</Popover>

View File

@ -12,7 +12,7 @@ Organizes content into multiple sections with tabbed navigation.
<TabTrigger name="code">Code</TabTrigger>
</TabList>
<TabContent name="preview">
<Story layout="centered" className="flex-col [&>*]:w-full">
<Story layout="centered" className="flex-col *:w-full">
<TabsDemo />
</Story>
</TabContent>

View File

@ -3,7 +3,7 @@ import { Link } from "react-router-dom";
function PageNotFound() {
return (
<main className="flex-grow h-full flex flex-col justify-center items-center gap-8">
<main className="grow h-full flex flex-col justify-center items-center gap-8">
<section className="flex flex-col justify-center items-center text-center gap-2">
<h1 className="text-4xl font-bold">Page not found</h1>
<p className="text-base">

View File

@ -3,7 +3,7 @@ import { Link } from "react-router-dom";
function UnexpectedError() {
return (
<main className="flex-grow h-full flex flex-col justify-center items-center gap-8">
<main className="grow h-full flex flex-col justify-center items-center gap-8">
<section className="flex flex-col justify-center items-center text-center gap-2">
<h1 className="text-4xl font-bold">Something went wrong</h1>
<p className="text-base">

View File

@ -14,21 +14,18 @@ const colors = {
danger: "border-red-400 dark:border-red-600",
},
background: {
default:
"bg-white dark:bg-black hover:bg-neutral-200 dark:hover:bg-neutral-800",
default: "bg-white dark:bg-black",
ghost:
"bg-black/0 dark:bg-white/0 hover:bg-black/20 dark:hover:bg-white/20",
success:
"bg-green-100 dark:bg-green-900 hover:bg-green-200 dark:hover:bg-green-800",
warning:
"bg-yellow-100 dark:bg-yellow-900 hover:bg-yellow-200 dark:hover:bg-yellow-800",
danger: "bg-red-100 dark:bg-red-900 hover:bg-red-200 dark:hover:bg-red-800",
success: "bg-green-100 dark:bg-green-900",
warning: "bg-yellow-100 dark:bg-yellow-900",
danger: "bg-red-100 dark:bg-red-900",
},
underline: "decoration-current",
};
const [buttonVariants, resolveVariants] = vcn({
base: `w-fit flex flex-row items-center justify-between rounded-md outline outline-1 outline-transparent outline-offset-2 ${colors.outline.focus} ${colors.disabled} transition-all cursor-pointer`,
base: `w-fit flex flex-row items-center justify-between rounded-md outline outline-1 outline-transparent outline-offset-2 hover:brightness-90 dark:hover:brightness-110 ${colors.outline.focus} ${colors.disabled} transition-all cursor-pointer`,
variants: {
size: {
link: "p-0 text-base",
@ -111,7 +108,8 @@ export interface ButtonProps
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
(props, ref) => {
const [variantProps, otherPropsCompressed] = resolveVariants(props);
const { asChild, type, ...otherPropsExtracted } = otherPropsCompressed;
const { asChild, type, role, ...otherPropsExtracted } =
otherPropsCompressed;
const Comp = asChild ? Slot : "button";
@ -120,6 +118,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
ref={ref}
type={type ?? "button"}
className={buttonVariants(variantProps)}
role={role ?? "button"}
{...otherPropsExtracted}
/>
);

View File

@ -2,6 +2,7 @@ import {
type AsChild,
Slot,
type VariantProps,
useAnimatedMount,
useDocument,
vcn,
} from "@pswui-lib";
@ -21,6 +22,8 @@ interface IDrawerContext {
closeThreshold: number;
movePercentage: number;
isDragging: boolean;
isMounted: boolean;
isRendered: boolean;
leaveWhileDragging: boolean;
}
const DrawerContextInitial: IDrawerContext = {
@ -28,6 +31,8 @@ const DrawerContextInitial: IDrawerContext = {
closeThreshold: 0.3,
movePercentage: 0,
isDragging: false,
isMounted: false,
isRendered: false,
leaveWhileDragging: false,
};
const DrawerContext = React.createContext<
@ -102,8 +107,14 @@ interface DrawerOverlayProps
const DrawerOverlay = forwardRef<HTMLDivElement, DrawerOverlayProps>(
(props, ref) => {
const internalRef = useRef<HTMLDivElement | null>(null);
const [state, setState] = useContext(DrawerContext);
const { isMounted, isRendered } = useAnimatedMount(
state.isDragging ? true : state.opened,
internalRef,
);
const [variantProps, restPropsCompressed] =
resolveDrawerOverlayVariantProps(props);
const { asChild, ...restPropsExtracted } = restPropsCompressed;
@ -128,22 +139,39 @@ const DrawerOverlay = forwardRef<HTMLDivElement, DrawerOverlayProps>(
const document = useDocument();
if (!document) return null;
return createPortal(
<Comp
{...restPropsExtracted}
className={drawerOverlayVariant({
...variantProps,
opened: state.isDragging ? true : state.opened,
})}
onClick={onOutsideClick}
style={{
backdropFilter,
WebkitBackdropFilter: backdropFilter,
transitionDuration: state.isDragging ? "0s" : undefined,
}}
ref={ref}
/>,
document.body,
return (
<>
<DrawerContext.Provider
value={[{ ...state, isMounted, isRendered }, setState]}
>
{isMounted
? createPortal(
<Comp
{...restPropsExtracted}
className={drawerOverlayVariant({
...variantProps,
opened: isRendered,
})}
onClick={onOutsideClick}
style={{
backdropFilter,
WebkitBackdropFilter: backdropFilter,
transitionDuration: state.isDragging ? "0s" : undefined,
}}
ref={(el: HTMLDivElement) => {
internalRef.current = el;
if (typeof ref === "function") {
ref(el);
} else if (ref) {
ref.current = el;
}
}}
/>,
document.body,
)
: null}
</DrawerContext.Provider>
</>
);
},
);
@ -158,10 +186,10 @@ const [drawerContentVariant, resolveDrawerContentVariantProps] = vcn({
base: `fixed ${drawerContentColors.background} ${drawerContentColors.border} transition-all p-4 flex flex-col justify-between gap-8 overflow-auto`,
variants: {
position: {
top: "top-0 inset-x-0 w-full max-w-screen rounded-t-lg border-b-2",
bottom: "bottom-0 inset-x-0 w-full max-w-screen rounded-b-lg border-t-2",
left: "left-0 inset-y-0 h-screen rounded-l-lg border-r-2",
right: "right-0 inset-y-0 h-screen rounded-r-lg border-l-2",
top: "top-0 w-full max-w-screen rounded-t-lg border-b-2",
bottom: "bottom-0 w-full max-w-screen rounded-b-lg border-t-2",
left: "left-0 h-screen rounded-l-lg border-r-2",
right: "right-0 h-screen rounded-r-lg border-l-2",
},
maxSize: {
sm: "[&.left-0]:max-w-sm [&.right-0]:max-w-sm",
@ -174,16 +202,36 @@ const [drawerContentVariant, resolveDrawerContentVariantProps] = vcn({
false:
"[&.top-0]:-translate-y-full [&.bottom-0]:translate-y-full [&.left-0]:-translate-x-full [&.right-0]:translate-x-full",
},
internal: {
true: "relative",
false: "fixed",
},
},
defaults: {
position: "left",
opened: false,
maxSize: "sm",
internal: false,
},
dynamics: [
({ position, internal }) => {
if (!internal) {
if (["top", "bottom"].includes(position)) {
return "inset-x-0";
}
return "inset-y-0";
}
return "w-fit";
},
],
});
interface DrawerContentProps
extends Omit<VariantProps<typeof drawerContentVariant>, "opened">,
extends Omit<
VariantProps<typeof drawerContentVariant>,
"opened" | "internal"
>,
AsChild,
ComponentPropsWithoutRef<"div"> {}
@ -324,7 +372,7 @@ const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
<div
className={drawerContentVariant({
...variantProps,
opened: true,
opened: state.isRendered,
className: dragState.isDragging
? "transition-[width] duration-0"
: variantProps.className,
@ -338,6 +386,7 @@ const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
0) +
(position === "top" ? dragState.delta : -dragState.delta),
padding: 0,
[`padding${position.slice(0, 1).toUpperCase()}${position.slice(1)}`]: `${dragState.delta}px`,
}
: {
width:
@ -345,6 +394,7 @@ const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
0) +
(position === "left" ? dragState.delta : -dragState.delta),
padding: 0,
[`padding${position.slice(0, 1).toUpperCase()}${position.slice(1)}`]: `${dragState.delta}px`,
}
: { width: 0, height: 0, padding: 0 }
}
@ -353,14 +403,16 @@ const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
{...restPropsExtracted}
className={drawerContentVariant({
...variantProps,
opened: state.opened,
opened: state.isRendered,
internal: true,
})}
style={{
transform: dragState.isDragging
? `translate${["top", "bottom"].includes(position) ? "Y" : "X"}(${
dragState.delta
}px)`
: undefined,
transform:
dragState.isDragging &&
((["top", "left"].includes(position) && dragState.delta < 0) ||
(["bottom", "right"].includes(position) && dragState.delta > 0))
? `translate${["top", "bottom"].includes(position) ? "Y" : "X"}(${dragState.delta}px)`
: undefined,
transitionDuration: dragState.isDragging ? "0s" : undefined,
userSelect: dragState.isDragging ? "none" : undefined,
}}
@ -439,7 +491,7 @@ const DrawerHeader = forwardRef<HTMLDivElement, DrawerHeaderProps>(
DrawerHeader.displayName = "DrawerHeader";
const [drawerBodyVariant, resolveDrawerBodyVariantProps] = vcn({
base: "flex-grow",
base: "grow",
variants: {},
defaults: {},
});

View File

@ -0,0 +1,183 @@
import { type AsChild, Slot, type VariantProps, vcn } from "@pswui-lib";
import {
type ComponentPropsWithoutRef,
createContext,
forwardRef,
useContext,
useEffect,
useRef,
} from "react";
/**
Form Item Context
**/
interface IFormItemContext {
invalid?: string | null | undefined;
}
const FormItemContext = createContext<IFormItemContext>({});
/**
FormItem
**/
const [formItemVariant, resolveFormItemVariantProps] = vcn({
base: "flex flex-col gap-2 items-start w-full",
variants: {},
defaults: {},
});
interface FormItemProps
extends VariantProps<typeof formItemVariant>,
AsChild,
ComponentPropsWithoutRef<"label"> {
invalid?: string | null | undefined;
}
const FormItem = forwardRef<HTMLLabelElement, FormItemProps>((props, ref) => {
const [variantProps, restPropsCompressed] =
resolveFormItemVariantProps(props);
const { asChild, children, invalid, ...restPropsExtracted } =
restPropsCompressed;
const innerRef = useRef<HTMLLabelElement | null>(null);
useEffect(() => {
const invalidAsString = invalid ? invalid : "";
const input = innerRef.current?.querySelector?.("input");
if (!input) return;
input.setCustomValidity(invalidAsString);
}, [invalid]);
const Comp = asChild ? Slot : "label";
return (
<FormItemContext.Provider value={{ invalid }}>
<Comp
ref={(el: HTMLLabelElement | null) => {
innerRef.current = el;
if (typeof ref === "function") {
ref(el);
} else if (ref) {
ref.current = el;
}
}}
className={formItemVariant(variantProps)}
{...restPropsExtracted}
>
{children}
</Comp>
</FormItemContext.Provider>
);
});
FormItem.displayName = "FormItem";
/**
FormLabel
**/
const [formLabelVariant, resolveFormLabelVariantProps] = vcn({
base: "text-sm font-bold",
variants: {},
defaults: {},
});
interface FormLabelProps
extends VariantProps<typeof formLabelVariant>,
AsChild,
ComponentPropsWithoutRef<"span"> {}
const FormLabel = forwardRef<HTMLSpanElement, FormLabelProps>((props, ref) => {
const [variantProps, otherPropsCompressed] =
resolveFormLabelVariantProps(props);
const { children, asChild, ...otherPropsExtracted } = otherPropsCompressed;
const Comp = asChild ? Slot : "span";
return (
<Comp
ref={ref}
className={formLabelVariant(variantProps)}
{...otherPropsExtracted}
>
{children}
</Comp>
);
});
FormLabel.displayName = "FormLabel";
/**
FormHelper
**/
const [formHelperVariant, resolveFormHelperVariantProps] = vcn({
base: "opacity-75 text-sm font-light",
variants: {},
defaults: {},
});
interface FormHelperProps
extends VariantProps<typeof formHelperVariant>,
AsChild,
ComponentPropsWithoutRef<"span"> {
hiddenOnInvalid?: boolean;
}
const FormHelper = forwardRef<HTMLSpanElement, FormHelperProps>(
(props, ref) => {
const [variantProps, otherPropsCompressed] =
resolveFormHelperVariantProps(props);
const { asChild, children, hiddenOnInvalid, ...otherPropsExtracted } =
otherPropsCompressed;
const item = useContext(FormItemContext);
if (item.invalid && hiddenOnInvalid) return null;
const Comp = asChild ? Slot : "span";
return (
<Comp
ref={ref}
className={formHelperVariant(variantProps)}
{...otherPropsExtracted}
>
{children}
</Comp>
);
},
);
FormHelper.displayName = "FormHelper";
/**
FormError
**/
const [formErrorVariant, resolveFormErrorVariantProps] = vcn({
base: "text-sm text-red-500",
variants: {},
defaults: {},
});
interface FormErrorProps
extends VariantProps<typeof formErrorVariant>,
AsChild,
Omit<ComponentPropsWithoutRef<"span">, "children"> {}
const FormError = forwardRef<HTMLSpanElement, FormErrorProps>((props, ref) => {
const [variantProps, otherPropsCompressed] =
resolveFormErrorVariantProps(props);
const { asChild, ...otherPropsExtracted } = otherPropsCompressed;
const item = useContext(FormItemContext);
const Comp = asChild ? Slot : "span";
return item.invalid ? (
<Comp
ref={ref}
className={formErrorVariant(variantProps)}
{...otherPropsExtracted}
>
{item.invalid}
</Comp>
) : null;
});
FormError.displayName = "FormError";
export { FormItem, FormLabel, FormHelper, FormError };

View File

@ -4,34 +4,35 @@ import React from "react";
const inputColors = {
background: {
default: "bg-neutral-50 dark:bg-neutral-900",
hover: "hover:bg-neutral-100 dark:hover:bg-neutral-800",
hover:
"hover:bg-neutral-100 dark:hover:bg-neutral-800 has-[input:hover]:bg-neutral-100 dark:has-[input:hover]:bg-neutral-800",
invalid:
"invalid:bg-red-100 invalid:dark:bg-red-900 has-[input:invalid]:bg-red-100 dark:has-[input:invalid]:bg-red-900",
"invalid:bg-red-100 dark:invalid:bg-red-900 has-[input:invalid]:bg-red-100 dark:has-[input:invalid]:bg-red-900",
invalidHover:
"hover:invalid:bg-red-200 dark:hover:invalid:bg-red-800 has-[input:invalid:hover]:bg-red-200 dark:has-[input:invalid:hover]:bg-red-800",
},
border: {
default: "border-neutral-400 dark:border-neutral-600",
invalid:
"invalid:border-red-400 invalid:dark:border-red-600 has-[input:invalid]:border-red-400 dark:has-[input:invalid]:border-red-600",
"invalid:border-red-400 dark:invalid:border-red-600 has-[input:invalid]:border-red-400 dark:has-[input:invalid]:border-red-600",
},
ring: {
default: "ring-transparent focus-within:ring-current",
invalid:
"invalid:focus-within:ring-red-400 invalid:focus-within:dark:ring-red-600 has-[input:invalid]:focus-within:ring-red-400 dark:has-[input:invalid]:focus-within:ring-red-600",
"invalid:focus-within:ring-red-400 dark:invalid:focus-within:ring-red-600 has-[input:invalid]:focus-within:ring-red-400 dark:has-[input:invalid]:focus-within:ring-red-600",
},
};
const [inputVariant, resolveInputVariantProps] = vcn({
base: `rounded-md p-2 border ring-1 outline-none transition-all duration-200 [appearance:textfield] disabled:brightness-50 disabled:saturate-0 disabled:cursor-not-allowed ${inputColors.background.default} ${inputColors.background.hover} ${inputColors.border.default} ${inputColors.ring.default} ${inputColors.background.invalid} ${inputColors.background.invalidHover} ${inputColors.border.invalid} ${inputColors.ring.invalid} [&:has(input)]:flex [&:has(input)]:w-fit`,
base: `rounded-md p-2 border ring-1 outline-hidden transition-all duration-200 [appearance:textfield] disabled:brightness-50 disabled:saturate-0 disabled:cursor-not-allowed ${inputColors.background.default} ${inputColors.background.hover} ${inputColors.border.default} ${inputColors.ring.default} ${inputColors.background.invalid} ${inputColors.background.invalidHover} ${inputColors.border.invalid} ${inputColors.ring.invalid} [&:has(input)]:flex`,
variants: {
unstyled: {
true: "bg-transparent border-none p-0 ring-0 hover:bg-transparent invalid:hover:bg-transparent invalid:focus-within:bg-transparent invalid:focus-within:ring-0",
false: "",
},
full: {
true: "w-full",
false: "w-fit",
true: "[&:has(input)]:w-full w-full",
false: "[&:has(input)]:w-fit w-fit",
},
},
defaults: {

View File

@ -65,7 +65,7 @@ const popoverColors = {
};
const [popoverContentVariant, resolvePopoverContentVariantProps] = vcn({
base: `absolute transition-all duration-150 border rounded-lg p-0.5 z-10 [&>*]:w-full ${popoverColors.background} ${popoverColors.border}`,
base: `absolute transition-all duration-150 border rounded-lg p-0.5 z-10 *:w-full ${popoverColors.background} ${popoverColors.border}`,
variants: {
direction: {
row: "",

View File

@ -39,7 +39,7 @@ const TabList = (props: TabListProps) => {
};
const [TabTriggerVariant, resolveTabTriggerVariantProps] = vcn({
base: "py-1.5 rounded-md flex-grow transition-all text-sm",
base: "py-1.5 rounded-md grow transition-all text-sm",
variants: {
active: {
true: "bg-white/100 dark:bg-black/100 text-black dark:text-white",

View File

@ -140,6 +140,7 @@ const TooltipContent = React.forwardRef<HTMLDivElement, TooltipContentProps>(
...variantProps,
position: contextState.position,
})}
role="tooltip"
{...rest}
/>
);

View File

@ -0,0 +1,85 @@
import { type MutableRefObject, useCallback, useEffect, useState } from "react";
function getCalculatedTransitionDuration(
ref: MutableRefObject<HTMLElement>,
): number {
let transitionDuration: {
value: number;
unit: string;
} | null;
if (ref.current.computedStyleMap !== undefined) {
transitionDuration = ref.current
.computedStyleMap()
.get("transition-duration") as { value: number; unit: string };
} else {
const style = /(\d+(\.\d+)?)(.+)/.exec(
window.getComputedStyle(ref.current).transitionDuration,
);
if (!style) return 0;
transitionDuration = {
value: Number.parseFloat(style[1] ?? "0"),
unit: style[3] ?? style[2] ?? "s",
};
}
return (
transitionDuration.value *
({
s: 1000,
ms: 1,
}[transitionDuration.unit] ?? 1)
);
}
/*
* isMounted: true isRendered: true isRendered: false isMounted: false
* Component Mount Component Appear Component Disappear Component Unmount
* v v v v
* |-|=================|------------------------|======================|-|
*/
function useAnimatedMount(
visible: boolean,
ref: MutableRefObject<HTMLElement | null>,
callbacks?: { onMount: () => void; onUnmount: () => void },
) {
const [state, setState] = useState<{
isMounted: boolean;
isRendered: boolean;
}>({ isMounted: visible, isRendered: visible });
const umountCallback = useCallback(() => {
setState((p) => ({ ...p, isRendered: false }));
const calculatedTransitionDuration = ref.current
? getCalculatedTransitionDuration(ref as MutableRefObject<HTMLElement>)
: 0;
setTimeout(() => {
setState((p) => ({ ...p, isMounted: false }));
callbacks?.onUnmount?.();
}, calculatedTransitionDuration);
}, [ref, callbacks]);
const mountCallback = useCallback(() => {
setState((p) => ({ ...p, isMounted: true }));
callbacks?.onMount?.();
requestAnimationFrame(function onMount() {
if (!ref.current) return requestAnimationFrame(onMount);
setState((p) => ({ ...p, isRendered: true }));
});
}, [ref.current, callbacks]);
useEffect(() => {
console.log(state);
if (!visible && state.isRendered) {
umountCallback();
} else if (visible && !state.isMounted) {
mountCallback();
}
}, [state, visible, mountCallback, umountCallback]);
return state;
}
export { getCalculatedTransitionDuration, useAnimatedMount };

View File

@ -1,8 +1,41 @@
@import url("https://cdn.jsdelivr.net/gh/wanteddev/wanted-sans@v1.0.3/packages/wanted-sans/fonts/webfonts/variable/split/WantedSansVariable.min.css");
@tailwind base;
@tailwind components;
@tailwind utilities;
@import url('https://cdn.jsdelivr.net/gh/wanteddev/wanted-sans@v1.0.3/packages/wanted-sans/fonts/webfonts/variable/split/WantedSansVariable.min.css')
layer(base);
@import 'tailwindcss';
@plugin '@tailwindcss/typography';
@plugin 'tailwind-scrollbar';
@source '../{components,stories,src}/**/*.{js,jsx,ts,tsx,css,mdx}';
@custom-variant dark {
@media (prefers-color-scheme: dark) {
&:is(.system *) {
@slot;
}
}
&:is(.dark *) {
@slot;
}
}
/*
The default border color has changed to `currentColor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}
@layer base {
:root {
--dark-bg-color: #000;

View File

@ -1,15 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./{components,stories,src}/**/*.{js,jsx,ts,tsx,css,mdx}"],
darkMode: [
"variant",
[
"@media (prefers-color-scheme: dark) { &:is(.system *) }",
"&:is(.dark *)",
],
],
theme: {
extend: {},
},
plugins: [require("@tailwindcss/typography"), require("tailwind-scrollbar")],
};

3699
yarn.lock

File diff suppressed because it is too large Load Diff