mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix(web): stabilize session list alignment and scrolling (#1196)
This commit is contained in:
@@ -679,7 +679,7 @@ function SessionListSearch(props: {
|
||||
return (
|
||||
<div className="px-2 pb-2">
|
||||
<div className="relative min-w-0">
|
||||
<div className="pointer-events-none absolute inset-y-0 left-2 flex items-center text-[var(--app-hint)]">
|
||||
<div className="pointer-events-none absolute inset-y-0 left-2.5 flex items-center text-[var(--app-hint)]">
|
||||
<SearchIcon className="h-3.5 w-3.5" />
|
||||
</div>
|
||||
<input
|
||||
@@ -861,7 +861,7 @@ function SessionItem(props: {
|
||||
<button
|
||||
type="button"
|
||||
{...longPressHandlers}
|
||||
className={`session-list-item group/session-row flex w-full flex-col gap-1 px-2.5 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--app-link)] select-none rounded-lg ${selected ? 'bg-[var(--app-secondary-bg)]' : ''}`}
|
||||
className={`session-list-item group/session-row flex w-full flex-col gap-1 py-2 pl-2.5 pr-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--app-link)] select-none rounded-lg ${selected ? 'bg-[var(--app-secondary-bg)]' : ''}`}
|
||||
style={{ WebkitTouchCallout: 'none' }}
|
||||
aria-current={selected ? 'page' : undefined}
|
||||
aria-describedby={describedBy}
|
||||
@@ -1217,13 +1217,14 @@ export function SessionList(props: {
|
||||
}, [allGroups])
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-content flex flex-col">
|
||||
<div className="flex min-h-0 w-full flex-1 flex-col">
|
||||
<div className="session-list-scrollbar-offset mx-auto w-full max-w-content shrink-0">
|
||||
{renderHeader ? (
|
||||
<div className="flex items-center justify-end px-3 py-1">
|
||||
<div className="flex items-center justify-end px-2 py-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={props.onNewSession}
|
||||
className="session-list-new-button p-1.5 rounded-full text-[var(--app-link)] transition-colors"
|
||||
className="session-list-new-button flex h-9 w-9 items-center justify-center rounded-full text-[var(--app-link)] transition-colors"
|
||||
title={t('sessions.new')}
|
||||
>
|
||||
<PlusIcon className="h-5 w-5" />
|
||||
@@ -1277,8 +1278,10 @@ export function SessionList(props: {
|
||||
onChange={setMachineFilter}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1 px-2 pt-1 pb-2">
|
||||
<div className="app-scroll-y session-list-scrollbar-left min-h-0 flex-1">
|
||||
<div className="mx-auto flex w-full max-w-content flex-col gap-1 pl-1.5 pr-2 pt-1 pb-2">
|
||||
{groups.map((group) => {
|
||||
const isCollapsed = isGroupCollapsed(group)
|
||||
const visibleGroupSessions = getVisibleGroupSessions(group)
|
||||
@@ -1294,7 +1297,7 @@ export function SessionList(props: {
|
||||
return (
|
||||
<div key={group.key}>
|
||||
<div
|
||||
className="group/project sticky top-0 z-10 flex items-center gap-2 px-1 py-1.5 text-left rounded-lg transition-colors hover:bg-[var(--app-subtle-bg)] cursor-pointer min-w-0 w-full select-none"
|
||||
className="group/project sticky top-0 z-10 flex items-center gap-2 py-1.5 pl-2 pr-2 text-left rounded-lg transition-colors hover:bg-[var(--app-subtle-bg)] cursor-pointer min-w-0 w-full select-none"
|
||||
onClick={() => toggleGroup(group.key, isCollapsed)}
|
||||
title={group.directory}
|
||||
>
|
||||
@@ -1347,7 +1350,7 @@ export function SessionList(props: {
|
||||
? showMoreSessions(group)
|
||||
: collapseSessionGroup(group)}
|
||||
className={cn(
|
||||
'mx-2 my-1 rounded-md px-2 py-1 text-center text-xs text-[var(--app-hint)] transition-colors hover:bg-[var(--app-subtle-bg)] hover:text-[var(--app-fg)]',
|
||||
'ml-2.5 mr-2 my-1 rounded-md px-2 py-1 text-center text-xs text-[var(--app-hint)] transition-colors hover:bg-[var(--app-subtle-bg)] hover:text-[var(--app-fg)]',
|
||||
hiddenSessionCount > 0 && 'border border-dashed border-[var(--app-border)]'
|
||||
)}
|
||||
>
|
||||
@@ -1364,5 +1367,6 @@ export function SessionList(props: {
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
+15
-8
@@ -352,19 +352,26 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep the session list scrollbar on the left at every viewport size. */
|
||||
.session-list-scrollbar-left {
|
||||
direction: rtl;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.session-list-scrollbar-left > * {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
/* Match fixed sidebar controls to the space reserved for the left scrollbar. */
|
||||
.session-list-scrollbar-offset {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/* Desktop sidebar: use custom width from CSS variable.
|
||||
Kept in sync with the Tailwind `split` breakpoint (see tailwind.config.ts)
|
||||
so the sidebar width/resize applies as soon as the split view appears on
|
||||
compact tablets. */
|
||||
@media (min-width: 920px) {
|
||||
.desktop-scrollbar-left {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.desktop-scrollbar-left > * {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
/* Apply resizable width to sidebar.
|
||||
Cap it against the viewport so a wide persisted width (up to 600px, from
|
||||
resizing on desktop) can't crush the detail pane on a compact split.
|
||||
|
||||
+4
-4
@@ -535,8 +535,8 @@ function SessionsPage() {
|
||||
className={`${isSessionsIndex ? 'flex' : 'hidden split:flex'} w-full shrink-0 flex-col bg-[var(--app-bg)]`}
|
||||
style={{ '--sidebar-w': `${sidebar.width}px` } as React.CSSProperties}
|
||||
>
|
||||
<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-end px-3 py-2">
|
||||
<div className="session-list-scrollbar-offset shrink-0 bg-[var(--app-bg)] pt-[env(safe-area-inset-top)]">
|
||||
<div className="mx-auto flex w-full max-w-content items-center justify-end px-2 py-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
@@ -579,7 +579,7 @@ function SessionsPage() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate({ to: '/sessions/new' })}
|
||||
className="session-list-new-button p-1.5 rounded-full text-[var(--app-link)] transition-colors"
|
||||
className="session-list-new-button flex h-9 w-9 items-center justify-center rounded-full text-[var(--app-link)] transition-colors"
|
||||
title={t('sessions.new')}
|
||||
>
|
||||
<PlusIcon className="h-5 w-5" />
|
||||
@@ -588,7 +588,7 @@ function SessionsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="app-scroll-y flex-1 min-h-0 desktop-scrollbar-left">
|
||||
<div className="flex min-h-0 flex-1 flex-col">
|
||||
{error ? (
|
||||
<div className="mx-auto w-full max-w-content px-3 py-2">
|
||||
<div className="text-sm text-red-600">{error}</div>
|
||||
|
||||
Reference in New Issue
Block a user