feat: let timed events show as coloured text or a dot in the month view #1449

This commit is contained in:
Linus Rath
2026-10-31 16:23:00 +01:00
parent 6b94f96e83
commit bc6fee7556
35 changed files with 379 additions and 6 deletions
@@ -0,0 +1,136 @@
import { render, screen, cleanup } from '@testing-library/react';
import { describe, it, expect, vi, afterEach } from 'vitest';
import { CalendarMonthView } from '../calendar-month-view';
import type { CalendarEvent, Calendar, CalendarParticipant } from '@/lib/jmap/types';
import { useSettingsStore, adminPolicyPatch } from '@/stores/settings-store';
// #1449: the month view can draw timed events as coloured text or as a dot
// and text, so they don't look like all-day events. All-day and multi-day
// events, and declined or cancelled ones, stay bars.
const calendars = [{ id: 'cal-1', name: 'Work', color: '#123456' }] as unknown as Calendar[];
function makeEvent(id: string, overrides: Partial<CalendarEvent> = {}): CalendarEvent {
return {
id,
'@type': 'Event',
uid: id,
title: 'Event ' + id,
start: '2026-09-09T09:30:00',
duration: 'PT30M',
showWithoutTime: false,
status: 'confirmed',
calendarIds: { 'cal-1': true },
...overrides,
} as unknown as CalendarEvent;
}
function renderView(events: CalendarEvent[]) {
return render(
<CalendarMonthView
focus={{ date: new Date(2026, 8, 9), nonce: 0 }}
windowKey="month:2026-09-09"
rangeStart={new Date(2026, 7, 31)}
rangeEnd={new Date(2026, 9, 4)}
selectedDate={new Date(2026, 8, 9)}
events={events}
calendars={calendars}
onSelectDate={vi.fn()}
onSelectEvent={vi.fn()}
currentUserEmails={['me@example.com']}
/>,
);
}
function card(title: string): HTMLElement {
return screen.getByText(title).closest('button')!;
}
const timed = makeEvent('timed');
const allDay = makeEvent('allday', { start: '2026-09-10T00:00:00', duration: 'P1D', showWithoutTime: true });
const multiDay = makeEvent('multi', { start: '2026-09-14T22:00:00', duration: 'PT26H' });
const cancelled = makeEvent('cancelled', { start: '2026-09-11T10:00:00', status: 'cancelled' });
const declined = makeEvent('declined', {
start: '2026-09-11T11:00:00',
participants: {
me: { '@type': 'Participant', calendarAddress: 'mailto:me@example.com', participationStatus: 'declined' },
} as unknown as Record<string, CalendarParticipant>,
});
const all = [timed, allDay, multiDay, cancelled, declined];
describe('month view timed event style (#1449)', () => {
afterEach(() => {
cleanup();
useSettingsStore.setState({ calendarMonthTimedEvents: 'bar', showTimeInMonthView: false });
});
it('draws every event as a bar by default', () => {
renderView(all);
for (const title of ['Event timed', 'Event allday', 'Event multi']) {
expect(card(title).style.backgroundColor, title).not.toBe('');
expect(card(title).hasAttribute('data-timed-style'), title).toBe(false);
}
});
it('draws a timed event as its time and title in the calendar colour', () => {
useSettingsStore.setState({ calendarMonthTimedEvents: 'text' });
renderView(all);
const el = card('Event timed');
expect(el.getAttribute('data-timed-style')).toBe('text');
expect(el.style.backgroundColor).toBe('');
expect(el.style.color).toContain('color-mix');
expect(el.style.color).toContain('#123456');
// The start time shows even with "Show time in month view" off.
expect(el.textContent).toContain('09:30');
expect(el.querySelector('[data-event-dot]')).toBeNull();
});
it('puts a dot in the calendar colour before a timed event in the text colour', () => {
useSettingsStore.setState({ calendarMonthTimedEvents: 'dot' });
renderView(all);
const el = card('Event timed');
expect(el.getAttribute('data-timed-style')).toBe('dot');
expect(el.style.backgroundColor).toBe('');
expect(el.style.color).toBe('var(--color-foreground)');
expect(el.querySelector<HTMLElement>('[data-event-dot]')?.style.backgroundColor).toBe('rgb(18, 52, 86)');
expect(el.textContent).toContain('09:30');
});
it.each(['text', 'dot'] as const)('keeps all-day, multi-day, cancelled and declined events as they were (%s)', (style) => {
useSettingsStore.setState({ calendarMonthTimedEvents: style });
renderView(all);
for (const title of ['Event allday', 'Event multi']) {
expect(card(title).hasAttribute('data-timed-style'), title).toBe(false);
expect(card(title).style.backgroundColor, title).not.toBe('');
}
for (const title of ['Event cancelled', 'Event declined']) {
expect(card(title).hasAttribute('data-timed-style'), title).toBe(false);
expect(card(title).style.boxShadow, title).toContain('inset 0 0 0 1px');
expect(screen.getByText(title)).toHaveClass('line-through');
}
});
it('keeps the selection ring on a text-style event', () => {
useSettingsStore.setState({ calendarMonthTimedEvents: 'text' });
render(
<CalendarMonthView
focus={{ date: new Date(2026, 8, 9), nonce: 0 }}
windowKey="month:2026-09-09"
rangeStart={new Date(2026, 7, 31)}
rangeEnd={new Date(2026, 9, 4)}
selectedDate={new Date(2026, 8, 9)}
events={[timed]}
calendars={calendars}
onSelectDate={vi.fn()}
onSelectEvent={vi.fn()}
selectedEventId="timed"
/>,
);
expect(card('Event timed').style.boxShadow).toContain('var(--color-primary)');
});
it('can be preset by an admin policy, and only with a known style', () => {
expect(adminPolicyPatch({}, { defaults: { calendarMonthTimedEvents: 'dot' } })).toEqual({ calendarMonthTimedEvents: 'dot' });
expect(adminPolicyPatch({}, { defaults: { calendarMonthTimedEvents: 'stripes' } })).toEqual({});
});
});
+6 -2
View File
@@ -13,7 +13,7 @@ import type { CalendarEvent, Calendar, CalendarTask } from "@/lib/jmap/types";
import { useAuthStore } from "@/stores/auth-store";
import { useCalendarStore } from "@/stores/calendar-store";
import { movedEventUpdates } from "@/lib/calendar-participants";
import { useSettingsStore } from "@/stores/settings-store";
import { useSettingsStore, type MonthTimedEventStyle } from "@/stores/settings-store";
import type { PendingEventPreview } from "./event-modal";
import { toast } from "@/stores/toast-store";
import { useCalendarLocale } from "@/hooks/use-calendar-locale";
@@ -98,6 +98,7 @@ export function CalendarMonthView({
const paint = useCvdColor();
const describeError = useCalendarErrorMessage();
const showTimeInMonthView = useSettingsStore((state) => state.showTimeInMonthView);
const timedStyle = useSettingsStore((state) => state.calendarMonthTimedEvents);
// On mobile the month view collapses events to dots unless the user opted
// into full entries via "Show time in month view" (#666).
const showChips = !isMobile || showTimeInMonthView;
@@ -531,6 +532,7 @@ export function CalendarMonthView({
currentUserEmails={currentUserEmails}
canEditEvent={canEditEvent}
inView={(d) => checkIsSameMonth(d, visibleMonthDate)}
timedStyle={timedStyle}
onSelectEvent={onSelectEvent}
onHoverEvent={onHoverEvent}
onHoverLeave={onHoverLeave}
@@ -567,7 +569,7 @@ type WeekSegment = ReturnType<typeof buildWeekSegments>[number];
*/
function MonthCellEntries({
dayIndex, week, segments, rowLimit, rowCount, dayEventRows, dayTasks, hidden, overlayTop, rowHeight, chipHeight,
isMobile, calendarMap, pendingPreview, selectedEventId, currentUserEmails, canEditEvent, inView,
isMobile, calendarMap, pendingPreview, selectedEventId, currentUserEmails, canEditEvent, inView, timedStyle,
onSelectEvent, onHoverEvent, onHoverLeave, onContextMenuEvent, onToggleTaskComplete, onSelectTask, onShowMore, moreLabel, paint,
}: {
dayIndex: number;
@@ -589,6 +591,7 @@ function MonthCellEntries({
canEditEvent: (event: CalendarEvent) => boolean;
/** Whether a day is in the month in view: only those take Tab. */
inView: (day: Date) => boolean;
timedStyle?: MonthTimedEventStyle;
onSelectEvent: (event: CalendarEvent, anchorRect: DOMRect) => void;
onHoverEvent?: (event: CalendarEvent, anchorRect: DOMRect) => void;
onHoverLeave?: () => void;
@@ -636,6 +639,7 @@ function MonthCellEntries({
isSelected={segment.event.id === selectedEventId}
calendar={calId ? calendarMap.get(calId) : undefined}
variant="span"
timedStyle={segment.span === 1 ? timedStyle : undefined}
continuesBefore={segment.continuesBefore}
continuesAfter={segment.continuesAfter}
onClick={(rect) => onSelectEvent(segment.event, rect)}
+62 -3
View File
@@ -8,7 +8,7 @@ import { format } from "date-fns";
import { ChevronDown, ChevronUp, Users } from "@/components/icons";
import { getParticipantCount, isDeclinedByUser } from "@/lib/calendar-participants";
import { getEventEndDate, getEventStartDate } from "@/lib/calendar-utils";
import { useSettingsStore } from "@/stores/settings-store";
import { useSettingsStore, type MonthTimedEventStyle } from "@/stores/settings-store";
import { readableTextOn } from "@/lib/color-transform";
import { useLongPress } from "@/hooks/use-long-press";
import { useCvdColor } from "@/hooks/use-cvd-color";
@@ -36,6 +36,12 @@ interface EventCardProps {
currentUserEmails?: string[];
/** False for a day scrolled out of view: Tab skips it (arrows still reach it). */
tabbable?: boolean;
/**
* Month view only (span variant): draw a timed event as coloured text or a
* dot and text instead of a bar (#1449). All-day events, events that run
* on past the drawn day, and declined or cancelled ones keep their look.
*/
timedStyle?: MonthTimedEventStyle;
}
function sanitizeColor(color: string | null | undefined, fallback = "#3b82f6"): string {
@@ -61,6 +67,15 @@ function getEventColor(event: CalendarEvent, calendar?: Calendar): string {
// (eventFillClass) and text in the text colour.
// Everything is drawn with box-shadow, so the selection ring joins the list
// here and the browser's focus outline stays free.
/**
* The event colour as text on the page background: mixed with the text
* colour so a light calendar colour stays readable in the light theme and a
* dark one in the dark theme.
*/
export function eventTextColor(color: string): string {
return `color-mix(in srgb, ${color} 55%, var(--color-foreground))`;
}
export function eventFillStyle(
color: string,
{ inactive, separated, selected, tinted }: { inactive: boolean; separated?: boolean; selected?: boolean; tinted?: boolean },
@@ -70,7 +85,7 @@ export function eventFillStyle(
else if (separated) shadows.push("0 0 0 1px var(--color-background)");
if (inactive) shadows.push(`inset 0 0 0 1px ${color}`);
const fill: CSSProperties = inactive
? { backgroundColor: "var(--color-background)", color: `color-mix(in srgb, ${color} 55%, var(--color-foreground))` }
? { backgroundColor: "var(--color-background)", color: eventTextColor(color) }
: tinted
? {
backgroundColor: `color-mix(in srgb, ${color} 20%, var(--color-background))`,
@@ -115,7 +130,7 @@ function createEventDragPreview(title: string, timeRange: string, color: string)
return el;
}
export function EventCard({ event, calendar, variant, onClick, onMouseEnter, onMouseLeave, onContextMenu, isSelected, draggable: isDraggable, continuesBefore = false, continuesAfter = false, className, style, currentUserEmails, tabbable = true }: EventCardProps) {
export function EventCard({ event, calendar, variant, onClick, onMouseEnter, onMouseLeave, onContextMenu, isSelected, draggable: isDraggable, continuesBefore = false, continuesAfter = false, className, style, currentUserEmails, tabbable = true, timedStyle = "bar" }: EventCardProps) {
const t = useTranslations("calendar");
const [isBeingDragged, setIsBeingDragged] = useState(false);
const paint = useCvdColor();
@@ -246,6 +261,50 @@ export function EventCard({ event, calendar, variant, onClick, onMouseEnter, onM
}
if (variant === "span") {
// Coloured text or dot and text for a timed event that starts and ends
// on the drawn day (#1449). Anything else stays a bar, so all-day and
// multi-day events read as blocked days.
const textStyle = (timedStyle === "text" || timedStyle === "dot")
&& !event.showWithoutTime && !continuesBefore && !continuesAfter && !isInactive
? timedStyle : null;
if (textStyle) {
return (
<button
onClick={(e) => { e.stopPropagation(); onClick?.(e.currentTarget.getBoundingClientRect()); }}
onMouseEnter={(e) => onMouseEnter?.(e.currentTarget.getBoundingClientRect())}
onMouseLeave={() => onMouseLeave?.()}
onContextMenu={handleContextMenu}
onKeyDown={handleKeyDown}
{...touchProps}
aria-label={ariaLabel}
tabIndex={tabbable ? undefined : -1}
{...dragProps}
data-timed-style={textStyle}
className={cn(
"w-full h-full text-start rounded-xs px-1.5 py-0.5 text-xs overflow-hidden",
"hover:bg-muted transition-colors cursor-pointer",
isBeingDragged && "opacity-50",
className
)}
style={{
color: textStyle === "text" ? eventTextColor(color) : "var(--color-foreground)",
...(isSelected ? { boxShadow: "0 0 0 2px var(--color-primary)" } : {}),
...style,
}}
>
<div className="flex items-center gap-1 min-w-0">
{textStyle === "dot" && (
<span data-event-dot aria-hidden="true" className="w-2 h-2 rounded-full flex-shrink-0" style={{ backgroundColor: color }} />
)}
<span className="flex-shrink-0 font-normal opacity-85">{safeFormat(startDate, timeFmt)}</span>
<span dir="auto" className="flex-1 min-w-0 truncate text-start font-medium">{event.title || t("events.no_title")}</span>
{visibleCalendarName && (
<span data-calendar-name dir="auto" className="min-w-0 max-w-[45%] shrink truncate font-normal opacity-85">{visibleCalendarName}</span>
)}
</div>
</button>
);
}
return (
<button
onClick={(e) => { e.stopPropagation(); onClick?.(e.currentTarget.getBoundingClientRect()); }}
+17 -1
View File
@@ -3,7 +3,7 @@
import { useEffect } from 'react';
import { useTranslations } from 'next-intl';
import { useCalendarStore, CalendarViewMode } from '@/stores/calendar-store';
import { useSettingsStore } from '@/stores/settings-store';
import { useSettingsStore, type MonthTimedEventStyle } from '@/stores/settings-store';
import { usePolicyStore } from '@/stores/policy-store';
import { useAuthStore } from '@/stores/auth-store';
import { toast } from '@/stores/toast-store';
@@ -35,6 +35,7 @@ export function CalendarSettings() {
const {
showTimeInMonthView,
calendarTintedEvents,
calendarMonthTimedEvents,
showWeekNumbers,
calendarFreeScroll,
enableCalendarTasks,
@@ -146,6 +147,21 @@ export function CalendarSettings() {
/>
</SettingItem>
<SettingItem
label={t('month_timed_events')}
description={t('month_timed_events_desc')}
>
<Select
value={calendarMonthTimedEvents}
onChange={(value) => updateSetting('calendarMonthTimedEvents', value as MonthTimedEventStyle)}
options={[
{ value: 'bar', label: t('month_timed_events_bar') },
{ value: 'text', label: t('month_timed_events_text') },
{ value: 'dot', label: t('month_timed_events_dot') },
]}
/>
</SettingItem>
<SettingItem
label={t('show_week_numbers')}
description={t('show_week_numbers_desc')}
+1
View File
@@ -311,6 +311,7 @@ export const tabSearchEntries: Record<Tab, SettingsSearchEntry[]> = {
{ label: 'calendar.settings.invitation_parsing' },
{ label: 'calendar.settings.show_time_in_month_view' },
{ label: 'calendar.settings.tinted_events' },
{ label: 'calendar.settings.month_timed_events' },
{ label: 'calendar.settings.show_week_numbers' },
{ label: 'calendar.settings.limit_hours' },
{ label: 'calendar.settings.visible_hours' },
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "عرض أوقات الأحداث في عرض التقويم الشهري",
"tinted_events": "ألوان أحداث فاتحة",
"tinted_events_desc": "عرض الأحداث بدرجة فاتحة من لون التقويم مع شريط ملوّن في البداية، كما في Outlook وفي النسخة المطبوعة، بدلاً من الكتل المصمتة.",
"month_timed_events": "الأحداث المحددة بوقت في عرض الشهر",
"month_timed_events_desc": "كيف تظهر الأحداث التي لها وقت بدء في عرض الشهر. تبقى أحداث اليوم الكامل والأحداث متعددة الأيام أشرطة.",
"month_timed_events_bar": "شريط",
"month_timed_events_text": "نص ملوّن",
"month_timed_events_dot": "نقطة ونص",
"show_week_numbers": "إظهار أرقام الأسابيع",
"show_week_numbers_desc": "عرض أرقام الأسابيع في التقويم المصغّر",
"calendar_free_scroll": "تمرير حر",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Mostra l'hora dels esdeveniments a la vista mensual del calendari. En pantalles petites, això mostra entrades completes d'esdeveniment en lloc de punts.",
"tinted_events": "Colors d'esdeveniment clars",
"tinted_events_desc": "Mostra els esdeveniments amb un to clar del color del calendari i una barra de color al principi, com a l'Outlook i a la impressió, en lloc de blocs sòlids.",
"month_timed_events": "Esdeveniments amb hora a la vista mensual",
"month_timed_events_desc": "Com es mostren a la vista mensual els esdeveniments amb hora d'inici. Els esdeveniments de tot el dia i de diversos dies continuen sent barres.",
"month_timed_events_bar": "Barra",
"month_timed_events_text": "Text de color",
"month_timed_events_dot": "Punt i text",
"show_week_numbers": "Mostra els números de setmana",
"show_week_numbers_desc": "Mostra els números de setmana al minicalendari",
"calendar_free_scroll": "Desplaçament lliure",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Zobrazovat časy událostí v měsíčním zobrazení kalendáře",
"tinted_events": "Světlé barvy událostí",
"tinted_events_desc": "Zobrazovat události ve světlém odstínu barvy kalendáře s barevným pruhem na začátku, jako v Outlooku a při tisku, místo plných bloků.",
"month_timed_events": "Události s časem v měsíčním zobrazení",
"month_timed_events_desc": "Jak vypadají události s časem začátku v měsíčním zobrazení. Celodenní a vícedenní události zůstávají pruhy.",
"month_timed_events_bar": "Pruh",
"month_timed_events_text": "Barevný text",
"month_timed_events_dot": "Tečka a text",
"show_week_numbers": "Zobrazit čísla týdnů",
"show_week_numbers_desc": "Zobrazovat čísla týdnů v minikalendáři",
"calendar_free_scroll": "Volné posouvání",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Vis begivenhedstider i månedskalendervisningen",
"tinted_events": "Lyse begivenhedsfarver",
"tinted_events_desc": "Vis begivenheder i en lys nuance af kalenderens farve med en farvet bjælke i starten, som i Outlook og på udskrifter, i stedet for udfyldte blokke.",
"month_timed_events": "Begivenheder med klokkeslæt i månedsvisning",
"month_timed_events_desc": "Hvordan begivenheder med starttidspunkt vises i månedsvisningen. Heldags- og flerdagsbegivenheder forbliver bjælker.",
"month_timed_events_bar": "Bjælke",
"month_timed_events_text": "Farvet tekst",
"month_timed_events_dot": "Prik og tekst",
"show_week_numbers": "Vis ugenumre",
"show_week_numbers_desc": "Vis ugenumre i minikalenderen",
"calendar_free_scroll": "Fri rulning",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Ereigniszeiten in der Monatskalenderansicht anzeigen",
"tinted_events": "Helle Terminfarben",
"tinted_events_desc": "Termine als helle Tönung ihrer Kalenderfarbe mit farbigem Balken am Anfang anzeigen, wie in Outlook und im Ausdruck, statt als volle Farbflächen.",
"month_timed_events": "Termine mit Uhrzeit in der Monatsansicht",
"month_timed_events_desc": "So erscheinen Termine mit Startzeit in der Monatsansicht. Ganztägige und mehrtägige Termine bleiben Balken.",
"month_timed_events_bar": "Balken",
"month_timed_events_text": "Farbiger Text",
"month_timed_events_dot": "Punkt und Text",
"show_week_numbers": "Kalenderwochen anzeigen",
"show_week_numbers_desc": "Kalenderwochen im Minikalender anzeigen",
"calendar_free_scroll": "Freies Scrollen",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Display event times in the month calendar view. On small screens this shows full event entries instead of dots.",
"tinted_events": "Light event colors",
"tinted_events_desc": "Show events as a light tint of their calendar color with a colored bar at the start, as in Outlook and on printouts, instead of solid blocks.",
"month_timed_events": "Timed events in month view",
"month_timed_events_desc": "How events with a start time look in the month view. All-day and multi-day events stay bars.",
"month_timed_events_bar": "Bar",
"month_timed_events_text": "Colored text",
"month_timed_events_dot": "Dot and text",
"show_week_numbers": "Show week numbers",
"show_week_numbers_desc": "Display week numbers in the mini-calendar",
"calendar_free_scroll": "Free scrolling",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Mostrar las horas de los eventos en la vista mensual del calendario",
"tinted_events": "Colores de evento claros",
"tinted_events_desc": "Mostrar los eventos con un tono claro del color de su calendario y una barra de color al inicio, como en Outlook y al imprimir, en lugar de bloques sólidos.",
"month_timed_events": "Eventos con hora en la vista mensual",
"month_timed_events_desc": "Cómo se muestran en la vista mensual los eventos con hora de inicio. Los eventos de todo el día y de varios días siguen siendo barras.",
"month_timed_events_bar": "Barra",
"month_timed_events_text": "Texto en color",
"month_timed_events_dot": "Punto y texto",
"show_week_numbers": "Mostrar números de semana",
"show_week_numbers_desc": "Mostrar los números de semana en el minicalendario",
"calendar_free_scroll": "Desplazamiento libre",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Kuva sündmuste kellaajad kuu kalendrivaates. Väikestel ekraanidel kuvatakse punktide asemel täielikud sündmuste kirjed.",
"tinted_events": "Heledad sündmuste värvid",
"tinted_events_desc": "Näita sündmusi kalendri värvi heleda tooniga ja värvilise ribaga alguses, nagu Outlookis ja väljatrükil, mitte täisvärviliste plokkidena.",
"month_timed_events": "Kellaajaga sündmused kuuvaates",
"month_timed_events_desc": "Kuidas algusajaga sündmused kuuvaates välja näevad. Terve päeva ja mitmepäevased sündmused jäävad ribadeks.",
"month_timed_events_bar": "Riba",
"month_timed_events_text": "Värviline tekst",
"month_timed_events_dot": "Täpp ja tekst",
"show_week_numbers": "Kuva nädalanumbrid",
"show_week_numbers_desc": "Kuva nädalanumbrid minikalendris",
"calendar_free_scroll": "Vaba kerimine",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "نمایش زمان رویدادها در نمای ماه تقویم",
"tinted_events": "رنگ‌های روشن رویداد",
"tinted_events_desc": "نمایش رویدادها با ته‌رنگ روشنی از رنگ تقویم و نواری رنگی در ابتدا، مانند Outlook و نسخهٔ چاپی، به‌جای بلوک‌های تمام‌رنگ.",
"month_timed_events": "رویدادهای زمان‌دار در نمای ماه",
"month_timed_events_desc": "نحوه نمایش رویدادهای دارای زمان شروع در نمای ماه. رویدادهای تمام‌روز و چندروزه به‌صورت نوار باقی می‌مانند.",
"month_timed_events_bar": "نوار",
"month_timed_events_text": "متن رنگی",
"month_timed_events_dot": "نقطه و متن",
"show_week_numbers": "نمایش شماره هفته",
"show_week_numbers_desc": "نمایش شماره هفته در تقویم کوچک",
"calendar_free_scroll": "پیمایش آزاد",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Afficher les heures des événements dans la vue mensuelle du calendrier",
"tinted_events": "Couleurs d'événement claires",
"tinted_events_desc": "Afficher les événements dans une teinte claire de la couleur de leur agenda avec une barre colorée au début, comme dans Outlook et à l'impression, plutôt qu'en blocs pleins.",
"month_timed_events": "Événements avec heure dans la vue mensuelle",
"month_timed_events_desc": "Apparence des événements avec une heure de début dans la vue mensuelle. Les événements sur la journée entière ou sur plusieurs jours restent des barres.",
"month_timed_events_bar": "Barre",
"month_timed_events_text": "Texte en couleur",
"month_timed_events_dot": "Point et texte",
"show_week_numbers": "Afficher les numéros de semaine",
"show_week_numbers_desc": "Afficher les numéros de semaine dans le mini-calendrier",
"calendar_free_scroll": "Défilement libre",
+5
View File
@@ -4418,6 +4418,11 @@
"show_time_in_month_view_desc": "הצג זמני אירועים בתצוגת לוח השנה של החודש",
"tinted_events": "צבעי אירועים בהירים",
"tinted_events_desc": "הצגת אירועים בגוון בהיר של צבע היומן עם פס צבעוני בהתחלה, כמו ב-Outlook ובהדפסה, במקום בלוקים מלאים.",
"month_timed_events": "אירועים עם שעה בתצוגת חודש",
"month_timed_events_desc": "איך אירועים עם שעת התחלה מוצגים בתצוגת החודש. אירועים של יום שלם ושל כמה ימים נשארים פסים.",
"month_timed_events_bar": "פס",
"month_timed_events_text": "טקסט צבעוני",
"month_timed_events_dot": "נקודה וטקסט",
"show_week_numbers": "הצג מספרי שבוע",
"show_week_numbers_desc": "הצג מספרי שבוע במיני-לוח שנה",
"calendar_free_scroll": "גלילה חופשית",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Események időpontjának mutatása a havi naptár nézetben",
"tinted_events": "Világos eseményszínek",
"tinted_events_desc": "Az események a naptár színének világos árnyalatával és az elején színes sávval jelennek meg, mint az Outlookban és nyomtatásban, teli blokkok helyett.",
"month_timed_events": "Időponthoz kötött események a havi nézetben",
"month_timed_events_desc": "Hogyan jelennek meg a kezdési időponttal rendelkező események a havi nézetben. Az egész napos és többnapos események sávok maradnak.",
"month_timed_events_bar": "Sáv",
"month_timed_events_text": "Színes szöveg",
"month_timed_events_dot": "Pont és szöveg",
"show_week_numbers": "Hétszámok mutatása",
"show_week_numbers_desc": "Hétszámok mutatása a mini naptárban",
"calendar_free_scroll": "Szabad görgetés",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Visualizza gli orari degli eventi nella vista mensile del calendario",
"tinted_events": "Colori evento chiari",
"tinted_events_desc": "Mostra gli eventi con una tinta chiara del colore del calendario e una barra colorata all'inizio, come in Outlook e nella stampa, invece che come blocchi pieni.",
"month_timed_events": "Eventi con orario nella vista mensile",
"month_timed_events_desc": "Come appaiono nella vista mensile gli eventi con un orario di inizio. Gli eventi di tutto il giorno e di più giorni restano barre.",
"month_timed_events_bar": "Barra",
"month_timed_events_text": "Testo colorato",
"month_timed_events_dot": "Punto e testo",
"show_week_numbers": "Mostra numeri di settimana",
"show_week_numbers_desc": "Mostra i numeri di settimana nel mini-calendario",
"calendar_free_scroll": "Scorrimento libero",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "月カレンダー表示でイベントの時刻を表示する",
"tinted_events": "淡いイベントカラー",
"tinted_events_desc": "予定を塗りつぶしではなく、Outlook や印刷時と同じように、カレンダーの色の淡い色合いと先頭の色付きバーで表示します。",
"month_timed_events": "月表示での時刻付きの予定",
"month_timed_events_desc": "月表示で開始時刻のある予定をどう表示するか。終日の予定と複数日の予定はバーのままです。",
"month_timed_events_bar": "バー",
"month_timed_events_text": "色付きテキスト",
"month_timed_events_dot": "点とテキスト",
"show_week_numbers": "週番号を表示",
"show_week_numbers_desc": "ミニカレンダーに週番号を表示する",
"calendar_free_scroll": "自由スクロール",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "월간 캘린더에서도 일정 시간을 표시해요",
"tinted_events": "연한 일정 색상",
"tinted_events_desc": "일정을 꽉 찬 블록 대신 Outlook이나 인쇄본처럼 캘린더 색상의 연한 색조와 앞쪽의 색상 막대로 표시합니다.",
"month_timed_events": "월 보기의 시간 지정 일정",
"month_timed_events_desc": "월 보기에서 시작 시간이 있는 일정을 표시하는 방식입니다. 종일 일정과 여러 날 일정은 막대로 유지됩니다.",
"month_timed_events_bar": "막대",
"month_timed_events_text": "색상 텍스트",
"month_timed_events_dot": "점과 텍스트",
"show_week_numbers": "주 차 번호 표시",
"show_week_numbers_desc": "미니 캘린더에 1년 중 몇 주 차인지 표시해요",
"calendar_free_scroll": "자유 스크롤",
+5
View File
@@ -4499,6 +4499,11 @@
"show_time_in_month_view_desc": "Rādīt pasākuma laiku mēneša kalendārā",
"tinted_events": "Gaišas notikumu krāsas",
"tinted_events_desc": "Rādīt notikumus kalendāra krāsas gaišā tonī ar krāsainu joslu sākumā, kā Outlook un izdrukās, nevis kā pilnus blokus.",
"month_timed_events": "Notikumi ar laiku mēneša skatā",
"month_timed_events_desc": "Kā mēneša skatā izskatās notikumi ar sākuma laiku. Visas dienas un vairāku dienu notikumi paliek joslas.",
"month_timed_events_bar": "Josla",
"month_timed_events_text": "Krāsains teksts",
"month_timed_events_dot": "Punkts un teksts",
"show_week_numbers": "Rādīt nedēļu numurus",
"show_week_numbers_desc": "Rādīt nedēļu numurus mini kalendārā",
"calendar_free_scroll": "Brīva ritināšana",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Сарын хуанлийн харагдац дээр үйл явдлын цагийг харуулах. Жижиг дэлгэц дээр энэ нь цэгийн оронд үйл явдлын бүрэн оруулгуудыг харуулдаг.",
"tinted_events": "Цайвар үйл явдлын өнгө",
"tinted_events_desc": "Үйл явдлыг бүтэн өнгөт блокийн оронд Outlook болон хэвлэсэн хувилбартай адил хуанлийн өнгөний цайвар өнгө, эхэндээ өнгөт зураастай харуулна.",
"month_timed_events": "Сарын харагдацын цагтай үйл явдлууд",
"month_timed_events_desc": "Эхлэх цагтай үйл явдлууд сарын харагдацад хэрхэн харагдах. Бүтэн өдрийн болон олон өдрийн үйл явдлууд зурвас хэвээр үлдэнэ.",
"month_timed_events_bar": "Зурвас",
"month_timed_events_text": "Өнгөт текст",
"month_timed_events_dot": "Цэг ба текст",
"show_week_numbers": "Долоо хоногийн дугаарыг харуулах",
"show_week_numbers_desc": "Ми-календарт долоо хоногийн тоог харуулах",
"calendar_free_scroll": "Чөлөөт гүйлгэлт",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Vis klokkeslettet for hendelser i månedsvisningen. På små skjermer vises hele hendelsesoppføringen i stedet for en prikk.",
"tinted_events": "Lyse hendelsesfarger",
"tinted_events_desc": "Vis hendelser i en lys nyanse av kalenderens farge med en farget stolpe i starten, som i Outlook og på utskrifter, i stedet for fylte blokker.",
"month_timed_events": "Hendelser med klokkeslett i månedsvisning",
"month_timed_events_desc": "Hvordan hendelser med starttidspunkt vises i månedsvisningen. Heldags- og flerdagshendelser forblir stolper.",
"month_timed_events_bar": "Stolpe",
"month_timed_events_text": "Farget tekst",
"month_timed_events_dot": "Prikk og tekst",
"show_week_numbers": "Vis ukenummer",
"show_week_numbers_desc": "Vis ukenummer i minikalenderen",
"calendar_free_scroll": "Fri rulling",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Evenementtijden weergeven in de maandkalenderweergave",
"tinted_events": "Lichte afspraakkleuren",
"tinted_events_desc": "Toon afspraken als een lichte tint van hun agendakleur met een gekleurde balk aan het begin, zoals in Outlook en op afdrukken, in plaats van volle blokken.",
"month_timed_events": "Afspraken met tijd in maandweergave",
"month_timed_events_desc": "Hoe afspraken met een begintijd eruitzien in de maandweergave. Afspraken voor de hele dag en meerdaagse afspraken blijven balken.",
"month_timed_events_bar": "Balk",
"month_timed_events_text": "Gekleurde tekst",
"month_timed_events_dot": "Stip en tekst",
"show_week_numbers": "Weeknummers weergeven",
"show_week_numbers_desc": "Weeknummers weergeven in de minikalender",
"calendar_free_scroll": "Vrij scrollen",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Wyświetlaj godziny wydarzeń w miesięcznym widoku kalendarza",
"tinted_events": "Jasne kolory wydarzeń",
"tinted_events_desc": "Pokazuj wydarzenia w jasnym odcieniu koloru kalendarza z kolorowym paskiem na początku, jak w Outlooku i na wydruku, zamiast pełnych bloków.",
"month_timed_events": "Wydarzenia z godziną w widoku miesiąca",
"month_timed_events_desc": "Jak wyglądają w widoku miesiąca wydarzenia z godziną rozpoczęcia. Wydarzenia całodniowe i wielodniowe pozostają paskami.",
"month_timed_events_bar": "Pasek",
"month_timed_events_text": "Kolorowy tekst",
"month_timed_events_dot": "Kropka i tekst",
"show_week_numbers": "Pokaż numery tygodni",
"show_week_numbers_desc": "Wyświetlaj numery tygodni w mini-kalendarzu",
"calendar_free_scroll": "Swobodne przewijanie",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Exibir horários dos eventos na visualização mensal do calendário",
"tinted_events": "Cores de evento claras",
"tinted_events_desc": "Mostrar os eventos com um tom claro da cor do calendário e uma barra colorida no início, como no Outlook e na impressão, em vez de blocos sólidos.",
"month_timed_events": "Eventos com hora na vista mensal",
"month_timed_events_desc": "Como aparecem na vista mensal os eventos com hora de início. Eventos de dia inteiro e de vários dias continuam a ser barras.",
"month_timed_events_bar": "Barra",
"month_timed_events_text": "Texto colorido",
"month_timed_events_dot": "Ponto e texto",
"show_week_numbers": "Mostrar números da semana",
"show_week_numbers_desc": "Exibir números da semana no minicalendário",
"calendar_free_scroll": "Deslocamento livre",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Afișați orele evenimentelor în vizualizarea calendarului lunar",
"tinted_events": "Culori deschise pentru evenimente",
"tinted_events_desc": "Afișează evenimentele într-o nuanță deschisă a culorii calendarului, cu o bară colorată la început, ca în Outlook și la tipărire, în loc de blocuri pline.",
"month_timed_events": "Evenimente cu oră în vizualizarea lunară",
"month_timed_events_desc": "Cum apar în vizualizarea lunară evenimentele cu oră de început. Evenimentele de o zi întreagă și cele de mai multe zile rămân bare.",
"month_timed_events_bar": "Bară",
"month_timed_events_text": "Text colorat",
"month_timed_events_dot": "Punct și text",
"show_week_numbers": "Afișați numerele săptămânilor",
"show_week_numbers_desc": "Afișați numerele săptămânilor în mini-calendar",
"calendar_free_scroll": "Derulare liberă",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Отображать время события в виде месячного календаря",
"tinted_events": "Светлые цвета событий",
"tinted_events_desc": "Показывать события светлым оттенком цвета календаря с цветной полосой в начале, как в Outlook и при печати, а не сплошными блоками.",
"month_timed_events": "События со временем в режиме месяца",
"month_timed_events_desc": "Как выглядят события со временем начала в режиме месяца. События на весь день и многодневные события остаются полосами.",
"month_timed_events_bar": "Полоса",
"month_timed_events_text": "Цветной текст",
"month_timed_events_dot": "Точка и текст",
"show_week_numbers": "Показывать номера недель",
"show_week_numbers_desc": "Отображать номера недель в мини-календаре",
"calendar_free_scroll": "Свободная прокрутка",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Zobrazovať časy udalostí v mesačnom zobrazení kalendára",
"tinted_events": "Svetlé farby udalostí",
"tinted_events_desc": "Zobrazovať udalosti vo svetlom odtieni farby kalendára s farebným pruhom na začiatku, ako v Outlooku a pri tlači, namiesto plných blokov.",
"month_timed_events": "Udalosti s časom v mesačnom zobrazení",
"month_timed_events_desc": "Ako vyzerajú udalosti s časom začiatku v mesačnom zobrazení. Celodenné a viacdňové udalosti zostávajú pruhmi.",
"month_timed_events_bar": "Pruh",
"month_timed_events_text": "Farebný text",
"month_timed_events_dot": "Bodka a text",
"show_week_numbers": "Zobraziť čísla týždňov",
"show_week_numbers_desc": "Zobrazovať čísla týždňov v minikalendári",
"calendar_free_scroll": "Voľné posúvanie",
+5
View File
@@ -3459,6 +3459,11 @@
"show_time_in_month_view_desc": "แสดงเวลากิจกรรมในมุมมองปฏิทินรายเดือน บนหน้าจอขนาดเล็กจะแสดงรายการกิจกรรมเต็มแทนการแสดงจุด",
"tinted_events": "สีกิจกรรมแบบอ่อน",
"tinted_events_desc": "แสดงกิจกรรมเป็นสีอ่อนของสีปฏิทินพร้อมแถบสีที่ด้านต้น เหมือนใน Outlook และฉบับพิมพ์ แทนที่จะเป็นบล็อกสีทึบ",
"month_timed_events": "กิจกรรมที่มีเวลาในมุมมองเดือน",
"month_timed_events_desc": "ลักษณะของกิจกรรมที่มีเวลาเริ่มในมุมมองเดือน กิจกรรมทั้งวันและหลายวันยังคงเป็นแถบ",
"month_timed_events_bar": "แถบ",
"month_timed_events_text": "ข้อความสี",
"month_timed_events_dot": "จุดและข้อความ",
"show_week_numbers": "แสดงหมายเลขสัปดาห์",
"show_week_numbers_desc": "แสดงหมายเลขสัปดาห์ในปฏิทินขนาดเล็ก",
"calendar_free_scroll": "การเลื่อนอย่างอิสระ",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Aylık takvim görünümünde etkinlik saatlerini görüntüle",
"tinted_events": "Açık etkinlik renkleri",
"tinted_events_desc": "Etkinlikleri düz bloklar yerine, Outlook'ta ve çıktılarda olduğu gibi takvim renginin açık bir tonu ve başta renkli bir çubukla göster.",
"month_timed_events": "Ay görünümünde saatli etkinlikler",
"month_timed_events_desc": "Başlangıç saati olan etkinliklerin ay görünümünde nasıl göründüğü. Tüm gün ve çok günlük etkinlikler çubuk olarak kalır.",
"month_timed_events_bar": "Çubuk",
"month_timed_events_text": "Renkli metin",
"month_timed_events_dot": "Nokta ve metin",
"show_week_numbers": "Hafta numaralarını göster",
"show_week_numbers_desc": "Mini takvimde hafta numaralarını görüntüle",
"calendar_free_scroll": "Serbest kaydırma",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "Відображення часу подій у місячному календарі",
"tinted_events": "Світлі кольори подій",
"tinted_events_desc": "Показувати події світлим відтінком кольору календаря з кольоровою смугою на початку, як в Outlook і під час друку, замість суцільних блоків.",
"month_timed_events": "Події з часом у режимі місяця",
"month_timed_events_desc": "Як виглядають події з часом початку в режимі місяця. Події на весь день і багатоденні події залишаються смугами.",
"month_timed_events_bar": "Смуга",
"month_timed_events_text": "Кольоровий текст",
"month_timed_events_dot": "Крапка й текст",
"show_week_numbers": "Показати номери тижнів",
"show_week_numbers_desc": "Відображення номерів тижнів у міні-календарі",
"calendar_free_scroll": "Вільне прокручування",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "在月檢視中顯示活動時間。小螢幕會顯示完整活動項目,而不只是圓點。",
"tinted_events": "淺色活動顏色",
"tinted_events_desc": "像 Outlook 和列印版本一樣,以行事曆顏色的淺色調加開頭的彩色直條顯示活動,而非實心色塊。",
"month_timed_events": "月檢視中的定時活動",
"month_timed_events_desc": "有開始時間的活動在月檢視中的顯示方式。全天和多天活動仍顯示為色條。",
"month_timed_events_bar": "色條",
"month_timed_events_text": "彩色文字",
"month_timed_events_dot": "圓點和文字",
"show_week_numbers": "顯示週數",
"show_week_numbers_desc": "在迷你行事曆中顯示週數",
"calendar_free_scroll": "自由捲動",
+5
View File
@@ -4500,6 +4500,11 @@
"show_time_in_month_view_desc": "在月历视图中显示事件时间",
"tinted_events": "浅色日程颜色",
"tinted_events_desc": "像 Outlook 和打印件那样,用日历颜色的浅色调加开头的彩色竖条显示日程,而不是实心色块。",
"month_timed_events": "月视图中的定时事件",
"month_timed_events_desc": "有开始时间的事件在月视图中的显示方式。全天和多天事件仍显示为色条。",
"month_timed_events_bar": "色条",
"month_timed_events_text": "彩色文字",
"month_timed_events_dot": "圆点和文字",
"show_week_numbers": "显示周数",
"show_week_numbers_desc": "在迷你日历中显示周数",
"calendar_free_scroll": "自由滚动",
+12
View File
@@ -337,6 +337,13 @@ export type MessageSpacing = 'auto' | 'always' | 'edge';
/** Font used to render text/plain email bodies. */
export type PlainTextFont = 'mono' | 'sans';
export type CalendarHoverPreview = 'off' | 'instant' | 'delay-500ms' | 'delay-1s' | 'delay-2s';
/**
* How the month view draws an event with a start time (#1449): a filled bar
* like all-day events, its time and title in the calendar colour, or a dot
* in the calendar colour before them.
*/
export type MonthTimedEventStyle = 'bar' | 'text' | 'dot';
export const MONTH_TIMED_EVENT_STYLES: readonly MonthTimedEventStyle[] = ['bar', 'text', 'dot'];
export type SendDelaySeconds = 0 | 10 | 30 | 60;
export type ProtocolOpenMode = 'active-session' | 'new-tab';
@@ -602,6 +609,8 @@ interface SettingsState extends AccessibilitySettings {
showTimeInMonthView: boolean;
/** Events as a light tint of their colour with a bar at the start (the Outlook look), not solid blocks. */
calendarTintedEvents: boolean;
/** Month view look of events with a start time (#1449). All-day and multi-day events stay bars. */
calendarMonthTimedEvents: MonthTimedEventStyle;
showWeekNumbers: boolean;
/** Scroll continuously through months/weeks/days (#759) instead of one period at a time. */
calendarFreeScroll: boolean;
@@ -920,6 +929,7 @@ const DEFAULT_SETTINGS = {
// Calendar
showTimeInMonthView: false,
calendarTintedEvents: false,
calendarMonthTimedEvents: 'bar' as MonthTimedEventStyle,
showWeekNumbers: false,
calendarFreeScroll: true,
calendarHoverPreview: 'delay-500ms' as CalendarHoverPreview,
@@ -1105,6 +1115,7 @@ function isAllowedPolicyValue(key: string, value: unknown, restriction?: Setting
if (key === 'sendDelaySeconds' && ![0, 10, 30, 60].includes(value as number)) return false;
if (key === 'subAddressDelimiter' && !isValidSubAddressDelimiter(value as string)) return false;
if (key === 'messageListOrderScope' && value !== 'inbox' && value !== 'all') return false;
if (key === 'calendarMonthTimedEvents' && !MONTH_TIMED_EVENT_STYLES.includes(value as MonthTimedEventStyle)) return false;
if (!isAllowedAccessibilityValue(key, value)) return false;
if (restriction && typeof restriction === 'object') {
if (Array.isArray(restriction.allowedValues) && !restriction.allowedValues.includes(value)) return false;
@@ -1358,6 +1369,7 @@ export const useSettingsStore = create<SettingsState>()(
expandedFilterView: state.expandedFilterView,
showTimeInMonthView: state.showTimeInMonthView,
calendarTintedEvents: state.calendarTintedEvents,
calendarMonthTimedEvents: state.calendarMonthTimedEvents,
showWeekNumbers: state.showWeekNumbers,
calendarFreeScroll: state.calendarFreeScroll,
calendarHoverPreview: state.calendarHoverPreview,