@remit/web-client 0.0.287 → 0.0.289
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/auth/SessionRateLimited.stories.tsx +1 -1
- package/src/components/calendar/AgendaStrip.stories.tsx +1 -1
- package/src/components/calendar/AgendaView.stories.tsx +1 -1
- package/src/components/calendar/CalendarComposePane.stories.tsx +1 -1
- package/src/components/calendar/CalendarEventPane.stories.tsx +1 -1
- package/src/components/calendar/CalendarShell.stories.tsx +1 -1
- package/src/components/calendar/CalendarViewPlaceholder.stories.tsx +1 -1
- package/src/components/calendar/CalendarWorkspace.stories.tsx +10 -1
- package/src/components/calendar/CalendarWorkspace.tsx +76 -43
- package/src/components/calendar/OpenCalendarEvent.stories.tsx +1 -1
- package/src/components/calendar/WriteCalendarEvent.stories.tsx +1 -1
- package/src/components/calendar/calendar-workspace.render.test.ts +24 -0
- package/src/components/compose/FromSelector.stories.tsx +1 -1
- package/src/components/config-import/config-import.stories.tsx +1 -1
- package/src/components/mail/authenticity-warning.stories.tsx +1 -1
- package/src/components/mail/blocked-images-notice.stories.tsx +1 -1
- package/src/components/mail/empty-trash-bar.stories.tsx +1 -1
- package/src/components/mail/intelligence-drawer.stories.tsx +1 -1
- package/src/components/settings/CalendarFeedCard.stories.tsx +1 -1
- package/src/components/settings/LabelsList.stories.tsx +1 -1
- package/src/components/settings/label-delete-confirm.stories.tsx +1 -1
- package/src/components/settings/settings-filter.stories.tsx +1 -1
- package/src/components/ui/ErrorBanner.stories.tsx +1 -1
- package/src/routes/calendar/$view.$date.tsx +23 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remit/web-client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.289",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Remit web client, published as composable primitives — the app shell, auth shells, and runtime config. A distributor imports what it composes and bundles it.",
|
|
6
6
|
"exports": {
|
|
@@ -7,7 +7,7 @@ import { SessionRateLimited } from "./SessionRateLimited";
|
|
|
7
7
|
* told everyone behind the address that they had been logged out.
|
|
8
8
|
*/
|
|
9
9
|
const meta: Meta<typeof SessionRateLimited> = {
|
|
10
|
-
title: "
|
|
10
|
+
title: "Playground/Shipped/Sign in/Session rate limited",
|
|
11
11
|
component: SessionRateLimited,
|
|
12
12
|
parameters: { layout: "fullscreen" },
|
|
13
13
|
args: {
|
|
@@ -45,7 +45,7 @@ const draft: EventDraft = {
|
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
const meta: Meta<typeof CalendarComposePane> = {
|
|
48
|
-
title: "
|
|
48
|
+
title: "Playground/Shipped/Calendar/Compose pane",
|
|
49
49
|
component: CalendarComposePane,
|
|
50
50
|
parameters: { layout: "fullscreen" },
|
|
51
51
|
args: {
|
|
@@ -44,7 +44,7 @@ const event: CalendarEventData = {
|
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
const meta: Meta<typeof CalendarEventPane> = {
|
|
47
|
-
title: "
|
|
47
|
+
title: "Playground/Shipped/Calendar/Event pane",
|
|
48
48
|
component: CalendarEventPane,
|
|
49
49
|
parameters: { layout: "fullscreen" },
|
|
50
50
|
args: {
|
|
@@ -11,7 +11,7 @@ import { CalendarViewPlaceholder } from "./CalendarViewPlaceholder";
|
|
|
11
11
|
* and the reader would plan around an empty screen.
|
|
12
12
|
*/
|
|
13
13
|
const meta: Meta<typeof CalendarViewPlaceholder> = {
|
|
14
|
-
title: "
|
|
14
|
+
title: "Playground/Shipped/Calendar/Not built yet",
|
|
15
15
|
component: CalendarViewPlaceholder,
|
|
16
16
|
parameters: { layout: "fullscreen" },
|
|
17
17
|
render: (args) => (
|
|
@@ -111,11 +111,13 @@ function Workspace({
|
|
|
111
111
|
events,
|
|
112
112
|
isLoading,
|
|
113
113
|
error,
|
|
114
|
+
touch,
|
|
114
115
|
}: {
|
|
115
116
|
initialView: CalendarViewId;
|
|
116
117
|
events: CalendarEventData[];
|
|
117
118
|
isLoading?: boolean;
|
|
118
119
|
error?: unknown;
|
|
120
|
+
touch?: boolean;
|
|
119
121
|
}) {
|
|
120
122
|
const [view, setView] = useState<CalendarViewId>(initialView);
|
|
121
123
|
const [density, setDensity] = useState<Density>("comfortable");
|
|
@@ -146,12 +148,13 @@ function Workspace({
|
|
|
146
148
|
onChangeDensity={setDensity}
|
|
147
149
|
onSelectEvent={setSelected}
|
|
148
150
|
onPickSlot={() => undefined}
|
|
151
|
+
touch={touch}
|
|
149
152
|
/>
|
|
150
153
|
);
|
|
151
154
|
}
|
|
152
155
|
|
|
153
156
|
const meta: Meta<typeof Workspace> = {
|
|
154
|
-
title: "
|
|
157
|
+
title: "Playground/Shipped/Calendar/Workspace",
|
|
155
158
|
component: Workspace,
|
|
156
159
|
parameters: { layout: "fullscreen" },
|
|
157
160
|
args: { initialView: "week", events: week },
|
|
@@ -169,6 +172,12 @@ export const Week: Story = {};
|
|
|
169
172
|
|
|
170
173
|
export const Day: Story = { args: { initialView: "day" } };
|
|
171
174
|
|
|
175
|
+
/** A phone: two rows of thumb-sized controls, and no year on the ladder. */
|
|
176
|
+
export const Phone: Story = {
|
|
177
|
+
args: { touch: true },
|
|
178
|
+
globals: { viewport: { value: "mobile" } },
|
|
179
|
+
};
|
|
180
|
+
|
|
172
181
|
/** Nothing scheduled is still a grid, never a "nothing here" surface. */
|
|
173
182
|
export const EmptyWeek: Story = { args: { events: [] } };
|
|
174
183
|
|
|
@@ -7,10 +7,11 @@ import {
|
|
|
7
7
|
type CalendarViewId,
|
|
8
8
|
CalendarViewSwitch,
|
|
9
9
|
type Density,
|
|
10
|
-
|
|
10
|
+
Segmented,
|
|
11
|
+
type SegmentOption,
|
|
11
12
|
} from "@remit/ui";
|
|
12
13
|
import { Loader2 } from "lucide-react";
|
|
13
|
-
import { type ReactNode,
|
|
14
|
+
import { type ReactNode, useState } from "react";
|
|
14
15
|
import { CalendarViewPlaceholder } from "@/components/calendar/CalendarViewPlaceholder";
|
|
15
16
|
import { NavMenuButton } from "@/components/mail/NavMenuButton";
|
|
16
17
|
import { ErrorState } from "@/components/ui/ErrorState";
|
|
@@ -30,9 +31,11 @@ import {
|
|
|
30
31
|
* rather than of this component's state.
|
|
31
32
|
*/
|
|
32
33
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
const PHONE_VIEWS: CalendarViewId[] = ["month", "week", "day", "agenda"];
|
|
35
|
+
|
|
36
|
+
const DENSITY_OPTIONS: SegmentOption<Density>[] = [
|
|
37
|
+
{ value: "comfortable", label: "Detail", shortLabel: "Detail" },
|
|
38
|
+
{ value: "compact", label: "Glance", shortLabel: "Glance" },
|
|
36
39
|
];
|
|
37
40
|
|
|
38
41
|
export interface CalendarWorkspaceProps {
|
|
@@ -68,11 +71,16 @@ export interface CalendarWorkspaceProps {
|
|
|
68
71
|
onSelectEvent: (eventId: string) => void;
|
|
69
72
|
onPickSlot: (pick: CalendarSlotPick) => void;
|
|
70
73
|
onStep: (direction: -1 | 1) => void;
|
|
74
|
+
/**
|
|
75
|
+
* A phone: the toolbar splits into two rows of thumb-sized controls, because
|
|
76
|
+
* one row of them is wider than the screen and pushes the grid off it.
|
|
77
|
+
*/
|
|
78
|
+
touch?: boolean;
|
|
71
79
|
}
|
|
72
80
|
|
|
73
81
|
/**
|
|
74
82
|
* The range the toolbar names before the grid has measured one, and the whole
|
|
75
|
-
* answer at a zoom that draws no grid.
|
|
83
|
+
* answer for a week and at a zoom that draws no grid.
|
|
76
84
|
*/
|
|
77
85
|
function fallbackRangeTitle(view: CalendarViewId, date: string): string {
|
|
78
86
|
const instant = new Date(`${date}T00:00:00`);
|
|
@@ -80,6 +88,17 @@ function fallbackRangeTitle(view: CalendarViewId, date: string): string {
|
|
|
80
88
|
return new Intl.DateTimeFormat(undefined, { year: "numeric" }).format(
|
|
81
89
|
instant,
|
|
82
90
|
);
|
|
91
|
+
if (view === "week") {
|
|
92
|
+
const monday = new Date(instant);
|
|
93
|
+
monday.setDate(instant.getDate() - ((instant.getDay() + 6) % 7));
|
|
94
|
+
const sunday = new Date(monday);
|
|
95
|
+
sunday.setDate(monday.getDate() + 6);
|
|
96
|
+
return new Intl.DateTimeFormat(undefined, {
|
|
97
|
+
day: "numeric",
|
|
98
|
+
month: "short",
|
|
99
|
+
year: "numeric",
|
|
100
|
+
}).formatRange(monday, sunday);
|
|
101
|
+
}
|
|
83
102
|
if (view === "day")
|
|
84
103
|
return new Intl.DateTimeFormat(undefined, {
|
|
85
104
|
weekday: "short",
|
|
@@ -112,53 +131,67 @@ export function CalendarWorkspace({
|
|
|
112
131
|
onSelectEvent,
|
|
113
132
|
onPickSlot,
|
|
114
133
|
onStep,
|
|
134
|
+
touch = false,
|
|
115
135
|
}: CalendarWorkspaceProps) {
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
// week — it knows where the week starts. Held against the address it was
|
|
119
|
-
// measured for, so leaving a view does not carry its title into the next one.
|
|
136
|
+
// Held against the address it was measured for, so leaving a view does not
|
|
137
|
+
// carry its title into the next one.
|
|
120
138
|
const [measured, setMeasured] = useState({ key: "", title: "" });
|
|
121
139
|
const addressKey = `${view}/${date}`;
|
|
122
140
|
const title =
|
|
123
|
-
measured.key === addressKey
|
|
141
|
+
view !== "week" && measured.key === addressKey
|
|
124
142
|
? measured.title
|
|
125
143
|
: fallbackRangeTitle(view, date);
|
|
126
144
|
|
|
145
|
+
const dateNav = (
|
|
146
|
+
<CalendarDateNav
|
|
147
|
+
title={title}
|
|
148
|
+
onPrev={() => onStep(-1)}
|
|
149
|
+
onNext={() => onStep(1)}
|
|
150
|
+
onToday={onToday}
|
|
151
|
+
touch={touch}
|
|
152
|
+
/>
|
|
153
|
+
);
|
|
154
|
+
const densitySwitch = (
|
|
155
|
+
<Segmented
|
|
156
|
+
ariaLabel="Calendar density"
|
|
157
|
+
options={DENSITY_OPTIONS}
|
|
158
|
+
value={density}
|
|
159
|
+
onChange={onChangeDensity}
|
|
160
|
+
touch={touch}
|
|
161
|
+
className="shrink-0"
|
|
162
|
+
/>
|
|
163
|
+
);
|
|
164
|
+
|
|
127
165
|
return (
|
|
128
166
|
<div className="flex h-full min-h-0 flex-col bg-surface">
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
/>
|
|
155
|
-
{option.label}
|
|
156
|
-
</label>
|
|
157
|
-
))}
|
|
158
|
-
</fieldset>
|
|
167
|
+
{touch ? (
|
|
168
|
+
<div className="flex shrink-0 flex-col gap-1 border-b border-line px-2 pb-1">
|
|
169
|
+
<div className="flex min-w-0 items-center gap-1">
|
|
170
|
+
<NavMenuButton />
|
|
171
|
+
<div className="min-w-0 flex-1">{dateNav}</div>
|
|
172
|
+
</div>
|
|
173
|
+
<div className="flex items-center gap-2">
|
|
174
|
+
<CalendarViewSwitch
|
|
175
|
+
value={view}
|
|
176
|
+
onChange={onChangeView}
|
|
177
|
+
views={PHONE_VIEWS}
|
|
178
|
+
touch
|
|
179
|
+
className="flex-1"
|
|
180
|
+
/>
|
|
181
|
+
{densitySwitch}
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
) : (
|
|
185
|
+
<div className="flex h-pane-header shrink-0 items-center gap-2 border-b border-line px-2">
|
|
186
|
+
{/* Below the nav pane the sidebar is a slide-over with nothing to open
|
|
187
|
+
it: the calendar is a whole surface of its own, so without this
|
|
188
|
+
the reader has no way back to their mail. */}
|
|
189
|
+
<NavMenuButton />
|
|
190
|
+
<div className="min-w-0 flex-1">{dateNav}</div>
|
|
191
|
+
{densitySwitch}
|
|
159
192
|
<CalendarViewSwitch value={view} onChange={onChangeView} />
|
|
160
|
-
</
|
|
161
|
-
|
|
193
|
+
</div>
|
|
194
|
+
)}
|
|
162
195
|
|
|
163
196
|
{/* The strip rolls its own range, so it answers for its own days: the
|
|
164
197
|
week this pane read is not what it is drawing, and gating it on that
|
|
@@ -74,7 +74,7 @@ function Opened({
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
const meta: Meta<typeof Opened> = {
|
|
77
|
-
title: "
|
|
77
|
+
title: "Playground/Shipped/Calendar/Open event",
|
|
78
78
|
component: Opened,
|
|
79
79
|
parameters: { layout: "fullscreen" },
|
|
80
80
|
args: { calendarObjectId: ROADMAP_OBJECT, server: answering() },
|
|
@@ -48,7 +48,7 @@ function Composer({ pick }: { pick?: CalendarSlotPick }) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const meta: Meta<typeof Composer> = {
|
|
51
|
-
title: "
|
|
51
|
+
title: "Playground/Shipped/Calendar/Write event",
|
|
52
52
|
component: Composer,
|
|
53
53
|
parameters: { layout: "fullscreen" },
|
|
54
54
|
};
|
|
@@ -156,6 +156,30 @@ describe("the toolbar", () => {
|
|
|
156
156
|
assert.ok(render("year").includes("2026"));
|
|
157
157
|
});
|
|
158
158
|
|
|
159
|
+
it("names a week that runs over a year end by both of its years", () => {
|
|
160
|
+
const html = renderQuietly(
|
|
161
|
+
createElement(CalendarWorkspace, {
|
|
162
|
+
...workspaceProps("week", []),
|
|
163
|
+
date: "2025-12-31",
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
166
|
+
const title = /<h2[^>]*>([^<]*)<\/h2>/.exec(html)?.[1] ?? "";
|
|
167
|
+
assert.match(title, /2025/, `"${title}" lost the year the week starts in`);
|
|
168
|
+
assert.match(title, /2026/, `"${title}" lost the year the week ends in`);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("leaves the year off the ladder on a phone", () => {
|
|
172
|
+
const html = renderQuietly(
|
|
173
|
+
createElement(CalendarWorkspace, {
|
|
174
|
+
...workspaceProps("week"),
|
|
175
|
+
touch: true,
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
assert.equal(html.includes(">Year<"), false);
|
|
179
|
+
assert.equal(html.includes(">Y<"), false);
|
|
180
|
+
assert.ok(html.includes(">W<"), "the phone ladder did not render");
|
|
181
|
+
});
|
|
182
|
+
|
|
159
183
|
it("carries the density control, which the address never names", () => {
|
|
160
184
|
const html = render("week");
|
|
161
185
|
assert.ok(html.includes("Calendar density"));
|
|
@@ -28,7 +28,7 @@ function seededClient(): QueryClient {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const meta: Meta<typeof FromSelector> = {
|
|
31
|
-
title: "
|
|
31
|
+
title: "Playground/Shipped/Mail/Compose/FromSelector",
|
|
32
32
|
component: FromSelector,
|
|
33
33
|
parameters: { layout: "padded" },
|
|
34
34
|
decorators: [
|
|
@@ -15,7 +15,7 @@ import { AuthenticityBanner } from "@/components/mail/AuthenticityBanner";
|
|
|
15
15
|
* body's own size rather than floating off to the right as chrome.
|
|
16
16
|
*/
|
|
17
17
|
const meta: Meta<typeof AuthenticityBanner> = {
|
|
18
|
-
title: "
|
|
18
|
+
title: "Playground/Shipped/Mail/Reading/Authenticity warning",
|
|
19
19
|
component: AuthenticityBanner,
|
|
20
20
|
parameters: { layout: "centered" },
|
|
21
21
|
};
|
|
@@ -12,7 +12,7 @@ import { BlockedImagesNotice } from "@/components/mail/BlockedImagesNotice";
|
|
|
12
12
|
* message that explains nothing is indistinguishable from a broken one.
|
|
13
13
|
*/
|
|
14
14
|
const meta: Meta<typeof BlockedImagesNotice> = {
|
|
15
|
-
title: "
|
|
15
|
+
title: "Playground/Shipped/Mail/Reading/Blocked images notice",
|
|
16
16
|
component: BlockedImagesNotice,
|
|
17
17
|
parameters: { layout: "padded" },
|
|
18
18
|
};
|
|
@@ -13,7 +13,7 @@ import { EmptyTrashBar } from "@/components/mail/EmptyTrashBar";
|
|
|
13
13
|
* lands under the button and stays there until the user repairs it.
|
|
14
14
|
*/
|
|
15
15
|
const meta: Meta<typeof EmptyTrashBar> = {
|
|
16
|
-
title: "
|
|
16
|
+
title: "Playground/Shipped/Mail/Empty trash",
|
|
17
17
|
component: EmptyTrashBar,
|
|
18
18
|
parameters: { layout: "fullscreen" },
|
|
19
19
|
};
|
|
@@ -27,7 +27,7 @@ import { useIntelligenceDrawer } from "@/hooks/useIntelligenceDrawer";
|
|
|
27
27
|
* mid-width panes both run on.
|
|
28
28
|
*/
|
|
29
29
|
const meta: Meta = {
|
|
30
|
-
title: "
|
|
30
|
+
title: "Playground/Shipped/Mail/Reading/Intelligence drawer",
|
|
31
31
|
parameters: { layout: "centered" },
|
|
32
32
|
};
|
|
33
33
|
export default meta;
|
|
@@ -7,7 +7,7 @@ import { CalendarFeedCard } from "./CalendarFeedCard";
|
|
|
7
7
|
* is legible and the two confirmations that take it away.
|
|
8
8
|
*/
|
|
9
9
|
const meta: Meta<typeof CalendarFeedCard> = {
|
|
10
|
-
title: "
|
|
10
|
+
title: "Playground/Shipped/Settings/Calendars/Subscription address",
|
|
11
11
|
component: CalendarFeedCard,
|
|
12
12
|
parameters: { layout: "padded" },
|
|
13
13
|
args: {
|
|
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
|
3
3
|
import { LabelsList } from "./LabelsList";
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof LabelsList> = {
|
|
6
|
-
title: "
|
|
6
|
+
title: "Playground/Shipped/Settings/Labels/LabelsList",
|
|
7
7
|
component: LabelsList,
|
|
8
8
|
parameters: { layout: "padded" },
|
|
9
9
|
decorators: [
|
|
@@ -10,7 +10,7 @@ import { deleteLabelConfirmCopy } from "@/lib/organize/label-delete-copy";
|
|
|
10
10
|
* approved surface, not a placeholder for one.
|
|
11
11
|
*/
|
|
12
12
|
const meta: Meta<typeof ConfirmDialog> = {
|
|
13
|
-
title: "
|
|
13
|
+
title: "Playground/Shipped/Settings/Labels/Delete confirmation",
|
|
14
14
|
component: ConfirmDialog,
|
|
15
15
|
parameters: { layout: "centered" },
|
|
16
16
|
};
|
|
@@ -103,7 +103,7 @@ function SheetStage({ children }: { children: ReactNode }) {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
const meta: Meta = {
|
|
106
|
-
title: "
|
|
106
|
+
title: "Playground/Shipped/Settings/Filters",
|
|
107
107
|
parameters: { layout: "fullscreen" },
|
|
108
108
|
};
|
|
109
109
|
export default meta;
|
|
@@ -12,7 +12,7 @@ import { ErrorBanner } from "./ErrorBanner";
|
|
|
12
12
|
* builds from build-time constants Storybook has no `define` for.
|
|
13
13
|
*/
|
|
14
14
|
const meta: Meta<typeof ErrorBanner> = {
|
|
15
|
-
title: "
|
|
15
|
+
title: "Playground/Shipped/Mail/ErrorBanner",
|
|
16
16
|
component: ErrorBanner,
|
|
17
17
|
parameters: { layout: "padded" },
|
|
18
18
|
args: {
|
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
* week instead of on an error.
|
|
15
15
|
*/
|
|
16
16
|
import type { CalendarSlotPick } from "@remit/ui";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
createFileRoute,
|
|
19
|
+
Navigate,
|
|
20
|
+
Outlet,
|
|
21
|
+
redirect,
|
|
22
|
+
} from "@tanstack/react-router";
|
|
18
23
|
import { useCallback, useState } from "react";
|
|
19
24
|
import { AgendaView } from "@/components/calendar/AgendaView";
|
|
20
25
|
import { CalendarComposeSeedProvider } from "@/components/calendar/CalendarComposeSeed";
|
|
@@ -22,6 +27,7 @@ import { CalendarShell } from "@/components/calendar/CalendarShell";
|
|
|
22
27
|
import { CalendarWorkspace } from "@/components/calendar/CalendarWorkspace";
|
|
23
28
|
import { calendarInstanceId, deviceTimeZone } from "@/hooks/calendar";
|
|
24
29
|
import { useCalendarData } from "@/hooks/useCalendarData";
|
|
30
|
+
import { useLayoutTier } from "@/hooks/useLayoutTier";
|
|
25
31
|
import {
|
|
26
32
|
readCalendarDensity,
|
|
27
33
|
writeCalendarDensity,
|
|
@@ -56,6 +62,21 @@ export const Route = createFileRoute("/calendar/$view/$date")({
|
|
|
56
62
|
});
|
|
57
63
|
|
|
58
64
|
function CalendarViewLayout() {
|
|
65
|
+
const { view, date } = useCalendarAddress();
|
|
66
|
+
const isPhone = useLayoutTier() === "phone";
|
|
67
|
+
if (isPhone && view === "year")
|
|
68
|
+
return (
|
|
69
|
+
<Navigate
|
|
70
|
+
to="/calendar/$view/$date"
|
|
71
|
+
params={{ view: "month", date }}
|
|
72
|
+
search={true}
|
|
73
|
+
replace
|
|
74
|
+
/>
|
|
75
|
+
);
|
|
76
|
+
return <CalendarView isPhone={isPhone} />;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function CalendarView({ isPhone }: { isPhone: boolean }) {
|
|
59
80
|
const { view, date, calendarIds } = useCalendarAddress();
|
|
60
81
|
// The strip fetches the days it holds a week at a time and draws none of
|
|
61
82
|
// this, so at that zoom the layout asks for nothing: the address rewrites on
|
|
@@ -121,6 +142,7 @@ function CalendarViewLayout() {
|
|
|
121
142
|
onChangeDensity={changeDensity}
|
|
122
143
|
onSelectEvent={selectEvent}
|
|
123
144
|
onPickSlot={pickSlot}
|
|
145
|
+
touch={isPhone}
|
|
124
146
|
/>
|
|
125
147
|
);
|
|
126
148
|
|