- Notifications
You must be signed in to change notification settings - Fork 0
Fix iOS mobile drawer input focus#32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c974959523c5f8a4e1f68309177dFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -6,20 +6,39 @@ import { Drawer as DrawerPrimitive } from "vaul"; | ||
| import { cn } from "~/lib/utils"; | ||
| const Drawer = ({ | ||
| shouldScaleBackground = true, | ||
| shouldScaleBackground = false, | ||
| noBodyStyles = true, | ||
| fixed = true, | ||
| repositionInputs = true, | ||
| open, | ||
| ...props | ||
| }: React.ComponentProps<typeof DrawerPrimitive.Root>) => ( | ||
| <DrawerPrimitive.Root | ||
| shouldScaleBackground={shouldScaleBackground} | ||
| {...props} | ||
| /> | ||
| ); | ||
| }: React.ComponentProps<typeof DrawerPrimitive.Root>) => { | ||
| React.useEffect(() => { | ||
| if (!open) return; | ||
| document.documentElement.dataset.mobileDrawerOpen = ""; | ||
| return () => { | ||
| delete document.documentElement.dataset.mobileDrawerOpen; | ||
| }; | ||
| }, [open]); | ||
Comment on lines
+16
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash# Find Drawer usages and check whether any are uncontrolled (no `open` prop)
rg -nP --type=tsx -C4 '<Drawer(\s|>)' srcRepository: Matia-R/Chptr Length of output: 179 🏁 Script executed: #!/bin/bashset -euo pipefail
printf'\n== Drawer component ==\n'
ast-grep outline src/app/_components/drawer.tsx --view expanded ||trueprintf'\n== Drawer usages ==\n'
rg -n -C 3 --glob '*.tsx''<Drawer(\s|>|[^A-Za-z])' src ||trueprintf'\n== Related scroll-lock selector ==\n'
rg -n 'mobileDrawerOpen|data-app-scroll-root|DrawerTrigger|defaultOpen|onOpenChange' src ||trueRepository: Matia-R/Chptr Length of output: 4391 🏁 Script executed: #!/bin/bashset -euo pipefail
printf'\n== src/app/_components/drawer.tsx ==\n'
cat -n src/app/_components/drawer.tsx | sed -n '1,220p'printf'\n== src/app/_components/document-actions.tsx ==\n'
cat -n src/app/_components/document-actions.tsx | sed -n '1,240p'printf'\n== src/app/_components/mobile-drawer/mobile-form-drawer.tsx ==\n'
cat -n src/app/_components/mobile-drawer/mobile-form-drawer.tsx | sed -n '1,260p'printf'\n== src/app/_components/document-breadcrumb.tsx ==\n'
cat -n src/app/_components/document-breadcrumb.tsx | sed -n '200,280p'Repository: Matia-R/Chptr Length of output: 22800 🏁 Script executed: #!/bin/bashset -euo pipefail
printf'\n== Where the drawer wrappers are mounted ==\n'
rg -n --glob '*.tsx''DocumentActions|DocumentBreadcrumb|MobileFormDrawer|Drawer\>' src/app src/hooks ||trueprintf'\n== Mobile drawer state coordination ==\n'
rg -n --glob '*.tsx''mobileDrawerOpen|setMobileDrawerOpen|mobileDrawerView|anyPublishPanelOpen|onAuxiliaryOpenChange' src/app src/hooks ||trueRepository: Matia-R/Chptr Length of output: 3008 🏁 Script executed: #!/bin/bashset -euo pipefail
printf'\n== All Drawer JSX usages ==\n'
rg -n --glob '*.tsx''<Drawer(\s|>|[[:space:]])' src/app src/hooks ||trueprintf'\n== Drawer wrapper consumers ==\n'
rg -n --glob '*.tsx''from ".*\/drawer"|from '\''.*\/drawer'\''' src/app src/hooks ||trueprintf'\n== document-publish hook ==\n'
cat -n src/hooks/use-document-publish.tsx | sed -n '250,340p'printf'\n== document-publish button ==\n'
cat -n src/app/_components/document-publish-button.tsx | sed -n '1,120p'Repository: Matia-R/Chptr Length of output: 7802 🏁 Script executed: #!/bin/bashset -euo pipefail
printf'\n== Drawer imports/usages ==\n'
rg -n --glob '*.tsx''import \{[^}]*Drawer[^}]*\} from .*drawer|<Drawer\b|<Drawer ' src/app src/hooks ||trueprintf'\n== Document page composition ==\n'
rg -n --glob '*.tsx''DocumentActions|DocumentBreadcrumb' src/app ||trueprintf'\n== Layouts/pages that render both ==\n'
rg -n --glob '*.tsx''DocumentActions|DocumentBreadcrumb' src/app | sed -n '1,120p'||trueprintf'\n== use-document-publish.tsx (state around drawer) ==\n'
cat -n src/hooks/use-document-publish.tsx | sed -n '1,220p'Repository: Matia-R/Chptr Length of output: 11030 🏁 Script executed: #!/bin/bashset -euo pipefail
printf'\n== Drawer imports/usages ==\n'
rg -n --glob '*.tsx''import \{[^}]*Drawer[^}]*\} from .*drawer|<Drawer\b|<Drawer ' src/app src/hooks ||trueprintf'\n== Document page composition ==\n'
rg -n --glob '*.tsx''DocumentActions|DocumentBreadcrumb' src/app ||trueprintf'\n== use-document-publish.tsx (state around drawer) ==\n'
cat -n src/hooks/use-document-publish.tsx | sed -n '1,220p'Repository: Matia-R/Chptr Length of output: 10511 🏁 Script executed: #!/bin/bashset -euo pipefail
printf'\n== DocumentActions / DocumentBreadcrumb references ==\n'
rg -n --glob '*.tsx''DocumentActions|DocumentBreadcrumb' src/app ||trueprintf'\n== Document page/layout files ==\n'
fd -t f -a 'layout.tsx' src/app ||true
fd -t f -a 'page.tsx' src/app ||trueprintf'\n== Document route layout ==\n'
cat -n src/app/documents/layout.tsx | sed -n '1,220p'Repository: Matia-R/Chptr Length of output: 4121 🏁 Script executed: #!/bin/bashset -euo pipefail
printf'\n== DocumentActions / DocumentBreadcrumb references ==\n'
rg -n --glob '*.tsx''DocumentActions|DocumentBreadcrumb' src/app ||trueprintf'\n== Document route tree ==\n'
fd -t f -a 'src/app/**/layout.tsx''src/app/**/page.tsx'2>/dev/null | sed -n '1,120p'||trueprintf'\n== app/documents/layout.tsx ==\n'
cat -n src/app/documents/layout.tsx | sed -n '1,220p'printf'\n== files that import DocumentActions or DocumentBreadcrumb ==\n'
rg -n --glob '*.tsx''from ".*document-actions"|from ".*document-breadcrumb"' src/app ||trueRepository: Matia-R/Chptr Length of output: 3757 🏁 Script executed: #!/bin/bashset -euo pipefail
printf'\n== document route layout ==\n'
cat -n src/app/documents/layout.tsx | sed -n '1,220p'printf'\n== document-related component imports ==\n'
rg -n --glob '*.tsx''from "~/app/_components/document-actions"|from "~/app/_components/document-breadcrumb"|from "./document-actions"|from "./document-breadcrumb"' src/app ||trueRepository: Matia-R/Chptr Length of output: 3170 🏁 Script executed: #!/bin/bashset -euo pipefail
printf'\n== src/app/_components/document-breadcrumb.tsx ==\n'
cat -n src/app/_components/document-breadcrumb.tsx | sed -n '1,220p'printf'\n== src/app/_components/editor/document-publish-store.ts ==\n'
cat -n src/app/_components/editor/document-publish-store.ts | sed -n '1,220p'Repository: Matia-R/Chptr Length of output: 11074 Shared drawer flag can be cleared too early 🤖 Prompt for AI Agents | ||
| return ( | ||
| <DrawerPrimitive.Root | ||
| shouldScaleBackground={shouldScaleBackground} | ||
| noBodyStyles={noBodyStyles} | ||
| fixed={fixed} | ||
| repositionInputs={repositionInputs} | ||
| open={open} | ||
| {...props} | ||
| /> | ||
| ); | ||
| }; | ||
| Drawer.displayName = "Drawer"; | ||
| const DrawerTrigger = DrawerPrimitive.Trigger; | ||
| const DrawerPortal = DrawerPrimitive.Portal; | ||
| const DrawerClose = DrawerPrimitive.Close; | ||
| const DrawerOverlay = React.forwardRef< | ||
| @@ -32,32 +51,59 @@ const DrawerOverlay = React.forwardRef< | ||
| {...props} | ||
| /> | ||
| )); | ||
| DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName; | ||
| const DrawerContent = React.forwardRef< | ||
| React.ElementRef<typeof DrawerPrimitive.Content>, | ||
| React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> & { | ||
| overlayClassName?: string; | ||
| showHandle?: boolean; | ||
| bottomUnderlay?: boolean; | ||
| bottomUnderlayHeight?: number; | ||
| } | ||
| >(({ className, overlayClassName, children, ...props }, ref) => ( | ||
| <DrawerPortal> | ||
| <DrawerOverlay className={overlayClassName} /> | ||
| <DrawerPrimitive.Content | ||
| ref={ref} | ||
| className={cn( | ||
| // overflow-hidden: Vaul adds a tall ::after for drag/backdrop; without clipping it becomes scrollable empty space. | ||
| // border-x/t only — shorthand `border` draws a bottom edge above the keyboard / home indicator. | ||
| "fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto max-h-none flex-col overflow-hidden rounded-t-[10px] border-x border-t border-sidebar-border bg-sidebar", | ||
| "duration-[280ms] ease-[cubic-bezier(0.22,1,0.36,1)] transition-[height,bottom]", | ||
| className, | ||
| )} | ||
| {...props} | ||
| > | ||
| <div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-sidebar-border" /> | ||
| {children} | ||
| </DrawerPrimitive.Content> | ||
| </DrawerPortal> | ||
| )); | ||
| >( | ||
| ( | ||
| { | ||
| className, | ||
| overlayClassName, | ||
| showHandle = true, | ||
| bottomUnderlay = false, | ||
| bottomUnderlayHeight, | ||
| children, | ||
| ...props | ||
| }, | ||
| ref, | ||
| ) => ( | ||
| <DrawerPortal> | ||
| <DrawerOverlay className={overlayClassName} /> | ||
| {bottomUnderlay && bottomUnderlayHeight && bottomUnderlayHeight > 0 ? ( | ||
| <div | ||
| aria-hidden="true" | ||
| className="pointer-events-none fixed inset-x-0 bottom-0 z-[51] bg-sidebar" | ||
| style={{ height: `${bottomUnderlayHeight}px` }} | ||
| /> | ||
| ) : null} | ||
| <DrawerPrimitive.Content | ||
| ref={ref} | ||
| className={cn( | ||
| "fixed inset-x-0 bottom-0 z-[52] mt-24 flex h-auto max-h-[calc(100dvh-1rem)] flex-col overflow-hidden rounded-t-[10px] border-x border-t border-sidebar-border bg-sidebar", | ||
| className, | ||
| )} | ||
| {...props} | ||
| > | ||
| {showHandle ? ( | ||
| <div className="mx-auto mt-4 h-2 w-[100px] shrink-0 rounded-full bg-sidebar-border" /> | ||
| ) : null} | ||
| {children} | ||
| </DrawerPrimitive.Content> | ||
| </DrawerPortal> | ||
| ), | ||
| ); | ||
| DrawerContent.displayName = "DrawerContent"; | ||
| const DrawerHeader = ({ | ||
| @@ -69,6 +115,7 @@ const DrawerHeader = ({ | ||
| {...props} | ||
| /> | ||
| ); | ||
| DrawerHeader.displayName = "DrawerHeader"; | ||
| const DrawerFooter = ({ | ||
| @@ -80,6 +127,7 @@ const DrawerFooter = ({ | ||
| {...props} | ||
| /> | ||
| ); | ||
| DrawerFooter.displayName = "DrawerFooter"; | ||
| const DrawerTitle = React.forwardRef< | ||
| @@ -95,6 +143,7 @@ const DrawerTitle = React.forwardRef< | ||
| {...props} | ||
| /> | ||
| )); | ||
| DrawerTitle.displayName = DrawerPrimitive.Title.displayName; | ||
| const DrawerDescription = React.forwardRef< | ||
| @@ -107,6 +156,7 @@ const DrawerDescription = React.forwardRef< | ||
| {...props} | ||
| /> | ||
| )); | ||
| DrawerDescription.displayName = DrawerPrimitive.Description.displayName; | ||
| export { | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -4,7 +4,6 @@ import { usePathname } from "next/navigation"; | ||
| import { SidebarTrigger } from "./sidebar"; | ||
| import { DocumentBreadcrumb } from "./document-breadcrumb"; | ||
| import { DocumentActions } from "./document-actions"; | ||
| import { CommandMenuButton } from "./command-menu-button"; | ||
| import { DocumentPublishButton } from "./document-publish-button"; | ||
| export function Header() { | ||
| @@ -13,7 +12,7 @@ export function Header() { | ||
| pathname.startsWith("/documents/") && pathname !== "/documents"; | ||
| return ( | ||
| <header className="flex h-12 shrink-0 items-center justify-between gap-2 rounded-t-2xl bg-background px-4"> | ||
| <header className="z-10 flex h-12 shrink-0 items-center justify-between gap-2 bg-background px-4 max-md:absolute max-md:inset-x-0 max-md:top-0 md:relative md:rounded-t-2xl"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Make the mobile header safe-area aware. With 🤖 Prompt for AI Agents | ||
| <div className="flex min-w-0 flex-1 items-center gap-2"> | ||
| <SidebarTrigger /> | ||
| {isDocumentPage ? ( | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Drop the legacy inset helper from the title-drawer path.
MobileFormDrawernow owns keyboard layout, but Lines 168-170 still queueapplyMobileDrawerKeyboardInset(), which imperatively resizes the first[data-vaul-drawer]in the document. That reintroduces the old global path on top of the new drawer-managed logic.Suggested cleanup
import { - applyMobileDrawerKeyboardInset, focusMobileDrawerInput, MobileFormDrawer, } from "~/app/_components/mobile-drawer"; @@ if (isMobile) { flushSync(() => { setDrawerOpen(true); }); focusMobileDrawerInput(titleInputRef.current); - window.setTimeout(() => {- applyMobileDrawerKeyboardInset();- }, 50); } else {Also applies to: 164-170
🤖 Prompt for AI Agents