mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
refactor: optimize safe area handling in web components
Apply safe area insets to sticky/fixed headers (using top inset) and bottom-fixed elements (using bottom inset), rather than to scroll containers. This ensures proper spacing on devices with notches and home indicators (e.g., iPhone). - Sessions List page: Move header outside scroll container with sticky positioning and pt-[env(safe-area-inset-top)] - SessionList component: Add renderHeader prop to optionally hide internal header when parent renders it - HappyComposer: Change bottom padding to pb-[calc(0.75rem+env(safe-area-inset-bottom))] for proper iPhone home indicator spacing
This commit is contained in:
@@ -436,7 +436,7 @@ export function HappyComposer(props: {
|
||||
])
|
||||
|
||||
return (
|
||||
<div className="px-3 pb-3 pt-2 bg-[var(--app-bg)]">
|
||||
<div className="px-3 pb-[calc(0.75rem+env(safe-area-inset-bottom))] pt-2 bg-[var(--app-bg)]">
|
||||
<div className="mx-auto w-full max-w-content">
|
||||
<ComposerPrimitive.Root className="relative">
|
||||
{overlays}
|
||||
|
||||
@@ -98,22 +98,27 @@ export function SessionList(props: {
|
||||
onNewSession: () => void
|
||||
onRefresh: () => void
|
||||
isLoading: boolean
|
||||
renderHeader?: boolean
|
||||
}) {
|
||||
const { renderHeader = true } = props
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-content flex flex-col">
|
||||
<div className="flex items-center justify-between px-3 py-1">
|
||||
<div className="text-xs text-[var(--app-hint)]">
|
||||
{props.sessions.length} sessions
|
||||
{renderHeader ? (
|
||||
<div className="flex items-center justify-between px-3 py-1">
|
||||
<div className="text-xs text-[var(--app-hint)]">
|
||||
{props.sessions.length} sessions
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={props.onNewSession}
|
||||
className="session-list-new-button p-1.5 rounded-full text-[var(--app-link)] transition-colors"
|
||||
title="New Session"
|
||||
>
|
||||
<PlusIcon className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={props.onNewSession}
|
||||
className="session-list-new-button p-1.5 rounded-full text-[var(--app-link)] transition-colors"
|
||||
title="New Session"
|
||||
>
|
||||
<PlusIcon className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="flex flex-col divide-y divide-[var(--app-divider)]">
|
||||
{props.sessions.map((s) => (
|
||||
|
||||
+54
-12
@@ -44,6 +44,26 @@ function BackIcon(props: { className?: string }) {
|
||||
)
|
||||
}
|
||||
|
||||
function PlusIcon(props: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={props.className}
|
||||
>
|
||||
<line x1="12" y1="5" x2="12" y2="19" />
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function SessionsPage() {
|
||||
const { api } = useAppContext()
|
||||
const navigate = useNavigate()
|
||||
@@ -54,18 +74,40 @@ function SessionsPage() {
|
||||
}, [refetch])
|
||||
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto p-4 pt-[calc(1rem+env(safe-area-inset-top))] space-y-4">
|
||||
{error ? <div className="text-sm text-red-600">{error}</div> : null}
|
||||
<SessionList
|
||||
sessions={sessions}
|
||||
onSelect={(sessionId) => navigate({
|
||||
to: '/sessions/$sessionId',
|
||||
params: { sessionId },
|
||||
})}
|
||||
onNewSession={() => navigate({ to: '/sessions/new' })}
|
||||
onRefresh={handleRefresh}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="bg-[var(--app-bg)] pt-[env(safe-area-inset-top)]">
|
||||
<div className="mx-auto w-full max-w-content flex items-center justify-between px-3 py-2">
|
||||
<div className="text-xs text-[var(--app-hint)]">
|
||||
{sessions.length} sessions
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate({ to: '/sessions/new' })}
|
||||
className="session-list-new-button p-1.5 rounded-full text-[var(--app-link)] transition-colors"
|
||||
title="New Session"
|
||||
>
|
||||
<PlusIcon className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{error ? (
|
||||
<div className="mx-auto w-full max-w-content px-3 py-2">
|
||||
<div className="text-sm text-red-600">{error}</div>
|
||||
</div>
|
||||
) : null}
|
||||
<SessionList
|
||||
sessions={sessions}
|
||||
onSelect={(sessionId) => navigate({
|
||||
to: '/sessions/$sessionId',
|
||||
params: { sessionId },
|
||||
})}
|
||||
onNewSession={() => navigate({ to: '/sessions/new' })}
|
||||
onRefresh={handleRefresh}
|
||||
isLoading={isLoading}
|
||||
renderHeader={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user