improve mobile responsiveness

This commit is contained in:
miloschwartz
2026-06-30 22:11:25 -04:00
parent f4854a3a74
commit bc759c5c9e
6 changed files with 66 additions and 57 deletions
@@ -48,7 +48,7 @@ export function LauncherGroupTrigger({
isOpen isOpen
}: LauncherGroupTriggerProps) { }: LauncherGroupTriggerProps) {
return ( return (
<CollapsibleTrigger className="flex w-full items-center gap-2.5 rounded-md bg-accent px-4 py-2.5 text-left transition-colors cursor-pointer"> <CollapsibleTrigger className="sticky top-0 z-20 md:top-16 flex w-full items-center gap-2.5 rounded-md bg-accent px-4 py-2.5 text-left transition-colors cursor-pointer">
{group.groupType === "site" || group.groupType === "label" ? ( {group.groupType === "site" || group.groupType === "label" ? (
<LauncherGroupStatusDot group={group} /> <LauncherGroupStatusDot group={group} />
) : null} ) : null}
@@ -27,18 +27,18 @@ export function LauncherResourceAccess({
if (variant === "list") { if (variant === "list") {
return ( return (
<div className="flex min-w-0 flex-1 items-center gap-2.5"> <div className="flex min-w-0 flex-1 items-center gap-2.5 max-md:min-w-[12rem] max-md:shrink-0 max-md:flex-none">
{canLink ? ( {canLink ? (
<Link <Link
href={href} href={href}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="min-w-0 truncate text-sm text-muted-foreground hover:underline" className="min-w-0 truncate text-sm text-muted-foreground hover:underline max-md:overflow-visible max-md:whitespace-nowrap"
> >
{accessDisplay} {accessDisplay}
</Link> </Link>
) : ( ) : (
<span className="min-w-0 truncate text-sm text-muted-foreground"> <span className="min-w-0 truncate text-sm text-muted-foreground max-md:overflow-visible max-md:whitespace-nowrap">
{accessDisplay} {accessDisplay}
</span> </span>
)} )}
@@ -29,8 +29,7 @@ export function LauncherResourceCard({
return ( return (
<div <div
className={cn( className={cn(
"flex min-w-0 flex-col gap-2.5 overflow-hidden rounded-xl border border-border bg-background p-4 transition-colors", "flex min-w-0 flex-col gap-2.5 overflow-hidden rounded-xl border border-border bg-background p-4",
isClickable && "hover:bg-accent/40",
clickProps.className clickProps.className
)} )}
onClick={clickProps.onClick} onClick={clickProps.onClick}
@@ -15,16 +15,18 @@ export function LauncherResourceList({
showSiteTags showSiteTags
}: LauncherResourceListProps) { }: LauncherResourceListProps) {
return ( return (
<div className="flex w-full flex-col"> <div className="w-full max-md:overflow-x-auto max-md:overflow-y-hidden">
{resources.map((resource, index) => ( <div className="flex w-full flex-col max-md:w-max">
<LauncherResourceRow {resources.map((resource, index) => (
key={resource.launcherResourceKey} <LauncherResourceRow
resource={resource} key={resource.launcherResourceKey}
showLabels={showLabels} resource={resource}
showSiteTags={showSiteTags} showLabels={showLabels}
isLast={index === resources.length - 1} showSiteTags={showSiteTags}
/> isLast={index === resources.length - 1}
))} />
))}
</div>
</div> </div>
); );
} }
@@ -36,9 +36,8 @@ export function LauncherResourceRow({
return ( return (
<div <div
className={cn( className={cn(
"flex items-center gap-2.5 p-4 transition-colors", "flex items-center gap-2.5 p-4 max-md:min-w-max max-md:whitespace-nowrap",
isLast ? undefined : "border-b border-border", isLast ? undefined : "border-b border-border",
isClickable && "hover:bg-accent/40",
clickProps.className clickProps.className
)} )}
onClick={clickProps.onClick} onClick={clickProps.onClick}
@@ -46,15 +45,24 @@ export function LauncherResourceRow({
role={clickProps.role} role={clickProps.role}
tabIndex={clickProps.tabIndex} tabIndex={clickProps.tabIndex}
> >
<LauncherResourceIcon <div
iconUrl={resource.iconUrl} className={cn(
name={resource.name} "flex shrink-0 items-center gap-2.5",
variant="list" "max-md:sticky max-md:left-0 max-md:z-10 max-md:min-w-[9rem]",
/> "max-md:-my-4 max-md:-ml-4 max-md:py-4 max-md:pl-4 max-md:pr-3",
"max-md:bg-card max-md:[mask-image:linear-gradient(to_left,transparent_0%,black_20px)]"
)}
>
<LauncherResourceIcon
iconUrl={resource.iconUrl}
name={resource.name}
variant="list"
/>
<span className="shrink-0 text-sm font-semibold text-foreground"> <span className="text-sm font-semibold text-foreground">
{resource.name} {resource.name}
</span> </span>
</div>
<LauncherResourceAccess <LauncherResourceAccess
accessDisplay={resource.accessDisplay} accessDisplay={resource.accessDisplay}
@@ -64,7 +72,7 @@ export function LauncherResourceRow({
/> />
{hasTags ? ( {hasTags ? (
<div className="ml-auto flex min-w-0 max-w-md shrink items-center justify-end gap-1"> <div className="flex min-w-0 max-w-md shrink items-center justify-end gap-1 max-md:shrink-0 max-md:max-w-none md:ml-auto">
{showSiteTags && resource.site ? ( {showSiteTags && resource.site ? (
<LabelBadge <LabelBadge
name={resource.site.name} name={resource.site.name}
@@ -371,36 +371,8 @@ export default function ResourceLauncher({
/> />
<div className="flex flex-col gap-3 mb-6"> <div className="flex flex-col gap-3 mb-6">
<div className="flex flex-col xl:flex-row xl:items-center gap-3 justify-between"> <div className="flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between">
<div className="flex flex-col sm:flex-row sm:items-center gap-3 min-w-0 flex-1"> <div className="flex items-center gap-2 shrink-0 justify-start xl:justify-end order-1 sm:order-2 xl:order-2">
<div className="relative w-full sm:max-w-sm shrink-0">
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
<Input
value={searchInput}
onChange={(event) => {
const value = event.target.value;
setSearchInput(value);
debouncedNavigateSearch(
activeViewIdRef.current,
value
);
}}
placeholder={t(
"resourceLauncherSearchPlaceholder"
)}
className="pl-8"
/>
</div>
<LauncherViewTabs
activeViewId={activeViewId}
savedViews={views.map((view) => ({
viewId: view.viewId,
name: view.name
}))}
onSelectView={selectView}
/>
</div>
<div className="flex items-center gap-2 shrink-0 justify-end">
<LauncherSaveViewMenu <LauncherSaveViewMenu
isDefaultView={isDefaultView} isDefaultView={isDefaultView}
isAdmin={isAdmin} isAdmin={isAdmin}
@@ -448,6 +420,34 @@ export default function ResourceLauncher({
}} }}
/> />
</div> </div>
<div className="flex flex-col sm:flex-row sm:items-center gap-3 min-w-0 flex-1 order-2 sm:order-1 xl:order-1">
<div className="relative w-full sm:max-w-sm shrink-0">
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
<Input
value={searchInput}
onChange={(event) => {
const value = event.target.value;
setSearchInput(value);
debouncedNavigateSearch(
activeViewIdRef.current,
value
);
}}
placeholder={t(
"resourceLauncherSearchPlaceholder"
)}
className="pl-8"
/>
</div>
<LauncherViewTabs
activeViewId={activeViewId}
savedViews={views.map((view) => ({
viewId: view.viewId,
name: view.name
}))}
onSelectView={selectView}
/>
</div>
</div> </div>
</div> </div>