mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-04 16:06:41 +00:00
feat: show five or seven days in the multi-day calendar view on wider screens
This commit is contained in:
@@ -134,6 +134,7 @@
|
|||||||
"update_error": "Aufgabe konnte nicht aktualisiert werden"
|
"update_error": "Aufgabe konnte nicht aktualisiert werden"
|
||||||
},
|
},
|
||||||
"views": {
|
"views": {
|
||||||
|
"n_days": "{count} Tage",
|
||||||
"schedule": "Terminübersicht",
|
"schedule": "Terminübersicht",
|
||||||
"three_days": "3 Tage"
|
"three_days": "3 Tage"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,6 +137,7 @@
|
|||||||
"update_error": "Failed to update task"
|
"update_error": "Failed to update task"
|
||||||
},
|
},
|
||||||
"views": {
|
"views": {
|
||||||
|
"n_days": "{count} days",
|
||||||
"schedule": "Schedule",
|
"schedule": "Schedule",
|
||||||
"three_days": "3 days"
|
"three_days": "3 days"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ type TimeFormat = '12h' | '24h';
|
|||||||
|
|
||||||
interface TimeGridScrollViewProps {
|
interface TimeGridScrollViewProps {
|
||||||
mode: TimeGridMode;
|
mode: TimeGridMode;
|
||||||
|
/** Days of the multi-day mode (`multiDayCount`). */
|
||||||
|
multiDays?: number;
|
||||||
/** The day the user navigated to; its week (week view) or itself (day view) is scrolled into view. */
|
/** The day the user navigated to; its week (week view) or itself (day view) is scrolled into view. */
|
||||||
focus: CalendarFocus;
|
focus: CalendarFocus;
|
||||||
/** The loaded window: one column per day. */
|
/** The loaded window: one column per day. */
|
||||||
@@ -96,6 +98,7 @@ interface TimeGridScrollViewProps {
|
|||||||
*/
|
*/
|
||||||
function TimeGridScrollViewInner({
|
function TimeGridScrollViewInner({
|
||||||
mode,
|
mode,
|
||||||
|
multiDays = 3,
|
||||||
focus,
|
focus,
|
||||||
window,
|
window,
|
||||||
selectedDate,
|
selectedDate,
|
||||||
@@ -120,7 +123,7 @@ function TimeGridScrollViewInner({
|
|||||||
[eventsByDay, events],
|
[eventsByDay, events],
|
||||||
);
|
);
|
||||||
|
|
||||||
const perScreen = columnsPerScreen(mode);
|
const perScreen = columnsPerScreen(mode, multiDays);
|
||||||
const { width: screenWidth } = useWindowDimensions();
|
const { width: screenWidth } = useWindowDimensions();
|
||||||
const [rootWidth, setRootWidth] = React.useState(screenWidth);
|
const [rootWidth, setRootWidth] = React.useState(screenWidth);
|
||||||
const listWidth = Math.max(perScreen, rootWidth - GUTTER_WIDTH);
|
const listWidth = Math.max(perScreen, rootWidth - GUTTER_WIDTH);
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ import {
|
|||||||
} from '../calendar-scroll-window';
|
} from '../calendar-scroll-window';
|
||||||
import {
|
import {
|
||||||
buildAllDaySegments,
|
buildAllDaySegments,
|
||||||
|
columnsPerScreen,
|
||||||
headerColumnRange,
|
headerColumnRange,
|
||||||
hourAtOffset,
|
hourAtOffset,
|
||||||
|
multiDayCount,
|
||||||
timeGridFocusColumn,
|
timeGridFocusColumn,
|
||||||
windowDays,
|
windowDays,
|
||||||
} from '../calendar-time-grid';
|
} from '../calendar-time-grid';
|
||||||
@@ -118,3 +120,18 @@ describe('hourAtOffset', () => {
|
|||||||
expect(hourAtOffset(-4, 48)).toBe(0);
|
expect(hourAtOffset(-4, 48)).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('multiDayCount', () => {
|
||||||
|
it('shows three days on a phone, five on a medium window and seven on a wide one', () => {
|
||||||
|
expect(multiDayCount(411)).toBe(3);
|
||||||
|
expect(multiDayCount(800)).toBe(5);
|
||||||
|
expect(multiDayCount(1280)).toBe(7);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('only changes the multi-day view', () => {
|
||||||
|
expect(columnsPerScreen('3day', 7)).toBe(7);
|
||||||
|
expect(columnsPerScreen('week', 5)).toBe(7);
|
||||||
|
expect(columnsPerScreen('day', 7)).toBe(1);
|
||||||
|
expect(columnsPerScreen('3day')).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -18,9 +18,18 @@ import type { DayRange, WeekStartsOn } from './calendar-scroll-window';
|
|||||||
|
|
||||||
export type TimeGridMode = 'week' | '3day' | 'day';
|
export type TimeGridMode = 'week' | '3day' | 'day';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Days the multi-day view ("3 days" on a phone) shows for a window `width`
|
||||||
|
* wide: three on a phone, five on a medium window, a full seven-day strip
|
||||||
|
* on a wide one, so the extra width buys days rather than wider columns.
|
||||||
|
*/
|
||||||
|
export function multiDayCount(width: number): number {
|
||||||
|
return width >= 840 ? 7 : width >= 600 ? 5 : 3;
|
||||||
|
}
|
||||||
|
|
||||||
/** Day columns on one screen. */
|
/** Day columns on one screen. */
|
||||||
export function columnsPerScreen(mode: TimeGridMode): number {
|
export function columnsPerScreen(mode: TimeGridMode, multiDays = 3): number {
|
||||||
return mode === 'week' ? 7 : mode === '3day' ? 3 : 1;
|
return mode === 'week' ? 7 : mode === '3day' ? multiDays : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Every day of the window, in order. */
|
/** Every day of the window, in order. */
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ import {
|
|||||||
StyleSheet,
|
StyleSheet,
|
||||||
Pressable,
|
Pressable,
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
|
useWindowDimensions,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
import { multiDayCount } from '../lib/calendar-time-grid';
|
||||||
import {
|
import {
|
||||||
Calendar as CalendarIcon,
|
Calendar as CalendarIcon,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
@@ -110,10 +112,10 @@ function windowMode(mode: ViewMode): ScrollViewMode {
|
|||||||
return mode === '3day' ? 'day' : mode;
|
return mode === '3day' ? 'day' : mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A window without free scrolling: one period, three days for the three-day grid.
|
// A window without free scrolling: one period, the multi-day grid's days.
|
||||||
function fixedWindowFor(mode: ViewMode, anchor: Date): ScrollWindowState {
|
function fixedWindowFor(mode: ViewMode, anchor: Date, multiDays: number): ScrollWindowState {
|
||||||
const state = fixedScrollWindowState(windowMode(mode), anchor);
|
const state = fixedScrollWindowState(windowMode(mode), anchor);
|
||||||
return mode === '3day' ? { ...state, after: 2 } : state;
|
return mode === '3day' ? { ...state, after: multiDays - 1 } : state;
|
||||||
}
|
}
|
||||||
type PendingAction =
|
type PendingAction =
|
||||||
| {
|
| {
|
||||||
@@ -189,6 +191,9 @@ export default function CalendarScreen() {
|
|||||||
);
|
);
|
||||||
// The sideways-scrolling week and day grids assume a left-to-right strip;
|
// The sideways-scrolling week and day grids assume a left-to-right strip;
|
||||||
// right-to-left layouts keep them paged.
|
// right-to-left layouts keep them paged.
|
||||||
|
// Wider windows show more days in the multi-day view.
|
||||||
|
const { width: windowWidth } = useWindowDimensions();
|
||||||
|
const multiDays = multiDayCount(windowWidth);
|
||||||
const freeScroll =
|
const freeScroll =
|
||||||
calendarFreeScroll
|
calendarFreeScroll
|
||||||
&& ((viewMode !== 'week' && viewMode !== '3day' && viewMode !== 'day') || !I18nManager.isRTL);
|
&& ((viewMode !== 'week' && viewMode !== '3day' && viewMode !== 'day') || !I18nManager.isRTL);
|
||||||
@@ -197,8 +202,8 @@ export default function CalendarScreen() {
|
|||||||
() =>
|
() =>
|
||||||
freeScroll
|
freeScroll
|
||||||
? normalizeScrollWindowState(windowState, windowMode(viewMode), parseDayKey(focusKey))
|
? normalizeScrollWindowState(windowState, windowMode(viewMode), parseDayKey(focusKey))
|
||||||
: fixedWindowFor(viewMode, parseDayKey(focusKey)),
|
: fixedWindowFor(viewMode, parseDayKey(focusKey), multiDays),
|
||||||
[freeScroll, windowState, viewMode, focusKey],
|
[freeScroll, windowState, viewMode, focusKey, multiDays],
|
||||||
);
|
);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (freeScroll && activeWindowState !== windowState) setWindowState(activeWindowState);
|
if (freeScroll && activeWindowState !== windowState) setWindowState(activeWindowState);
|
||||||
@@ -482,21 +487,21 @@ export default function CalendarScreen() {
|
|||||||
const base = visibleDate ?? focus.date;
|
const base = visibleDate ?? focus.date;
|
||||||
jumpTo(
|
jumpTo(
|
||||||
viewMode === 'week' ? subWeeks(base, 1)
|
viewMode === 'week' ? subWeeks(base, 1)
|
||||||
: viewMode === '3day' ? subDays(base, 3)
|
: viewMode === '3day' ? subDays(base, multiDays)
|
||||||
: viewMode === 'day' ? subDays(base, 1)
|
: viewMode === 'day' ? subDays(base, 1)
|
||||||
: subMonths(base, 1),
|
: subMonths(base, 1),
|
||||||
);
|
);
|
||||||
}, [viewMode, visibleDate, focus.date, jumpTo]);
|
}, [viewMode, visibleDate, focus.date, jumpTo, multiDays]);
|
||||||
|
|
||||||
const goNext = React.useCallback(() => {
|
const goNext = React.useCallback(() => {
|
||||||
const base = visibleDate ?? focus.date;
|
const base = visibleDate ?? focus.date;
|
||||||
jumpTo(
|
jumpTo(
|
||||||
viewMode === 'week' ? addWeeks(base, 1)
|
viewMode === 'week' ? addWeeks(base, 1)
|
||||||
: viewMode === '3day' ? addDays(base, 3)
|
: viewMode === '3day' ? addDays(base, multiDays)
|
||||||
: viewMode === 'day' ? addDays(base, 1)
|
: viewMode === 'day' ? addDays(base, 1)
|
||||||
: addMonths(base, 1),
|
: addMonths(base, 1),
|
||||||
);
|
);
|
||||||
}, [viewMode, visibleDate, focus.date, jumpTo]);
|
}, [viewMode, visibleDate, focus.date, jumpTo, multiDays]);
|
||||||
|
|
||||||
const goToday = React.useCallback(() => {
|
const goToday = React.useCallback(() => {
|
||||||
jumpTo(displayNow());
|
jumpTo(displayNow());
|
||||||
@@ -520,11 +525,17 @@ export default function CalendarScreen() {
|
|||||||
() => [
|
() => [
|
||||||
{ key: 'agenda', label: t('calendar.views.schedule', 'Schedule'), Icon: Rows2 },
|
{ key: 'agenda', label: t('calendar.views.schedule', 'Schedule'), Icon: Rows2 },
|
||||||
{ key: 'day', label: t('calendar.views.day', 'Day'), Icon: Square },
|
{ key: 'day', label: t('calendar.views.day', 'Day'), Icon: Square },
|
||||||
{ key: '3day', label: t('calendar.views.three_days', '3 days'), Icon: Columns3 },
|
{
|
||||||
|
key: '3day',
|
||||||
|
label: multiDays === 3
|
||||||
|
? t('calendar.views.three_days', '3 days')
|
||||||
|
: t('calendar.views.n_days', '{count} days', { count: multiDays }),
|
||||||
|
Icon: Columns3,
|
||||||
|
},
|
||||||
{ key: 'week', label: t('calendar.views.week', 'Week'), Icon: Columns4 },
|
{ key: 'week', label: t('calendar.views.week', 'Week'), Icon: Columns4 },
|
||||||
{ key: 'month', label: t('calendar.views.month', 'Month'), Icon: Grid3x3 },
|
{ key: 'month', label: t('calendar.views.month', 'Month'), Icon: Grid3x3 },
|
||||||
],
|
],
|
||||||
[t],
|
[t, multiDays],
|
||||||
);
|
);
|
||||||
|
|
||||||
// The month that drops down under the title; the title follows the month
|
// The month that drops down under the title; the title follows the month
|
||||||
@@ -1094,6 +1105,7 @@ export default function CalendarScreen() {
|
|||||||
<TimeGridScrollView
|
<TimeGridScrollView
|
||||||
key={windowKey}
|
key={windowKey}
|
||||||
mode={viewMode}
|
mode={viewMode}
|
||||||
|
multiDays={multiDays}
|
||||||
focus={focus}
|
focus={focus}
|
||||||
window={scrollWindow}
|
window={scrollWindow}
|
||||||
onExtendStart={freeScroll && scrollWindow.canExtendStart ? extendWindowStart : undefined}
|
onExtendStart={freeScroll && scrollWindow.canExtendStart ? extendWindowStart : undefined}
|
||||||
|
|||||||
Reference in New Issue
Block a user