Ensure appear works using the Transition component (even when used with SSR) (#2646)
* ensure `appear` works in combination with SSR * add appear transition example * update changelog * add scale to appear example * trigger immediate transition once the DOM is ready * ensure React doesn't change the `className` underneath us * handle all base classes We are bypassing React when handling classes in the Transition component. Let's ensure the base classes from the prop are also added correctly. * add missing `base` to tests * simplify `useTransition` hook * add react-hot-toast example * make TS happy * ensure the `classNames` are unique * remove classNames if it results in an empty string This will ensure that we don't end up with `class=""` in the DOM * ensure `unmount` is defaulting to `true` * do not read from `prevShow` in render After fixing the other bugs, this part only caused bugs right now. Even when re-rendering the Transition component while transitioning. Dropping this fixes that behaviour. * extend `appear` demo with appear, show, unmount booleans + a `lazily` one to mimic a conditional render on the client instead of a fresh page refresh.
This commit is contained in:
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Disable smooth scrolling when opening/closing `Dialog` components on iOS ([#2635](https://github.com/tailwindlabs/headlessui/pull/2635))
|
||||
- Don't assume `<Tab />` components are available when setting the next index ([#2642](https://github.com/tailwindlabs/headlessui/pull/2642))
|
||||
- Fix incorrectly focused `Combobox.Input` component on page load ([#2654](https://github.com/tailwindlabs/headlessui/pull/2654))
|
||||
- Ensure `appear` works using the `Transition` component (even when used with SSR) ([#2646](https://github.com/tailwindlabs/headlessui/pull/2646))
|
||||
|
||||
## [1.7.16] - 2023-07-27
|
||||
|
||||
|
||||
@@ -302,7 +302,7 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
|
||||
} = props as typeof props
|
||||
let container = useRef<HTMLElement | null>(null)
|
||||
let transitionRef = useSyncRefs(container, ref)
|
||||
let strategy = rest.unmount ? RenderStrategy.Unmount : RenderStrategy.Hidden
|
||||
let strategy = rest.unmount ?? true ? RenderStrategy.Unmount : RenderStrategy.Hidden
|
||||
|
||||
let { show, appear, initial } = useTransitionContext()
|
||||
|
||||
@@ -310,7 +310,6 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
|
||||
|
||||
let parentNesting = useParentNesting()
|
||||
let { register, unregister } = parentNesting
|
||||
let prevShow = useRef<boolean | null>(null)
|
||||
|
||||
useEffect(() => register(container), [register, container])
|
||||
|
||||
@@ -332,6 +331,7 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
|
||||
}, [state, container, register, unregister, show, strategy])
|
||||
|
||||
let classes = useLatestValue({
|
||||
base: splitClasses(rest.className),
|
||||
enter: splitClasses(enter),
|
||||
enterFrom: splitClasses(enterFrom),
|
||||
enterTo: splitClasses(enterTo),
|
||||
@@ -358,11 +358,11 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
|
||||
|
||||
// Skipping initial transition
|
||||
let skip = initial && !appear
|
||||
let immediate = appear && show && initial
|
||||
|
||||
let transitionDirection = (() => {
|
||||
if (!ready) return 'idle'
|
||||
if (skip) return 'idle'
|
||||
if (prevShow.current === show) return 'idle'
|
||||
return show ? 'enter' : 'leave'
|
||||
})() as TransitionDirection
|
||||
|
||||
@@ -404,6 +404,7 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
|
||||
}, parentNesting)
|
||||
|
||||
useTransition({
|
||||
immediate,
|
||||
container,
|
||||
classes,
|
||||
direction: transitionDirection,
|
||||
@@ -422,25 +423,23 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
|
||||
}),
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!skip) return
|
||||
|
||||
if (strategy === RenderStrategy.Hidden) {
|
||||
prevShow.current = null
|
||||
} else {
|
||||
prevShow.current = show
|
||||
}
|
||||
}, [show, skip, state])
|
||||
|
||||
let theirProps = rest
|
||||
let ourProps = { ref: transitionRef }
|
||||
|
||||
if (appear && show && initial) {
|
||||
if (immediate) {
|
||||
theirProps = {
|
||||
...theirProps,
|
||||
// Already apply the `enter` and `enterFrom` on the server if required
|
||||
className: classNames(rest.className, ...classes.current.enter, ...classes.current.enterFrom),
|
||||
}
|
||||
} else {
|
||||
// When we re-render while we are in the middle of the transition, then we should take the
|
||||
// incoming className and the current classes that are applied.
|
||||
//
|
||||
// This is a bit dirty, but we need to make sure React is not applying changes to the class
|
||||
// attribute while we are transitioning.
|
||||
theirProps.className = classNames(rest.className, container.current?.className)
|
||||
if (theirProps.className === '') delete theirProps.className
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -476,7 +475,7 @@ function TransitionRootFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_C
|
||||
ref: Ref<HTMLElement>
|
||||
) {
|
||||
// @ts-expect-error
|
||||
let { show, appear = false, unmount, ...theirProps } = props as typeof props
|
||||
let { show, appear = false, unmount = true, ...theirProps } = props as typeof props
|
||||
let internalTransitionRef = useRef<HTMLElement | null>(null)
|
||||
let transitionRef = useSyncRefs(internalTransitionRef, ref)
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ it('should be possible to transition', async () => {
|
||||
transition(
|
||||
element,
|
||||
{
|
||||
base: [],
|
||||
enter: ['enter'],
|
||||
enterFrom: ['enterFrom'],
|
||||
enterTo: ['enterTo'],
|
||||
@@ -87,6 +88,7 @@ it('should wait the correct amount of time to finish a transition', async () =>
|
||||
transition(
|
||||
element,
|
||||
{
|
||||
base: [],
|
||||
enter: ['enter'],
|
||||
enterFrom: ['enterFrom'],
|
||||
enterTo: ['enterTo'],
|
||||
@@ -156,6 +158,7 @@ it('should keep the delay time into account', async () => {
|
||||
transition(
|
||||
element,
|
||||
{
|
||||
base: [],
|
||||
enter: ['enter'],
|
||||
enterFrom: ['enterFrom'],
|
||||
enterTo: ['enterTo'],
|
||||
|
||||
@@ -77,6 +77,7 @@ function waitForTransition(node: HTMLElement, done: () => void) {
|
||||
export function transition(
|
||||
node: HTMLElement,
|
||||
classes: {
|
||||
base: string[]
|
||||
enter: string[]
|
||||
enterFrom: string[]
|
||||
enterTo: string[]
|
||||
@@ -116,6 +117,7 @@ export function transition(
|
||||
|
||||
removeClasses(
|
||||
node,
|
||||
...classes.base,
|
||||
...classes.enter,
|
||||
...classes.enterTo,
|
||||
...classes.enterFrom,
|
||||
@@ -124,15 +126,15 @@ export function transition(
|
||||
...classes.leaveTo,
|
||||
...classes.entered
|
||||
)
|
||||
addClasses(node, ...base, ...from)
|
||||
addClasses(node, ...classes.base, ...base, ...from)
|
||||
|
||||
d.nextFrame(() => {
|
||||
removeClasses(node, ...from)
|
||||
addClasses(node, ...to)
|
||||
removeClasses(node, ...classes.base, ...base, ...from)
|
||||
addClasses(node, ...classes.base, ...base, ...to)
|
||||
|
||||
waitForTransition(node, () => {
|
||||
removeClasses(node, ...base)
|
||||
addClasses(node, ...classes.entered)
|
||||
removeClasses(node, ...classes.base, ...base)
|
||||
addClasses(node, ...classes.base, ...classes.entered)
|
||||
|
||||
return _done()
|
||||
})
|
||||
|
||||
@@ -9,8 +9,11 @@ import { useIsoMorphicEffect } from './use-iso-morphic-effect'
|
||||
import { useLatestValue } from './use-latest-value'
|
||||
|
||||
interface TransitionArgs {
|
||||
immediate: boolean
|
||||
container: MutableRefObject<HTMLElement | null>
|
||||
classes: MutableRefObject<{
|
||||
base: string[]
|
||||
|
||||
enter: string[]
|
||||
enterFrom: string[]
|
||||
enterTo: string[]
|
||||
@@ -26,12 +29,25 @@ interface TransitionArgs {
|
||||
onStop: MutableRefObject<(direction: TransitionArgs['direction']) => void>
|
||||
}
|
||||
|
||||
export function useTransition({ container, direction, classes, onStart, onStop }: TransitionArgs) {
|
||||
export function useTransition({
|
||||
immediate,
|
||||
container,
|
||||
direction,
|
||||
classes,
|
||||
onStart,
|
||||
onStop,
|
||||
}: TransitionArgs) {
|
||||
let mounted = useIsMounted()
|
||||
let d = useDisposables()
|
||||
|
||||
let latestDirection = useLatestValue(direction)
|
||||
|
||||
useIsoMorphicEffect(() => {
|
||||
if (!immediate) return
|
||||
|
||||
latestDirection.current = 'enter'
|
||||
}, [immediate])
|
||||
|
||||
useIsoMorphicEffect(() => {
|
||||
let dd = disposables()
|
||||
d.add(dd.dispose)
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
export function classNames(...classes: (false | null | undefined | string)[]): string {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
return Array.from(
|
||||
new Set(
|
||||
classes.flatMap((value) => {
|
||||
if (typeof value === 'string') {
|
||||
return value.split(' ')
|
||||
}
|
||||
|
||||
return []
|
||||
})
|
||||
)
|
||||
)
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-flatpickr": "^3.10.9",
|
||||
"react-hot-toast": "2.3.0",
|
||||
"tailwindcss": "^3.2.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
import { Transition } from '@headlessui/react'
|
||||
import { Fragment, useState } from 'react'
|
||||
import { Button } from '../../components/button'
|
||||
|
||||
export default function AppearExample() {
|
||||
let [show, setShow] = useState(true)
|
||||
let [lazy, setLazy] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="space-y-4 p-8">
|
||||
<div className="flex items-center gap-3">
|
||||
<Button onClick={() => setShow((v) => !v)}>Toggle show</Button>
|
||||
<Button onClick={() => setLazy((v) => !v)}>Toggle lazy</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="rounded-md bg-white p-4 shadow ring-1 ring-black/5">
|
||||
<span className="mb-2">Initial render</span>
|
||||
<div className="grid max-w-6xl grid-cols-4 gap-4">
|
||||
<Transition
|
||||
show={show}
|
||||
appear={true}
|
||||
unmount={true}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
className="aspect-square flex-1 rounded-md bg-blue-200 p-4"
|
||||
>
|
||||
Appear: true, unmount: true
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
show={show}
|
||||
appear={true}
|
||||
unmount={true}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div className="aspect-square flex-1 rounded-md bg-blue-200 p-4">
|
||||
Appear: true, as={`Fragment`}, unmount: true
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
show={show}
|
||||
appear={false}
|
||||
unmount={true}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
className="aspect-square flex-1 rounded-md bg-blue-200 p-4"
|
||||
>
|
||||
Appear: false, unmount: true
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
show={show}
|
||||
appear={false}
|
||||
unmount={true}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div className="aspect-square flex-1 rounded-md bg-blue-200 p-4">
|
||||
Appear: false, as={`Fragment`}, unmount: true
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
show={show}
|
||||
appear={true}
|
||||
unmount={false}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
className="aspect-square flex-1 rounded-md bg-blue-200 p-4"
|
||||
>
|
||||
Appear: true, unmount: false
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
show={show}
|
||||
appear={true}
|
||||
unmount={false}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div className="aspect-square flex-1 rounded-md bg-blue-200 p-4">
|
||||
Appear: true, as={`Fragment`}, unmount: false
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
show={show}
|
||||
appear={false}
|
||||
unmount={false}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
className="aspect-square flex-1 rounded-md bg-blue-200 p-4"
|
||||
>
|
||||
Appear: false, unmount: false
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
show={show}
|
||||
appear={false}
|
||||
unmount={false}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div className="aspect-square flex-1 rounded-md bg-blue-200 p-4">
|
||||
Appear: false, as={`Fragment`}, unmount: false
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{lazy && (
|
||||
<div className="rounded-md bg-white p-4 shadow ring-1 ring-black/5">
|
||||
<span className="mb-2">Not on the initial render</span>
|
||||
<div className="grid max-w-6xl grid-cols-4 gap-4">
|
||||
<Transition
|
||||
show={show}
|
||||
appear={true}
|
||||
unmount={true}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
className="aspect-square flex-1 rounded-md bg-blue-200 p-4"
|
||||
>
|
||||
Appear: true, unmount: true
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
show={show}
|
||||
appear={true}
|
||||
unmount={true}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div className="aspect-square flex-1 rounded-md bg-blue-200 p-4">
|
||||
Appear: true, as={`Fragment`}, unmount: true
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
show={show}
|
||||
appear={false}
|
||||
unmount={true}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
className="aspect-square flex-1 rounded-md bg-blue-200 p-4"
|
||||
>
|
||||
Appear: false, unmount: true
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
show={show}
|
||||
appear={false}
|
||||
unmount={true}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div className="aspect-square flex-1 rounded-md bg-blue-200 p-4">
|
||||
Appear: false, as={`Fragment`}, unmount: true
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
show={show}
|
||||
appear={true}
|
||||
unmount={false}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
className="aspect-square flex-1 rounded-md bg-blue-200 p-4"
|
||||
>
|
||||
Appear: true, unmount: false
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
show={show}
|
||||
appear={true}
|
||||
unmount={false}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div className="aspect-square flex-1 rounded-md bg-blue-200 p-4">
|
||||
Appear: true, as={`Fragment`}, unmount: false
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
show={show}
|
||||
appear={false}
|
||||
unmount={false}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
className="aspect-square flex-1 rounded-md bg-blue-200 p-4"
|
||||
>
|
||||
Appear: false, unmount: false
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
show={show}
|
||||
appear={false}
|
||||
unmount={false}
|
||||
enter="duration-1000 transition"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-1000 transition"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div className="aspect-square flex-1 rounded-md bg-blue-200 p-4">
|
||||
Appear: false, as={`Fragment`}, unmount: false
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import React from 'react'
|
||||
import { Transition } from '@headlessui/react'
|
||||
import { Toaster, ToastIcon, toast, resolveValue } from 'react-hot-toast'
|
||||
|
||||
const TailwindToaster = () => {
|
||||
return (
|
||||
<Toaster position="top-right">
|
||||
{(t) => (
|
||||
<Transition
|
||||
appear
|
||||
show={t.visible}
|
||||
className="flex transform rounded bg-white p-4 shadow-lg"
|
||||
enter="transition-all duration-500"
|
||||
enterFrom="opacity-0 scale-50"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="transition-all duration-150"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-75"
|
||||
>
|
||||
<ToastIcon toast={t} />
|
||||
<p className="px-2">{resolveValue(t.message, t)}</p>
|
||||
</Transition>
|
||||
)}
|
||||
</Toaster>
|
||||
)
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="m-8">
|
||||
<button
|
||||
className="rounded bg-blue-500 p-4 text-white"
|
||||
onClick={() => toast.success('This is Tailwind CSS')}
|
||||
>
|
||||
Create TailwindCSS Toast
|
||||
</button>
|
||||
<TailwindToaster />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -2746,6 +2746,11 @@ globals@^11.1.0:
|
||||
resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
|
||||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
||||
|
||||
goober@^2.1.10:
|
||||
version "2.1.13"
|
||||
resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.13.tgz#e3c06d5578486212a76c9eba860cbc3232ff6d7c"
|
||||
integrity sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==
|
||||
|
||||
graceful-fs@^4.1.2, graceful-fs@^4.2.4:
|
||||
version "4.2.9"
|
||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz"
|
||||
@@ -4719,6 +4724,13 @@ react-flatpickr@^3.10.9:
|
||||
flatpickr "^4.6.2"
|
||||
prop-types "^15.5.10"
|
||||
|
||||
react-hot-toast@2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.3.0.tgz#70b3d183ac2a4afb6b17cda4a7f4cfe02e730415"
|
||||
integrity sha512-/RxV+bfjld7tSJR1SCLzMAXgFuNW7fCpK6+vbYqfmbGSWcqTMz2rizrvfWKvtcPH5HK0NqxmBaC5SrAy1F42zA==
|
||||
dependencies:
|
||||
goober "^2.1.10"
|
||||
|
||||
react-is@^16.13.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
||||
|
||||
Reference in New Issue
Block a user