@remit/web-client 0.0.319 → 0.0.320
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/components/calendar/CalendarShell.stories.tsx +2 -0
- package/src/components/calendar/CalendarViewPlaceholder.stories.tsx +5 -18
- package/src/components/calendar/CalendarViewPlaceholder.tsx +3 -22
- package/src/components/calendar/CalendarWorkspace.stories.tsx +41 -6
- package/src/components/calendar/CalendarWorkspace.tsx +7 -1
- package/src/components/calendar/calendar-workspace.render.test.ts +9 -12
- package/src/hooks/calendar/window.test.ts +18 -5
- package/src/hooks/calendar/window.ts +1 -1
- package/src/lib/calendar-route.test.ts +3 -3
- package/src/lib/calendar-route.ts +4 -4
- package/src/routes/calendar/$view.$date.tsx +4 -0
- package/src/routing/calendar.ts +22 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remit/web-client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.320",
|
|
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": {
|
|
@@ -115,6 +115,8 @@ function Shell({
|
|
|
115
115
|
onChangeView={() => undefined}
|
|
116
116
|
onToday={() => undefined}
|
|
117
117
|
onStep={() => undefined}
|
|
118
|
+
dayHref={(day) => `#${day}`}
|
|
119
|
+
onZoomDay={() => undefined}
|
|
118
120
|
onChangeDensity={setDensity}
|
|
119
121
|
onSelectEvent={() => undefined}
|
|
120
122
|
onPickSlot={() => undefined}
|
|
@@ -5,7 +5,7 @@ import { CalendarViewPlaceholder } from "./CalendarViewPlaceholder";
|
|
|
5
5
|
/**
|
|
6
6
|
* A zoom the ladder offers and does not draw yet.
|
|
7
7
|
*
|
|
8
|
-
* The route is addressable at all five zooms, so
|
|
8
|
+
* The route is addressable at all five zooms, so one of them lands on this. What
|
|
9
9
|
* it has to do is say which of the two things it is: a view that rendered
|
|
10
10
|
* nothing would be indistinguishable from a month with nothing booked in it,
|
|
11
11
|
* and the reader would plan around an empty screen.
|
|
@@ -14,9 +14,9 @@ const meta: Meta<typeof CalendarViewPlaceholder> = {
|
|
|
14
14
|
title: "Playground/Shipped/Calendar/Not built yet",
|
|
15
15
|
component: CalendarViewPlaceholder,
|
|
16
16
|
parameters: { layout: "fullscreen" },
|
|
17
|
-
render: (
|
|
17
|
+
render: () => (
|
|
18
18
|
<div className="h-dvh bg-canvas">
|
|
19
|
-
<CalendarViewPlaceholder
|
|
19
|
+
<CalendarViewPlaceholder />
|
|
20
20
|
</div>
|
|
21
21
|
),
|
|
22
22
|
};
|
|
@@ -24,9 +24,8 @@ export default meta;
|
|
|
24
24
|
|
|
25
25
|
type Story = StoryObj<typeof CalendarViewPlaceholder>;
|
|
26
26
|
|
|
27
|
-
/** It names the zoom it is waiting on, and the
|
|
27
|
+
/** It names the zoom it is waiting on, and the four that work today. */
|
|
28
28
|
export const Year: Story = {
|
|
29
|
-
args: { view: "year" },
|
|
30
29
|
play: async ({ canvasElement }) => {
|
|
31
30
|
const canvas = within(canvasElement);
|
|
32
31
|
await expect(
|
|
@@ -36,19 +35,7 @@ export const Year: Story = {
|
|
|
36
35
|
);
|
|
37
36
|
await expect(canvas.getByText("Not built yet")).toBeVisible();
|
|
38
37
|
await expect(
|
|
39
|
-
canvas.getByText(/Week, Day and Agenda work now\./),
|
|
38
|
+
canvas.getByText(/Month, Week, Day and Agenda work now\./),
|
|
40
39
|
).toBeVisible();
|
|
41
40
|
},
|
|
42
41
|
};
|
|
43
|
-
|
|
44
|
-
export const Month: Story = {
|
|
45
|
-
args: { view: "month" },
|
|
46
|
-
play: async ({ canvasElement }) => {
|
|
47
|
-
const canvas = within(canvasElement);
|
|
48
|
-
await expect(
|
|
49
|
-
canvas.getByTestId("calendar-placeholder-month"),
|
|
50
|
-
).toHaveTextContent(
|
|
51
|
-
"The month grid arrives with the rest of the zoom ladder.",
|
|
52
|
-
);
|
|
53
|
-
},
|
|
54
|
-
};
|
|
@@ -1,34 +1,15 @@
|
|
|
1
|
-
import type { CalendarViewId } from "@remit/ui";
|
|
2
1
|
import { CalendarRange } from "lucide-react";
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
* A zoom level that is named but not drawn yet.
|
|
6
|
-
*
|
|
7
|
-
* The ladder is five steps and two of them arrive later, so the route stays
|
|
8
|
-
* addressable and says what it is waiting on. A view that silently rendered
|
|
9
|
-
* nothing would be indistinguishable from a week with nothing in it.
|
|
10
|
-
*/
|
|
11
|
-
const WAITING_ON: Record<string, string> = {
|
|
12
|
-
year: "The year grid arrives with the rest of the zoom ladder.",
|
|
13
|
-
month: "The month grid arrives with the rest of the zoom ladder.",
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export interface CalendarViewPlaceholderProps {
|
|
17
|
-
view: CalendarViewId;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function CalendarViewPlaceholder({
|
|
21
|
-
view,
|
|
22
|
-
}: CalendarViewPlaceholderProps) {
|
|
3
|
+
export function CalendarViewPlaceholder() {
|
|
23
4
|
return (
|
|
24
5
|
<div
|
|
25
6
|
className="flex h-full flex-col items-center justify-center gap-2 bg-surface p-8 text-center"
|
|
26
|
-
data-testid=
|
|
7
|
+
data-testid="calendar-placeholder-year"
|
|
27
8
|
>
|
|
28
9
|
<CalendarRange className="size-8 text-fg-subtle" aria-hidden="true" />
|
|
29
10
|
<p className="text-sm font-medium text-fg">Not built yet</p>
|
|
30
11
|
<p className="max-w-xs text-sm text-fg-muted">
|
|
31
|
-
|
|
12
|
+
The year grid arrives with the rest of the zoom ladder. Month, Week, Day
|
|
32
13
|
and Agenda work now.
|
|
33
14
|
</p>
|
|
34
15
|
</div>
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
} from "@remit/ui";
|
|
9
9
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
10
10
|
import { useState } from "react";
|
|
11
|
+
import { expect, userEvent, waitFor, within } from "storybook/test";
|
|
12
|
+
import { stepCalendarDate } from "@/lib/calendar-route";
|
|
11
13
|
import { CalendarWaiting } from "./CalendarWaiting";
|
|
12
14
|
import { CalendarWorkspace } from "./CalendarWorkspace";
|
|
13
15
|
|
|
@@ -206,12 +208,13 @@ function Workspace({
|
|
|
206
208
|
waiting?: "none" | "some" | "refused";
|
|
207
209
|
}) {
|
|
208
210
|
const [view, setView] = useState<CalendarViewId>(initialView);
|
|
211
|
+
const [date, setDate] = useState(DATE);
|
|
209
212
|
const [density, setDensity] = useState<Density>("comfortable");
|
|
210
213
|
const [selected, setSelected] = useState("");
|
|
211
214
|
return (
|
|
212
215
|
<CalendarWorkspace
|
|
213
216
|
view={view}
|
|
214
|
-
date={
|
|
217
|
+
date={date}
|
|
215
218
|
events={events}
|
|
216
219
|
colorByCalendarId={colorByCalendarId}
|
|
217
220
|
// The strip loads its own days and is storied on its own; here it
|
|
@@ -235,7 +238,14 @@ function Workspace({
|
|
|
235
238
|
now={NOW}
|
|
236
239
|
onChangeView={setView}
|
|
237
240
|
onToday={() => undefined}
|
|
238
|
-
onStep={() =>
|
|
241
|
+
onStep={(direction) =>
|
|
242
|
+
setDate((current) => stepCalendarDate(current, view, direction))
|
|
243
|
+
}
|
|
244
|
+
dayHref={(day) => `/calendar/day/${day}`}
|
|
245
|
+
onZoomDay={(day) => {
|
|
246
|
+
setView("day");
|
|
247
|
+
setDate(day);
|
|
248
|
+
}}
|
|
239
249
|
onChangeDensity={setDensity}
|
|
240
250
|
onSelectEvent={setSelected}
|
|
241
251
|
onPickSlot={() => undefined}
|
|
@@ -263,6 +273,33 @@ export const Week: Story = {};
|
|
|
263
273
|
|
|
264
274
|
export const Day: Story = { args: { initialView: "day" } };
|
|
265
275
|
|
|
276
|
+
export const Month: Story = {
|
|
277
|
+
args: { initialView: "month" },
|
|
278
|
+
play: async ({ canvasElement }) => {
|
|
279
|
+
const canvas = within(canvasElement);
|
|
280
|
+
await waitFor(() =>
|
|
281
|
+
expect(canvas.getAllByText("Roadmap review").length).toBeGreaterThan(0),
|
|
282
|
+
);
|
|
283
|
+
await userEvent.click(canvas.getByRole("button", { name: "Next" }));
|
|
284
|
+
await waitFor(() =>
|
|
285
|
+
expect(canvas.getByRole("heading", { level: 2 })).toHaveTextContent(
|
|
286
|
+
/July 2026/,
|
|
287
|
+
),
|
|
288
|
+
);
|
|
289
|
+
const day = await canvas.findByRole("link", {
|
|
290
|
+
name: /15 July 2026$/,
|
|
291
|
+
});
|
|
292
|
+
await expect(day).toHaveAttribute("href", "/calendar/day/2026-07-15");
|
|
293
|
+
await userEvent.click(day);
|
|
294
|
+
await waitFor(() =>
|
|
295
|
+
expect(canvas.getByRole("radio", { name: "Day" })).toBeChecked(),
|
|
296
|
+
);
|
|
297
|
+
await expect(canvas.getByRole("heading", { level: 2 })).toHaveTextContent(
|
|
298
|
+
/15 July 2026|July 15, 2026/,
|
|
299
|
+
);
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
|
|
266
303
|
/** A phone: two rows of thumb-sized controls, and no year on the ladder. */
|
|
267
304
|
export const Phone: Story = {
|
|
268
305
|
args: { touch: true },
|
|
@@ -288,13 +325,11 @@ export const CouldNotLoad: Story = {
|
|
|
288
325
|
};
|
|
289
326
|
|
|
290
327
|
/**
|
|
291
|
-
* The
|
|
292
|
-
*
|
|
328
|
+
* The zoom that is named but not drawn yet. It says what it is waiting on, so
|
|
329
|
+
* the route is addressable without pretending to be finished.
|
|
293
330
|
*/
|
|
294
331
|
export const YearNotBuiltYet: Story = { args: { initialView: "year" } };
|
|
295
332
|
|
|
296
|
-
export const MonthNotBuiltYet: Story = { args: { initialView: "month" } };
|
|
297
|
-
|
|
298
333
|
/**
|
|
299
334
|
* Invitations the mail is still asking about, beside the week they would land
|
|
300
335
|
* in. One at a time, as a deck; nothing here is on the calendar until added.
|
|
@@ -76,6 +76,8 @@ export interface CalendarWorkspaceProps {
|
|
|
76
76
|
onSelectEvent: (eventId: string) => void;
|
|
77
77
|
onPickSlot: (pick: CalendarSlotPick) => void;
|
|
78
78
|
onStep: (direction: -1 | 1) => void;
|
|
79
|
+
dayHref: (date: string) => string;
|
|
80
|
+
onZoomDay: (date: string) => void;
|
|
79
81
|
/**
|
|
80
82
|
* A phone: the toolbar splits into two rows of thumb-sized controls, because
|
|
81
83
|
* one row of them is wider than the screen and pushes the grid off it.
|
|
@@ -137,6 +139,8 @@ export function CalendarWorkspace({
|
|
|
137
139
|
onSelectEvent,
|
|
138
140
|
onPickSlot,
|
|
139
141
|
onStep,
|
|
142
|
+
dayHref,
|
|
143
|
+
onZoomDay,
|
|
140
144
|
touch = false,
|
|
141
145
|
}: CalendarWorkspaceProps) {
|
|
142
146
|
// Held against the address it was measured for, so leaving a view does not
|
|
@@ -237,12 +241,14 @@ export function CalendarWorkspace({
|
|
|
237
241
|
now={now}
|
|
238
242
|
onSelectEvent={onSelectEvent}
|
|
239
243
|
onPickSlot={onPickSlot}
|
|
244
|
+
dayHref={dayHref}
|
|
245
|
+
onZoomDay={onZoomDay}
|
|
240
246
|
onRangeChange={(measuredTitle) =>
|
|
241
247
|
setMeasured({ key: addressKey, title: measuredTitle })
|
|
242
248
|
}
|
|
243
249
|
/>
|
|
244
250
|
) : (
|
|
245
|
-
<CalendarViewPlaceholder
|
|
251
|
+
<CalendarViewPlaceholder />
|
|
246
252
|
)}
|
|
247
253
|
</div>
|
|
248
254
|
)}
|
|
@@ -70,6 +70,8 @@ const workspaceProps = (
|
|
|
70
70
|
onChangeView: noop,
|
|
71
71
|
onToday: noop,
|
|
72
72
|
onStep: noop,
|
|
73
|
+
dayHref: (day) => `/calendar/day/${day}`,
|
|
74
|
+
onZoomDay: noop,
|
|
73
75
|
onChangeDensity: noop,
|
|
74
76
|
onSelectEvent: noop,
|
|
75
77
|
onPickSlot: noop,
|
|
@@ -103,8 +105,8 @@ const render = (
|
|
|
103
105
|
renderQuietly(createElement(CalendarWorkspace, workspaceProps(view, events)));
|
|
104
106
|
|
|
105
107
|
describe("the zoom the address names", () => {
|
|
106
|
-
it("draws the grid for the week and the day", () => {
|
|
107
|
-
for (const view of ["week", "day"] as const) {
|
|
108
|
+
it("draws the grid for the month, the week and the day", () => {
|
|
109
|
+
for (const view of ["month", "week", "day"] as const) {
|
|
108
110
|
const html = render(view);
|
|
109
111
|
assert.ok(
|
|
110
112
|
html.includes("Roadmap review"),
|
|
@@ -122,15 +124,10 @@ describe("the zoom the address names", () => {
|
|
|
122
124
|
});
|
|
123
125
|
|
|
124
126
|
it("says so for a zoom that is not drawn yet", () => {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
`${view} rendered no placeholder`,
|
|
130
|
-
);
|
|
131
|
-
assert.ok(html.includes("Not built yet"));
|
|
132
|
-
assert.ok(html.includes("Week, Day and Agenda work now."));
|
|
133
|
-
}
|
|
127
|
+
const html = render("year");
|
|
128
|
+
assert.ok(html.includes("calendar-placeholder-year"));
|
|
129
|
+
assert.ok(html.includes("Not built yet"));
|
|
130
|
+
assert.ok(html.includes("Month, Week, Day and Agenda work now."));
|
|
134
131
|
});
|
|
135
132
|
});
|
|
136
133
|
|
|
@@ -143,7 +140,7 @@ describe("the toolbar", () => {
|
|
|
143
140
|
});
|
|
144
141
|
|
|
145
142
|
it("offers back, forward and today at a zoom that draws no grid", () => {
|
|
146
|
-
const html = render("
|
|
143
|
+
const html = render("year");
|
|
147
144
|
assert.ok(html.includes('aria-label="Previous"'));
|
|
148
145
|
assert.ok(html.includes('aria-label="Next"'));
|
|
149
146
|
assert.ok(html.includes(">Today<"));
|
|
@@ -47,14 +47,27 @@ describe("the window a view covers", () => {
|
|
|
47
47
|
assert.equal(day(window.to), "2026-06-11");
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
it("takes the
|
|
51
|
-
const window = calendarWindow("month", "2026-
|
|
52
|
-
assert.equal(day(window.from), "2026-06-
|
|
53
|
-
assert.equal(day(window.to), "2026-
|
|
50
|
+
it("takes every week the month touches, so the grid's edge days are read", () => {
|
|
51
|
+
const window = calendarWindow("month", "2026-07-10");
|
|
52
|
+
assert.equal(day(window.from), "2026-06-29");
|
|
53
|
+
assert.equal(day(window.to), "2026-08-03");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("takes exactly the month when it starts on a Monday and ends on a Sunday", () => {
|
|
57
|
+
const window = calendarWindow("month", "2027-02-14");
|
|
58
|
+
assert.equal(day(window.from), "2027-02-01");
|
|
59
|
+
assert.equal(day(window.to), "2027-03-01");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("gives every day of one month the same window", () => {
|
|
63
|
+
const first = calendarWindow("month", "2026-06-01");
|
|
64
|
+
for (const date of ["2026-06-15", "2026-06-30"]) {
|
|
65
|
+
assert.deepEqual(calendarWindow("month", date), first);
|
|
66
|
+
}
|
|
54
67
|
});
|
|
55
68
|
|
|
56
69
|
it("rolls a December month into the next year", () => {
|
|
57
|
-
assert.equal(day(calendarWindow("month", "2026-12-31").to), "2027-01-
|
|
70
|
+
assert.equal(day(calendarWindow("month", "2026-12-31").to), "2027-01-04");
|
|
58
71
|
});
|
|
59
72
|
|
|
60
73
|
it("takes the calendar year", () => {
|
|
@@ -154,7 +154,7 @@ function bounds(view: CalendarViewId, date: string): [string, string] {
|
|
|
154
154
|
const first = `${year}-${pad(month)}-01`;
|
|
155
155
|
const next =
|
|
156
156
|
month === 12 ? `${year + 1}-01-01` : `${year}-${pad(month + 1)}-01`;
|
|
157
|
-
return [first, next];
|
|
157
|
+
return [weekStart(first), addDays(weekStart(addDays(next, -1)), 7)];
|
|
158
158
|
}
|
|
159
159
|
if (view === "day") return [date, addDays(date, 1)];
|
|
160
160
|
const start = weekStart(date);
|
|
@@ -85,14 +85,14 @@ describe("canonicalCalendarParams", () => {
|
|
|
85
85
|
});
|
|
86
86
|
|
|
87
87
|
describe("calendarViewMountsGrid", () => {
|
|
88
|
-
it("draws the week and the day", () => {
|
|
88
|
+
it("draws the month, the week and the day", () => {
|
|
89
|
+
assert.equal(calendarViewMountsGrid("month"), true);
|
|
89
90
|
assert.equal(calendarViewMountsGrid("week"), true);
|
|
90
91
|
assert.equal(calendarViewMountsGrid("day"), true);
|
|
91
92
|
});
|
|
92
93
|
|
|
93
|
-
it("leaves the agenda to the strip and the
|
|
94
|
+
it("leaves the agenda to the strip and the year to a later stage", () => {
|
|
94
95
|
assert.equal(calendarViewMountsGrid("year"), false);
|
|
95
|
-
assert.equal(calendarViewMountsGrid("month"), false);
|
|
96
96
|
assert.equal(calendarViewMountsGrid("agenda"), false);
|
|
97
97
|
});
|
|
98
98
|
});
|
|
@@ -32,11 +32,11 @@ const calendarViewSchema = z.enum(calendarViews);
|
|
|
32
32
|
export const DEFAULT_CALENDAR_VIEW: CalendarViewId = "week";
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* The views the grid draws.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* The views the grid draws. The year is a named surface that arrives in a later
|
|
36
|
+
* stage, and it says so on screen rather than matching a route that renders
|
|
37
|
+
* nothing.
|
|
38
38
|
*/
|
|
39
|
-
const GRID_VIEWS = new Set<CalendarViewId>(["week", "day"]);
|
|
39
|
+
const GRID_VIEWS = new Set<CalendarViewId>(["month", "week", "day"]);
|
|
40
40
|
|
|
41
41
|
export const calendarViewMountsGrid = (view: CalendarViewId): boolean =>
|
|
42
42
|
GRID_VIEWS.has(view);
|
|
@@ -93,6 +93,8 @@ function CalendarView({ isPhone }: { isPhone: boolean }) {
|
|
|
93
93
|
openEvent,
|
|
94
94
|
openComposer,
|
|
95
95
|
changeSuggestionFirst,
|
|
96
|
+
zoomToDay,
|
|
97
|
+
dayHref,
|
|
96
98
|
} = useCalendarNavigation();
|
|
97
99
|
const openedEvent = useOpenCalendarEvent();
|
|
98
100
|
const isWriting = useIsWritingEvent();
|
|
@@ -147,6 +149,8 @@ function CalendarView({ isPhone }: { isPhone: boolean }) {
|
|
|
147
149
|
onChangeView={goToView}
|
|
148
150
|
onToday={goToToday}
|
|
149
151
|
onStep={step}
|
|
152
|
+
dayHref={dayHref}
|
|
153
|
+
onZoomDay={zoomToDay}
|
|
150
154
|
onChangeDensity={changeDensity}
|
|
151
155
|
onSelectEvent={selectEvent}
|
|
152
156
|
onPickSlot={pickSlot}
|
package/src/routing/calendar.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { CalendarViewId } from "@remit/ui";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
useNavigate,
|
|
4
|
+
useParams,
|
|
5
|
+
useRouter,
|
|
6
|
+
useSearch,
|
|
7
|
+
} from "@tanstack/react-router";
|
|
3
8
|
import { useCallback, useMemo } from "react";
|
|
4
9
|
import {
|
|
5
10
|
canonicalCalendarParams,
|
|
@@ -101,6 +106,7 @@ export interface CalendarNavigation {
|
|
|
101
106
|
setAnchorDate: (date: string) => void;
|
|
102
107
|
/** Out of the list and into the grid for one day. */
|
|
103
108
|
zoomToDay: (date: string) => void;
|
|
109
|
+
dayHref: (date: string) => string;
|
|
104
110
|
/**
|
|
105
111
|
* One occurrence names the series it belongs to and the instance under it;
|
|
106
112
|
* a resource that does not recur names only itself.
|
|
@@ -121,6 +127,7 @@ export interface CalendarNavigation {
|
|
|
121
127
|
*/
|
|
122
128
|
export function useCalendarNavigation(): CalendarNavigation {
|
|
123
129
|
const navigate = useNavigate();
|
|
130
|
+
const router = useRouter();
|
|
124
131
|
const retainPanels = useRetainOpenPanels();
|
|
125
132
|
const { view, date } = useCalendarAddress();
|
|
126
133
|
|
|
@@ -136,6 +143,17 @@ export function useCalendarNavigation(): CalendarNavigation {
|
|
|
136
143
|
[navigate, retainPanels],
|
|
137
144
|
);
|
|
138
145
|
|
|
146
|
+
const dayHref = useCallback(
|
|
147
|
+
(next: string) =>
|
|
148
|
+
router.buildLocation({
|
|
149
|
+
to: VIEW_ROUTE,
|
|
150
|
+
params: { view: "day", date: next },
|
|
151
|
+
search: true,
|
|
152
|
+
hash: retainPanels,
|
|
153
|
+
}).href,
|
|
154
|
+
[router, retainPanels],
|
|
155
|
+
);
|
|
156
|
+
|
|
139
157
|
return useMemo(
|
|
140
158
|
() => ({
|
|
141
159
|
goToView: (next: CalendarViewId) => goTo({ view: next, date }),
|
|
@@ -150,7 +168,8 @@ export function useCalendarNavigation(): CalendarNavigation {
|
|
|
150
168
|
hash: retainPanels,
|
|
151
169
|
replace: true,
|
|
152
170
|
}),
|
|
153
|
-
zoomToDay: (next: string) =>
|
|
171
|
+
zoomToDay: (next: string) => navigate({ href: dayHref(next) }),
|
|
172
|
+
dayHref,
|
|
154
173
|
openEvent: (calendarObjectId: string, recurrenceId?: string) =>
|
|
155
174
|
recurrenceId === undefined || recurrenceId === ""
|
|
156
175
|
? navigate({
|
|
@@ -181,7 +200,7 @@ export function useCalendarNavigation(): CalendarNavigation {
|
|
|
181
200
|
hash: retainPanels,
|
|
182
201
|
}),
|
|
183
202
|
}),
|
|
184
|
-
[goTo, navigate, retainPanels, view, date],
|
|
203
|
+
[goTo, navigate, dayHref, retainPanels, view, date],
|
|
185
204
|
);
|
|
186
205
|
}
|
|
187
206
|
|