@primer/react 38.19.0-rc.c09ee891a → 38.19.0-rc.f3b0d9cc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/ThemeProvider.d.ts.map +1 -1
- package/dist/ThemeProvider.js +163 -104
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
- [#7677](https://github.com/primer/react/pull/7677) [`c1a81b1`](https://github.com/primer/react/commit/c1a81b178742ba547b85a3df3ed3c27bcff6b7c5) Thanks [@TylerJDev](https://github.com/TylerJDev)! - AnchoredOverlay: Add Popover API to AnchoredOverlay (behind `primer_react_css_anchor_positioning` feature flag)
|
|
10
10
|
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#7695](https://github.com/primer/react/pull/7695) [`780fc3d`](https://github.com/primer/react/commit/780fc3d7b52fd0f9b63f313af6355398180a0118) Thanks [@mattcosta7](https://github.com/mattcosta7)! - perf(ThemeProvider): Reduce unnecessary renders and effect cascades
|
|
14
|
+
|
|
15
|
+
- Replace `useState` + `useEffect` SSR hydration handoff with `useSyncExternalStore` — eliminates post-hydration re-render
|
|
16
|
+
- Replace `useState` + `useEffect` in `useSystemColorMode` with `useSyncExternalStore` — eliminates effect gap and stale-then-update flicker
|
|
17
|
+
- Cache `getServerHandoff` DOM read + JSON.parse per ID (runs once, not on every call)
|
|
18
|
+
- Memoize context value object to prevent unnecessary re-renders of all consumers
|
|
19
|
+
|
|
11
20
|
## 38.18.0
|
|
12
21
|
|
|
13
22
|
### Minor Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeProvider.d.ts","sourceRoot":"","sources":["../src/ThemeProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"ThemeProvider.d.ts","sourceRoot":"","sources":["../src/ThemeProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,eAAO,MAAM,gBAAgB,QAAQ,CAAA;AAKrC,MAAM,MAAM,KAAK,GAAG;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAC,CAAA;AACxC,KAAK,SAAS,GAAG,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAA;AACnD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,MAAM,CAAA;AAElD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,CAAC,EAAE,iBAAiB,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B,CAAA;AA8CD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CA6E/E,CAAA;AAED,wBAAgB,QAAQ;YA1Hd,KAAK;kBACC,MAAM;gBACR,iBAAiB;wBACT,SAAS;0BACP,MAAM;gBAChB,MAAM;kBACJ,MAAM;kBACN,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;kBACvD,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;oBAC1C,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;EAmH7D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,UAG1F;AAqED,eAAe,aAAa,CAAA"}
|
package/dist/ThemeProvider.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { c } from 'react-compiler-runtime';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import ReactDOM from 'react-dom';
|
|
4
3
|
import theme from './theme.js';
|
|
5
4
|
import deepmerge from 'deepmerge';
|
|
6
5
|
import { useSyncedState } from './hooks/useSyncedState.js';
|
|
@@ -20,71 +19,115 @@ const ThemeContext = /*#__PURE__*/React.createContext({
|
|
|
20
19
|
});
|
|
21
20
|
|
|
22
21
|
// inspired from __NEXT_DATA__, we use application/json to avoid CSRF policy with inline scripts
|
|
22
|
+
const serverHandoffCache = new Map();
|
|
23
|
+
const emptyHandoff = {};
|
|
23
24
|
const getServerHandoff = id => {
|
|
25
|
+
if (typeof document === 'undefined') return emptyHandoff;
|
|
26
|
+
const cached = serverHandoffCache.get(id);
|
|
27
|
+
if (cached !== undefined) return cached;
|
|
24
28
|
try {
|
|
25
29
|
var _document$getElementB;
|
|
26
30
|
const serverData = (_document$getElementB = document.getElementById(`__PRIMER_DATA_${id}__`)) === null || _document$getElementB === void 0 ? void 0 : _document$getElementB.textContent;
|
|
27
|
-
if (serverData)
|
|
31
|
+
if (serverData) {
|
|
32
|
+
const parsed = JSON.parse(serverData);
|
|
33
|
+
serverHandoffCache.set(id, parsed);
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
28
36
|
} catch (_error) {
|
|
29
37
|
// if document/element does not exist or JSON is invalid, suppress error
|
|
30
38
|
}
|
|
31
|
-
|
|
39
|
+
const empty = {};
|
|
40
|
+
serverHandoffCache.set(id, empty);
|
|
41
|
+
return empty;
|
|
32
42
|
};
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
...props
|
|
36
|
-
}) => {
|
|
43
|
+
const emptySubscribe = () => () => {};
|
|
44
|
+
const ThemeProvider = t0 => {
|
|
37
45
|
var _ref, _props$colorMode, _ref2, _props$dayScheme, _ref3, _props$nightScheme;
|
|
38
|
-
|
|
46
|
+
const $ = c(42);
|
|
47
|
+
let children;
|
|
48
|
+
let props;
|
|
49
|
+
if ($[0] !== t0) {
|
|
50
|
+
({
|
|
51
|
+
children,
|
|
52
|
+
...props
|
|
53
|
+
} = t0);
|
|
54
|
+
$[0] = t0;
|
|
55
|
+
$[1] = children;
|
|
56
|
+
$[2] = props;
|
|
57
|
+
} else {
|
|
58
|
+
children = $[1];
|
|
59
|
+
props = $[2];
|
|
60
|
+
}
|
|
39
61
|
const {
|
|
40
62
|
theme: fallbackTheme,
|
|
41
63
|
colorMode: fallbackColorMode,
|
|
42
64
|
dayScheme: fallbackDayScheme,
|
|
43
65
|
nightScheme: fallbackNightScheme
|
|
44
66
|
} = useTheme();
|
|
45
|
-
|
|
46
|
-
// Initialize state
|
|
47
67
|
const theme$1 = fallbackTheme !== null && fallbackTheme !== void 0 ? fallbackTheme : theme;
|
|
48
68
|
const uniqueDataId = useId();
|
|
49
|
-
const {
|
|
50
|
-
resolvedServerColorMode
|
|
51
|
-
} = getServerHandoff(uniqueDataId);
|
|
52
|
-
const resolvedColorModePassthrough = React.useRef(resolvedServerColorMode);
|
|
53
69
|
const [colorMode, setColorMode] = useSyncedState((_ref = (_props$colorMode = props.colorMode) !== null && _props$colorMode !== void 0 ? _props$colorMode : fallbackColorMode) !== null && _ref !== void 0 ? _ref : defaultColorMode);
|
|
54
70
|
const [dayScheme, setDayScheme] = useSyncedState((_ref2 = (_props$dayScheme = props.dayScheme) !== null && _props$dayScheme !== void 0 ? _props$dayScheme : fallbackDayScheme) !== null && _ref2 !== void 0 ? _ref2 : defaultDayScheme);
|
|
55
71
|
const [nightScheme, setNightScheme] = useSyncedState((_ref3 = (_props$nightScheme = props.nightScheme) !== null && _props$nightScheme !== void 0 ? _props$nightScheme : fallbackNightScheme) !== null && _ref3 !== void 0 ? _ref3 : defaultNightScheme);
|
|
56
72
|
const systemColorMode = useSystemColorMode();
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
73
|
+
let t1;
|
|
74
|
+
if ($[3] !== colorMode || $[4] !== systemColorMode) {
|
|
75
|
+
t1 = resolveColorMode(colorMode, systemColorMode);
|
|
76
|
+
$[3] = colorMode;
|
|
77
|
+
$[4] = systemColorMode;
|
|
78
|
+
$[5] = t1;
|
|
79
|
+
} else {
|
|
80
|
+
t1 = $[5];
|
|
81
|
+
}
|
|
82
|
+
const clientColorMode = t1;
|
|
83
|
+
let t2;
|
|
84
|
+
if ($[6] !== clientColorMode) {
|
|
85
|
+
t2 = () => clientColorMode;
|
|
86
|
+
$[6] = clientColorMode;
|
|
87
|
+
$[7] = t2;
|
|
88
|
+
} else {
|
|
89
|
+
t2 = $[7];
|
|
90
|
+
}
|
|
91
|
+
let t3;
|
|
92
|
+
if ($[8] !== clientColorMode || $[9] !== uniqueDataId) {
|
|
93
|
+
t3 = () => {
|
|
94
|
+
var _getServerHandoff$res;
|
|
95
|
+
return (_getServerHandoff$res = getServerHandoff(uniqueDataId).resolvedServerColorMode) !== null && _getServerHandoff$res !== void 0 ? _getServerHandoff$res : clientColorMode;
|
|
96
|
+
};
|
|
97
|
+
$[8] = clientColorMode;
|
|
98
|
+
$[9] = uniqueDataId;
|
|
99
|
+
$[10] = t3;
|
|
100
|
+
} else {
|
|
101
|
+
t3 = $[10];
|
|
102
|
+
}
|
|
103
|
+
const resolvedColorMode = React.useSyncExternalStore(emptySubscribe, t2, t3);
|
|
104
|
+
let t4;
|
|
105
|
+
if ($[11] !== dayScheme || $[12] !== nightScheme || $[13] !== resolvedColorMode) {
|
|
106
|
+
t4 = chooseColorScheme(resolvedColorMode, dayScheme, nightScheme);
|
|
107
|
+
$[11] = dayScheme;
|
|
108
|
+
$[12] = nightScheme;
|
|
109
|
+
$[13] = resolvedColorMode;
|
|
110
|
+
$[14] = t4;
|
|
111
|
+
} else {
|
|
112
|
+
t4 = $[14];
|
|
113
|
+
}
|
|
114
|
+
const colorScheme = t4;
|
|
115
|
+
let t5;
|
|
116
|
+
if ($[15] !== colorScheme || $[16] !== theme$1) {
|
|
117
|
+
t5 = applyColorScheme(theme$1, colorScheme);
|
|
118
|
+
$[15] = colorScheme;
|
|
119
|
+
$[16] = theme$1;
|
|
120
|
+
$[17] = t5;
|
|
121
|
+
} else {
|
|
122
|
+
t5 = $[17];
|
|
123
|
+
}
|
|
60
124
|
const {
|
|
61
125
|
resolvedTheme,
|
|
62
126
|
resolvedColorScheme
|
|
63
|
-
} =
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const resolvedColorModeOnClient = resolveColorMode(colorMode, systemColorMode);
|
|
68
|
-
if (resolvedColorModePassthrough.current) {
|
|
69
|
-
// if the resolved color mode passed on from the server is not the resolved color mode on client, change it!
|
|
70
|
-
if (resolvedColorModePassthrough.current !== resolvedColorModeOnClient) {
|
|
71
|
-
window.setTimeout(() => {
|
|
72
|
-
// use ReactDOM.flushSync to prevent automatic batching of state updates since React 18
|
|
73
|
-
// ref: https://github.com/reactwg/react-18/discussions/21
|
|
74
|
-
ReactDOM.flushSync(() => {
|
|
75
|
-
// override colorMode to whatever is resolved on the client to get a re-render
|
|
76
|
-
setColorMode(resolvedColorModeOnClient);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// immediately after that, set the colorMode to what the user passed to respond to system color mode changes
|
|
80
|
-
setColorMode(colorMode);
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
resolvedColorModePassthrough.current = null;
|
|
84
|
-
}
|
|
85
|
-
}, [colorMode, systemColorMode, setColorMode]);
|
|
86
|
-
return /*#__PURE__*/jsx(ThemeContext.Provider, {
|
|
87
|
-
value: {
|
|
127
|
+
} = t5;
|
|
128
|
+
let t6;
|
|
129
|
+
if ($[18] !== colorMode || $[19] !== colorScheme || $[20] !== dayScheme || $[21] !== nightScheme || $[22] !== resolvedColorMode || $[23] !== resolvedColorScheme || $[24] !== resolvedTheme || $[25] !== setColorMode || $[26] !== setDayScheme || $[27] !== setNightScheme) {
|
|
130
|
+
t6 = {
|
|
88
131
|
theme: resolvedTheme,
|
|
89
132
|
colorScheme,
|
|
90
133
|
colorMode,
|
|
@@ -95,24 +138,72 @@ const ThemeProvider = ({
|
|
|
95
138
|
setColorMode,
|
|
96
139
|
setDayScheme,
|
|
97
140
|
setNightScheme
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
141
|
+
};
|
|
142
|
+
$[18] = colorMode;
|
|
143
|
+
$[19] = colorScheme;
|
|
144
|
+
$[20] = dayScheme;
|
|
145
|
+
$[21] = nightScheme;
|
|
146
|
+
$[22] = resolvedColorMode;
|
|
147
|
+
$[23] = resolvedColorScheme;
|
|
148
|
+
$[24] = resolvedTheme;
|
|
149
|
+
$[25] = setColorMode;
|
|
150
|
+
$[26] = setDayScheme;
|
|
151
|
+
$[27] = setNightScheme;
|
|
152
|
+
$[28] = t6;
|
|
153
|
+
} else {
|
|
154
|
+
t6 = $[28];
|
|
155
|
+
}
|
|
156
|
+
const contextValue = t6;
|
|
157
|
+
const t7 = colorMode === "auto" ? "auto" : colorScheme.includes("dark") ? "dark" : "light";
|
|
158
|
+
let t8;
|
|
159
|
+
if ($[29] !== props.preventSSRMismatch || $[30] !== resolvedColorMode || $[31] !== uniqueDataId) {
|
|
160
|
+
t8 = props.preventSSRMismatch ? /*#__PURE__*/jsx("script", {
|
|
161
|
+
type: "application/json",
|
|
162
|
+
id: `__PRIMER_DATA_${uniqueDataId}__`,
|
|
163
|
+
dangerouslySetInnerHTML: {
|
|
164
|
+
__html: JSON.stringify({
|
|
165
|
+
resolvedServerColorMode: resolvedColorMode
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
}) : null;
|
|
169
|
+
$[29] = props.preventSSRMismatch;
|
|
170
|
+
$[30] = resolvedColorMode;
|
|
171
|
+
$[31] = uniqueDataId;
|
|
172
|
+
$[32] = t8;
|
|
173
|
+
} else {
|
|
174
|
+
t8 = $[32];
|
|
175
|
+
}
|
|
176
|
+
let t9;
|
|
177
|
+
if ($[33] !== children || $[34] !== dayScheme || $[35] !== nightScheme || $[36] !== t7 || $[37] !== t8) {
|
|
178
|
+
t9 = /*#__PURE__*/jsxs("div", {
|
|
179
|
+
"data-color-mode": t7,
|
|
101
180
|
"data-light-theme": dayScheme,
|
|
102
181
|
"data-dark-theme": nightScheme,
|
|
103
|
-
children: [children,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
182
|
+
children: [children, t8]
|
|
183
|
+
});
|
|
184
|
+
$[33] = children;
|
|
185
|
+
$[34] = dayScheme;
|
|
186
|
+
$[35] = nightScheme;
|
|
187
|
+
$[36] = t7;
|
|
188
|
+
$[37] = t8;
|
|
189
|
+
$[38] = t9;
|
|
190
|
+
} else {
|
|
191
|
+
t9 = $[38];
|
|
192
|
+
}
|
|
193
|
+
let t10;
|
|
194
|
+
if ($[39] !== contextValue || $[40] !== t9) {
|
|
195
|
+
t10 = /*#__PURE__*/jsx(ThemeContext.Provider, {
|
|
196
|
+
value: contextValue,
|
|
197
|
+
children: t9
|
|
198
|
+
});
|
|
199
|
+
$[39] = contextValue;
|
|
200
|
+
$[40] = t9;
|
|
201
|
+
$[41] = t10;
|
|
202
|
+
} else {
|
|
203
|
+
t10 = $[41];
|
|
204
|
+
}
|
|
205
|
+
return t10;
|
|
114
206
|
};
|
|
115
|
-
ThemeProvider.displayName = "ThemeProvider";
|
|
116
207
|
function useTheme() {
|
|
117
208
|
return React.useContext(ThemeContext);
|
|
118
209
|
}
|
|
@@ -124,57 +215,25 @@ function useColorSchemeVar(values, fallback) {
|
|
|
124
215
|
const colorScheme = t0 === undefined ? "" : t0;
|
|
125
216
|
return (_values$colorScheme = values[colorScheme]) !== null && _values$colorScheme !== void 0 ? _values$colorScheme : fallback;
|
|
126
217
|
}
|
|
218
|
+
function subscribeToSystemColorMode(callback) {
|
|
219
|
+
var _window, _window$matchMedia;
|
|
220
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
221
|
+
const media = (_window = window) === null || _window === void 0 ? void 0 : (_window$matchMedia = _window.matchMedia) === null || _window$matchMedia === void 0 ? void 0 : _window$matchMedia.call(_window, '(prefers-color-scheme: dark)');
|
|
222
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
223
|
+
media === null || media === void 0 ? void 0 : media.addEventListener('change', callback);
|
|
224
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
225
|
+
return () => media === null || media === void 0 ? void 0 : media.removeEventListener('change', callback);
|
|
226
|
+
}
|
|
127
227
|
function useSystemColorMode() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
133
|
-
t0 = () => {
|
|
134
|
-
var _window, _window$matchMedia;
|
|
135
|
-
const media = (_window = window) === null || _window === void 0 ? void 0 : (_window$matchMedia = _window.matchMedia) === null || _window$matchMedia === void 0 ? void 0 : _window$matchMedia.call(_window, "(prefers-color-scheme: dark)");
|
|
136
|
-
const matchesMediaToColorMode = function matchesMediaToColorMode(matches) {
|
|
137
|
-
return matches ? "night" : "day";
|
|
138
|
-
};
|
|
139
|
-
const handleChange = function handleChange(event) {
|
|
140
|
-
const isNight = event.matches;
|
|
141
|
-
setSystemColorMode(matchesMediaToColorMode(isNight));
|
|
142
|
-
};
|
|
143
|
-
if (media) {
|
|
144
|
-
const isNight_0 = media.matches;
|
|
145
|
-
setSystemColorMode(matchesMediaToColorMode(isNight_0));
|
|
146
|
-
if (media.addEventListener !== undefined) {
|
|
147
|
-
media.addEventListener("change", handleChange);
|
|
148
|
-
return function cleanup() {
|
|
149
|
-
media.removeEventListener("change", handleChange);
|
|
150
|
-
};
|
|
151
|
-
} else {
|
|
152
|
-
if (media.addListener !== undefined) {
|
|
153
|
-
media.addListener(handleChange);
|
|
154
|
-
return function cleanup() {
|
|
155
|
-
media.removeListener(handleChange);
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
t1 = [];
|
|
162
|
-
$[0] = t0;
|
|
163
|
-
$[1] = t1;
|
|
164
|
-
} else {
|
|
165
|
-
t0 = $[0];
|
|
166
|
-
t1 = $[1];
|
|
167
|
-
}
|
|
168
|
-
React.useEffect(t0, t1);
|
|
169
|
-
return systemColorMode;
|
|
228
|
+
return React.useSyncExternalStore(subscribeToSystemColorMode, getSystemColorMode, _temp);
|
|
229
|
+
}
|
|
230
|
+
function _temp() {
|
|
231
|
+
return "day";
|
|
170
232
|
}
|
|
171
233
|
function getSystemColorMode() {
|
|
172
|
-
var
|
|
234
|
+
var _window2, _window2$matchMedia, _window2$matchMedia$c;
|
|
173
235
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
174
|
-
|
|
175
|
-
return 'night';
|
|
176
|
-
}
|
|
177
|
-
return 'day';
|
|
236
|
+
return (_window2 = window) !== null && _window2 !== void 0 && (_window2$matchMedia = _window2.matchMedia) !== null && _window2$matchMedia !== void 0 && (_window2$matchMedia$c = _window2$matchMedia.call(_window2, '(prefers-color-scheme: dark)')) !== null && _window2$matchMedia$c !== void 0 && _window2$matchMedia$c.matches ? 'night' : 'day';
|
|
178
237
|
}
|
|
179
238
|
function resolveColorMode(colorMode, systemColorMode) {
|
|
180
239
|
switch (colorMode) {
|
package/package.json
CHANGED