@newtonedev/components 0.1.11 → 0.1.13
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/_COMPONENT_TEMPLATE/ComponentName.styles.d.ts +3 -2
- package/dist/_COMPONENT_TEMPLATE/ComponentName.styles.d.ts.map +1 -1
- package/dist/_COMPONENT_TEMPLATE/ComponentName.types.d.ts +1 -1
- package/dist/_COMPONENT_TEMPLATE/ComponentName.types.d.ts.map +1 -1
- package/dist/composites/actions/Button/Button.styles.d.ts +1 -1
- package/dist/composites/actions/Button/Button.styles.d.ts.map +1 -1
- package/dist/composites/form-controls/Select/Select.d.ts.map +1 -1
- package/dist/composites/form-controls/Select/Select.styles.d.ts +2 -2
- package/dist/composites/form-controls/Select/Select.styles.d.ts.map +1 -1
- package/dist/composites/form-controls/Select/SelectOption.d.ts.map +1 -1
- package/dist/composites/form-controls/TextInput/TextInput.d.ts.map +1 -1
- package/dist/composites/form-controls/TextInput/TextInput.styles.d.ts +2 -2
- package/dist/composites/form-controls/TextInput/TextInput.styles.d.ts.map +1 -1
- package/dist/composites/form-controls/Toggle/Toggle.styles.d.ts +2 -2
- package/dist/composites/form-controls/Toggle/Toggle.styles.d.ts.map +1 -1
- package/dist/composites/layout/AppShell/AppShell.styles.d.ts +2 -2
- package/dist/composites/layout/AppShell/AppShell.styles.d.ts.map +1 -1
- package/dist/composites/layout/Card/Card.styles.d.ts +2 -2
- package/dist/composites/layout/Card/Card.styles.d.ts.map +1 -1
- package/dist/composites/layout/Card/Card.types.d.ts +1 -1
- package/dist/composites/layout/Card/Card.types.d.ts.map +1 -1
- package/dist/composites/layout/Navbar/Navbar.styles.d.ts +3 -2
- package/dist/composites/layout/Navbar/Navbar.styles.d.ts.map +1 -1
- package/dist/composites/layout/Sidebar/Sidebar.styles.d.ts +3 -2
- package/dist/composites/layout/Sidebar/Sidebar.styles.d.ts.map +1 -1
- package/dist/composites/overlays/Popover/Popover.styles.d.ts +2 -2
- package/dist/composites/overlays/Popover/Popover.styles.d.ts.map +1 -1
- package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.d.ts +1 -1
- package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.d.ts.map +1 -1
- package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.d.ts +2 -2
- package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.d.ts.map +1 -1
- package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.types.d.ts +2 -0
- package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.types.d.ts.map +1 -1
- package/dist/composites/range-inputs/HueSlider/HueSlider.d.ts +3 -4
- package/dist/composites/range-inputs/HueSlider/HueSlider.d.ts.map +1 -1
- package/dist/composites/range-inputs/HueSlider/HueSlider.styles.d.ts +4 -4
- package/dist/composites/range-inputs/HueSlider/HueSlider.styles.d.ts.map +1 -1
- package/dist/composites/range-inputs/Slider/Slider.d.ts.map +1 -1
- package/dist/composites/range-inputs/Slider/Slider.styles.d.ts +2 -2
- package/dist/composites/range-inputs/Slider/Slider.styles.d.ts.map +1 -1
- package/dist/index.cjs +1245 -1824
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +12 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1096 -1737
- package/dist/index.js.map +1 -1
- package/dist/primitives/Frame/Frame.d.ts.map +1 -1
- package/dist/primitives/Frame/Frame.styles.d.ts +3 -2
- package/dist/primitives/Frame/Frame.styles.d.ts.map +1 -1
- package/dist/primitives/Frame/Frame.types.d.ts +1 -1
- package/dist/primitives/Frame/Frame.types.d.ts.map +1 -1
- package/dist/primitives/Frame/Frame.utils.d.ts +1 -1
- package/dist/primitives/Frame/Frame.utils.d.ts.map +1 -1
- package/dist/primitives/Icon/Icon.d.ts.map +1 -1
- package/dist/primitives/Text/Text.d.ts +1 -1
- package/dist/primitives/Text/Text.d.ts.map +1 -1
- package/dist/primitives/Text/Text.types.d.ts +1 -1
- package/dist/primitives/Text/Text.types.d.ts.map +1 -1
- package/dist/primitives/Wrapper/Wrapper.styles.d.ts +1 -1
- package/dist/primitives/Wrapper/Wrapper.styles.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/_COMPONENT_TEMPLATE/ComponentName.styles.ts +4 -4
- package/src/_COMPONENT_TEMPLATE/ComponentName.tsx +2 -2
- package/src/_COMPONENT_TEMPLATE/ComponentName.types.ts +1 -1
- package/src/composites/actions/Button/Button.styles.ts +37 -36
- package/src/composites/actions/Button/Button.tsx +1 -1
- package/src/composites/form-controls/Select/Select.styles.ts +8 -8
- package/src/composites/form-controls/Select/Select.tsx +4 -5
- package/src/composites/form-controls/Select/SelectOption.tsx +7 -8
- package/src/composites/form-controls/TextInput/TextInput.styles.ts +7 -8
- package/src/composites/form-controls/TextInput/TextInput.tsx +3 -4
- package/src/composites/form-controls/Toggle/Toggle.styles.ts +6 -6
- package/src/composites/form-controls/Toggle/Toggle.tsx +2 -2
- package/src/composites/layout/AppShell/AppShell.styles.ts +3 -4
- package/src/composites/layout/AppShell/AppShell.tsx +2 -2
- package/src/composites/layout/Card/Card.styles.ts +4 -5
- package/src/composites/layout/Card/Card.tsx +2 -2
- package/src/composites/layout/Card/Card.types.ts +1 -1
- package/src/composites/layout/Navbar/Navbar.styles.ts +5 -5
- package/src/composites/layout/Navbar/Navbar.tsx +2 -2
- package/src/composites/layout/Sidebar/Sidebar.styles.ts +5 -5
- package/src/composites/layout/Sidebar/Sidebar.tsx +2 -2
- package/src/composites/overlays/Popover/Popover.styles.ts +4 -4
- package/src/composites/overlays/Popover/Popover.tsx +2 -2
- package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.ts +5 -6
- package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.tsx +6 -3
- package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.types.ts +2 -0
- package/src/composites/range-inputs/HueSlider/HueSlider.styles.ts +14 -21
- package/src/composites/range-inputs/HueSlider/HueSlider.tsx +8 -9
- package/src/composites/range-inputs/Slider/Slider.styles.ts +9 -10
- package/src/composites/range-inputs/Slider/Slider.tsx +18 -4
- package/src/index.ts +73 -60
- package/src/primitives/Frame/Frame.styles.ts +8 -7
- package/src/primitives/Frame/Frame.tsx +9 -9
- package/src/primitives/Frame/Frame.types.ts +1 -1
- package/src/primitives/Frame/Frame.utils.ts +1 -1
- package/src/primitives/Icon/Icon.tsx +2 -3
- package/src/primitives/Text/Text.spans.ts +1 -1
- package/src/primitives/Text/Text.tsx +16 -16
- package/src/primitives/Text/Text.types.ts +1 -1
- package/src/primitives/Wrapper/Wrapper.styles.ts +1 -1
- package/src/primitives/Wrapper/Wrapper.tsx +1 -1
- package/dist/fonts/GoogleFontLoader.d.ts +0 -20
- package/dist/fonts/GoogleFontLoader.d.ts.map +0 -1
- package/dist/fonts/IconFontLoader.d.ts +0 -13
- package/dist/fonts/IconFontLoader.d.ts.map +0 -1
- package/dist/fonts/SelfHostedFontLoader.d.ts +0 -14
- package/dist/fonts/SelfHostedFontLoader.d.ts.map +0 -1
- package/dist/fonts/buildGoogleFontsUrl.d.ts +0 -2
- package/dist/fonts/buildGoogleFontsUrl.d.ts.map +0 -1
- package/dist/fonts/measureFont.d.ts +0 -19
- package/dist/fonts/measureFont.d.ts.map +0 -1
- package/dist/fonts/reportQueue.d.ts +0 -7
- package/dist/fonts/reportQueue.d.ts.map +0 -1
- package/dist/fonts/useLocalCalibration.d.ts +0 -19
- package/dist/fonts/useLocalCalibration.d.ts.map +0 -1
- package/dist/fonts/useTypographyCalibrations.d.ts +0 -11
- package/dist/fonts/useTypographyCalibrations.d.ts.map +0 -1
- package/dist/theme/FrameContext.d.ts +0 -26
- package/dist/theme/FrameContext.d.ts.map +0 -1
- package/dist/theme/NewtoneProvider.d.ts +0 -40
- package/dist/theme/NewtoneProvider.d.ts.map +0 -1
- package/dist/theme/defaults.d.ts +0 -8
- package/dist/theme/defaults.d.ts.map +0 -1
- package/dist/theme/types.d.ts +0 -156
- package/dist/theme/types.d.ts.map +0 -1
- package/dist/theme/useBreakpoint.d.ts +0 -9
- package/dist/theme/useBreakpoint.d.ts.map +0 -1
- package/dist/tokens/computeTokens.d.ts +0 -151
- package/dist/tokens/computeTokens.d.ts.map +0 -1
- package/dist/tokens/types.d.ts +0 -162
- package/dist/tokens/types.d.ts.map +0 -1
- package/dist/tokens/useTokens.d.ts +0 -26
- package/dist/tokens/useTokens.d.ts.map +0 -1
- package/src/fonts/GoogleFontLoader.tsx +0 -80
- package/src/fonts/IconFontLoader.tsx +0 -51
- package/src/fonts/SelfHostedFontLoader.tsx +0 -44
- package/src/fonts/buildGoogleFontsUrl.ts +0 -2
- package/src/fonts/measureFont.ts +0 -55
- package/src/fonts/reportQueue.ts +0 -54
- package/src/fonts/useLocalCalibration.ts +0 -97
- package/src/fonts/useTypographyCalibrations.ts +0 -15
- package/src/theme/FrameContext.tsx +0 -31
- package/src/theme/NewtoneProvider.tsx +0 -84
- package/src/theme/defaults.ts +0 -71
- package/src/theme/types.ts +0 -191
- package/src/theme/useBreakpoint.ts +0 -14
- package/src/tokens/computeTokens.ts +0 -516
- package/src/tokens/types.ts +0 -146
- package/src/tokens/useTokens.ts +0 -62
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react';
|
|
2
|
-
|
|
3
|
-
interface SelfHostedFontLoaderProps {
|
|
4
|
-
/** Raw @font-face CSS string from the theme API. */
|
|
5
|
-
readonly fontFaceCss: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Injects self-hosted @font-face CSS into <head> via a <style> tag.
|
|
10
|
-
* Used instead of GoogleFontLoader when self-hosted font CSS is available.
|
|
11
|
-
*
|
|
12
|
-
* Uses imperative DOM manipulation because react-native-web's View
|
|
13
|
-
* cannot render <style> elements.
|
|
14
|
-
*/
|
|
15
|
-
export function SelfHostedFontLoader({ fontFaceCss }: SelfHostedFontLoaderProps) {
|
|
16
|
-
const styleRef = useRef<HTMLStyleElement | null>(null);
|
|
17
|
-
|
|
18
|
-
useEffect(() => {
|
|
19
|
-
if (typeof document === 'undefined') return;
|
|
20
|
-
|
|
21
|
-
// Clean up previous style
|
|
22
|
-
if (styleRef.current) {
|
|
23
|
-
styleRef.current.remove();
|
|
24
|
-
styleRef.current = null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (!fontFaceCss) return;
|
|
28
|
-
|
|
29
|
-
const style = document.createElement('style');
|
|
30
|
-
style.setAttribute('data-newtone-fonts', 'self-hosted');
|
|
31
|
-
style.textContent = fontFaceCss;
|
|
32
|
-
document.head.appendChild(style);
|
|
33
|
-
styleRef.current = style;
|
|
34
|
-
|
|
35
|
-
return () => {
|
|
36
|
-
if (styleRef.current) {
|
|
37
|
-
styleRef.current.remove();
|
|
38
|
-
styleRef.current = null;
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
}, [fontFaceCss]);
|
|
42
|
-
|
|
43
|
-
return null;
|
|
44
|
-
}
|
package/src/fonts/measureFont.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reference string for character width measurement.
|
|
3
|
-
* Covers uppercase, lowercase, digits, and a space — 63 characters total.
|
|
4
|
-
*/
|
|
5
|
-
const REF_STRING =
|
|
6
|
-
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ';
|
|
7
|
-
|
|
8
|
-
/** Race a promise against a timeout. Resolves with fallback if the promise doesn't settle in time. */
|
|
9
|
-
function withTimeout<T>(promise: Promise<T>, ms: number, fallback: T): Promise<T> {
|
|
10
|
-
return Promise.race([
|
|
11
|
-
promise,
|
|
12
|
-
new Promise<T>((resolve) => setTimeout(() => resolve(fallback), ms)),
|
|
13
|
-
]);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Measure the average character width ratio for a font using the Canvas API.
|
|
18
|
-
*
|
|
19
|
-
* Waits for the font to load via `document.fonts.load()` before measuring,
|
|
20
|
-
* with a 3-second timeout to prevent hangs when fonts fail to load silently.
|
|
21
|
-
* Falls back to 0.55 if the browser context is unavailable, the font fails
|
|
22
|
-
* to load, or canvas is not supported.
|
|
23
|
-
*
|
|
24
|
-
* The returned ratio represents `avgCharWidth / fontSize`. Multiply by a
|
|
25
|
-
* desired fontSize to estimate character widths for that font.
|
|
26
|
-
*
|
|
27
|
-
* @param fontFamily - Font family name, e.g. "Inter" or "system-ui"
|
|
28
|
-
* @param fontWeight - CSS font-weight number, e.g. 400
|
|
29
|
-
* @param fallback - CSS fallback stack, e.g. "sans-serif"
|
|
30
|
-
* @param fontSize - Reference font size in px (default 16)
|
|
31
|
-
* @returns Average character width ratio, e.g. 0.52 for Inter
|
|
32
|
-
*/
|
|
33
|
-
export async function measureAvgCharWidth(
|
|
34
|
-
fontFamily: string,
|
|
35
|
-
fontWeight: number,
|
|
36
|
-
fallback: string,
|
|
37
|
-
fontSize = 16,
|
|
38
|
-
): Promise<number> {
|
|
39
|
-
if (typeof document === 'undefined') return 0.55;
|
|
40
|
-
try {
|
|
41
|
-
await withTimeout(
|
|
42
|
-
document.fonts.load(`${fontWeight} ${fontSize}px "${fontFamily}"`),
|
|
43
|
-
3000,
|
|
44
|
-
[] as FontFace[],
|
|
45
|
-
);
|
|
46
|
-
const canvas = document.createElement('canvas');
|
|
47
|
-
const ctx = canvas.getContext('2d');
|
|
48
|
-
if (!ctx) return 0.55;
|
|
49
|
-
ctx.font = `${fontWeight} ${fontSize}px "${fontFamily}", ${fallback}`;
|
|
50
|
-
const ratio = ctx.measureText(REF_STRING).width / REF_STRING.length / fontSize;
|
|
51
|
-
return Math.round(ratio * 1000) / 1000;
|
|
52
|
-
} catch {
|
|
53
|
-
return 0.55;
|
|
54
|
-
}
|
|
55
|
-
}
|
package/src/fonts/reportQueue.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { ObserverPayload } from '@newtonedev/fonts';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Module-level batch queue for typography observations (Layer 3 reporting).
|
|
5
|
-
*
|
|
6
|
-
* Lives outside React's component lifecycle so it survives re-renders.
|
|
7
|
-
* Observations are enqueued immediately and flushed in a single batch after
|
|
8
|
-
* a 2s debounce — minimising network requests and allowing coalescence.
|
|
9
|
-
*
|
|
10
|
-
* Usage: call `enqueueObservation(endpoint, payload)` from responsive Text
|
|
11
|
-
* instances. Set `reportingEndpoint` on `NewtoneProvider` to opt in.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
interface QueuedObservation {
|
|
15
|
-
readonly endpoint: string;
|
|
16
|
-
readonly payload: ObserverPayload;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const queue: QueuedObservation[] = [];
|
|
20
|
-
let flushTimer: ReturnType<typeof setTimeout> | undefined;
|
|
21
|
-
|
|
22
|
-
function flush(): void {
|
|
23
|
-
if (queue.length === 0) return;
|
|
24
|
-
|
|
25
|
-
// Group by endpoint so a page with multiple endpoints gets one request each
|
|
26
|
-
const byEndpoint = new Map<string, ObserverPayload[]>();
|
|
27
|
-
for (const item of queue) {
|
|
28
|
-
const group = byEndpoint.get(item.endpoint) ?? [];
|
|
29
|
-
group.push(item.payload);
|
|
30
|
-
byEndpoint.set(item.endpoint, group);
|
|
31
|
-
}
|
|
32
|
-
queue.length = 0;
|
|
33
|
-
|
|
34
|
-
for (const [endpoint, observations] of byEndpoint) {
|
|
35
|
-
// Fire-and-forget — reporting failures are silent and never affect the UI
|
|
36
|
-
fetch(endpoint, {
|
|
37
|
-
method: 'POST',
|
|
38
|
-
headers: { 'Content-Type': 'application/json' },
|
|
39
|
-
body: JSON.stringify({ observations }),
|
|
40
|
-
// keepalive: true allows the request to outlive the page
|
|
41
|
-
keepalive: true,
|
|
42
|
-
}).catch(() => {});
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Enqueue a typography observation for batch reporting.
|
|
48
|
-
* Resets the 2s debounce timer on each call.
|
|
49
|
-
*/
|
|
50
|
-
export function enqueueObservation(endpoint: string, payload: ObserverPayload): void {
|
|
51
|
-
queue.push({ endpoint, payload });
|
|
52
|
-
if (flushTimer !== undefined) clearTimeout(flushTimer);
|
|
53
|
-
flushTimer = setTimeout(flush, 2000);
|
|
54
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
2
|
-
import { measureAvgCharWidth } from './measureFont';
|
|
3
|
-
|
|
4
|
-
const STORAGE_KEY = 'newtone:font-metrics:v1';
|
|
5
|
-
const TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
6
|
-
|
|
7
|
-
interface CacheEntry {
|
|
8
|
-
readonly ratio: number;
|
|
9
|
-
readonly measuredAt: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
type FontMetricsCache = Record<string, CacheEntry>;
|
|
13
|
-
|
|
14
|
-
function readCache(): FontMetricsCache {
|
|
15
|
-
if (typeof localStorage === 'undefined') return {};
|
|
16
|
-
try {
|
|
17
|
-
const raw = localStorage.getItem(STORAGE_KEY);
|
|
18
|
-
return raw ? (JSON.parse(raw) as FontMetricsCache) : {};
|
|
19
|
-
} catch {
|
|
20
|
-
return {};
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function writeCache(cache: FontMetricsCache): void {
|
|
25
|
-
if (typeof localStorage === 'undefined') return;
|
|
26
|
-
try {
|
|
27
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify(cache));
|
|
28
|
-
} catch {
|
|
29
|
-
// Ignore write errors (quota exceeded, private browsing)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function cacheKey(fontFamily: string, fontWeight: number): string {
|
|
34
|
-
return `${fontFamily}:${fontWeight}`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Returns the avgCharWidth ratio for a font, using a device-local measurement
|
|
39
|
-
* cached in localStorage (Layer 2 calibration).
|
|
40
|
-
*
|
|
41
|
-
* Resolution order:
|
|
42
|
-
* 1. localStorage cache (if fresh, < 7 days old)
|
|
43
|
-
* 2. `baseCalibration` (Layer 1 — editor-time canvas measurement from theme config)
|
|
44
|
-
* 3. 0.55 (fallback constant)
|
|
45
|
-
*
|
|
46
|
-
* On mount, measures the font if the cache is stale or absent, then updates
|
|
47
|
-
* localStorage and triggers a re-render with the device-accurate ratio.
|
|
48
|
-
*
|
|
49
|
-
* @param fontFamily - CSS font family name, e.g. "Inter"
|
|
50
|
-
* @param fontWeight - CSS font-weight number, e.g. 400
|
|
51
|
-
* @param fallback - CSS fallback stack, e.g. "sans-serif"
|
|
52
|
-
* @param baseCalibration - Layer 1 ratio from `themeConfig.typography.calibrations`
|
|
53
|
-
*/
|
|
54
|
-
export function useLocalCalibration(
|
|
55
|
-
fontFamily: string,
|
|
56
|
-
fontWeight: number,
|
|
57
|
-
fallback: string,
|
|
58
|
-
baseCalibration?: number,
|
|
59
|
-
): number {
|
|
60
|
-
const key = cacheKey(fontFamily, fontWeight);
|
|
61
|
-
|
|
62
|
-
// Initialise synchronously from cache so the first render uses a calibrated ratio
|
|
63
|
-
const [ratio, setRatio] = useState<number>(() => {
|
|
64
|
-
const cache = readCache();
|
|
65
|
-
const entry = cache[key];
|
|
66
|
-
if (entry && Date.now() - entry.measuredAt < TTL_MS) {
|
|
67
|
-
return entry.ratio;
|
|
68
|
-
}
|
|
69
|
-
return baseCalibration ?? 0.55;
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
const cache = readCache();
|
|
74
|
-
const entry = cache[key];
|
|
75
|
-
if (entry && Date.now() - entry.measuredAt < TTL_MS) {
|
|
76
|
-
// Cache is fresh — no re-measurement needed
|
|
77
|
-
if (entry.ratio !== ratio) setRatio(entry.ratio);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Cache is stale or absent — measure in the background
|
|
82
|
-
let cancelled = false;
|
|
83
|
-
measureAvgCharWidth(fontFamily, fontWeight, fallback).then((measured) => {
|
|
84
|
-
if (cancelled) return;
|
|
85
|
-
const updated = { ...readCache(), [key]: { ratio: measured, measuredAt: Date.now() } };
|
|
86
|
-
writeCache(updated);
|
|
87
|
-
setRatio(measured);
|
|
88
|
-
});
|
|
89
|
-
return () => {
|
|
90
|
-
cancelled = true;
|
|
91
|
-
};
|
|
92
|
-
// Intentionally excluding `ratio` from deps — we only want to re-measure on key/font change
|
|
93
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
94
|
-
}, [key, fontFamily, fontWeight, fallback]);
|
|
95
|
-
|
|
96
|
-
return ratio;
|
|
97
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { useNewtoneTheme } from '../theme/NewtoneProvider';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Returns the per-font avgCharWidth calibrations from the current theme config.
|
|
5
|
-
*
|
|
6
|
-
* These are Layer 1 (editor-time) calibrations — measured with canvas at publish
|
|
7
|
-
* time and stored in `themeConfig.typography.calibrations`. Use `useLocalCalibration`
|
|
8
|
-
* to overlay device-local (Layer 2) measurements on top.
|
|
9
|
-
*
|
|
10
|
-
* @returns Record of fontFamily → avgCharWidthRatio, or an empty object if absent.
|
|
11
|
-
*/
|
|
12
|
-
export function useTypographyCalibrations(): Record<string, number> {
|
|
13
|
-
const { config } = useNewtoneTheme();
|
|
14
|
-
return config.typography.calibrations ?? {};
|
|
15
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { createContext, useContext } from 'react';
|
|
2
|
-
import type { ElevationLevel } from './types';
|
|
3
|
-
import type { ResolvedTokens } from '../tokens/types';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Context value provided by Frame to its descendants.
|
|
7
|
-
* Contains the resolved elevation and pre-computed tokens.
|
|
8
|
-
* Tokens are included to avoid redundant computeTokens calls in child components.
|
|
9
|
-
*/
|
|
10
|
-
export interface FrameContextValue {
|
|
11
|
-
readonly elevation: ElevationLevel;
|
|
12
|
-
readonly tokens: ResolvedTokens;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* FrameContext - Propagates elevation overrides from Frame to descendants.
|
|
17
|
-
*
|
|
18
|
-
* When null, components fall back to default elevation (1).
|
|
19
|
-
* When present, useTokens() reads from this context instead.
|
|
20
|
-
*/
|
|
21
|
-
export const FrameContext = createContext<FrameContextValue | null>(null);
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* useFrameContext - Read the nearest Frame's context, if any.
|
|
25
|
-
*
|
|
26
|
-
* Returns null when no parent Frame exists. This is intentionally not an error —
|
|
27
|
-
* components outside a Frame simply fall back to the NewtoneProvider defaults.
|
|
28
|
-
*/
|
|
29
|
-
export function useFrameContext(): FrameContextValue | null {
|
|
30
|
-
return useContext(FrameContext);
|
|
31
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useState, useMemo, useContext } from 'react';
|
|
2
|
-
import type { NewtoneThemeConfig, NewtoneThemeContext, ColorMode } from './types';
|
|
3
|
-
import { DEFAULT_THEME_CONFIG } from './defaults';
|
|
4
|
-
import { GoogleFontLoader } from '../fonts/GoogleFontLoader';
|
|
5
|
-
import { SelfHostedFontLoader } from '../fonts/SelfHostedFontLoader';
|
|
6
|
-
import { IconFontLoader } from '../fonts/IconFontLoader';
|
|
7
|
-
|
|
8
|
-
const ThemeContext = createContext<NewtoneThemeContext | null>(null);
|
|
9
|
-
|
|
10
|
-
export interface NewtoneProviderProps {
|
|
11
|
-
readonly config?: NewtoneThemeConfig;
|
|
12
|
-
readonly initialMode?: ColorMode;
|
|
13
|
-
readonly children: React.ReactNode;
|
|
14
|
-
/** Optional URL for typography telemetry. When set, adaptive Text instances report observations. */
|
|
15
|
-
readonly reportingEndpoint?: string;
|
|
16
|
-
/**
|
|
17
|
-
* Self-hosted @font-face CSS from the theme API.
|
|
18
|
-
* When provided, injects this CSS instead of loading from Google CDN.
|
|
19
|
-
* Falls back to Google CDN when absent or null.
|
|
20
|
-
*/
|
|
21
|
-
readonly fontFaceCss?: string | null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* NewtoneProvider - Provides theme context to all Newtone components
|
|
26
|
-
*
|
|
27
|
-
* Wrap your app with this provider to enable mode switching.
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* ```tsx
|
|
31
|
-
* <NewtoneProvider initialMode="light">
|
|
32
|
-
* <App />
|
|
33
|
-
* </NewtoneProvider>
|
|
34
|
-
* ```
|
|
35
|
-
*/
|
|
36
|
-
export function NewtoneProvider({
|
|
37
|
-
config = DEFAULT_THEME_CONFIG,
|
|
38
|
-
initialMode = 'light',
|
|
39
|
-
children,
|
|
40
|
-
reportingEndpoint,
|
|
41
|
-
fontFaceCss,
|
|
42
|
-
}: NewtoneProviderProps) {
|
|
43
|
-
const [mode, setMode] = useState<ColorMode>(initialMode);
|
|
44
|
-
|
|
45
|
-
const value = useMemo(
|
|
46
|
-
() => ({
|
|
47
|
-
config,
|
|
48
|
-
mode,
|
|
49
|
-
setMode,
|
|
50
|
-
reportingEndpoint,
|
|
51
|
-
}),
|
|
52
|
-
[config, mode, reportingEndpoint]
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
return (
|
|
56
|
-
<ThemeContext.Provider value={value}>
|
|
57
|
-
{fontFaceCss ? (
|
|
58
|
-
<SelfHostedFontLoader fontFaceCss={fontFaceCss} />
|
|
59
|
-
) : (
|
|
60
|
-
<GoogleFontLoader fonts={config.typography.fonts} />
|
|
61
|
-
)}
|
|
62
|
-
<IconFontLoader icons={config.icons} />
|
|
63
|
-
{children}
|
|
64
|
-
</ThemeContext.Provider>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* useNewtoneTheme - Hook to access theme context
|
|
70
|
-
*
|
|
71
|
-
* Must be used within a NewtoneProvider.
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* ```tsx
|
|
75
|
-
* const { mode, setMode } = useNewtoneTheme();
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
export function useNewtoneTheme(): NewtoneThemeContext {
|
|
79
|
-
const context = useContext(ThemeContext);
|
|
80
|
-
if (!context) {
|
|
81
|
-
throw new Error('useNewtoneTheme must be used within NewtoneProvider');
|
|
82
|
-
}
|
|
83
|
-
return context;
|
|
84
|
-
}
|
package/src/theme/defaults.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import type { NewtoneThemeConfig } from './types';
|
|
2
|
-
import {
|
|
3
|
-
DEFAULT_NEUTRAL_HUE,
|
|
4
|
-
DEFAULT_NEUTRAL_SATURATION,
|
|
5
|
-
DEFAULT_ACCENT_HUE,
|
|
6
|
-
DEFAULT_ACCENT_SATURATION,
|
|
7
|
-
DEFAULT_SUCCESS_HUE,
|
|
8
|
-
DEFAULT_SUCCESS_SATURATION,
|
|
9
|
-
DEFAULT_WARNING_HUE,
|
|
10
|
-
DEFAULT_WARNING_SATURATION,
|
|
11
|
-
DEFAULT_ERROR_HUE,
|
|
12
|
-
DEFAULT_ERROR_SATURATION,
|
|
13
|
-
} from 'newtone';
|
|
14
|
-
import { DEFAULT_FONT_SLOTS, DEFAULT_FONT_SIZES, DEFAULT_LINE_HEIGHTS, DEFAULT_ROLE_SCALES } from '@newtonedev/fonts';
|
|
15
|
-
|
|
16
|
-
// Re-export typography defaults from @newtonedev/fonts (canonical source)
|
|
17
|
-
export { DEFAULT_FONT_SIZES, DEFAULT_LINE_HEIGHTS, DEFAULT_ROLE_SCALES } from '@newtonedev/fonts';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Default theme configuration matching playground defaults.
|
|
21
|
-
* Palettes: [0] Neutral, [1] Accent, [2] Success, [3] Warning, [4] Error
|
|
22
|
-
*/
|
|
23
|
-
export const DEFAULT_THEME_CONFIG: NewtoneThemeConfig = {
|
|
24
|
-
colorSystem: {
|
|
25
|
-
dynamicRange: {
|
|
26
|
-
lightest: 1,
|
|
27
|
-
darkest: 1,
|
|
28
|
-
},
|
|
29
|
-
palettes: [
|
|
30
|
-
{ hue: DEFAULT_NEUTRAL_HUE, saturation: DEFAULT_NEUTRAL_SATURATION },
|
|
31
|
-
{ hue: DEFAULT_ACCENT_HUE, saturation: DEFAULT_ACCENT_SATURATION },
|
|
32
|
-
{ hue: DEFAULT_SUCCESS_HUE, saturation: DEFAULT_SUCCESS_SATURATION },
|
|
33
|
-
{ hue: DEFAULT_WARNING_HUE, saturation: DEFAULT_WARNING_SATURATION },
|
|
34
|
-
{ hue: DEFAULT_ERROR_HUE, saturation: DEFAULT_ERROR_SATURATION },
|
|
35
|
-
],
|
|
36
|
-
},
|
|
37
|
-
spacing: {
|
|
38
|
-
'00': 0, // base * 0
|
|
39
|
-
'02': 2, // base * 0.25
|
|
40
|
-
'04': 4, // base * 0.5
|
|
41
|
-
'06': 6, // base * 0.75
|
|
42
|
-
'08': 8, // base * 1 (Medium preset, 8px base)
|
|
43
|
-
'10': 10, // base * 1.25
|
|
44
|
-
'12': 12, // base * 1.5
|
|
45
|
-
'16': 16, // base * 2
|
|
46
|
-
'20': 20, // base * 2.5
|
|
47
|
-
'24': 24, // base * 3
|
|
48
|
-
'32': 32, // base * 4
|
|
49
|
-
'40': 40, // base * 5
|
|
50
|
-
'48': 48, // base * 6
|
|
51
|
-
},
|
|
52
|
-
radius: {
|
|
53
|
-
none: 0,
|
|
54
|
-
sm: 4,
|
|
55
|
-
md: 6,
|
|
56
|
-
lg: 8,
|
|
57
|
-
xl: 12,
|
|
58
|
-
pill: 999,
|
|
59
|
-
},
|
|
60
|
-
typography: {
|
|
61
|
-
fonts: DEFAULT_FONT_SLOTS,
|
|
62
|
-
fontSizes: DEFAULT_FONT_SIZES,
|
|
63
|
-
lineHeights: DEFAULT_LINE_HEIGHTS,
|
|
64
|
-
roles: DEFAULT_ROLE_SCALES,
|
|
65
|
-
},
|
|
66
|
-
icons: {
|
|
67
|
-
variant: 'rounded', // Material Design 3 aesthetic
|
|
68
|
-
weight: 400, // Normal weight
|
|
69
|
-
autoGrade: true, // Enable mode-aware grade
|
|
70
|
-
},
|
|
71
|
-
};
|
package/src/theme/types.ts
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import type { DynamicRange, PaletteConfig } from 'newtone';
|
|
2
|
-
|
|
3
|
-
// Re-export font/typography types from @newtonedev/fonts (canonical source)
|
|
4
|
-
export type {
|
|
5
|
-
FontConfig,
|
|
6
|
-
FontWeights,
|
|
7
|
-
FontSlot,
|
|
8
|
-
FontSizeScale,
|
|
9
|
-
LineHeightScale,
|
|
10
|
-
RoleSizeStep,
|
|
11
|
-
RoleScale,
|
|
12
|
-
RoleScales,
|
|
13
|
-
BreakpointKey,
|
|
14
|
-
Breakpoints,
|
|
15
|
-
BreakpointRoleScales,
|
|
16
|
-
FontRuntimeMetrics,
|
|
17
|
-
} from '@newtonedev/fonts';
|
|
18
|
-
import type { FontSlot, FontSizeScale, LineHeightScale, RoleScales, BreakpointRoleScales, FontRuntimeMetrics, TextRole } from '@newtonedev/fonts';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Color mode: light or dark
|
|
22
|
-
*/
|
|
23
|
-
export type ColorMode = 'light' | 'dark';
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Elevation levels for surfaces (internal)
|
|
27
|
-
* 0 = sunken, 1 = default, 2 = elevated
|
|
28
|
-
*/
|
|
29
|
-
export type ElevationLevel = 0 | 1 | 2;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* User-facing elevation for Frame component
|
|
33
|
-
* -2 = deeply sunken (inset shadow), -1 = sunken, 0 = default,
|
|
34
|
-
* 1 = elevated, 2 = prominently elevated (drop shadow)
|
|
35
|
-
*/
|
|
36
|
-
export type FrameElevation = -2 | -1 | 0 | 1 | 2;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Complete color system configuration
|
|
40
|
-
*/
|
|
41
|
-
export interface ColorSystemConfig {
|
|
42
|
-
readonly dynamicRange: DynamicRange;
|
|
43
|
-
readonly palettes: ReadonlyArray<PaletteConfig>;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Overrides for hardcoded token computation constants.
|
|
48
|
-
* All values are optional — omitted fields fall back to engine defaults.
|
|
49
|
-
*
|
|
50
|
-
* All values are NV (normalized value / luminosity) offsets on a unified scale.
|
|
51
|
-
* 0.10 means the same 10% luminosity shift whether applied to borders, text,
|
|
52
|
-
* or interactive components. Direction auto-inverts per effective mode
|
|
53
|
-
* (darker in light mode, lighter in dark mode) unless noted otherwise.
|
|
54
|
-
*/
|
|
55
|
-
export interface TokenOverrides {
|
|
56
|
-
// --- Offset fields (all magnitudes, direction auto-inverts per effective mode) ---
|
|
57
|
-
// Light mode
|
|
58
|
-
readonly interactiveComponentOffset?: number; // default: 0.04 (auto-inverts)
|
|
59
|
-
readonly hoverShift?: number; // default: 0.06 (auto-inverts)
|
|
60
|
-
readonly activeShift?: number; // default: 0.08 (auto-inverts)
|
|
61
|
-
readonly borderOffset?: number; // default: 0.08 (auto-inverts)
|
|
62
|
-
// Dark mode
|
|
63
|
-
readonly interactiveComponentOffsetDark?: number; // default: 0.04 (auto-inverts)
|
|
64
|
-
readonly hoverShiftDark?: number; // default: 0.06 (auto-inverts)
|
|
65
|
-
readonly activeShiftDark?: number; // default: 0.08 (auto-inverts)
|
|
66
|
-
readonly borderOffsetDark?: number; // default: 0.08 (auto-inverts)
|
|
67
|
-
readonly borderFocusedOffset?: number; // default: 0.16 (auto-inverts)
|
|
68
|
-
readonly borderFocusedOffsetDark?: number; // default: 0.16 (auto-inverts)
|
|
69
|
-
readonly borderFilledOffset?: number; // default: 0.24 (auto-inverts)
|
|
70
|
-
readonly borderFilledOffsetDark?: number; // default: 0.24 (auto-inverts)
|
|
71
|
-
|
|
72
|
-
// --- Normalized tokens (light mode: 0 = lightest, 1 = darkest) ---
|
|
73
|
-
// When present, computeTokens uses absolute positions instead of baseNv + offset.
|
|
74
|
-
// All themes share the same lightness positions; palette (hue/sat) varies per theme.
|
|
75
|
-
// Background: absolute positions — ground is Background/01 (elevated)
|
|
76
|
-
readonly backgroundElevated?: number; // default: 0 (ground — lightest)
|
|
77
|
-
readonly backgroundDefault?: number; // default: 0.03
|
|
78
|
-
readonly backgroundSunken?: number; // default: 0.06
|
|
79
|
-
// Text: absolute positions at bg01 — elevation-compensated by computeTokens on deeper surfaces
|
|
80
|
-
readonly textPrimaryNormalized?: number; // default: 0.9 (max contrast)
|
|
81
|
-
readonly textSecondaryNormalized?: number; // default: 0.7
|
|
82
|
-
readonly textTertiaryNormalized?: number; // default: 0.5
|
|
83
|
-
readonly textDisabledNormalized?: number; // default: 0.3
|
|
84
|
-
|
|
85
|
-
// --- Normalized tokens (dark mode: 0 = darkest, 1 = lightest) ---
|
|
86
|
-
// Same semantics as light mode but scale direction is inverted.
|
|
87
|
-
// Background: ground is Background/01 (elevated = lightest dark surface)
|
|
88
|
-
readonly backgroundElevatedDark?: number; // default: 0.24 (ground — lightest dark surface)
|
|
89
|
-
readonly backgroundDefaultDark?: number; // default: 0.20
|
|
90
|
-
readonly backgroundSunkenDark?: number; // default: 0.16
|
|
91
|
-
// Text: absolute positions at bg01 — elevation-compensated by computeTokens on deeper surfaces
|
|
92
|
-
readonly textPrimaryNormalizedDark?: number; // default: 1.0 (max contrast — lightest)
|
|
93
|
-
readonly textSecondaryNormalizedDark?: number; // default: 0.8
|
|
94
|
-
readonly textTertiaryNormalizedDark?: number; // default: 0.6
|
|
95
|
-
readonly textDisabledNormalizedDark?: number; // default: 0.4
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Newtone theme configuration
|
|
100
|
-
*/
|
|
101
|
-
export interface NewtoneThemeConfig {
|
|
102
|
-
readonly colorSystem: ColorSystemConfig;
|
|
103
|
-
readonly spacing: {
|
|
104
|
-
readonly '00': number; // base * 0
|
|
105
|
-
readonly '02': number; // base * 0.25
|
|
106
|
-
readonly '04': number; // base * 0.5
|
|
107
|
-
readonly '06': number; // base * 0.75
|
|
108
|
-
readonly '08': number; // base * 1
|
|
109
|
-
readonly '10': number; // base * 1.25
|
|
110
|
-
readonly '12': number; // base * 1.5
|
|
111
|
-
readonly '16': number; // base * 2
|
|
112
|
-
readonly '20': number; // base * 2.5
|
|
113
|
-
readonly '24': number; // base * 3
|
|
114
|
-
readonly '32': number; // base * 4
|
|
115
|
-
readonly '40': number; // base * 5
|
|
116
|
-
readonly '48': number; // base * 6
|
|
117
|
-
};
|
|
118
|
-
readonly radius: {
|
|
119
|
-
readonly none: number; // 0
|
|
120
|
-
readonly sm: number; // 4 * multiplier
|
|
121
|
-
readonly md: number; // 6 * multiplier
|
|
122
|
-
readonly lg: number; // 8 * multiplier
|
|
123
|
-
readonly xl: number; // 12 * multiplier
|
|
124
|
-
readonly pill: 999; // Fixed large value
|
|
125
|
-
};
|
|
126
|
-
readonly typography: {
|
|
127
|
-
readonly fonts: {
|
|
128
|
-
readonly main: FontSlot; // Default/body font (fallback for all scopes)
|
|
129
|
-
readonly display: FontSlot; // Headlines, titles, large display text
|
|
130
|
-
readonly mono: FontSlot; // Code snippets, technical content
|
|
131
|
-
readonly currency: FontSlot; // Monetary amounts, financial data
|
|
132
|
-
};
|
|
133
|
-
/** Primitive font size scale — numbered tokens ('01'-'16'), values in px. */
|
|
134
|
-
readonly fontSizes: FontSizeScale;
|
|
135
|
-
/** Primitive line height scale — numbered tokens ('01'-'16'), values in px. */
|
|
136
|
-
readonly lineHeights: LineHeightScale;
|
|
137
|
-
/** Role mapping — pairs fontSizes and lineHeights for each role x size combination. */
|
|
138
|
-
readonly roles: RoleScales;
|
|
139
|
-
/**
|
|
140
|
-
* Per-font avgCharWidth ratios, keyed by font family name.
|
|
141
|
-
* Measured with canvas at editor publish time (Layer 1 calibration).
|
|
142
|
-
* Used by resolveResponsiveSize to improve line break estimation accuracy.
|
|
143
|
-
* Falls back to 0.55 when absent.
|
|
144
|
-
*/
|
|
145
|
-
readonly calibrations?: Record<string, number>;
|
|
146
|
-
/**
|
|
147
|
-
* Pre-computed role scales for each viewport breakpoint (sm/md/lg).
|
|
148
|
-
* Enables CSS-only consumers to generate @media rules from the theme JSON
|
|
149
|
-
* without importing newtone-fonts. Runtime components use useBreakpoint
|
|
150
|
-
* instead and compute scaling on the fly.
|
|
151
|
-
*/
|
|
152
|
-
readonly breakpointRoles?: BreakpointRoleScales;
|
|
153
|
-
/**
|
|
154
|
-
* Per-font OpenType-derived metrics, keyed by font family name.
|
|
155
|
-
* Extracted from font binaries at upload time, looked up at publish time.
|
|
156
|
-
* Used by Text for line height correction (adjustLineHeight),
|
|
157
|
-
* vertical centering (verticalCenterOffset), and font feature validation.
|
|
158
|
-
* Falls back to Inter-equivalent defaults when absent.
|
|
159
|
-
*/
|
|
160
|
-
readonly fontMetrics?: Readonly<Record<string, FontRuntimeMetrics>>;
|
|
161
|
-
/**
|
|
162
|
-
* Per-role font weight (CSS font-weight 100–900).
|
|
163
|
-
* Primary weight path — the Text component uses these values directly.
|
|
164
|
-
* When populated via toThemeConfig, defaults are merged with user overrides.
|
|
165
|
-
* Per-instance weight prop still takes priority.
|
|
166
|
-
*/
|
|
167
|
-
readonly roleWeights?: Partial<Record<TextRole, number>>;
|
|
168
|
-
};
|
|
169
|
-
readonly icons: {
|
|
170
|
-
readonly variant: 'outlined' | 'rounded' | 'sharp';
|
|
171
|
-
readonly weight: 100 | 200 | 300 | 400 | 500 | 600 | 700;
|
|
172
|
-
readonly autoGrade: boolean; // true = mode-aware grade (light=-25, dark=200)
|
|
173
|
-
};
|
|
174
|
-
readonly tokenOverrides?: TokenOverrides;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Current theme context state
|
|
179
|
-
*/
|
|
180
|
-
export interface NewtoneThemeContext {
|
|
181
|
-
readonly config: NewtoneThemeConfig;
|
|
182
|
-
readonly mode: ColorMode;
|
|
183
|
-
readonly setMode: (mode: ColorMode) => void;
|
|
184
|
-
/**
|
|
185
|
-
* Optional URL for the typography telemetry reporting endpoint.
|
|
186
|
-
* When set, the Text component (in responsive mode) will batch and POST
|
|
187
|
-
* ObserverPayload observations to this URL for cross-site aggregation.
|
|
188
|
-
* When absent, no telemetry is sent (opt-in).
|
|
189
|
-
*/
|
|
190
|
-
readonly reportingEndpoint?: string;
|
|
191
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { useWindowDimensions } from 'react-native';
|
|
2
|
-
import { getBreakpointForWidth } from '@newtonedev/fonts';
|
|
3
|
-
import type { BreakpointKey } from '@newtonedev/fonts';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Returns the current viewport breakpoint key ('sm' | 'md' | 'lg').
|
|
7
|
-
*
|
|
8
|
-
* Uses `useWindowDimensions` from react-native (cross-platform — works
|
|
9
|
-
* on web via react-native-web) and re-evaluates on window resize.
|
|
10
|
-
*/
|
|
11
|
-
export function useBreakpoint(): BreakpointKey {
|
|
12
|
-
const { width } = useWindowDimensions();
|
|
13
|
-
return getBreakpointForWidth(width);
|
|
14
|
-
}
|