[#646] improve dark color management (#648)

Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
Camille Moussu
2026-03-17 16:26:37 +01:00
committed by GitHub
parent a771b311b5
commit e665ea4a79
12 changed files with 49 additions and 101 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
import { getAccessiblePair } from "@/components/Calendar/utils/calendarColorsUtils";
import { getAccessiblePair } from "@/utils/getAccessiblePair";
import { stringAvatar } from "@/components/Event/utils/eventUtils";
import { useUserSearch } from "./useUserSearch";
import { SnackbarAlert } from "@/components/Loading/SnackBarAlert";
+1 -1
View File
@@ -1,4 +1,4 @@
import { getAccessiblePair } from "@/components/Calendar/utils/calendarColorsUtils";
import { getAccessiblePair } from "@/utils/getAccessiblePair";
import { useUserSearch } from "./useUserSearch";
import { ResourceIcon } from "./ResourceIcon";
import { SnackbarAlert } from "@/components/Loading/SnackBarAlert";
+1 -26
View File
@@ -20,7 +20,7 @@ import interactionPlugin from "@fullcalendar/interaction";
import momentTimezonePlugin from "@fullcalendar/moment-timezone";
import FullCalendar from "@fullcalendar/react";
import timeGridPlugin from "@fullcalendar/timegrid";
import { Box, Button, radius, useTheme } from "@linagora/twake-mui";
import { Box, Button, radius } from "@linagora/twake-mui";
import AddIcon from "@mui/icons-material/Add";
import { MutableRefObject, useEffect, useMemo, useRef, useState } from "react";
import { useI18n } from "twake-i18n";
@@ -38,7 +38,6 @@ import { useCalendarViewHandlers } from "./hooks/useCalendarViewHandlers";
import { MiniCalendar } from "./MiniCalendar";
import { TempCalendarsInput } from "./TempCalendarsInput";
import { TimezoneSelector } from "./TimezoneSelector";
import { updateDarkColor } from "./utils/calendarColorsUtils";
import {
eventToFullCalendarFormat,
extractEvents,
@@ -75,7 +74,6 @@ export default function CalendarApp({
const userId =
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
const dispatch = useAppDispatch();
const theme = useTheme();
const view = useAppSelector((state) => state.settings.view);
const userData = useAppSelector((state) => state.user.userData);
const workingDays = useAppSelector(
@@ -103,13 +101,6 @@ export default function CalendarApp({
const [selectedCalendars, setSelectedCalendars] =
useState<string[]>(storedCalendars);
const calendarLightSignature = useMemo(() => {
return Object.values(calendars || {})
.map((cal) => `${cal.id}:${cal.color?.light ?? ""}`)
.sort()
.join("|");
}, [calendars]);
const calendarIdsString = useMemo(
() =>
Object.keys(calendars || {})
@@ -167,22 +158,6 @@ export default function CalendarApp({
}
}, [selectedCalendars, calendarIds.length]);
const prevCalendarLightSignature = useRef<string | null>(null);
useEffect(() => {
if (!calendarLightSignature) {
prevCalendarLightSignature.current = calendarLightSignature;
return;
}
if (prevCalendarLightSignature.current === calendarLightSignature) {
return;
}
prevCalendarLightSignature.current = calendarLightSignature;
updateDarkColor(calendars || {}, theme, dispatch);
}, [calendarLightSignature, calendars, theme, dispatch]);
useEffect(() => {
if (calendarIds.length === 0) return;
const validCalendarIds = new Set(calendarIds);
@@ -11,7 +11,7 @@ import {
import { useState, useEffect } from "react";
import { HexColorPicker } from "react-colorful";
import { useI18n } from "twake-i18n";
import { getAccessiblePair } from "./utils/calendarColorsUtils";
import { getAccessiblePair } from "@/utils/getAccessiblePair";
import { defaultColors } from "@/utils/defaultColors";
export function ColorPicker({
@@ -16,7 +16,7 @@ import { useRef, useState } from "react";
import { useI18n } from "twake-i18n";
import { ResponsiveDialog } from "../Dialog";
import { ColorPicker } from "./CalendarColorPicker";
import { getAccessiblePair } from "./utils/calendarColorsUtils";
import { getAccessiblePair } from "@/utils/getAccessiblePair";
import { defaultColors } from "@/utils/defaultColors";
import { addCalendarResourceAsync } from "@/features/Calendars/api/addCalendarResourceAsync";
import { Resource, ResourceSearch } from "../Attendees/ResourceSearch";
+1 -1
View File
@@ -20,7 +20,7 @@ import { PeopleSearch, User } from "../Attendees/PeopleSearch";
import { ResponsiveDialog } from "../Dialog";
import { stringAvatar } from "../Event/utils/eventUtils";
import { ColorPicker } from "./CalendarColorPicker";
import { getAccessiblePair } from "./utils/calendarColorsUtils";
import { getAccessiblePair } from "@/utils/getAccessiblePair";
import { defaultColors } from "@/utils/defaultColors";
interface CalendarWithOwner {
@@ -7,7 +7,7 @@ import { TextField, useTheme } from "@linagora/twake-mui";
import { useRef } from "react";
import { useI18n } from "twake-i18n";
import { PeopleSearch, User } from "../Attendees/PeopleSearch";
import { getAccessiblePair } from "./utils/calendarColorsUtils";
import { getAccessiblePair } from "@/utils/getAccessiblePair";
const requestControllers = new Map<string, AbortController>();
@@ -1,56 +0,0 @@
import { AppDispatch } from "@/app/store";
import { updateCalColor } from "@/features/Calendars/CalendarSlice";
import { Calendar } from "@/features/Calendars/CalendarTypes";
import { darken, getContrastRatio, lighten, Theme } from "@linagora/twake-mui";
import { defaultColors } from "@/utils/defaultColors";
export function updateDarkColor(
calendars: Record<string, Calendar>,
theme: Theme,
dispatch: AppDispatch
) {
Object.values(calendars).forEach((cal) => {
if (!cal?.color?.light || typeof cal.color.light !== "string") return;
const baseColor = cal.color.light;
const isDefault = Object.values(defaultColors).find(
(c) => c.light === baseColor
);
const darkColor = isDefault
? isDefault.dark
: getAccessiblePair(baseColor, theme);
if (cal.color?.dark === darkColor) {
return;
}
dispatch(
updateCalColor({
id: cal.id,
color: { light: baseColor, dark: darkColor },
})
);
});
}
export function getAccessiblePair(baseColor: string, theme: Theme): string {
if (typeof baseColor !== "string") {
return theme.palette.getContrastText("#000");
}
const contrastToBlack = getContrastRatio(baseColor, "#000");
const contrastToWhite = getContrastRatio(baseColor, "#fff");
const isLight = contrastToBlack > contrastToWhite;
const adjusted = isLight ? darken(baseColor, 0.6) : lighten(baseColor, 0.7);
// Check if contrast meets 4.5
const contrast = getContrastRatio(baseColor, adjusted);
if (contrast >= 4.5) return adjusted;
if (isLight) {
return "#ffffffff";
}
return theme.palette.getContrastText(baseColor);
}