Skip to content

Commit af5844d

Browse files
committed
Feature useBreakpointsValues
1 parent 931d57c commit af5844d

File tree

8 files changed

+117
-18
lines changed

8 files changed

+117
-18
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@
129129
"./next-pagesdir": "./dist/next-pagesdir.js",
130130
"./useIsDark": "./dist/useIsDark/index.js",
131131
"./useColors": "./dist/useColors.js",
132+
"./useBreakpointsValues": "./dist/useBreakpointsValues.js",
132133
"./mui": "./dist/mui.js",
133134
"./tss": "./dist/tss.js",
134135
"./tools/cx": "./dist/tools/cx.js",

src/fr/breakpoints.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import type { BreakpointKeys } from "./generatedFromCss/breakpoints";
22
import {
3-
breakpointValues as values,
4-
breakpointValuesUnit as unit,
3+
BreakpointsValues as values,
4+
BreakpointsValuesUnit as unit,
55
breakpointKeys as keys
66
} from "./generatedFromCss/breakpoints";
7-
import { assert } from "tsafe/assert";
7+
import { type Equals, assert } from "tsafe/assert";
8+
import { getBaseFontSizePx } from "../tools/getBaseFontSizePx";
89

910
export type { BreakpointKeys };
1011

12+
/** Breakpoint values in px */
13+
export type BreakpointsValues = Record<BreakpointKeys, number>;
14+
1115
const epsilon = 0.003125;
1216

1317
export const breakpoints = {
@@ -38,5 +42,22 @@ export const breakpoints = {
3842
return breakpoints
3943
.between(key, keys[keys.indexOf(key) + 1])
4044
.replace("@media", "@media not all and");
45+
},
46+
/**
47+
* Returns the breakpoint values in px.
48+
*
49+
* Warning: It reflects the values at a given time, if the root font size changes so will the breakpointsValues.
50+
* Plus this function is rather expensive to call.
51+
* If you're in react you should use the
52+
* import { useBreakpointsValues } from "@codegouvfr/react-dsfr/useBreakpointsValues";
53+
*/
54+
"getBreakpointsValues": (): BreakpointsValues => {
55+
assert<Equals<typeof unit, "em">>();
56+
57+
const factor = getBaseFontSizePx();
58+
59+
return Object.fromEntries(
60+
Object.entries(values).map(([key, value]) => [key, value * factor])
61+
) as any;
4162
}
4263
};

src/mui.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
/* eslint-disable @typescript-eslint/no-non-null-assertion */
44
import React, { useMemo, type ReactNode } from "react";
5-
import { breakpointValues } from "./fr/generatedFromCss/breakpoints";
65
import type { Theme as MuiTheme, ThemeOptions } from "@mui/material/styles";
76
import { createTheme, ThemeProvider as MuiThemeProvider } from "@mui/material/styles";
87
import type { Shadows } from "@mui/material/styles";
@@ -14,9 +13,13 @@ import { spacingTokenByValue } from "./fr/generatedFromCss/spacing";
1413
import { assert } from "tsafe/assert";
1514
import { objectKeys } from "tsafe/objectKeys";
1615
import { id } from "tsafe/id";
16+
import { useBreakpointsValues, type BreakpointsValues } from "./useBreakpointsValues";
1717

18-
export function getMuiDsfrThemeOptions(params: { isDark: boolean }): ThemeOptions {
19-
const { isDark } = params;
18+
export function getMuiDsfrThemeOptions(params: {
19+
isDark: boolean;
20+
breakpointsValues: BreakpointsValues;
21+
}): ThemeOptions {
22+
const { isDark, breakpointsValues } = params;
2023

2124
const { options, decisions } = getColors(isDark);
2225

@@ -25,11 +28,9 @@ export function getMuiDsfrThemeOptions(params: { isDark: boolean }): ThemeOption
2528
"borderRadius": 0
2629
},
2730
"breakpoints": {
28-
//"unit": breakpointValuesUnit,
31+
//NOTE: We would use "unit": breakpointsValuesUnit but only "px" works.
2932
"unit": "px",
30-
"values": Object.fromEntries(
31-
Object.entries(breakpointValues).map(([key, value]) => [key, value * 16])
32-
) as any
33+
"values": breakpointsValues
3334
},
3435
"palette": {
3536
"mode": isDark ? "dark" : "light",
@@ -244,7 +245,10 @@ export function getMuiDsfrThemeOptions(params: { isDark: boolean }): ThemeOption
244245
*
245246
* @returns — A complete, ready-to-use mui theme object.
246247
*/
247-
export function createMuiDsfrTheme(params: { isDark: boolean }, ...args: object[]): MuiTheme {
248+
export function createMuiDsfrTheme(
249+
params: { isDark: boolean; breakpointsValues: BreakpointsValues },
250+
...args: object[]
251+
): MuiTheme {
248252
const muiTheme = createTheme(getMuiDsfrThemeOptions(params), ...args);
249253

250254
return muiTheme;
@@ -273,8 +277,10 @@ export function createMuiDsfrThemeProvider(params: {
273277

274278
const { isDark } = useIsDark_props();
275279

280+
const { breakpointsValues } = useBreakpointsValues();
281+
276282
const theme = useMemo(() => {
277-
const nonAugmentedMuiTheme = createMuiDsfrTheme({ isDark });
283+
const nonAugmentedMuiTheme = createMuiDsfrTheme({ isDark, breakpointsValues });
278284

279285
return augmentMuiTheme === undefined
280286
? nonAugmentedMuiTheme

src/scripts/build/cssToTs/breakpoints.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,15 +143,15 @@ export function generateBreakpointsTsCode(rawCssCode: string): string {
143143
`import { assert } from "tsafe/assert";`,
144144
`import type { Extends } from "tsafe";`,
145145
``,
146-
`export const breakpointValuesUnit = "em";`,
146+
`export const BreakpointsValuesUnit = "em";`,
147147
``,
148148
`export const breakpointKeys = ["xs", ${sortedKeys
149149
.map(key => `"${key}"`)
150150
.join(", ")}] as const;`,
151151
``,
152152
`export type BreakpointKeys = typeof breakpointKeys[number];`,
153153
``,
154-
`export const breakpointValues = {`,
154+
`export const BreakpointsValues = {`,
155155
JSON.stringify(
156156
Object.fromEntries(
157157
(["xs", ...sortedKeys] as const).map(key => [
@@ -170,7 +170,7 @@ export function generateBreakpointsTsCode(rawCssCode: string): string {
170170
.join("\n"),
171171
`} as const;`,
172172
``,
173-
`assert<Extends<typeof breakpointValues, Record<BreakpointKeys, number>>>();`,
173+
`assert<Extends<typeof BreakpointsValues, Record<BreakpointKeys, number>>>();`,
174174
``
175175
].join("\n");
176176
}

src/scripts/list-exports.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const newExports = {
2525
"./next-pagesdir": "./dist/next-pagesdir.js",
2626
"./useIsDark": "./dist/useIsDark/index.js",
2727
"./useColors": "./dist/useColors.js",
28+
"./useBreakpointsValues": "./dist/useBreakpointsValues.js",
2829
"./mui": "./dist/mui.js",
2930
"./tss": "./dist/tss.js",
3031
"./tools/cx": "./dist/tools/cx.js",

src/tools/getBaseFontSizePx.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { assert } from "tsafe/assert";
2+
import { isBrowser } from "./isBrowser";
3+
4+
export function getBaseFontSizePx(): number {
5+
if (!isBrowser) {
6+
return 16;
7+
}
8+
9+
const htmlElement = document.querySelector("html");
10+
11+
assert(htmlElement !== null);
12+
13+
const computedStyle = window.getComputedStyle(htmlElement);
14+
const fontSize = computedStyle.getPropertyValue("font-size");
15+
16+
const fontSizeInPixels = parseFloat(fontSize);
17+
18+
return fontSizeInPixels;
19+
}

src/useBreakpointsValues.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { useReducer, useEffect, useMemo } from "react";
2+
import { assert } from "tsafe/assert";
3+
import { breakpoints, type BreakpointKeys, type BreakpointsValues } from "./fr/breakpoints";
4+
5+
export type { BreakpointKeys, BreakpointsValues };
6+
7+
/** Return the breakpoint values in px, the values ger refreshed
8+
* when the base font size change. */
9+
export function useBreakpointsValues() {
10+
const [breakpointsValuesDependency, triggerRefresh] = useReducer(() => ({}), {});
11+
12+
useEffect(() => {
13+
const htmlElement = document.querySelector("html");
14+
15+
assert(htmlElement !== null);
16+
17+
// Create a new MutationObserver to detect changes in the base font size
18+
const observer = new MutationObserver(mutationsList => {
19+
if (
20+
mutationsList.find(
21+
mutation =>
22+
mutation.target === htmlElement &&
23+
mutation.attributeName === "style" &&
24+
(assert(mutation.oldValue !== null), mutation.oldValue).indexOf(
25+
"font-size"
26+
) !== -1
27+
) !== undefined
28+
) {
29+
triggerRefresh();
30+
}
31+
});
32+
33+
// Observe changes to the style attribute of the html element
34+
observer.observe(htmlElement, {
35+
"attributes": true,
36+
"attributeOldValue": true,
37+
"attributeFilter": ["style"]
38+
});
39+
40+
return () => {
41+
observer.disconnect();
42+
};
43+
}, []);
44+
45+
const breakpointsValues = useMemo(
46+
() => breakpoints.getBreakpointsValues(),
47+
[breakpointsValuesDependency]
48+
);
49+
50+
return { breakpointsValues };
51+
}

test/runtime/scripts/breakpoints/generateBreakpointsTsCode.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,21 @@ it("Generation of TS code for breakpoints", () => {
4545
import { assert } from "tsafe/assert";
4646
import type { Extends } from "tsafe";
4747
48-
export const breakpointValuesUnit = "em";
48+
export const BreakpointsValuesUnit = "em";
4949
5050
export const breakpointKeys = ["xs", "sm", "md", "lg", "xl"] as const;
5151
5252
export type BreakpointKeys = typeof breakpointKeys[number];
5353
54-
export const breakpointValues = {
54+
export const BreakpointsValues = {
5555
"xs": 0,
5656
"sm": 36,
5757
"md": 48,
5858
"lg": 62,
5959
"xl": 78
6060
} as const;
6161
62-
assert<Extends<typeof breakpointValues, Record<BreakpointKeys, number>>>();
62+
assert<Extends<typeof BreakpointsValues, Record<BreakpointKeys, number>>>();
6363
`.replace(/^\n/, "");
6464

6565
const got = generateBreakpointsTsCode(input);

0 commit comments

Comments
 (0)