@aurea-uds/react 0.32.0 → 0.34.0

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/dist/system.js CHANGED
@@ -22,11 +22,28 @@ import { cx, StringsContext, SpriteContext, PortalContext, ThemeContext, Density
22
22
  // Sem ela nada muda: o Base UI monta no `document.body`, que é o que permite empilhar e
23
23
  // posicionar sem herdar `overflow` nem `transform`. Com ela, o consumidor aponta para um nó
24
24
  // dentro do landmark dele e o conteúdo do popup deixa de ficar fora de qualquer marco.
25
- export function AureaProvider({ children, strings, direction = "ltr", spriteUrl = defaultSpriteUrl, portalContainer, theme, defaultTheme, onThemeChange, density, defaultDensity, onDensityChange, toastPlacement = "bottom" }) {
25
+ export function AureaProvider({ children, strings, direction = "ltr", spriteUrl = defaultSpriteUrl, portalContainer, theme, defaultTheme, onThemeChange, onThemePreferenceChange, density, defaultDensity, onDensityChange, toastPlacement = "bottom" }) {
26
26
  const value = React.useMemo(() => strings ? { ...defaultStrings, ...strings } : defaultStrings, [strings]);
27
- const temaCtx = useEixoDoDocumento("theme", theme, defaultTheme, onThemeChange);
27
+ // "SYSTEM" (15/10/2026, a chave de três posições da referência): a PREFERÊNCIA pode ser `"system"`, e aí o tema na
28
+ // tela é o do sistema (`prefers-color-scheme`), e muda junto com ele. O documento recebe sempre o tema RESOLVIDO —
29
+ // o CSS só conhece `dark` e `light`. Sem preferência, o provider continua adotando o que o documento trouxer.
30
+ const [prefInterna, setPrefInterna] = React.useState(defaultTheme);
31
+ const preferencia = theme ?? prefInterna;
32
+ // Só quem SEGUE o sistema pergunta a ele: o provider sem `"system"` não abre `matchMedia` nenhum (o custo por
33
+ // aplicação é cobrado em `custo-responsivo` e `ssr-responsivo`, que pegaram a primeira versão, 15/10/2026).
34
+ const segue = preferencia === "system";
35
+ const assina = React.useCallback((cb) => segue ? assinaEsquema(cb) : () => { }, [segue]);
36
+ const sistemaEscuro = React.useSyncExternalStore(assina, segue ? leEsquemaEscuro : semEsquema, semEsquema);
37
+ const resolvido = preferencia === "system" ? (sistemaEscuro ? "dark" : "light") : preferencia;
38
+ const temaCtx = useEixoDoDocumento("theme", resolvido, undefined, undefined);
39
+ const escolher = React.useCallback((p) => {
40
+ if (theme === undefined)
41
+ setPrefInterna(p);
42
+ onThemePreferenceChange?.(p);
43
+ onThemeChange?.(p === "system" ? (leEsquemaEscuro() ? "dark" : "light") : p);
44
+ }, [theme, onThemeChange, onThemePreferenceChange]);
28
45
  const densCtx = useEixoDoDocumento("density", density, defaultDensity, onDensityChange);
29
- const tema = React.useMemo(() => ({ theme: temaCtx.valor, setTheme: temaCtx.set,
46
+ const tema = React.useMemo(() => ({ theme: temaCtx.valor, preference: preferencia ?? temaCtx.valor, setTheme: escolher,
30
47
  // `toggleTheme` existe porque é o gesto real: um botão de tema não escolhe entre dois
31
48
  // valores, ele inverte o atual. Deixar isso para o consumidor é como o catálogo e os docs
32
49
  // acabaram com duas implementações divergentes da mesma coisa.
@@ -38,13 +55,24 @@ export function AureaProvider({ children, strings, direction = "ltr", spriteUrl
38
55
  // ⚠ Achado em 09/09/2026 SÓ porque o `useAureaTheme` passou a delegar para cá: a divergência
39
56
  // existia desde sempre e nenhum teste a pegava, porque cada metade era testada sozinha.
40
57
  // Agora a regra é uma: desconhecido resolve para claro e alterna para ESCURO.
41
- toggleTheme: () => temaCtx.set(temaCtx.valor === "dark" ? "light" : "dark") }), [temaCtx]);
58
+ toggleTheme: () => escolher(temaCtx.valor === "dark" ? "light" : "dark") }), [temaCtx, preferencia, escolher]);
42
59
  const dens = React.useMemo(() => ({ density: densCtx.valor, setDensity: densCtx.set }), [densCtx]);
43
60
  // GAR-14: os avisos presos a um botão vivem num gerenciador do MÓDULO (ver `avisosAncorados`), então
44
61
  // só o provedor de FORA monta o hospedeiro deles — com dois, cada aviso apareceria duas vezes.
45
62
  const deFora = !React.useContext(DentroDoProviderContext);
46
63
  return _jsx(DentroDoProviderContext.Provider, { value: true, children: _jsx(StringsContext.Provider, { value: value, children: _jsx(SpriteContext.Provider, { value: spriteUrl, children: _jsx(PortalContext.Provider, { value: portalContainer, children: _jsx(ThemeContext.Provider, { value: tema, children: _jsx(DensityContext.Provider, { value: dens, children: _jsx(DirectionProvider, { direction: direction, children: _jsx(BaseToast.Provider, { timeout: TEMPO_DO_AVISO, children: _jsxs(BaseTooltip.Provider, { children: [children, _jsx(AureaToastViewport, { placement: toastPlacement }), deFora && _jsx(BaseToast.Provider, { toastManager: avisosAncorados, children: _jsx(AvisosAncorados, {}) })] }) }) }) }) }) }) }) }) });
47
64
  }
65
+ // O esquema do sistema, para a preferência `"system"`. Sem `matchMedia` (servidor, jsdom), claro.
66
+ const ESQUEMA_ESCURO = "(prefers-color-scheme: dark)";
67
+ const assinaEsquema = (cb) => {
68
+ if (typeof matchMedia !== "function")
69
+ return () => { };
70
+ const m = matchMedia(ESQUEMA_ESCURO);
71
+ m.addEventListener("change", cb);
72
+ return () => m.removeEventListener("change", cb);
73
+ };
74
+ const leEsquemaEscuro = () => typeof matchMedia === "function" && matchMedia(ESQUEMA_ESCURO).matches;
75
+ const semEsquema = () => false;
48
76
  /** Um eixo de apresentação que vive no `<html>`: tema ou densidade. A conta é a mesma para os
49
77
  * dois, e escrevê-la duas vezes seria o defeito do §21 cometido dentro do próprio conserto
50
78
  * dele. */
@@ -121,12 +149,15 @@ export function useAureaTheme() {
121
149
  const ctxDens = React.useContext(DensityContext);
122
150
  return React.useMemo(() => ({
123
151
  theme, density,
152
+ /** O que foi escolhido (15/10/2026): pode ser `"system"`. Fora de um provider, o tema na tela. */
153
+ preference: (dentroDeProvider ? ctxTema.preference : theme) ?? theme,
124
154
  setTheme: (t) => {
125
155
  if (dentroDeProvider) {
126
156
  ctxTema.setTheme(t);
127
157
  return;
128
158
  }
129
- document.documentElement.dataset.theme = t;
159
+ // Fora de provider, `"system"` escreve o tema do sistema AGORA; seguir as mudanças dele é do provider.
160
+ document.documentElement.dataset.theme = t === "system" ? (leEsquemaEscuro() ? "dark" : "light") : t;
130
161
  },
131
162
  setDensity: (d) => {
132
163
  if (dentroDeProvider) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurea-uds/react",
3
- "version": "0.32.0",
3
+ "version": "0.34.0",
4
4
  "description": "Os componentes React da Aurea UDS. Importe @aurea-uds/fonts/css e @aurea-uds/core/css junto.",
5
5
  "keywords": [
6
6
  "aurea",