Skip to content

Commit 741eb17

Browse files
committed
make it more mobile friendly I guess
1 parent 1f505d8 commit 741eb17

File tree

10 files changed

+4733
-4848
lines changed

10 files changed

+4733
-4848
lines changed

app/components/button-link.tsx

Whitespace-only changes.

app/components/floating-toolbar.tsx

Lines changed: 0 additions & 2 deletions
This file was deleted.

app/components/ui/button.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Slot } from '@radix-ui/react-slot'
2+
import { Link, type LinkProps } from '@remix-run/react'
23
import { cva, type VariantProps } from 'class-variance-authority'
34
import * as React from 'react'
45

@@ -27,6 +28,10 @@ const buttonVariants = cva(
2728
pill: 'px-12 py-3 leading-3',
2829
icon: 'h-10 w-10',
2930
},
31+
icon: {
32+
true: 'flex h-10 w-10 items-center justify-center rounded-full p-2',
33+
false: 'px-6 py-2',
34+
},
3035
},
3136
defaultVariants: {
3237
variant: 'default',
@@ -55,4 +60,16 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
5560
)
5661
Button.displayName = 'Button'
5762

63+
export function ButtonLink({
64+
variant,
65+
66+
icon,
67+
className,
68+
...props
69+
}: LinkProps & VariantProps<typeof buttonVariants>) {
70+
return (
71+
<Link className={buttonVariants({ variant, icon, className })} {...props} />
72+
)
73+
}
74+
5875
export { Button, buttonVariants }

app/routes/_app+/recipients+/$recipientId.index.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -270,13 +270,14 @@ async function updateMessageOrderAction({ formData }: MessageActionArgs) {
270270
export default function RecipientRoute() {
271271
const data = useLoaderData<typeof loader>()
272272

273-
// TODO: add optimistic UI
274-
275273
return (
276-
<ul className="flex flex-col gap-12">
274+
<ul className="flex flex-col gap-6 sm:gap-12">
277275
{data.futureMessages.length ? (
278-
data.futureMessages.map(m => (
279-
<li key={m.id} className="flex justify-start gap-2 align-top">
276+
data.futureMessages.map((m) => (
277+
<li
278+
key={m.id}
279+
className="flex flex-col gap-4 sm:flex-row sm:justify-start sm:gap-2"
280+
>
280281
<MessageForms message={m} />
281282
</li>
282283
))
@@ -304,8 +305,8 @@ function MessageForms({ message }: { message: FutureMessage }) {
304305

305306
return (
306307
<>
307-
<div className="flex w-full items-center gap-4">
308-
<div>
308+
<div className="flex w-full flex-col gap-4 sm:flex-row sm:items-center">
309+
<div className="flex justify-center gap-2 sm:flex-col">
309310
<UpdateOrderForm message={message} direction="earlier" />
310311
<UpdateOrderForm message={message} direction="later" />
311312
</div>
@@ -327,11 +328,11 @@ function MessageForms({ message }: { message: FutureMessage }) {
327328
errors={updateContentFields.content.errors}
328329
/>
329330
</updateContentFetcher.Form>
330-
<div className="flex flex-col gap-2">
331+
<div className="flex flex-row gap-2 sm:flex-col">
331332
<StatusButton
332333
form={updateContentForm.id}
333334
status={updateContentFetcher.state !== 'idle' ? 'pending' : 'idle'}
334-
className="w-full"
335+
className="flex-1 sm:w-full"
335336
type="submit"
336337
name="intent"
337338
value={updateMessageContentActionIntent}

app/routes/_app+/recipients+/$recipientId.tsx

Lines changed: 54 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,15 @@
11
import { invariantResponse } from '@epic-web/invariant'
2-
import { type MetaFunction, type LoaderFunctionArgs } from '@remix-run/node'
2+
import { type LoaderFunctionArgs, type MetaFunction } from '@remix-run/node'
33
import {
44
Link,
55
json,
66
useLoaderData,
77
useMatches,
8-
useNavigate,
98
useOutlet,
109
} from '@remix-run/react'
1110
import { useEffect, useRef } from 'react'
1211
import { GeneralErrorBoundary } from '#app/components/error-boundary.js'
13-
import { floatingToolbarClassName } from '#app/components/floating-toolbar.js'
14-
import { Button } from '#app/components/ui/button.js'
1512
import { Icon } from '#app/components/ui/icon.js'
16-
import {
17-
Tabs,
18-
TabsContent,
19-
TabsList,
20-
TabsTrigger,
21-
} from '#app/components/ui/tabs.js'
2213
import { SimpleTooltip } from '#app/components/ui/tooltip.js'
2314
import { requireUserId } from '#app/utils/auth.server.js'
2415
import { getHints } from '#app/utils/client-hints.js'
@@ -68,39 +59,24 @@ export default function RecipientRoute() {
6859
const data = useLoaderData<typeof loader>()
6960
const firstLinkRef = useRef<HTMLAnchorElement | null>(null)
7061
const outlet = useOutlet()
71-
const navigate = useNavigate()
7262
const matches = useMatches()
7363
const lastMatch = matches[matches.length - 1]
7464
const idPortion = lastMatch?.id.split('.')?.at(-1) ?? '.'
75-
const tab = idPortion === 'index' ? '.' : idPortion
65+
const currentPath = idPortion === 'index' ? '.' : idPortion
7666

7767
useEffect(() => {
7868
firstLinkRef.current?.focus()
7969
}, [data.recipient.id])
8070

8171
useEffect(() => {
82-
if (tab === '.') firstLinkRef.current?.focus()
83-
}, [tab])
84-
85-
// The links are just for progressive enhancement. We disable their default
86-
function handleLinkClick(event: React.MouseEvent<HTMLAnchorElement>) {
87-
if (event.metaKey) {
88-
// TODO: this doesn't seem to be preventing the tab from switching properly
89-
event.stopPropagation()
90-
} else {
91-
event.preventDefault()
92-
}
93-
}
72+
if (currentPath === '.') firstLinkRef.current?.focus()
73+
}, [currentPath])
9474

9575
return (
96-
<Tabs
97-
defaultValue="."
98-
onValueChange={newValue => navigate(newValue)}
99-
value={tab}
100-
>
101-
<div className="absolute inset-0 flex flex-col px-10">
102-
<h2 className="mb-2 h-36 pt-12 text-h2 lg:mb-6">
103-
{data.recipient.name}
76+
<div className="px-10 py-6">
77+
<div className="flex flex-col justify-between gap-4 md:flex-row">
78+
<div className="mb-2 pt-12 lg:mb-6">
79+
<h2 className="text-h2">{data.recipient.name}</h2>
10480
<small className="flex gap-1 text-sm font-normal text-secondary-foreground">
10581
{data.recipient.phoneNumber}
10682
{data.optedOut ? (
@@ -122,52 +98,54 @@ export default function RecipientRoute() {
12298
</button>
12399
</SimpleTooltip>
124100
</small>
125-
</h2>
126-
<div className="absolute left-3 right-3 top-[8.7rem] rounded-lg bg-muted/80 px-2 py-2 shadow-xl shadow-accent backdrop-blur-sm">
127-
<TabsList className="grid w-full grid-cols-3 bg-transparent">
128-
<TabsTrigger value="." asChild>
129-
<Link
130-
to="."
131-
preventScrollReset
132-
onClick={handleLinkClick}
133-
ref={firstLinkRef}
134-
>
135-
Upcoming
136-
</Link>
137-
</TabsTrigger>
138-
<TabsTrigger value="new" asChild>
139-
<Link to="new" preventScrollReset onClick={handleLinkClick}>
140-
New
141-
</Link>
142-
</TabsTrigger>
143-
<TabsTrigger value="past" asChild>
144-
<Link to="past" preventScrollReset onClick={handleLinkClick}>
145-
Past
146-
</Link>
147-
</TabsTrigger>
148-
</TabsList>
149-
</div>
150-
<div className="overflow-y-auto px-4 py-24">
151-
<TabsContent value=".">{outlet}</TabsContent>
152-
<TabsContent value="new">{outlet}</TabsContent>
153-
<TabsContent value="past">{outlet}</TabsContent>
154-
</div>
155-
<div className={floatingToolbarClassName}>
156-
<div className="grid flex-1 grid-cols-2 justify-end gap-2 min-[525px]:flex md:gap-4">
157-
<Button
158-
asChild
159-
className="min-[525px]:max-md:aspect-square min-[525px]:max-md:px-0"
160-
>
161-
<Link to="edit">
162-
<Icon name="pencil-1" className="scale-125 max-md:scale-150">
163-
<span className="max-md:hidden">Edit</span>
164-
</Icon>
165-
</Link>
166-
</Button>
167-
</div>
168101
</div>
102+
<nav>
103+
<Link
104+
to="."
105+
className={`flex items-center gap-2 rounded-md px-3 py-2 transition-colors ${
106+
currentPath === '.'
107+
? 'bg-accent text-accent-foreground'
108+
: 'hover:bg-accent/50'
109+
}`}
110+
ref={firstLinkRef}
111+
>
112+
<Icon name="clock">Upcoming</Icon>
113+
</Link>
114+
<Link
115+
to="new"
116+
className={`flex items-center gap-2 rounded-md px-3 py-2 transition-colors ${
117+
currentPath === 'new'
118+
? 'bg-accent text-accent-foreground'
119+
: 'hover:bg-accent/50'
120+
}`}
121+
>
122+
<Icon name="plus">New</Icon>
123+
</Link>
124+
<Link
125+
to="past"
126+
className={`flex items-center gap-2 rounded-md px-3 py-2 transition-colors ${
127+
currentPath === 'past'
128+
? 'bg-accent text-accent-foreground'
129+
: 'hover:bg-accent/50'
130+
}`}
131+
>
132+
<Icon name="chevron-down">Past</Icon>
133+
</Link>
134+
135+
<Link
136+
to="edit"
137+
className={`flex items-center gap-2 rounded-md px-3 py-2 transition-colors ${
138+
currentPath === 'past'
139+
? 'bg-accent text-accent-foreground'
140+
: 'hover:bg-accent/50'
141+
}`}
142+
>
143+
<Icon name="pencil-1">Edit</Icon>
144+
</Link>
145+
</nav>
169146
</div>
170-
</Tabs>
147+
<div className="overflow-y-auto px-4 py-6">{outlet}</div>
148+
</div>
171149
)
172150
}
173151

app/routes/_app+/recipients+/__editor.tsx

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { type SerializeFrom } from '@remix-run/node'
1010
import { Form, useActionData, useFetcher } from '@remix-run/react'
1111
import { z } from 'zod'
1212
import { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'
13-
import { floatingToolbarClassName } from '#app/components/floating-toolbar.tsx'
1413
import { ErrorList, Field, SelectField } from '#app/components/forms.tsx'
1514
import { Icon } from '#app/components/ui/icon.js'
1615
import { StatusButton } from '#app/components/ui/status-button.tsx'
@@ -66,10 +65,9 @@ export function RecipientEditor({
6665
})
6766

6867
return (
69-
<div className="absolute inset-0">
68+
<div>
7069
<div className="flex justify-end gap-2 p-6">
7170
{needsVerification ? <VerifyForm /> : null}
72-
{recipient?.id ? <DeleteRecipient id={recipient.id} /> : null}
7371
</div>
7472
{needsVerification ? (
7573
<div className="px-6">
@@ -85,7 +83,7 @@ export function RecipientEditor({
8583
) : null}
8684
<Form
8785
method="POST"
88-
className="flex h-full flex-col gap-y-4 overflow-y-auto overflow-x-hidden px-10 pb-28 pt-12"
86+
className="flex flex-col gap-y-4 overflow-y-auto px-4 py-4"
8987
{...getFormProps(form)}
9088
>
9189
{/*
@@ -141,7 +139,7 @@ export function RecipientEditor({
141139
labelProps={{ children: 'Time Zone' }}
142140
selectProps={{
143141
...getSelectProps(fields.timeZone),
144-
children: supportedTimeZones.map(tz => (
142+
children: supportedTimeZones.map((tz) => (
145143
<option key={tz} value={tz}>
146144
{tz}
147145
</option>
@@ -152,7 +150,8 @@ export function RecipientEditor({
152150
</div>
153151
<ErrorList id={form.errorId} errors={form.errors} />
154152
</Form>
155-
<div className={floatingToolbarClassName}>
153+
<div className="flex justify-between gap-2 p-6">
154+
{recipient?.id ? <DeleteRecipient id={recipient.id} /> : null}
156155
<StatusButton
157156
form={form.id}
158157
type="submit"
@@ -198,28 +197,20 @@ function DeleteRecipient({ id }: { id: string }) {
198197
<input type="hidden" name="recipientId" value={id} />
199198
<StatusButton
200199
variant="destructive"
201-
status={isPending ? 'pending' : form.status ?? 'idle'}
200+
status={isPending ? 'pending' : (form.status ?? 'idle')}
202201
{...dc.getButtonProps({
203202
type: 'submit',
204203
title: dc.doubleCheck ? 'Are you sure?' : 'Delete recipient',
205204
name: 'intent',
206205
value: deleteRecipientActionIntent,
207206
disabled: isPending,
208-
className:
209-
'w-full max-md:aspect-square max-md:px-0 data-[safe-delay=true]:opacity-50',
207+
className: 'data-[safe-delay=true]:opacity-50',
210208
})}
211209
>
212210
{dc.doubleCheck ? (
213-
<Icon
214-
name="question-mark-circled"
215-
className="scale-125 max-md:scale-150"
216-
>
217-
<span className="max-md:hidden">Confirm</span>
218-
</Icon>
211+
<Icon name="question-mark-circled">Confirm</Icon>
219212
) : (
220-
<Icon name="trash" className="scale-125 max-md:scale-150">
221-
<span className="max-md:hidden">Delete</span>
222-
</Icon>
213+
<Icon name="trash">Delete</Icon>
223214
)}
224215
</StatusButton>
225216
<ErrorList errors={form.errors} id={form.errorId} />

0 commit comments

Comments
 (0)