@newtonedev/components 0.1.13 → 0.1.14
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 +1 -1
- package/dist/composites/actions/Button/Button.d.ts +11 -1
- package/dist/composites/actions/Button/Button.d.ts.map +1 -1
- package/dist/composites/actions/Button/Button.styles.d.ts.map +1 -1
- package/dist/composites/actions/Button/Button.types.d.ts +11 -1
- package/dist/composites/actions/Button/Button.types.d.ts.map +1 -1
- package/dist/composites/branding/LogoMonogram/LogoMonogram.d.ts +10 -0
- package/dist/composites/branding/LogoMonogram/LogoMonogram.d.ts.map +1 -0
- package/dist/composites/branding/LogoMonogram/LogoMonogram.types.d.ts +35 -0
- package/dist/composites/branding/LogoMonogram/LogoMonogram.types.d.ts.map +1 -0
- package/dist/composites/branding/LogoMonogram/index.d.ts +3 -0
- package/dist/composites/branding/LogoMonogram/index.d.ts.map +1 -0
- package/dist/composites/branding/LogoWordmark/LogoWordmark.d.ts +9 -0
- package/dist/composites/branding/LogoWordmark/LogoWordmark.d.ts.map +1 -0
- package/dist/composites/branding/LogoWordmark/LogoWordmark.types.d.ts +26 -0
- package/dist/composites/branding/LogoWordmark/LogoWordmark.types.d.ts.map +1 -0
- package/dist/composites/branding/LogoWordmark/index.d.ts +3 -0
- package/dist/composites/branding/LogoWordmark/index.d.ts.map +1 -0
- package/dist/composites/display/Chip/Chip.d.ts +25 -0
- package/dist/composites/display/Chip/Chip.d.ts.map +1 -0
- package/dist/composites/display/Chip/Chip.styles.d.ts +29 -0
- package/dist/composites/display/Chip/Chip.styles.d.ts.map +1 -0
- package/dist/composites/display/Chip/Chip.types.d.ts +63 -0
- package/dist/composites/display/Chip/Chip.types.d.ts.map +1 -0
- package/dist/composites/display/Chip/index.d.ts +3 -0
- package/dist/composites/display/Chip/index.d.ts.map +1 -0
- 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.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.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.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/ContentCard/ContentCard.d.ts +33 -0
- package/dist/composites/layout/ContentCard/ContentCard.d.ts.map +1 -0
- package/dist/composites/layout/ContentCard/ContentCard.styles.d.ts +10 -0
- package/dist/composites/layout/ContentCard/ContentCard.styles.d.ts.map +1 -0
- package/dist/composites/layout/ContentCard/ContentCard.types.d.ts +52 -0
- package/dist/composites/layout/ContentCard/ContentCard.types.d.ts.map +1 -0
- package/dist/composites/layout/ContentCard/index.d.ts +3 -0
- package/dist/composites/layout/ContentCard/index.d.ts.map +1 -0
- package/dist/composites/layout/Divider/Divider.d.ts +25 -0
- package/dist/composites/layout/Divider/Divider.d.ts.map +1 -0
- package/dist/composites/layout/Divider/Divider.styles.d.ts +8 -0
- package/dist/composites/layout/Divider/Divider.styles.d.ts.map +1 -0
- package/dist/composites/layout/Divider/Divider.types.d.ts +25 -0
- package/dist/composites/layout/Divider/Divider.types.d.ts.map +1 -0
- package/dist/composites/layout/Divider/index.d.ts +3 -0
- package/dist/composites/layout/Divider/index.d.ts.map +1 -0
- package/dist/composites/layout/Navbar/Navbar.d.ts.map +1 -1
- package/dist/composites/layout/Navbar/Navbar.styles.d.ts +4 -3
- package/dist/composites/layout/Navbar/Navbar.styles.d.ts.map +1 -1
- package/dist/composites/layout/Sidebar/Sidebar.d.ts.map +1 -1
- package/dist/composites/layout/Sidebar/Sidebar.styles.d.ts +4 -3
- package/dist/composites/layout/Sidebar/Sidebar.styles.d.ts.map +1 -1
- package/dist/composites/overlays/Popover/Popover.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.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/HueSlider/HueSlider.styles.d.ts +2 -2
- package/dist/composites/range-inputs/HueSlider/HueSlider.styles.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 +903 -396
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +830 -344
- package/dist/index.js.map +1 -1
- package/dist/primitives/Frame/Frame.d.ts +1 -35
- package/dist/primitives/Frame/Frame.d.ts.map +1 -1
- package/dist/primitives/Frame/Frame.styles.d.ts +13 -4
- package/dist/primitives/Frame/Frame.styles.d.ts.map +1 -1
- package/dist/primitives/Frame/Frame.types.d.ts +99 -1
- package/dist/primitives/Frame/Frame.types.d.ts.map +1 -1
- package/dist/primitives/Frame/index.d.ts +1 -1
- package/dist/primitives/Frame/index.d.ts.map +1 -1
- package/dist/primitives/Icon/Icon.d.ts.map +1 -1
- package/dist/primitives/Icon/Icon.types.d.ts +2 -2
- package/dist/primitives/Icon/Icon.types.d.ts.map +1 -1
- package/dist/primitives/Text/Text.d.ts +5 -3
- package/dist/primitives/Text/Text.d.ts.map +1 -1
- package/dist/primitives/Text/Text.spans.d.ts.map +1 -1
- package/dist/primitives/Text/Text.types.d.ts +3 -6
- package/dist/primitives/Text/Text.types.d.ts.map +1 -1
- package/dist/primitives/Wrapper/Wrapper.d.ts +1 -1
- package/dist/primitives/Wrapper/Wrapper.d.ts.map +1 -1
- package/dist/primitives/Wrapper/Wrapper.styles.d.ts +9 -1
- package/dist/primitives/Wrapper/Wrapper.styles.d.ts.map +1 -1
- package/dist/primitives/Wrapper/Wrapper.types.d.ts +31 -1
- package/dist/primitives/Wrapper/Wrapper.types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/composites/actions/Button/Button.styles.ts +71 -55
- package/src/composites/actions/Button/Button.tsx +34 -13
- package/src/composites/actions/Button/Button.types.ts +13 -1
- package/src/composites/branding/LogoMonogram/LogoMonogram.tsx +29 -0
- package/src/composites/branding/LogoMonogram/LogoMonogram.types.ts +35 -0
- package/src/composites/branding/LogoMonogram/index.ts +2 -0
- package/src/composites/branding/LogoWordmark/LogoWordmark.tsx +29 -0
- package/src/composites/branding/LogoWordmark/LogoWordmark.types.ts +25 -0
- package/src/composites/branding/LogoWordmark/index.ts +2 -0
- package/src/composites/display/Chip/Chip.styles.ts +189 -0
- package/src/composites/display/Chip/Chip.tsx +97 -0
- package/src/composites/display/Chip/Chip.types.ts +74 -0
- package/src/composites/display/Chip/index.ts +2 -0
- package/src/composites/form-controls/Select/Select.styles.ts +10 -10
- package/src/composites/form-controls/Select/Select.tsx +9 -6
- package/src/composites/form-controls/Select/SelectOption.tsx +10 -7
- package/src/composites/form-controls/TextInput/TextInput.styles.ts +12 -8
- package/src/composites/form-controls/TextInput/TextInput.tsx +7 -4
- package/src/composites/form-controls/Toggle/Toggle.styles.ts +10 -7
- package/src/composites/form-controls/Toggle/Toggle.tsx +4 -3
- package/src/composites/layout/AppShell/AppShell.styles.ts +8 -3
- package/src/composites/layout/AppShell/AppShell.tsx +6 -2
- package/src/composites/layout/Card/Card.styles.ts +10 -4
- package/src/composites/layout/Card/Card.tsx +4 -3
- package/src/composites/layout/ContentCard/ContentCard.styles.ts +44 -0
- package/src/composites/layout/ContentCard/ContentCard.tsx +71 -0
- package/src/composites/layout/ContentCard/ContentCard.types.ts +60 -0
- package/src/composites/layout/ContentCard/index.ts +2 -0
- package/src/composites/layout/Divider/Divider.styles.ts +30 -0
- package/src/composites/layout/Divider/Divider.tsx +46 -0
- package/src/composites/layout/Divider/Divider.types.ts +28 -0
- package/src/composites/layout/Divider/index.ts +2 -0
- package/src/composites/layout/Navbar/Navbar.styles.ts +7 -5
- package/src/composites/layout/Navbar/Navbar.tsx +4 -3
- package/src/composites/layout/Sidebar/Sidebar.styles.ts +7 -5
- package/src/composites/layout/Sidebar/Sidebar.tsx +4 -3
- package/src/composites/overlays/Popover/Popover.styles.ts +7 -5
- package/src/composites/overlays/Popover/Popover.tsx +4 -3
- package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.styles.ts +5 -5
- package/src/composites/range-inputs/ColorScaleSlider/ColorScaleSlider.tsx +4 -3
- package/src/composites/range-inputs/HueSlider/HueSlider.styles.ts +7 -7
- package/src/composites/range-inputs/HueSlider/HueSlider.tsx +1 -1
- package/src/composites/range-inputs/Slider/Slider.styles.ts +9 -9
- package/src/composites/range-inputs/Slider/Slider.tsx +1 -1
- package/src/index.ts +43 -9
- package/src/primitives/Frame/Frame.styles.ts +55 -12
- package/src/primitives/Frame/Frame.tsx +138 -140
- package/src/primitives/Frame/Frame.types.ts +119 -1
- package/src/primitives/Frame/index.ts +4 -0
- package/src/primitives/Icon/Icon.tsx +9 -6
- package/src/primitives/Icon/Icon.types.ts +2 -2
- package/src/primitives/Text/Text.spans.ts +9 -5
- package/src/primitives/Text/Text.tsx +26 -15
- package/src/primitives/Text/Text.types.ts +3 -6
- package/src/primitives/Wrapper/Wrapper.styles.ts +32 -0
- package/src/primitives/Wrapper/Wrapper.tsx +22 -3
- package/src/primitives/Wrapper/Wrapper.types.ts +45 -0
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
export {
|
|
3
3
|
NewtoneProvider,
|
|
4
4
|
useNewtoneTheme,
|
|
5
|
+
useScheme,
|
|
5
6
|
FrameContext,
|
|
6
7
|
useFrameContext,
|
|
7
8
|
useBreakpoint,
|
|
@@ -11,18 +12,21 @@ export {
|
|
|
11
12
|
DEFAULT_ROLE_SCALES,
|
|
12
13
|
useTokens,
|
|
13
14
|
computeTokens,
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
computeSwatches,
|
|
16
|
+
computeColors,
|
|
17
|
+
resolveTheme,
|
|
18
|
+
PRIMARY_DEFAULTS,
|
|
19
|
+
SECONDARY_DEFAULTS,
|
|
20
|
+
TERTIARY_DEFAULTS,
|
|
16
21
|
SUCCESS_DEFAULTS,
|
|
17
22
|
WARNING_DEFAULTS,
|
|
18
23
|
ERROR_DEFAULTS,
|
|
24
|
+
DEFAULT_THEME_MAPPINGS,
|
|
19
25
|
buildGoogleFontsUrl,
|
|
20
26
|
measureAvgCharWidth,
|
|
21
27
|
useLocalCalibration,
|
|
22
28
|
useTypographyCalibrations,
|
|
23
29
|
enqueueObservation,
|
|
24
|
-
migrateV1ToV2,
|
|
25
|
-
isV2TokenOverrides,
|
|
26
30
|
} from 'newtone-api';
|
|
27
31
|
export type {
|
|
28
32
|
ColorMode,
|
|
@@ -36,11 +40,16 @@ export type {
|
|
|
36
40
|
FontConfig,
|
|
37
41
|
FontWeights,
|
|
38
42
|
FontSlot,
|
|
39
|
-
TokenOverrides,
|
|
40
43
|
TokenColor,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
TokenName,
|
|
45
|
+
ContrastLevel,
|
|
46
|
+
PaletteId,
|
|
47
|
+
ThemeName,
|
|
48
|
+
AppearanceName,
|
|
49
|
+
ColorRef,
|
|
50
|
+
AppearanceMapping,
|
|
51
|
+
ThemeMapping,
|
|
52
|
+
ThemeMappings,
|
|
44
53
|
FontSizeScale,
|
|
45
54
|
LineHeightScale,
|
|
46
55
|
RoleSizeStep,
|
|
@@ -53,8 +62,17 @@ export type {
|
|
|
53
62
|
FrameContextValue,
|
|
54
63
|
UseTokensResult,
|
|
55
64
|
PaletteDefaults,
|
|
65
|
+
ContrastLevelDefaults,
|
|
66
|
+
SwatchDefaults,
|
|
56
67
|
ResolvedTokens,
|
|
57
|
-
|
|
68
|
+
ResolvedSwatches,
|
|
69
|
+
PaletteColors,
|
|
70
|
+
ContrastLevelColors,
|
|
71
|
+
ElevationColors,
|
|
72
|
+
ElevationName,
|
|
73
|
+
AppearanceTokens,
|
|
74
|
+
ThemeTokens,
|
|
75
|
+
ResolvedColor,
|
|
58
76
|
DynamicRange,
|
|
59
77
|
ColorResult,
|
|
60
78
|
Srgb,
|
|
@@ -113,6 +131,15 @@ export type { ButtonProps, ButtonVariant, ButtonSemantic, ButtonSize, ButtonIcon
|
|
|
113
131
|
export { Card } from './composites/layout/Card/Card';
|
|
114
132
|
export type { CardProps } from './composites/layout/Card/Card.types';
|
|
115
133
|
|
|
134
|
+
export { Chip } from './composites/display/Chip/Chip';
|
|
135
|
+
export type { ChipProps, ChipVariant, ChipSemantic, ChipSize } from './composites/display/Chip/Chip.types';
|
|
136
|
+
|
|
137
|
+
export { Divider } from './composites/layout/Divider/Divider';
|
|
138
|
+
export type { DividerProps, DividerOrientation } from './composites/layout/Divider/Divider.types';
|
|
139
|
+
|
|
140
|
+
export { ContentCard } from './composites/layout/ContentCard/ContentCard';
|
|
141
|
+
export type { ContentCardProps, ContentCardVariant } from './composites/layout/ContentCard/ContentCard.types';
|
|
142
|
+
|
|
116
143
|
export { TextInput } from './composites/form-controls/TextInput/TextInput';
|
|
117
144
|
export type { TextInputProps } from './composites/form-controls/TextInput/TextInput.types';
|
|
118
145
|
|
|
@@ -145,6 +172,13 @@ export type { SidebarProps } from './composites/layout/Sidebar/Sidebar.types';
|
|
|
145
172
|
export { Navbar } from './composites/layout/Navbar/Navbar';
|
|
146
173
|
export type { NavbarProps } from './composites/layout/Navbar/Navbar.types';
|
|
147
174
|
|
|
175
|
+
// Branding
|
|
176
|
+
export { LogoMonogram } from './composites/branding/LogoMonogram/LogoMonogram';
|
|
177
|
+
export type { LogoMonogramProps } from './composites/branding/LogoMonogram/LogoMonogram.types';
|
|
178
|
+
|
|
179
|
+
export { LogoWordmark } from './composites/branding/LogoWordmark/LogoWordmark';
|
|
180
|
+
export type { LogoWordmarkProps } from './composites/branding/LogoWordmark/LogoWordmark.types';
|
|
181
|
+
|
|
148
182
|
// Component registry + code generation
|
|
149
183
|
export type {
|
|
150
184
|
CategoryMeta,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ViewStyle } from 'react-native';
|
|
2
2
|
import { StyleSheet } from 'react-native';
|
|
3
|
-
import type { ResolvedTokens,
|
|
4
|
-
import type { FrameElevation } from 'newtone-api';
|
|
3
|
+
import type { ResolvedTokens, AppearanceTokens } from 'newtone-api';
|
|
4
|
+
import type { FrameElevation, ThemeName, AppearanceName } from 'newtone-api';
|
|
5
5
|
import type {
|
|
6
6
|
PaddingProp,
|
|
7
7
|
GapProp,
|
|
@@ -11,6 +11,9 @@ import type {
|
|
|
11
11
|
Justification,
|
|
12
12
|
RadiusProp,
|
|
13
13
|
LayoutMode,
|
|
14
|
+
PositionType,
|
|
15
|
+
OffsetValue,
|
|
16
|
+
OverflowMode,
|
|
14
17
|
} from './Frame.types';
|
|
15
18
|
import {
|
|
16
19
|
resolvePadding,
|
|
@@ -27,9 +30,12 @@ import {
|
|
|
27
30
|
|
|
28
31
|
export interface FrameStyleInput {
|
|
29
32
|
readonly tokens: ResolvedTokens;
|
|
30
|
-
readonly gamut: ColorGamut;
|
|
31
33
|
readonly frameElevation: FrameElevation;
|
|
32
34
|
|
|
35
|
+
// Theme / Appearance
|
|
36
|
+
readonly theme?: ThemeName;
|
|
37
|
+
readonly appearance?: AppearanceName;
|
|
38
|
+
|
|
33
39
|
// Layout
|
|
34
40
|
readonly layout?: LayoutMode;
|
|
35
41
|
readonly direction?: Direction;
|
|
@@ -54,6 +60,16 @@ export interface FrameStyleInput {
|
|
|
54
60
|
readonly minHeight?: number;
|
|
55
61
|
readonly maxHeight?: number;
|
|
56
62
|
|
|
63
|
+
// Positioning
|
|
64
|
+
readonly position?: PositionType;
|
|
65
|
+
readonly top?: OffsetValue;
|
|
66
|
+
readonly right?: OffsetValue;
|
|
67
|
+
readonly bottom?: OffsetValue;
|
|
68
|
+
readonly left?: OffsetValue;
|
|
69
|
+
readonly zIndex?: number;
|
|
70
|
+
readonly overflow?: OverflowMode;
|
|
71
|
+
readonly opacity?: number;
|
|
72
|
+
|
|
57
73
|
// Appearance
|
|
58
74
|
readonly radius?: RadiusProp;
|
|
59
75
|
readonly bordered?: boolean;
|
|
@@ -87,8 +103,9 @@ export interface FrameStyles {
|
|
|
87
103
|
export function getFrameStyles(input: FrameStyleInput): FrameStyles {
|
|
88
104
|
const {
|
|
89
105
|
tokens,
|
|
90
|
-
gamut,
|
|
91
106
|
frameElevation,
|
|
107
|
+
theme = 'primary',
|
|
108
|
+
appearance = 'main',
|
|
92
109
|
layout = 'flex',
|
|
93
110
|
direction = 'vertical',
|
|
94
111
|
wrap = false,
|
|
@@ -105,6 +122,14 @@ export function getFrameStyles(input: FrameStyleInput): FrameStyles {
|
|
|
105
122
|
maxWidth,
|
|
106
123
|
minHeight,
|
|
107
124
|
maxHeight,
|
|
125
|
+
position,
|
|
126
|
+
top,
|
|
127
|
+
right,
|
|
128
|
+
bottom,
|
|
129
|
+
left,
|
|
130
|
+
zIndex,
|
|
131
|
+
overflow,
|
|
132
|
+
opacity,
|
|
108
133
|
radius,
|
|
109
134
|
bordered = false,
|
|
110
135
|
disabled = false,
|
|
@@ -116,9 +141,12 @@ export function getFrameStyles(input: FrameStyleInput): FrameStyles {
|
|
|
116
141
|
const container: Record<string, unknown> = {};
|
|
117
142
|
|
|
118
143
|
// ── Background & foreground ──
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
144
|
+
// Resolve the appearance tokens for the selected theme + appearance.
|
|
145
|
+
const appearanceTokens: AppearanceTokens = tokens.colors[theme][appearance];
|
|
146
|
+
|
|
147
|
+
// Set the surface color and default text color from the resolved appearance.
|
|
148
|
+
container.backgroundColor = appearanceTokens.background;
|
|
149
|
+
container.color = appearanceTokens.fontPrimary;
|
|
122
150
|
|
|
123
151
|
// ── Layout mode ──
|
|
124
152
|
if (layout === 'flex') {
|
|
@@ -172,6 +200,15 @@ export function getFrameStyles(input: FrameStyleInput): FrameStyles {
|
|
|
172
200
|
if (minHeight !== undefined) container.minHeight = minHeight;
|
|
173
201
|
if (maxHeight !== undefined) container.maxHeight = maxHeight;
|
|
174
202
|
|
|
203
|
+
// ── Positioning ──
|
|
204
|
+
// Set CSS position mode and offsets for absolute/fixed/sticky positioning.
|
|
205
|
+
if (position) container.position = position;
|
|
206
|
+
if (top !== undefined) container.top = top;
|
|
207
|
+
if (right !== undefined) container.right = right;
|
|
208
|
+
if (bottom !== undefined) container.bottom = bottom;
|
|
209
|
+
if (left !== undefined) container.left = left;
|
|
210
|
+
if (zIndex !== undefined) container.zIndex = zIndex;
|
|
211
|
+
|
|
175
212
|
// ── Radius ──
|
|
176
213
|
// Round the corners of the Frame (e.g. for cards, buttons, pills).
|
|
177
214
|
if (radius !== undefined) {
|
|
@@ -188,11 +225,15 @@ export function getFrameStyles(input: FrameStyleInput): FrameStyles {
|
|
|
188
225
|
}
|
|
189
226
|
}
|
|
190
227
|
|
|
228
|
+
// ── Overflow ──
|
|
229
|
+
// Explicit overflow prop overrides the auto-overflow from radius.
|
|
230
|
+
if (overflow) container.overflow = overflow;
|
|
231
|
+
|
|
191
232
|
// ── Border ──
|
|
192
233
|
// Add a thin border using the theme's border color.
|
|
193
234
|
if (bordered) {
|
|
194
235
|
container.borderWidth = 1;
|
|
195
|
-
container.borderColor =
|
|
236
|
+
container.borderColor = appearanceTokens.fontTertiary;
|
|
196
237
|
}
|
|
197
238
|
|
|
198
239
|
// ── Outer shadow (elevation 2) ──
|
|
@@ -207,9 +248,11 @@ export function getFrameStyles(input: FrameStyleInput): FrameStyles {
|
|
|
207
248
|
container.elevation = 4; // Android-specific shadow depth
|
|
208
249
|
}
|
|
209
250
|
|
|
210
|
-
// ──
|
|
211
|
-
//
|
|
212
|
-
if (
|
|
251
|
+
// ── Opacity ──
|
|
252
|
+
// Explicit opacity takes precedence over the disabled fade.
|
|
253
|
+
if (opacity !== undefined) {
|
|
254
|
+
container.opacity = opacity;
|
|
255
|
+
} else if (disabled) {
|
|
213
256
|
container.opacity = 0.5;
|
|
214
257
|
}
|
|
215
258
|
|
|
@@ -217,7 +260,7 @@ export function getFrameStyles(input: FrameStyleInput): FrameStyles {
|
|
|
217
260
|
// When the user is pressing an interactive Frame, shift the background
|
|
218
261
|
// to a darker "sunken" shade to give visual feedback.
|
|
219
262
|
const pressed = StyleSheet.create({
|
|
220
|
-
s: { backgroundColor:
|
|
263
|
+
s: { backgroundColor: appearanceTokens.fontSecondary },
|
|
221
264
|
}).s;
|
|
222
265
|
|
|
223
266
|
// ── Grid web style (web-only) ──
|
|
@@ -2,19 +2,15 @@ import React, { useMemo } from 'react';
|
|
|
2
2
|
import { View, Pressable, Text } from 'react-native';
|
|
3
3
|
import type { ViewStyle, TextStyle } from 'react-native';
|
|
4
4
|
import type { FrameProps } from './Frame.types';
|
|
5
|
-
import type { ElevationLevel, FrameElevation } from 'newtone-api';
|
|
5
|
+
import type { ElevationLevel, FrameElevation, ElevationName, ThemeName, AppearanceName } from 'newtone-api';
|
|
6
6
|
import { FrameContext, useFrameContext } from 'newtone-api';
|
|
7
|
-
import { useNewtoneTheme } from 'newtone-api';
|
|
7
|
+
import { useNewtoneTheme, _ThemeContext } from 'newtone-api';
|
|
8
8
|
import { computeTokens } from 'newtone-api';
|
|
9
9
|
import { getFrameStyles } from './Frame.styles';
|
|
10
10
|
import { useFocusVisible } from '../useFocusVisible';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Wrap raw string/number children in <Text> so they display correctly.
|
|
14
|
-
*
|
|
15
|
-
* In React Native, raw text like <View>"hello"</View> will crash on native
|
|
16
|
-
* and show console warnings on web. All text must be inside a <Text> element.
|
|
17
|
-
* This helper scans children and auto-wraps any bare strings or numbers.
|
|
18
14
|
*/
|
|
19
15
|
function wrapTextChildren(
|
|
20
16
|
children: React.ReactNode,
|
|
@@ -28,16 +24,15 @@ function wrapTextChildren(
|
|
|
28
24
|
});
|
|
29
25
|
}
|
|
30
26
|
|
|
27
|
+
/** Map numeric elevation levels to named elevations for the swatches layer. */
|
|
28
|
+
const ELEVATION_MAP: Record<ElevationLevel, ElevationName> = {
|
|
29
|
+
0: 'sunken',
|
|
30
|
+
1: 'grounded',
|
|
31
|
+
2: 'elevated',
|
|
32
|
+
};
|
|
33
|
+
|
|
31
34
|
/**
|
|
32
35
|
* Convert user-facing FrameElevation (-2..2) to internal ElevationLevel (0..2).
|
|
33
|
-
*
|
|
34
|
-
* | Frame | Internal | Background |
|
|
35
|
-
* |:-----:|:--------:|:-----------|
|
|
36
|
-
* | -2 | 0 | Sunken |
|
|
37
|
-
* | -1 | 0 | Sunken |
|
|
38
|
-
* | 0 | 1 | Default |
|
|
39
|
-
* | 1 | 2 | Elevated |
|
|
40
|
-
* | 2 | 2 | Elevated |
|
|
41
36
|
*/
|
|
42
37
|
function toElevationLevel(frameElevation: FrameElevation): ElevationLevel {
|
|
43
38
|
if (frameElevation <= -1) return 0;
|
|
@@ -53,45 +48,16 @@ function toElevationLevel(frameElevation: FrameElevation): ElevationLevel {
|
|
|
53
48
|
* and interactivity capabilities.
|
|
54
49
|
*
|
|
55
50
|
* Frames can be nested. Inner frames override outer frames for their subtree.
|
|
56
|
-
* Unspecified props inherit from the nearest parent Frame.
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```tsx
|
|
60
|
-
* // Basic layout
|
|
61
|
-
* <Frame direction="horizontal" gap="md" padding="lg" align="center">
|
|
62
|
-
* <Button onPress={() => {}}>Save</Button>
|
|
63
|
-
* <Button variant="tertiary" onPress={() => {}}>Cancel</Button>
|
|
64
|
-
* </Frame>
|
|
65
|
-
* ```
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```tsx
|
|
69
|
-
* // Card-like frame
|
|
70
|
-
* <Frame
|
|
71
|
-
* elevation={2}
|
|
72
|
-
* radius="lg"
|
|
73
|
-
* padding="xl"
|
|
74
|
-
* bordered
|
|
75
|
-
* onPress={() => navigate('/details')}
|
|
76
|
-
* >
|
|
77
|
-
* <Text>Clickable card</Text>
|
|
78
|
-
* </Frame>
|
|
79
|
-
* ```
|
|
80
|
-
*
|
|
81
|
-
* @example
|
|
82
|
-
* ```tsx
|
|
83
|
-
* // Grid layout (web)
|
|
84
|
-
* <Frame layout="grid" columns={3} gap="md" padding="lg">
|
|
85
|
-
* <Frame radius="md" padding="md" bordered>Cell 1</Frame>
|
|
86
|
-
* <Frame radius="md" padding="md" bordered>Cell 2</Frame>
|
|
87
|
-
* <Frame radius="md" padding="md" bordered>Cell 3</Frame>
|
|
88
|
-
* </Frame>
|
|
89
|
-
* ```
|
|
90
51
|
*/
|
|
91
52
|
export function Frame({
|
|
92
53
|
children,
|
|
93
54
|
// Elevation
|
|
94
55
|
elevation,
|
|
56
|
+
// Scheme
|
|
57
|
+
scheme,
|
|
58
|
+
// Theme / Appearance
|
|
59
|
+
theme,
|
|
60
|
+
appearance,
|
|
95
61
|
// Layout
|
|
96
62
|
layout,
|
|
97
63
|
direction,
|
|
@@ -112,6 +78,16 @@ export function Frame({
|
|
|
112
78
|
maxWidth,
|
|
113
79
|
minHeight,
|
|
114
80
|
maxHeight,
|
|
81
|
+
// Positioning
|
|
82
|
+
position,
|
|
83
|
+
top,
|
|
84
|
+
right,
|
|
85
|
+
bottom,
|
|
86
|
+
left,
|
|
87
|
+
zIndex,
|
|
88
|
+
overflow,
|
|
89
|
+
pointerEvents,
|
|
90
|
+
opacity,
|
|
115
91
|
// Appearance
|
|
116
92
|
radius,
|
|
117
93
|
bordered,
|
|
@@ -129,45 +105,54 @@ export function Frame({
|
|
|
129
105
|
// Style override
|
|
130
106
|
style,
|
|
131
107
|
}: FrameProps) {
|
|
132
|
-
|
|
133
|
-
const {
|
|
134
|
-
// Read the elevation from the nearest parent Frame (if nested).
|
|
108
|
+
const themeCtx = useNewtoneTheme();
|
|
109
|
+
const { mode, gamut } = themeCtx;
|
|
135
110
|
const parentFrameCtx = useFrameContext();
|
|
136
111
|
|
|
137
|
-
//
|
|
112
|
+
// Resolve which config to use: explicit scheme > inherited from parent > default.
|
|
113
|
+
const resolvedConfig = useMemo(() => {
|
|
114
|
+
if (scheme && themeCtx.schemes) {
|
|
115
|
+
const schemeConfig = themeCtx.schemes[scheme];
|
|
116
|
+
if (schemeConfig) return schemeConfig;
|
|
117
|
+
}
|
|
118
|
+
return themeCtx.config;
|
|
119
|
+
}, [scheme, themeCtx.schemes, themeCtx.config]);
|
|
120
|
+
|
|
121
|
+
// Track whether this Frame switches the scheme (needs theme context override).
|
|
122
|
+
const isSchemeOverride = resolvedConfig !== themeCtx.config;
|
|
123
|
+
|
|
124
|
+
// Resolve theme/appearance: explicit prop > parent Frame > defaults.
|
|
125
|
+
const resolvedTheme: ThemeName = theme ?? parentFrameCtx?.theme ?? 'primary';
|
|
126
|
+
const resolvedAppearance: AppearanceName = appearance ?? parentFrameCtx?.appearance ?? 'main';
|
|
127
|
+
|
|
138
128
|
const resolvedFrameElevation: FrameElevation = elevation ?? 0;
|
|
139
129
|
|
|
140
|
-
// Convert user-facing elevation to internal level (0-2) for token computation.
|
|
141
|
-
// When no elevation is set, inherit from parent Frame or default to 1.
|
|
142
130
|
const resolvedElevation: ElevationLevel = elevation !== undefined
|
|
143
131
|
? toElevationLevel(elevation)
|
|
144
132
|
: parentFrameCtx?.elevation ?? 1;
|
|
145
133
|
|
|
146
|
-
//
|
|
147
|
-
// Frame computes its own tokens instead of using useTokens() because
|
|
148
|
-
// useTokens() reads from FrameContext — but this Frame IS the provider,
|
|
149
|
-
// so it needs to compute fresh tokens from the resolved theme/elevation.
|
|
150
|
-
// Wrapped in useMemo so it only recalculates when the theme/mode/elevation changes.
|
|
134
|
+
// Compute tokens with gamut pre-applied (no [gamut] indexing needed downstream).
|
|
151
135
|
const tokens = useMemo(() => {
|
|
152
136
|
return computeTokens(
|
|
153
|
-
|
|
137
|
+
resolvedConfig.colorSystem,
|
|
154
138
|
mode,
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
139
|
+
gamut,
|
|
140
|
+
ELEVATION_MAP[resolvedElevation],
|
|
141
|
+
resolvedConfig.spacing,
|
|
142
|
+
resolvedConfig.radius,
|
|
143
|
+
resolvedConfig.typography,
|
|
144
|
+
resolvedConfig.icons,
|
|
145
|
+
resolvedConfig.themeMappings,
|
|
146
|
+
resolvedConfig.swatchDefaults,
|
|
161
147
|
);
|
|
162
|
-
}, [
|
|
148
|
+
}, [resolvedConfig, mode, gamut, resolvedElevation]);
|
|
163
149
|
|
|
164
|
-
// Calculate all visual styles (background, layout, border, shadow, etc.).
|
|
165
|
-
// Only recalculates when one of the style-related props changes.
|
|
166
150
|
const styles = useMemo(
|
|
167
151
|
() => getFrameStyles({
|
|
168
152
|
tokens,
|
|
169
|
-
gamut,
|
|
170
153
|
frameElevation: resolvedFrameElevation,
|
|
154
|
+
theme: resolvedTheme,
|
|
155
|
+
appearance: resolvedAppearance,
|
|
171
156
|
layout,
|
|
172
157
|
direction,
|
|
173
158
|
wrap,
|
|
@@ -184,29 +169,53 @@ export function Frame({
|
|
|
184
169
|
maxWidth,
|
|
185
170
|
minHeight,
|
|
186
171
|
maxHeight,
|
|
172
|
+
position,
|
|
173
|
+
top,
|
|
174
|
+
right,
|
|
175
|
+
bottom,
|
|
176
|
+
left,
|
|
177
|
+
zIndex,
|
|
178
|
+
overflow,
|
|
179
|
+
opacity,
|
|
187
180
|
radius,
|
|
188
181
|
bordered,
|
|
189
182
|
disabled,
|
|
190
183
|
}),
|
|
191
184
|
[
|
|
192
|
-
tokens,
|
|
185
|
+
tokens, resolvedFrameElevation, resolvedTheme, resolvedAppearance,
|
|
193
186
|
layout, direction, wrap, reverse, columns, rows,
|
|
194
187
|
align, justify, padding, gap,
|
|
195
188
|
width, height, minWidth, maxWidth, minHeight, maxHeight,
|
|
189
|
+
position, top, right, bottom, left, zIndex, overflow, opacity,
|
|
196
190
|
radius, bordered, disabled,
|
|
197
191
|
],
|
|
198
192
|
);
|
|
199
193
|
|
|
200
|
-
//
|
|
201
|
-
|
|
194
|
+
// Resolved scheme name: explicit prop > inherited from parent Frame.
|
|
195
|
+
const resolvedScheme = scheme ?? parentFrameCtx?.scheme;
|
|
196
|
+
|
|
202
197
|
const contextValue = useMemo(
|
|
203
|
-
() => ({
|
|
204
|
-
|
|
198
|
+
() => ({
|
|
199
|
+
elevation: resolvedElevation,
|
|
200
|
+
tokens,
|
|
201
|
+
scheme: resolvedScheme,
|
|
202
|
+
theme: resolvedTheme,
|
|
203
|
+
appearance: resolvedAppearance,
|
|
204
|
+
}),
|
|
205
|
+
[resolvedElevation, tokens, resolvedScheme, resolvedTheme, resolvedAppearance],
|
|
205
206
|
);
|
|
206
207
|
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
|
|
208
|
+
// When this Frame switches the scheme, override ThemeContext so that
|
|
209
|
+
// useNewtoneTheme().config returns the scheme's config for descendants.
|
|
210
|
+
const schemeThemeCtx = useMemo(() => {
|
|
211
|
+
if (!isSchemeOverride) return null;
|
|
212
|
+
return {
|
|
213
|
+
...themeCtx,
|
|
214
|
+
config: resolvedConfig,
|
|
215
|
+
activeScheme: scheme ?? themeCtx.activeScheme,
|
|
216
|
+
};
|
|
217
|
+
}, [isSchemeOverride, themeCtx, resolvedConfig, scheme]);
|
|
218
|
+
|
|
210
219
|
const webOverrides: ViewStyle[] = [];
|
|
211
220
|
if (styles.gridWebStyle) {
|
|
212
221
|
webOverrides.push(styles.gridWebStyle as unknown as ViewStyle);
|
|
@@ -215,94 +224,83 @@ export function Frame({
|
|
|
215
224
|
webOverrides.push({ boxShadow: styles.insetBoxShadow } as unknown as ViewStyle);
|
|
216
225
|
}
|
|
217
226
|
|
|
218
|
-
// Normalize user's custom styles into an array for merging.
|
|
219
227
|
const userStyles = Array.isArray(style) ? style : style ? [style] : [];
|
|
220
228
|
|
|
221
|
-
|
|
222
|
-
// In that case we render a Pressable (tappable); otherwise a plain View.
|
|
229
|
+
|
|
223
230
|
const isInteractive = onPress !== undefined || href !== undefined;
|
|
224
231
|
|
|
225
|
-
// Detect keyboard-only focus (Tab, arrows) vs mouse/touch focus.
|
|
226
|
-
// Only shows a visible focus ring when the user navigated via keyboard,
|
|
227
|
-
// matching the browser's native :focus-visible behavior.
|
|
228
232
|
const { isFocusVisible, focusProps } = useFocusVisible();
|
|
229
233
|
|
|
230
|
-
// Focus ring
|
|
231
|
-
// offset by 2px so it doesn't overlap the Frame's border.
|
|
232
|
-
// Uses CSS outline properties — silently ignored on native platforms.
|
|
234
|
+
// Focus ring: uses the emphasis background of the resolved theme as the outline color.
|
|
233
235
|
const focusRingStyle = isFocusVisible && !disabled ? {
|
|
234
236
|
outlineWidth: 2,
|
|
235
237
|
outlineStyle: 'solid',
|
|
236
|
-
outlineColor: tokens.
|
|
238
|
+
outlineColor: tokens.colors[resolvedTheme].emphasis.background,
|
|
237
239
|
outlineOffset: 2,
|
|
238
|
-
} as unknown as ViewStyle : undefined;
|
|
240
|
+
} as unknown as ViewStyle : undefined;
|
|
239
241
|
|
|
240
|
-
|
|
241
|
-
// handlers supported by react-native-web — silently ignored on native.
|
|
242
|
-
const webFocusProps = isInteractive ? focusProps as any : {}; // web-only
|
|
242
|
+
const webFocusProps = isInteractive ? focusProps as any : {};
|
|
243
243
|
|
|
244
|
-
// Default text style for any raw strings/numbers passed as children.
|
|
245
|
-
// Uses the theme's primary text color, default font, and base size.
|
|
246
244
|
const textStyle = useMemo<TextStyle>(
|
|
247
245
|
() => ({
|
|
248
|
-
color: tokens.
|
|
246
|
+
color: tokens.colors[resolvedTheme][resolvedAppearance].fontPrimary,
|
|
249
247
|
fontSize: tokens.typography.fontSizes['05'],
|
|
250
248
|
fontFamily: tokens.typography.fonts.main.family,
|
|
251
249
|
lineHeight: tokens.typography.lineHeights['06'],
|
|
252
250
|
}),
|
|
253
|
-
[tokens],
|
|
251
|
+
[tokens, resolvedTheme, resolvedAppearance],
|
|
254
252
|
);
|
|
255
|
-
// Auto-wrap bare text ("hello") in <Text> elements (required by React Native).
|
|
256
|
-
// Wrapped in useMemo so it only re-scans children when they or the text style changes.
|
|
257
253
|
const wrappedChildren = useMemo(
|
|
258
254
|
() => wrapTextChildren(children, textStyle),
|
|
259
255
|
[children, textStyle],
|
|
260
256
|
);
|
|
261
257
|
|
|
262
|
-
|
|
263
|
-
|
|
258
|
+
const content = isInteractive ? (
|
|
259
|
+
<Pressable
|
|
260
|
+
ref={ref}
|
|
261
|
+
testID={testID}
|
|
262
|
+
nativeID={nativeID}
|
|
263
|
+
pointerEvents={pointerEvents}
|
|
264
|
+
accessibilityLabel={accessibilityLabel}
|
|
265
|
+
accessibilityHint={accessibilityHint}
|
|
266
|
+
accessibilityState={disabled ? { disabled: true } : undefined}
|
|
267
|
+
onPress={onPress}
|
|
268
|
+
disabled={disabled}
|
|
269
|
+
{...(href ? { href, accessibilityRole: 'link' as const } : { accessibilityRole: 'button' as const })}
|
|
270
|
+
{...webFocusProps}
|
|
271
|
+
style={({ pressed }) => [
|
|
272
|
+
styles.container,
|
|
273
|
+
pressed && !disabled && styles.pressed,
|
|
274
|
+
focusRingStyle,
|
|
275
|
+
...webOverrides,
|
|
276
|
+
...userStyles,
|
|
277
|
+
]}
|
|
278
|
+
>
|
|
279
|
+
{wrappedChildren}
|
|
280
|
+
</Pressable>
|
|
281
|
+
) : (
|
|
282
|
+
<View
|
|
283
|
+
ref={ref}
|
|
284
|
+
testID={testID}
|
|
285
|
+
nativeID={nativeID}
|
|
286
|
+
pointerEvents={pointerEvents}
|
|
287
|
+
accessibilityLabel={accessibilityLabel}
|
|
288
|
+
accessibilityHint={accessibilityHint}
|
|
289
|
+
style={[styles.container, ...webOverrides, ...userStyles]}
|
|
290
|
+
>
|
|
291
|
+
{wrappedChildren}
|
|
292
|
+
</View>
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
const wrappedContent = schemeThemeCtx ? (
|
|
296
|
+
<_ThemeContext.Provider value={schemeThemeCtx}>
|
|
297
|
+
{content}
|
|
298
|
+
</_ThemeContext.Provider>
|
|
299
|
+
) : content;
|
|
300
|
+
|
|
264
301
|
return (
|
|
265
302
|
<FrameContext.Provider value={contextValue}>
|
|
266
|
-
{
|
|
267
|
-
// Pressable handles taps. When href is set, react-native-web renders
|
|
268
|
-
// it as an <a> tag so it works like a regular link on the web.
|
|
269
|
-
<Pressable
|
|
270
|
-
ref={ref}
|
|
271
|
-
testID={testID}
|
|
272
|
-
nativeID={nativeID}
|
|
273
|
-
accessibilityLabel={accessibilityLabel}
|
|
274
|
-
accessibilityHint={accessibilityHint}
|
|
275
|
-
// Tell screen readers this is disabled so assistive technology can announce it.
|
|
276
|
-
accessibilityState={disabled ? { disabled: true } : undefined}
|
|
277
|
-
onPress={onPress}
|
|
278
|
-
disabled={disabled}
|
|
279
|
-
{...(href ? { href, accessibilityRole: 'link' as const } : { accessibilityRole: 'button' as const })}
|
|
280
|
-
{...webFocusProps}
|
|
281
|
-
// The style callback receives { pressed: true/false } so we can
|
|
282
|
-
// change the background when the user is actively pressing.
|
|
283
|
-
style={({ pressed }) => [
|
|
284
|
-
styles.container,
|
|
285
|
-
pressed && !disabled && styles.pressed,
|
|
286
|
-
focusRingStyle,
|
|
287
|
-
...webOverrides,
|
|
288
|
-
...userStyles,
|
|
289
|
-
]}
|
|
290
|
-
>
|
|
291
|
-
{wrappedChildren}
|
|
292
|
-
</Pressable>
|
|
293
|
-
) : (
|
|
294
|
-
// Non-interactive Frame: just a plain View with no tap handling.
|
|
295
|
-
<View
|
|
296
|
-
ref={ref}
|
|
297
|
-
testID={testID}
|
|
298
|
-
nativeID={nativeID}
|
|
299
|
-
accessibilityLabel={accessibilityLabel}
|
|
300
|
-
accessibilityHint={accessibilityHint}
|
|
301
|
-
style={[styles.container, ...webOverrides, ...userStyles]}
|
|
302
|
-
>
|
|
303
|
-
{wrappedChildren}
|
|
304
|
-
</View>
|
|
305
|
-
)}
|
|
303
|
+
{wrappedContent}
|
|
306
304
|
</FrameContext.Provider>
|
|
307
305
|
);
|
|
308
306
|
}
|