反应热吐司卡在<dialog>元素

问题描述 投票:0回答:1

Toast 通知无法通过
<dialog>
元素看到

tl;博士

使用内置

dialog.showModal()
会导致
<dialog>
元素始终位于顶部,因此 Toast 通知隐藏在
::backdrop
后面。

简介

不久前,

<dialog>
元素获得了一些很好的功能,可以使用 JavaScript 轻松显示和关闭它。它还附带了一个新的
::backdrop
伪元素,用于在对话框打开时设置背景覆盖层的样式。

我的大多数项目都使用 Next.js 和 Tailwind CSS。因为 Next.js 是基于 React 构建的,所以我可以利用 Tailwind Labs 的 Headless UI 包,并且我之前使用过他们的

<Dialog>
组件而不是
<dialog>
元素。然而,在这个项目中,我决定尝试内置的
<dialog>
元素功能,因为它具有辅助功能。

在本例中,我使用对话框允许用户更改密码。当然,向用户显示通知非常重要,让他们知道密码是否已成功更改。当然,我正在使用 React Hot Toast 来处理这些通知。在之前的项目中,当使用 Headless UI 的

<Dialog>
组件时,我在显示这些通知时没有遇到任何问题。

使用内置的

<dialog>
元素,通知会停留在其后面。如果
::backdrop
伪元素是透明的,这不是一个大问题,但如果是透明的,它看起来总是更好。

已完成测试

我已经测试过通过 Tailwind 类和

z-index
属性更改
<dialog>
元素和
<Toaster>
组件上的
style
。即使使用极端值,这两种方法都不起作用。

代码

请记住,我正在使用应用程序路由器。

<RootLayout>

<Toaster
  position='top-right'
  toastOptions={{
    duration: 3000
  }}
/>

<ChangePassword>
组件

function ChangePassword({ username }: { username: string | undefined }) {

    const changePasswordDialogRef = useRef<HTMLDialogElement>(null),
          currentPasswordInputRef = useRef<HTMLInputElement>(null),
          newPasswordInputRef = useRef<HTMLInputElement>(null),
          formRef = useRef<HTMLFormElement>(null)

    const currentPasswordValueRef = useRef(''),
          newPasswordValueRef = useRef('')

    const currentPasswordIsValidRef = useRef(false),
          newPasswordIsValidRef = useRef(false)

    const openPasswordChangeModal = () => changePasswordDialogRef.current?.showModal()

    const closePasswordChangeModal = () => {
      formRef.current?.reset()
      changePasswordDialogRef.current?.close()
    }

    const [formSubmissionState, setFormSubmissionState] = useState<
      'incomplete' | 'ready' | 'loading' | 'success' | 'error'
    >('incomplete')

    const validateInputs = useCallback(
        (inputId: string, newValue: string) => {
          if (
            (inputId === 'currentPassword' && newValue.length >= 8) ||
            (inputId !== 'currentPassword' &&
            currentPasswordValueRef.current.length >= 8) ||
            getComputedStyle(currentPasswordInputRef.current as Element).position === 'static'
          ) {
            currentPasswordIsValidRef.current = true
          } else {
            currentPasswordIsValidRef.current = false
          }

          if (
            (inputId === 'newPassword' && newValue.length >= 8) ||
            (inputId !== 'newPassword' &&
            newPasswordValueRef.current.length >= 8) ||
            getComputedStyle(newPasswordInputRef.current as Element).position === 'static'
          ) {
            newPasswordIsValidRef.current = true
          } else {
        newPasswordIsValidRef.current = false
      }

      const currentPasswordIsValid = currentPasswordIsValidRef.current,
            newPasswordIsValid = newPasswordIsValidRef.current

      const validInputs = [currentPasswordIsValid, newPasswordIsValid]

      const inputsAreValid = validInputs.includes(false) ? false : true

      if (inputsAreValid && formSubmissionState !== 'ready')
        setFormSubmissionState('ready')
      if (!inputsAreValid && formSubmissionState !== 'incomplete')
        setFormSubmissionState('incomplete')
      },
      [formSubmissionState]
    )

    let inputChangeTimeout: NodeJS.Timeout

    const handleInputChange: ChangeEventHandler<HTMLInputElement> = e => {
      clearTimeout(inputChangeTimeout)

      inputChangeTimeout = setTimeout(async () => {
        const { id, value } = e.target

        switch (id) {
          case 'currentPassword':
            currentPasswordValueRef.current = value
            break
          case 'newPassword':
            newPasswordValueRef.current = value
            break
          default:
            null
        }

        validateInputs(id, value)
      }, 250)
    }

    const handleChangePassword = async () => {
      const currentPassword = currentPasswordValueRef.current,
            newPassword = newPasswordValueRef.current

      if (currentPassword === newPassword) {
        toast.error('You cannot set the new password to your old password.')
        throw new Error('You cannot set the new password to your old password.')
      }
      return true
    }

    const toastChangePassword: FormEventHandler<HTMLFormElement> = e => {
      e.preventDefault()
      e.stopPropagation()

      toast.promise(handleChangePassword(), {
        loading: 'Saving Password...',
        success: () => {
          closePasswordChangeModal()
          return 'Successfully changed password.'
        },
        error: 'Failed to change password. Please try again.'
      })
    }

    return (
        <>
          <div className='w-fit sm:ml-auto -mt-2'>
            <a
              href='#top'
              onClick={openPasswordChangeModal}
            >
              Change Password
            </a>
          </div>

          <dialog
            ref={changePasswordDialogRef}
            className='
              w-[calc(100%-2rem)] max-w-xs
              rounded-3xl
              shadow-xl dark:shadow-zinc-700

              backdrop:bg-zinc-100/60 dark:backdrop:bg-zinc-900/60
              backdrop:backdrop-blur-sm dark:backdrop:brightness-125
            '
          >

          <form
            ref={formRef}
            onSubmit={toastChangePassword}
          >

          <input
            ref={currentPasswordInputRef}
            name='Current Password'
            type='password'
            placeholder='••••••••'
            className='placeholder:font-black placeholder:tracking-widest autofill:static'
            minLength={8}
            onChange={handleInputChange}
            autoComplete='old-password'
            required
          />

          <input
            ref={newPasswordInputRef}
            name='New Password'
            type='password'
            placeholder='••••••••'
            className='placeholder:font-black placeholder:tracking-widest autofill:static'
            minLength={8}
            onChange={handleInputChange}
            autoComplete='new-password'
            required
          />

          <button type='submit'>Update Password</button>

        </form>
      </dialog>
    </>
  )
}
javascript html next.js dialog react-hot-toast
1个回答
0
投票

我使用的软件包略有不同,但遇到了与我解决的

react-hot-toast
相同的问题。

我在 Next.js 应用程序中使用material-tailwind Dialog

react-hot-toast
。我发现将
containerStyle
组件中的
Toaster
属性设置为
{{zIndex: 99999}}
将 Toast 移动到
Dialog
背景模糊前面,以便 Toast 警报处于焦点位置。我尝试了 9999 来获取
zIndex
的值,但仍然不起作用,但 99999 对我有用。

react-hot-toast
样式可以在这里找到:https://react-hot-toast.com/docs/styling

对我有用的完整

Toaster
组件在这里,放置在
RootLayout
中的
layout.tsx
中:

<Toaster
  position="bottom-left"
  containerStyle={{zIndex: 99999}}
  toastOptions={{
    success: {
      style: {
        background: 'lightblue',
      },
      iconTheme: {
        primary: 'white',
        secondary: 'black',
      }
    },
    error: {
      style: {
        background: 'palevioletred',
      },
    },
  }}
/>

© www.soinside.com 2019 - 2024. All rights reserved.