fix(ux): auto focus input when top modal dismiss

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei 2024-10-15 20:59:46 +08:00
parent 74abfd3907
commit 3c27768830
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
4 changed files with 39 additions and 12 deletions

View File

@ -21,9 +21,9 @@ const defaultCtxValue: CurrentModalContentProps = {
export const CurrentModalContext = reactCreateContext<CurrentModalContentProps>(defaultCtxValue)
export const CurrentModalStateContext = createContextSelector<{
isActive: boolean
isTop: boolean
}>({
isActive: false,
isTop: true,
})
export type ModalContentComponent<T = object> = FC<ModalActionsInternal & T>

View File

@ -2,13 +2,14 @@ import type { DragControls } from "framer-motion"
import type { ResizeCallback, ResizeStartCallback } from "re-resizable"
import { useCallback, useContext, useId, useRef, useState } from "react"
import { flushSync } from "react-dom"
import { useContextSelector } from "use-context-selector"
import { useEventCallback } from "usehooks-ts"
import { getUISettings } from "~/atoms/settings/ui"
import { jotaiStore } from "~/lib/jotai"
import { modalStackAtom } from "./atom"
import { CurrentModalContext } from "./context"
import { CurrentModalContext, CurrentModalStateContext } from "./context"
import type { ModalProps, ModalStackOptions } from "./types"
export const modalIdToPropsMap = {} as Record<string, ModalProps>
@ -147,3 +148,5 @@ export const useResizeableModal = (
preferDragDir,
}
}
export const useIsTopModal = () => useContextSelector(CurrentModalStateContext, (v) => v.isTop)

View File

@ -3,7 +3,7 @@ import type { BoundingBox } from "framer-motion"
import { produce } from "immer"
import { useSetAtom } from "jotai"
import { Resizable } from "re-resizable"
import type { PropsWithChildren, SyntheticEvent } from "react"
import type { FC, PropsWithChildren, SyntheticEvent } from "react"
import {
createElement,
forwardRef,
@ -34,7 +34,7 @@ import { EllipsisHorizontalTextWithTooltip } from "../../typography"
import { modalStackAtom } from "./atom"
import { MODAL_STACK_Z_INDEX, modalMontionConfig } from "./constants"
import type { CurrentModalContentProps, ModalActionsInternal } from "./context"
import { CurrentModalContext } from "./context"
import { CurrentModalContext, CurrentModalStateContext } from "./context"
import { useModalAnimate } from "./internal/use-animate"
import { useModalResizeAndDrag } from "./internal/use-drag"
import { useModalSelect } from "./internal/use-select"
@ -220,6 +220,7 @@ export const ModalInternal = memo(
}
/>
)
if (CustomModalComponent) {
return (
<Wrapper>
@ -251,9 +252,9 @@ export const ModalInternal = memo(
onSelect={handleSelectStart}
onKeyUp={handleDetectSelectEnd}
>
<CurrentModalContext.Provider value={ModalContextProps}>
<ModalContext modalContextProps={ModalContextProps} isTop={!!isTop}>
<CustomModalComponent>{finalChildren}</CustomModalComponent>
</CurrentModalContext.Provider>
</ModalContext>
</div>
</div>
</Dialog.Content>
@ -350,9 +351,9 @@ export const ModalInternal = memo(
<Divider className="my-2 shrink-0 border-slate-200 opacity-80 dark:border-neutral-800" />
<div className="min-h-0 shrink grow overflow-auto px-4 py-2">
<CurrentModalContext.Provider value={ModalContextProps}>
<ModalContext modalContextProps={ModalContextProps} isTop={!!isTop}>
{finalChildren}
</CurrentModalContext.Provider>
</ModalContext>
</div>
</ResizeSwitch>
</m.div>
@ -374,3 +375,25 @@ const useShortcutScope = () => {
}
}, [switchHotkeyScope])
}
const ModalContext: FC<
PropsWithChildren & {
modalContextProps: CurrentModalContentProps
isTop: boolean
}
> = ({ modalContextProps, isTop, children }) => {
return (
<CurrentModalContext.Provider value={modalContextProps}>
<CurrentModalStateContext.Provider
value={useMemo(
() => ({
isTop: !!isTop,
}),
[isTop],
)}
>
{children}
</CurrentModalStateContext.Provider>
</CurrentModalContext.Provider>
)
}

View File

@ -14,7 +14,7 @@ import { CopyButton } from "~/components/ui/code-highlighter"
import { Form, FormItem, FormLabel } from "~/components/ui/form"
import { Input } from "~/components/ui/input"
import { Markdown } from "~/components/ui/markdown"
import { useCurrentModal, useModalStack } from "~/components/ui/modal"
import { useCurrentModal, useIsTopModal, useModalStack } from "~/components/ui/modal"
import {
Select,
SelectContent,
@ -226,12 +226,13 @@ export const DiscoverFeedForm = ({
)
const formElRef = useRef<HTMLFormElement>(null)
const isTop = useIsTopModal()
useLayoutEffect(() => {
if (!isTop) return
const $form = formElRef.current
if (!$form) return
$form.querySelectorAll("input")[0]?.focus()
}, [formElRef])
}, [formElRef, isTop])
const modal = useCurrentModal()