@newtonedev/components 0.1.7 → 0.1.8
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/composites/actions/Button/Button.d.ts.map +1 -1
- package/dist/composites/form-controls/Select/Select.styles.d.ts.map +1 -1
- package/dist/composites/form-controls/TextInput/TextInput.styles.d.ts.map +1 -1
- package/dist/composites/form-controls/Toggle/Toggle.styles.d.ts.map +1 -1
- package/dist/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.d.ts.map +1 -1
- package/dist/composites/range-inputs/HueSlider/HueSlider.styles.d.ts.map +1 -1
- package/dist/composites/range-inputs/Slider/Slider.styles.d.ts.map +1 -1
- package/dist/fonts/GoogleFontLoader.d.ts +5 -4
- package/dist/fonts/GoogleFontLoader.d.ts.map +1 -1
- package/dist/fonts/SelfHostedFontLoader.d.ts +14 -0
- package/dist/fonts/SelfHostedFontLoader.d.ts.map +1 -0
- package/dist/fonts/buildGoogleFontsUrl.d.ts +1 -16
- package/dist/fonts/buildGoogleFontsUrl.d.ts.map +1 -1
- package/dist/fonts/measureFont.d.ts +18 -0
- package/dist/fonts/measureFont.d.ts.map +1 -0
- package/dist/fonts/reportQueue.d.ts +7 -0
- package/dist/fonts/reportQueue.d.ts.map +1 -0
- package/dist/fonts/useLocalCalibration.d.ts +19 -0
- package/dist/fonts/useLocalCalibration.d.ts.map +1 -0
- package/dist/fonts/useTypographyCalibrations.d.ts +11 -0
- package/dist/fonts/useTypographyCalibrations.d.ts.map +1 -0
- package/dist/index.cjs +628 -422
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +567 -376
- package/dist/index.js.map +1 -1
- package/dist/primitives/Icon/Icon.types.d.ts +1 -1
- package/dist/primitives/Icon/Icon.types.d.ts.map +1 -1
- package/dist/primitives/Text/Text.d.ts +33 -8
- package/dist/primitives/Text/Text.d.ts.map +1 -1
- package/dist/primitives/Text/Text.spans.d.ts +22 -0
- package/dist/primitives/Text/Text.spans.d.ts.map +1 -0
- package/dist/primitives/Text/Text.types.d.ts +75 -27
- package/dist/primitives/Text/Text.types.d.ts.map +1 -1
- package/dist/primitives/Text/index.d.ts +23 -2
- package/dist/primitives/Text/index.d.ts.map +1 -1
- package/dist/primitives/index.d.ts +1 -1
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/registry/codegen.d.ts.map +1 -1
- package/dist/registry/registry.d.ts.map +1 -1
- package/dist/registry/types.d.ts +2 -0
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/theme/NewtoneProvider.d.ts +9 -1
- package/dist/theme/NewtoneProvider.d.ts.map +1 -1
- package/dist/theme/defaults.d.ts +1 -0
- package/dist/theme/defaults.d.ts.map +1 -1
- package/dist/theme/types.d.ts +48 -32
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/useBreakpoint.d.ts +9 -0
- package/dist/theme/useBreakpoint.d.ts.map +1 -0
- package/dist/tokens/computeTokens.d.ts +9 -22
- package/dist/tokens/computeTokens.d.ts.map +1 -1
- package/dist/tokens/types.d.ts +40 -22
- package/dist/tokens/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/composites/actions/Button/Button.styles.ts +3 -3
- package/src/composites/actions/Button/Button.tsx +3 -2
- package/src/composites/form-controls/Select/Select.styles.ts +8 -8
- package/src/composites/form-controls/Select/Select.tsx +1 -1
- package/src/composites/form-controls/Select/SelectOption.tsx +3 -3
- package/src/composites/form-controls/TextInput/TextInput.styles.ts +5 -5
- package/src/composites/form-controls/Toggle/Toggle.styles.ts +3 -3
- package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.ts +6 -6
- package/src/composites/range-inputs/HueSlider/HueSlider.styles.ts +9 -9
- package/src/composites/range-inputs/Slider/Slider.styles.ts +9 -9
- package/src/fonts/GoogleFontLoader.tsx +25 -10
- package/src/fonts/SelfHostedFontLoader.tsx +44 -0
- package/src/fonts/buildGoogleFontsUrl.ts +2 -31
- package/src/fonts/measureFont.ts +42 -0
- package/src/fonts/reportQueue.ts +54 -0
- package/src/fonts/useLocalCalibration.ts +97 -0
- package/src/fonts/useTypographyCalibrations.ts +15 -0
- package/src/index.ts +16 -7
- package/src/primitives/Frame/Frame.tsx +3 -3
- package/src/primitives/Icon/Icon.tsx +1 -1
- package/src/primitives/Icon/Icon.types.ts +1 -1
- package/src/primitives/Text/Text.spans.ts +57 -0
- package/src/primitives/Text/Text.tsx +205 -53
- package/src/primitives/Text/Text.types.ts +80 -27
- package/src/primitives/Text/index.ts +27 -3
- package/src/primitives/index.ts +3 -2
- package/src/registry/codegen.ts +1 -0
- package/src/registry/registry.ts +55 -53
- package/src/registry/types.ts +2 -0
- package/src/theme/NewtoneProvider.tsx +18 -2
- package/src/theme/defaults.ts +8 -28
- package/src/theme/types.ts +63 -33
- package/src/theme/useBreakpoint.ts +14 -0
- package/src/tokens/computeTokens.ts +23 -19
- package/src/tokens/types.ts +10 -24
- package/dist/fonts/googleFonts.d.ts +0 -20
- package/dist/fonts/googleFonts.d.ts.map +0 -1
- package/src/fonts/googleFonts.ts +0 -87
package/dist/tokens/types.d.ts
CHANGED
|
@@ -106,32 +106,50 @@ export interface ResolvedTokens {
|
|
|
106
106
|
readonly xl: number;
|
|
107
107
|
readonly pill: 999;
|
|
108
108
|
};
|
|
109
|
-
/** Typography tokens for fonts
|
|
109
|
+
/** Typography tokens for fonts (per-scope) and primitive size/lineHeight scales */
|
|
110
110
|
readonly typography: {
|
|
111
|
+
/** Per-scope font family + weight mapping */
|
|
111
112
|
readonly fonts: {
|
|
112
|
-
readonly
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
readonly main: {
|
|
114
|
+
readonly family: string;
|
|
115
|
+
readonly weights: {
|
|
116
|
+
readonly regular: number;
|
|
117
|
+
readonly medium: number;
|
|
118
|
+
readonly bold: number;
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
readonly display: {
|
|
122
|
+
readonly family: string;
|
|
123
|
+
readonly weights: {
|
|
124
|
+
readonly regular: number;
|
|
125
|
+
readonly medium: number;
|
|
126
|
+
readonly bold: number;
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
readonly mono: {
|
|
130
|
+
readonly family: string;
|
|
131
|
+
readonly weights: {
|
|
132
|
+
readonly regular: number;
|
|
133
|
+
readonly medium: number;
|
|
134
|
+
readonly bold: number;
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
readonly currency: {
|
|
138
|
+
readonly family: string;
|
|
139
|
+
readonly weights: {
|
|
140
|
+
readonly regular: number;
|
|
141
|
+
readonly medium: number;
|
|
142
|
+
readonly bold: number;
|
|
143
|
+
};
|
|
144
|
+
};
|
|
115
145
|
};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
readonly
|
|
119
|
-
readonly base: number;
|
|
120
|
-
readonly md: number;
|
|
121
|
-
readonly lg: number;
|
|
122
|
-
readonly xl: number;
|
|
123
|
-
readonly xxl: number;
|
|
146
|
+
/** Primitive font size scale — numbered tokens ('01'–'16'), values in px */
|
|
147
|
+
readonly fontSizes: {
|
|
148
|
+
readonly [key: string]: number;
|
|
124
149
|
};
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
readonly
|
|
128
|
-
readonly relaxed: number;
|
|
129
|
-
};
|
|
130
|
-
readonly weight: {
|
|
131
|
-
readonly regular: number;
|
|
132
|
-
readonly medium: number;
|
|
133
|
-
readonly semibold: number;
|
|
134
|
-
readonly bold: number;
|
|
150
|
+
/** Primitive line height scale — numbered tokens ('01'–'16'), values in px */
|
|
151
|
+
readonly lineHeights: {
|
|
152
|
+
readonly [key: string]: number;
|
|
135
153
|
};
|
|
136
154
|
};
|
|
137
155
|
/** Icon tokens for Material Symbols configuration */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tokens/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tokens/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAE5B,wGAAwG;IACxG,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,wCAAwC;IACxC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;IAChC,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC;IACjC,wGAAwG;IACxG,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAG7B,qDAAqD;IACrD,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC;IACjC,sCAAsC;IACtC,QAAQ,CAAC,kBAAkB,EAAE,WAAW,CAAC;IACzC,oCAAoC;IACpC,QAAQ,CAAC,gBAAgB,EAAE,WAAW,CAAC;IAGvC,4DAA4D;IAC5D,QAAQ,CAAC,qBAAqB,EAAE,WAAW,CAAC;IAC5C,kEAAkE;IAClE,QAAQ,CAAC,0BAA0B,EAAE,WAAW,CAAC;IACjD,2EAA2E;IAC3E,QAAQ,CAAC,2BAA2B,EAAE,WAAW,CAAC;IAGlD,yCAAyC;IACzC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,2CAA2C;IAC3C,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC;IACpC,wCAAwC;IACxC,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC;IACnC,oCAAoC;IACpC,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC;IAGnC,6BAA6B;IAC7B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAG7B,+CAA+C;IAC/C,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC;IACjC,6DAA6D;IAC7D,QAAQ,CAAC,kBAAkB,EAAE,WAAW,CAAC;IACzC,iEAAiE;IACjE,QAAQ,CAAC,gBAAgB,EAAE,WAAW,CAAC;IACvC;;;;;;;;OAQG;IACH,QAAQ,CAAC,qBAAqB,EAAE,WAAW,CAAC;IAC5C,4EAA4E;IAC5E,QAAQ,CAAC,0BAA0B,EAAE,WAAW,CAAC;IACjD,sFAAsF;IACtF,QAAQ,CAAC,2BAA2B,EAAE,WAAW,CAAC;IAClD,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,gFAAgF;IAChF,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC;IACpC,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC;IACnC,yCAAyC;IACzC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAI7B,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAE9B;;mEAE+D;IAC/D,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC;KACpB,CAAC;IACF,mFAAmF;IACnF,QAAQ,CAAC,UAAU,EAAE;QACnB,6CAA6C;QAC7C,QAAQ,CAAC,KAAK,EAAE;YACd,QAAQ,CAAC,IAAI,EAAE;gBAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAAC,QAAQ,CAAC,OAAO,EAAE;oBAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;oBAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;oBAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;iBAAE,CAAA;aAAE,CAAC;YAC5I,QAAQ,CAAC,OAAO,EAAE;gBAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAAC,QAAQ,CAAC,OAAO,EAAE;oBAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;oBAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;oBAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;iBAAE,CAAA;aAAE,CAAC;YAC/I,QAAQ,CAAC,IAAI,EAAE;gBAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAAC,QAAQ,CAAC,OAAO,EAAE;oBAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;oBAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;oBAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;iBAAE,CAAA;aAAE,CAAC;YAC5I,QAAQ,CAAC,QAAQ,EAAE;gBAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAAC,QAAQ,CAAC,OAAO,EAAE;oBAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;oBAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;oBAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;iBAAE,CAAA;aAAE,CAAC;SACjJ,CAAC;QACF,4EAA4E;QAC5E,QAAQ,CAAC,SAAS,EAAE;YAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACvD,8EAA8E;QAC9E,QAAQ,CAAC,WAAW,EAAE;YAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;KAC1D,CAAC;IACF,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;QACnD,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;QACzD,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;KAC/B,CAAC;CACH"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newtonedev/components",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "React + React Native Web component library for Newtone",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"react-native": ">=0.70.0"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
+
"@newtonedev/fonts": "file:../../../newtone-fonts",
|
|
40
41
|
"newtone": "^0.1.0",
|
|
41
42
|
"react-native-web": "^0.19.10"
|
|
42
43
|
},
|
|
@@ -184,21 +184,21 @@ function getSizeConfig(size: ButtonSize, tokens: UseTokensResult) {
|
|
|
184
184
|
padding: 8,
|
|
185
185
|
gap: tokens.spacing['08'],
|
|
186
186
|
borderRadius: 8,
|
|
187
|
-
textSize: '
|
|
187
|
+
textSize: 'md', // 16px
|
|
188
188
|
iconSize: 24,
|
|
189
189
|
},
|
|
190
190
|
md: {
|
|
191
191
|
padding: 12,
|
|
192
192
|
gap: tokens.spacing['08'],
|
|
193
193
|
borderRadius: 12,
|
|
194
|
-
textSize: '
|
|
194
|
+
textSize: 'md', // 16px
|
|
195
195
|
iconSize: 24,
|
|
196
196
|
},
|
|
197
197
|
lg: {
|
|
198
198
|
padding: 16,
|
|
199
199
|
gap: tokens.spacing['08'],
|
|
200
200
|
borderRadius: 16,
|
|
201
|
-
textSize: '
|
|
201
|
+
textSize: 'md', // 16px
|
|
202
202
|
iconSize: 24,
|
|
203
203
|
},
|
|
204
204
|
};
|
|
@@ -4,7 +4,7 @@ import type { ButtonProps } from './Button.types';
|
|
|
4
4
|
import { getButtonConfig, computeButtonPadding } from './Button.styles';
|
|
5
5
|
import { useTokens } from '../../../tokens/useTokens';
|
|
6
6
|
import { Icon } from '../../../primitives/Icon/Icon';
|
|
7
|
-
import { Text } from '../../../primitives/Text
|
|
7
|
+
import { Text } from '../../../primitives/Text';
|
|
8
8
|
import { Wrapper } from '../../../primitives/Wrapper/Wrapper';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -99,8 +99,9 @@ export function Button({
|
|
|
99
99
|
{children && (
|
|
100
100
|
// Text primitive with semantic props + color style override
|
|
101
101
|
<Text
|
|
102
|
+
role="label"
|
|
102
103
|
size={sizeTokens.textSize}
|
|
103
|
-
|
|
104
|
+
centerVertically
|
|
104
105
|
style={[
|
|
105
106
|
{ color: variantColors.textColor },
|
|
106
107
|
...(Array.isArray(textStyle) ? textStyle : textStyle ? [textStyle] : []),
|
|
@@ -9,7 +9,7 @@ export function getSelectStyles(
|
|
|
9
9
|
isOpen: boolean
|
|
10
10
|
) {
|
|
11
11
|
const isSm = size === 'sm';
|
|
12
|
-
const fontSize = isSm ? tokens.typography.
|
|
12
|
+
const fontSize = isSm ? tokens.typography.fontSizes['04'] : tokens.typography.fontSizes['05'];
|
|
13
13
|
const iconSize = fontSize + 2;
|
|
14
14
|
const iconSpace = iconSize + tokens.spacing['08'];
|
|
15
15
|
const paddingVertical = isSm ? tokens.spacing['04'] : tokens.spacing['08'];
|
|
@@ -21,9 +21,9 @@ export function getSelectStyles(
|
|
|
21
21
|
zIndex: isOpen ? 999 : 0,
|
|
22
22
|
},
|
|
23
23
|
label: {
|
|
24
|
-
fontFamily: tokens.typography.fonts.
|
|
25
|
-
fontSize: tokens.typography.
|
|
26
|
-
fontWeight: tokens.typography.
|
|
24
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
25
|
+
fontSize: tokens.typography.fontSizes['04'],
|
|
26
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
27
27
|
color: srgbToHex(tokens.textSecondary.srgb),
|
|
28
28
|
},
|
|
29
29
|
trigger: {
|
|
@@ -40,7 +40,7 @@ export function getSelectStyles(
|
|
|
40
40
|
},
|
|
41
41
|
triggerText: {
|
|
42
42
|
flex: 1,
|
|
43
|
-
fontFamily: tokens.typography.fonts.
|
|
43
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
44
44
|
fontSize,
|
|
45
45
|
color: disabled
|
|
46
46
|
? srgbToHex(tokens.textSecondary.srgb)
|
|
@@ -54,9 +54,9 @@ export function getSelectStyles(
|
|
|
54
54
|
justifyContent: 'center',
|
|
55
55
|
},
|
|
56
56
|
groupLabel: {
|
|
57
|
-
fontFamily: tokens.typography.fonts.
|
|
58
|
-
fontSize: tokens.typography.
|
|
59
|
-
fontWeight: tokens.typography.
|
|
57
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
58
|
+
fontSize: tokens.typography.fontSizes['01'],
|
|
59
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
60
60
|
color: srgbToHex(tokens.textSecondary.srgb),
|
|
61
61
|
textTransform: 'uppercase',
|
|
62
62
|
letterSpacing: 0.5,
|
|
@@ -86,7 +86,7 @@ export function Select({
|
|
|
86
86
|
<View style={styles.iconWrapper} pointerEvents="none">
|
|
87
87
|
<Icon
|
|
88
88
|
name={isOpen ? 'expand_less' : 'expand_more'}
|
|
89
|
-
size={size === 'sm' ? tokens.typography.
|
|
89
|
+
size={size === 'sm' ? tokens.typography.fontSizes['04'] + 2 : tokens.typography.fontSizes['05'] + 2}
|
|
90
90
|
color={iconColor}
|
|
91
91
|
/>
|
|
92
92
|
</View>
|
|
@@ -26,7 +26,7 @@ export function SelectOptionRow({
|
|
|
26
26
|
|
|
27
27
|
const paddingVertical = size === 'sm' ? tokens.spacing['04'] : tokens.spacing['08'];
|
|
28
28
|
const paddingHorizontal = size === 'sm' ? tokens.spacing['08'] : tokens.spacing['12'];
|
|
29
|
-
const fontSize = size === 'sm' ? tokens.typography.
|
|
29
|
+
const fontSize = size === 'sm' ? tokens.typography.fontSizes['04'] : tokens.typography.fontSizes['05'];
|
|
30
30
|
|
|
31
31
|
if (renderOption) {
|
|
32
32
|
return (
|
|
@@ -74,12 +74,12 @@ export function SelectOptionRow({
|
|
|
74
74
|
style={[
|
|
75
75
|
{
|
|
76
76
|
flex: 1,
|
|
77
|
-
fontFamily: tokens.typography.fonts.
|
|
77
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
78
78
|
fontSize,
|
|
79
79
|
color: srgbToHex(tokens.textPrimary.srgb),
|
|
80
80
|
},
|
|
81
81
|
isSelected && {
|
|
82
|
-
fontWeight: tokens.typography.
|
|
82
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
83
83
|
color: srgbToHex(tokens.accent.fill.srgb),
|
|
84
84
|
},
|
|
85
85
|
option.disabled && {
|
|
@@ -8,20 +8,20 @@ export function getTextInputStyles(tokens: ResolvedTokens, disabled: boolean) {
|
|
|
8
8
|
gap: tokens.spacing['04'],
|
|
9
9
|
},
|
|
10
10
|
label: {
|
|
11
|
-
fontFamily: tokens.typography.fonts.
|
|
12
|
-
fontSize: tokens.typography.
|
|
13
|
-
fontWeight: tokens.typography.
|
|
11
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
12
|
+
fontSize: tokens.typography.fontSizes['04'],
|
|
13
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
14
14
|
color: srgbToHex(tokens.textSecondary.srgb),
|
|
15
15
|
},
|
|
16
16
|
input: {
|
|
17
|
-
fontFamily: tokens.typography.fonts.
|
|
17
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
18
18
|
backgroundColor: srgbToHex(tokens.backgroundSunken.srgb),
|
|
19
19
|
borderWidth: 1,
|
|
20
20
|
borderColor: srgbToHex(tokens.border.srgb),
|
|
21
21
|
borderRadius: tokens.radius.md,
|
|
22
22
|
paddingVertical: tokens.spacing['08'],
|
|
23
23
|
paddingHorizontal: tokens.spacing['12'],
|
|
24
|
-
fontSize: tokens.typography.
|
|
24
|
+
fontSize: tokens.typography.fontSizes['05'],
|
|
25
25
|
color: disabled
|
|
26
26
|
? srgbToHex(tokens.textSecondary.srgb)
|
|
27
27
|
: srgbToHex(tokens.textPrimary.srgb),
|
|
@@ -20,9 +20,9 @@ export function getToggleStyles(
|
|
|
20
20
|
opacity: disabled ? 0.5 : 1,
|
|
21
21
|
},
|
|
22
22
|
label: {
|
|
23
|
-
fontFamily: tokens.typography.fonts.
|
|
24
|
-
fontSize: tokens.typography.
|
|
25
|
-
fontWeight: tokens.typography.
|
|
23
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
24
|
+
fontSize: tokens.typography.fontSizes['04'],
|
|
25
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
26
26
|
color: srgbToHex(tokens.textSecondary.srgb),
|
|
27
27
|
},
|
|
28
28
|
track: {
|
|
@@ -17,9 +17,9 @@ export function getColorScaleSliderStyles(tokens: ResolvedTokens, disabled: bool
|
|
|
17
17
|
alignItems: 'center',
|
|
18
18
|
},
|
|
19
19
|
label: {
|
|
20
|
-
fontFamily: tokens.typography.fonts.
|
|
21
|
-
fontSize: tokens.typography.
|
|
22
|
-
fontWeight: tokens.typography.
|
|
20
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
21
|
+
fontSize: tokens.typography.fontSizes['04'],
|
|
22
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
23
23
|
color: srgbToHex(tokens.textSecondary.srgb),
|
|
24
24
|
},
|
|
25
25
|
trackContainer: {
|
|
@@ -49,9 +49,9 @@ export function getColorScaleSliderStyles(tokens: ResolvedTokens, disabled: bool
|
|
|
49
49
|
borderColor: srgbToHex(tokens.border.srgb),
|
|
50
50
|
},
|
|
51
51
|
warning: {
|
|
52
|
-
fontFamily: tokens.typography.fonts.
|
|
53
|
-
fontSize: tokens.typography.
|
|
54
|
-
fontWeight: tokens.typography.
|
|
52
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
53
|
+
fontSize: tokens.typography.fontSizes['01'],
|
|
54
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
55
55
|
color: srgbToHex(tokens.error.fill.srgb),
|
|
56
56
|
},
|
|
57
57
|
});
|
|
@@ -41,15 +41,15 @@ export function getHueSliderStyles(tokens: ResolvedTokens, disabled: boolean) {
|
|
|
41
41
|
alignItems: 'center',
|
|
42
42
|
},
|
|
43
43
|
label: {
|
|
44
|
-
fontFamily: tokens.typography.fonts.
|
|
45
|
-
fontSize: tokens.typography.
|
|
46
|
-
fontWeight: tokens.typography.
|
|
44
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
45
|
+
fontSize: tokens.typography.fontSizes['04'],
|
|
46
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
47
47
|
color: srgbToHex(tokens.textSecondary.srgb),
|
|
48
48
|
},
|
|
49
49
|
value: {
|
|
50
|
-
fontFamily: tokens.typography.fonts.
|
|
51
|
-
fontSize: tokens.typography.
|
|
52
|
-
fontWeight: tokens.typography.
|
|
50
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
51
|
+
fontSize: tokens.typography.fontSizes['04'],
|
|
52
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
53
53
|
color: srgbToHex(tokens.textPrimary.srgb),
|
|
54
54
|
},
|
|
55
55
|
valueInput: {
|
|
@@ -61,9 +61,9 @@ export function getHueSliderStyles(tokens: ResolvedTokens, disabled: boolean) {
|
|
|
61
61
|
borderRadius: 4,
|
|
62
62
|
backgroundColor: 'transparent',
|
|
63
63
|
color: srgbToHex(tokens.textPrimary.srgb),
|
|
64
|
-
fontFamily: tokens.typography.fonts.
|
|
65
|
-
fontSize: tokens.typography.
|
|
66
|
-
fontWeight: tokens.typography.
|
|
64
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
65
|
+
fontSize: tokens.typography.fontSizes['04'],
|
|
66
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
67
67
|
textAlign: 'right',
|
|
68
68
|
},
|
|
69
69
|
trackContainer: {
|
|
@@ -17,15 +17,15 @@ export function getSliderStyles(tokens: ResolvedTokens, disabled: boolean) {
|
|
|
17
17
|
alignItems: 'center',
|
|
18
18
|
},
|
|
19
19
|
label: {
|
|
20
|
-
fontFamily: tokens.typography.fonts.
|
|
21
|
-
fontSize: tokens.typography.
|
|
22
|
-
fontWeight: tokens.typography.
|
|
20
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
21
|
+
fontSize: tokens.typography.fontSizes['04'],
|
|
22
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
23
23
|
color: srgbToHex(tokens.textSecondary.srgb),
|
|
24
24
|
},
|
|
25
25
|
value: {
|
|
26
|
-
fontFamily: tokens.typography.fonts.
|
|
27
|
-
fontSize: tokens.typography.
|
|
28
|
-
fontWeight: tokens.typography.
|
|
26
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
27
|
+
fontSize: tokens.typography.fontSizes['04'],
|
|
28
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
29
29
|
color: srgbToHex(tokens.textPrimary.srgb),
|
|
30
30
|
},
|
|
31
31
|
valueInput: {
|
|
@@ -37,9 +37,9 @@ export function getSliderStyles(tokens: ResolvedTokens, disabled: boolean) {
|
|
|
37
37
|
borderRadius: 4,
|
|
38
38
|
backgroundColor: 'transparent',
|
|
39
39
|
color: srgbToHex(tokens.textPrimary.srgb),
|
|
40
|
-
fontFamily: tokens.typography.fonts.
|
|
41
|
-
fontSize: tokens.typography.
|
|
42
|
-
fontWeight: tokens.typography.
|
|
40
|
+
fontFamily: tokens.typography.fonts.main.family,
|
|
41
|
+
fontSize: tokens.typography.fontSizes['04'],
|
|
42
|
+
fontWeight: tokens.typography.fonts.main.weights.medium as any,
|
|
43
43
|
textAlign: 'right',
|
|
44
44
|
},
|
|
45
45
|
trackContainer: {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { FontSlot } from '../theme/types';
|
|
3
3
|
import { buildGoogleFontsUrl } from './buildGoogleFontsUrl';
|
|
4
4
|
|
|
5
5
|
interface GoogleFontLoaderProps {
|
|
6
6
|
readonly fonts: {
|
|
7
|
-
readonly
|
|
8
|
-
readonly display:
|
|
9
|
-
readonly
|
|
7
|
+
readonly main: FontSlot;
|
|
8
|
+
readonly display: FontSlot;
|
|
9
|
+
readonly mono: FontSlot;
|
|
10
|
+
readonly currency: FontSlot;
|
|
10
11
|
};
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -53,12 +54,26 @@ export function GoogleFontLoader({ fonts }: GoogleFontLoaderProps) {
|
|
|
53
54
|
}
|
|
54
55
|
};
|
|
55
56
|
}, [
|
|
56
|
-
fonts.
|
|
57
|
-
fonts.
|
|
58
|
-
fonts.
|
|
59
|
-
fonts.
|
|
60
|
-
fonts.
|
|
61
|
-
fonts.
|
|
57
|
+
fonts.main.config.family,
|
|
58
|
+
fonts.main.config.type,
|
|
59
|
+
fonts.main.weights.regular,
|
|
60
|
+
fonts.main.weights.medium,
|
|
61
|
+
fonts.main.weights.bold,
|
|
62
|
+
fonts.display.config.family,
|
|
63
|
+
fonts.display.config.type,
|
|
64
|
+
fonts.display.weights.regular,
|
|
65
|
+
fonts.display.weights.medium,
|
|
66
|
+
fonts.display.weights.bold,
|
|
67
|
+
fonts.mono.config.family,
|
|
68
|
+
fonts.mono.config.type,
|
|
69
|
+
fonts.mono.weights.regular,
|
|
70
|
+
fonts.mono.weights.medium,
|
|
71
|
+
fonts.mono.weights.bold,
|
|
72
|
+
fonts.currency.config.family,
|
|
73
|
+
fonts.currency.config.type,
|
|
74
|
+
fonts.currency.weights.regular,
|
|
75
|
+
fonts.currency.weights.medium,
|
|
76
|
+
fonts.currency.weights.bold,
|
|
62
77
|
]);
|
|
63
78
|
|
|
64
79
|
return null;
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
}
|
|
@@ -1,31 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Build a Google Fonts CSS API v2 URL for all google-type fonts in the config.
|
|
5
|
-
* Returns null if no Google fonts are present.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```
|
|
9
|
-
* buildGoogleFontsUrl(fonts)
|
|
10
|
-
* // => "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code:wght@400;500;600;700&display=swap"
|
|
11
|
-
* ```
|
|
12
|
-
*/
|
|
13
|
-
export function buildGoogleFontsUrl(
|
|
14
|
-
fonts: { readonly mono: FontConfig; readonly display: FontConfig; readonly default: FontConfig },
|
|
15
|
-
): string | null {
|
|
16
|
-
const googleFonts = [fonts.mono, fonts.display, fonts.default].filter(
|
|
17
|
-
(f) => f.type === 'google',
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
if (googleFonts.length === 0) return null;
|
|
21
|
-
|
|
22
|
-
// Deduplicate by family name
|
|
23
|
-
const unique = [...new Map(googleFonts.map((f) => [f.family, f])).values()];
|
|
24
|
-
|
|
25
|
-
const families = unique.map((f) => {
|
|
26
|
-
const encoded = f.family.replace(/ /g, '+');
|
|
27
|
-
return `family=${encoded}:wght@400;500;600;700`;
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
return `https://fonts.googleapis.com/css2?${families.join('&')}&display=swap`;
|
|
31
|
-
}
|
|
1
|
+
// Re-export from @newtonedev/fonts (canonical source)
|
|
2
|
+
export { buildGoogleFontsUrl } from '@newtonedev/fonts';
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Measure the average character width ratio for a font using the Canvas API.
|
|
10
|
+
*
|
|
11
|
+
* Waits for the font to load via `document.fonts.load()` before measuring.
|
|
12
|
+
* Falls back to 0.55 if the browser context is unavailable, the font fails
|
|
13
|
+
* to load, or canvas is not supported.
|
|
14
|
+
*
|
|
15
|
+
* The returned ratio represents `avgCharWidth / fontSize`. Multiply by a
|
|
16
|
+
* desired fontSize to estimate character widths for that font.
|
|
17
|
+
*
|
|
18
|
+
* @param fontFamily - Font family name, e.g. "Inter" or "system-ui"
|
|
19
|
+
* @param fontWeight - CSS font-weight number, e.g. 400
|
|
20
|
+
* @param fallback - CSS fallback stack, e.g. "sans-serif"
|
|
21
|
+
* @param fontSize - Reference font size in px (default 16)
|
|
22
|
+
* @returns Average character width ratio, e.g. 0.52 for Inter
|
|
23
|
+
*/
|
|
24
|
+
export async function measureAvgCharWidth(
|
|
25
|
+
fontFamily: string,
|
|
26
|
+
fontWeight: number,
|
|
27
|
+
fallback: string,
|
|
28
|
+
fontSize = 16,
|
|
29
|
+
): Promise<number> {
|
|
30
|
+
if (typeof document === 'undefined') return 0.55;
|
|
31
|
+
try {
|
|
32
|
+
await document.fonts.load(`${fontWeight} ${fontSize}px "${fontFamily}"`);
|
|
33
|
+
const canvas = document.createElement('canvas');
|
|
34
|
+
const ctx = canvas.getContext('2d');
|
|
35
|
+
if (!ctx) return 0.55;
|
|
36
|
+
ctx.font = `${fontWeight} ${fontSize}px "${fontFamily}", ${fallback}`;
|
|
37
|
+
const ratio = ctx.measureText(REF_STRING).width / REF_STRING.length / fontSize;
|
|
38
|
+
return Math.round(ratio * 1000) / 1000;
|
|
39
|
+
} catch {
|
|
40
|
+
return 0.55;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
}
|