Path: blob/master/src/packages/frontend/app/context.tsx
5808 views
/*1* This file is part of CoCalc: Copyright © 2023 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { theme, ThemeConfig } from "antd";6import { debounce } from "lodash";7import { ReactNode, useEffect, useMemo, useState } from "react";8import { useIntl } from "react-intl";910import { useTypedRedux } from "@cocalc/frontend/app-framework";11import { IntlMessage, isIntlMessage } from "@cocalc/frontend/i18n";12import { ACTIVITY_BAR_LABELS } from "@cocalc/frontend/project/page/activity-bar-consts";13import { A11Y } from "@cocalc/util/consts/ui";14import { COLORS } from "@cocalc/util/theme";15import { getBaseAntdTheme } from "./antd-base-theme";16import { NARROW_THRESHOLD_PX, PageStyle } from "./top-nav-consts";17import useAppContext, { AppContext, AppState, calcStyle } from "./use-context";1819export { AppContext, useAppContext };2021export function useAppContextProvider(): AppState {22const intl = useIntl();23const other_settings = useTypedRedux("account", "other_settings");24const showActBarLabels = other_settings.get(ACTIVITY_BAR_LABELS) ?? true;2526const [pageWidthPx, setPageWidthPx] = useState<number>(window.innerWidth);2728const [narrow, setNarrow] = useState<boolean>(isNarrow());2930function update() {31setNarrow(isNarrow());32if (window.innerWidth != pageWidthPx) {33setPageWidthPx(window.innerWidth);34}35}3637useEffect(() => {38const handleResize = debounce(update, 50, {39leading: false,40trailing: true,41});4243window.addEventListener("resize", handleResize);44return () => window.removeEventListener("resize", handleResize);45}, []);4647// avoid updating the style on every resize event48const pageStyle: PageStyle = useMemo(() => {49return calcStyle(narrow);50}, [narrow]);5152function formatIntl(53msg: IntlMessage | ReactNode | string,54): ReactNode | string {55if (isIntlMessage(msg)) {56return intl.formatMessage(msg);57} else {58return msg;59}60}6162function displayI18N(63label: string | IntlMessage | ReactNode,64): string | ReactNode {65if (isIntlMessage(label)) {66return intl.formatMessage(label);67} else {68return label;69}70}7172return {73formatIntl,74displayI18N,75pageWidthPx,76pageStyle,77showActBarLabels,78};79}8081export function useAntdStyleProvider() {82const other_settings = useTypedRedux("account", "other_settings");83const baseTheme = getBaseAntdTheme();84const rounded = other_settings?.get("antd_rounded", true);85const animate = other_settings?.get("antd_animate", true);86const branded = other_settings?.get("antd_brandcolors", false);87const compact = other_settings?.get("antd_compact", false);8889// Parse accessibility settings90const accessibilityStr = other_settings?.get(A11Y);91let accessibilityEnabled = false;92if (accessibilityStr) {93try {94const accessibilitySettings = JSON.parse(accessibilityStr);95accessibilityEnabled = accessibilitySettings.enabled ?? false;96} catch {97// Ignore parse errors98}99}100101const borderStyle = rounded102? undefined103: { borderRadius: 0, borderRadiusLG: 0, borderRadiusSM: 0 };104105const animationStyle = animate ? undefined : { motion: false };106107const primaryColor = branded108? undefined109: { colorPrimary: COLORS.ANTD_LINK_BLUE };110111// Accessibility: Set all text to pure black for maximum contrast112const accessibilityTextColor = accessibilityEnabled113? {114colorText: "#000000",115colorTextSecondary: "#000000",116colorTextTertiary: "#000000",117colorTextQuaternary: "#000000",118}119: undefined;120121const algorithm = compact ? { algorithm: theme.compactAlgorithm } : undefined;122123const antdTheme: ThemeConfig = {124...baseTheme,125...algorithm,126token: {127...(baseTheme.token ?? {}),128...primaryColor,129...borderStyle,130...animationStyle,131...accessibilityTextColor,132},133components: {134Button: {135...primaryColor,136},137},138};139140return {141antdTheme,142};143}144145function isNarrow(): boolean {146return window.innerWidth != null && window.innerWidth <= NARROW_THRESHOLD_PX;147}148149150