zyzz 0.0.22 → 0.0.23
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/babel/NativeJsx.d.ts.map +1 -1
- package/dist/babel/NativeJsx.js +14 -4
- package/dist/babel/NativeJsx.js.map +1 -1
- package/dist/compiler/Graph.js +78 -35
- package/dist/compiler/Graph.js.map +1 -1
- package/dist/compiler/internal/Dynamic.d.ts.map +1 -1
- package/dist/compiler/internal/Dynamic.js +3 -0
- package/dist/compiler/internal/Dynamic.js.map +1 -1
- package/dist/compiler/internal/Expression.js +2 -2
- package/dist/compiler/internal/Expression.js.map +1 -1
- package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
- package/dist/compiler/internal/NativeBindings.js +1 -0
- package/dist/compiler/internal/NativeBindings.js.map +1 -1
- package/dist/compiler/internal/NativeVars.d.ts.map +1 -1
- package/dist/compiler/internal/NativeVars.js +3 -0
- package/dist/compiler/internal/NativeVars.js.map +1 -1
- package/dist/compiler/internal/Themes.d.ts +2 -0
- package/dist/compiler/internal/Themes.d.ts.map +1 -1
- package/dist/compiler/internal/Themes.js +24 -22
- package/dist/compiler/internal/Themes.js.map +1 -1
- package/dist/metro/Graph.d.ts.map +1 -1
- package/dist/metro/Graph.js +11 -2
- package/dist/metro/Graph.js.map +1 -1
- package/dist/react-native/StyleSheet.d.ts +7 -3
- package/dist/react-native/StyleSheet.d.ts.map +1 -1
- package/dist/react-native/StyleSheet.js +8 -0
- package/dist/react-native/StyleSheet.js.map +1 -1
- package/dist/react-native/internal/Device.js +28 -1
- package/dist/react-native/internal/Device.js.map +2 -2
- package/dist/react-native/internal/Scalar.d.ts +16 -0
- package/dist/react-native/internal/Scalar.d.ts.map +1 -1
- package/dist/react-native/internal/Scalar.js +53 -2
- package/dist/react-native/internal/Scalar.js.map +1 -1
- package/dist/react-native/internal/Styles.d.ts +19 -0
- package/dist/react-native/internal/Styles.d.ts.map +1 -0
- package/dist/react-native/internal/Styles.js +35 -0
- package/dist/react-native/internal/Styles.js.map +1 -0
- package/dist/react-native/react.d.ts +39 -14
- package/dist/react-native/react.d.ts.map +1 -1
- package/dist/react-native/react.js +8 -39
- package/dist/react-native/react.js.map +1 -1
- package/dist/runtime/NativeDynamic.d.ts.map +1 -1
- package/dist/runtime/NativeDynamic.js +5 -0
- package/dist/runtime/NativeDynamic.js.map +1 -1
- package/package.json +1 -1
- package/src/babel/NativeJsx.ts +17 -8
- package/src/compiler/Graph.ts +89 -38
- package/src/compiler/internal/Dynamic.ts +4 -0
- package/src/compiler/internal/Expression.ts +2 -2
- package/src/compiler/internal/NativeBindings.ts +1 -0
- package/src/compiler/internal/NativeVars.ts +3 -0
- package/src/compiler/internal/Themes.ts +29 -23
- package/src/metro/Graph.ts +17 -2
- package/src/react-native/StyleSheet.ts +32 -9
- package/src/react-native/internal/Device.ts +67 -1
- package/src/react-native/internal/Scalar.ts +73 -2
- package/src/react-native/internal/Styles.ts +77 -0
- package/src/react-native/react.ts +61 -61
- package/src/runtime/NativeDynamic.ts +7 -0
|
@@ -120,6 +120,34 @@ export type VarsRead = {
|
|
|
120
120
|
readonly vars: Readonly<Record<string, Theme.Definition>>
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
/** Resolves the variable alternatives a linked configuration or variable set exposes to native hooks. */
|
|
124
|
+
export function varsRead(
|
|
125
|
+
link: Pick<Link, 'call' | 'definition' | 'members'>,
|
|
126
|
+
): Omit<VarsRead, 'end' | 'start'> {
|
|
127
|
+
const named = link.call.options?.themes !== undefined
|
|
128
|
+
const vars = (() => {
|
|
129
|
+
if (!named) return { default: link.definition }
|
|
130
|
+
return Object.fromEntries(
|
|
131
|
+
Object.entries(link.members ?? {}).flatMap((entry) => {
|
|
132
|
+
const [key, member] = entry
|
|
133
|
+
const path = JSON.parse(key) as string[]
|
|
134
|
+
if (path.length === 1 && link.call.selection)
|
|
135
|
+
return [[path[0]!, member.definition]]
|
|
136
|
+
if (path.length === 2 && path[0] === 'themes')
|
|
137
|
+
return [[path[1]!, member.definition]]
|
|
138
|
+
return []
|
|
139
|
+
}),
|
|
140
|
+
)
|
|
141
|
+
})()
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
defaultVars: named ? String(link.call.options!.defaultTheme) : 'default',
|
|
145
|
+
owner: link.call.name,
|
|
146
|
+
unnamed: !named,
|
|
147
|
+
vars,
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
123
151
|
/** Shared graph data; no filesystem or runtime evaluation is involved. */
|
|
124
152
|
export type Context = {
|
|
125
153
|
/** Canonical identifier names collected during graph validation. */
|
|
@@ -1796,29 +1824,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
|
|
|
1796
1824
|
)
|
|
1797
1825
|
return false
|
|
1798
1826
|
|
|
1799
|
-
|
|
1800
|
-
const vars = (() => {
|
|
1801
|
-
if (!named) return { default: link.definition }
|
|
1802
|
-
return Object.fromEntries(
|
|
1803
|
-
Object.entries(link.members ?? {}).flatMap((entry) => {
|
|
1804
|
-
const [key, member] = entry
|
|
1805
|
-
const path = JSON.parse(key) as string[]
|
|
1806
|
-
if (path.length === 1 && link.call.selection)
|
|
1807
|
-
return [[path[0]!, member.definition]]
|
|
1808
|
-
if (path.length === 2 && path[0] === 'themes')
|
|
1809
|
-
return [[path[1]!, member.definition]]
|
|
1810
|
-
return []
|
|
1811
|
-
}),
|
|
1812
|
-
)
|
|
1813
|
-
})()
|
|
1814
|
-
varsReads.push({
|
|
1815
|
-
defaultVars: named ? String(link.call.options!.defaultTheme) : 'default',
|
|
1816
|
-
end: node.end,
|
|
1817
|
-
owner: link.call.name,
|
|
1818
|
-
start: node.start,
|
|
1819
|
-
unnamed: !named,
|
|
1820
|
-
vars,
|
|
1821
|
-
})
|
|
1827
|
+
varsReads.push({ ...varsRead(link), end: node.end, start: node.start })
|
|
1822
1828
|
return true
|
|
1823
1829
|
}
|
|
1824
1830
|
|
package/src/metro/Graph.ts
CHANGED
|
@@ -148,7 +148,10 @@ export function read(
|
|
|
148
148
|
seen.add(filename)
|
|
149
149
|
const source = snapshot.readSync(filename)
|
|
150
150
|
const file = manifest(filename)
|
|
151
|
-
if (
|
|
151
|
+
if (
|
|
152
|
+
file &&
|
|
153
|
+
!/['"]zyzz(?:\/default|\/react-native(?:\/react)?)?['"]/.test(source)
|
|
154
|
+
) {
|
|
152
155
|
const data = JSON.parse(snapshot.readSync(file)) as Record<
|
|
153
156
|
string,
|
|
154
157
|
unknown
|
|
@@ -174,7 +177,19 @@ export function read(
|
|
|
174
177
|
for (const node of snapshot.parse({ moduleId: identity(filename), source })
|
|
175
178
|
.program.body) {
|
|
176
179
|
const name = specifier(node)
|
|
177
|
-
|
|
180
|
+
// Consumers import vars profiles from defineConfig modules, so those modules must compile.
|
|
181
|
+
const native =
|
|
182
|
+
(name === 'zyzz/react-native' || name === 'zyzz/react-native/react') &&
|
|
183
|
+
node.type === 'ImportDeclaration' &&
|
|
184
|
+
node.specifiers.some(
|
|
185
|
+
(entry) =>
|
|
186
|
+
entry.type === 'ImportSpecifier' &&
|
|
187
|
+
entry.importKind !== 'type' &&
|
|
188
|
+
(entry.imported.type === 'Identifier'
|
|
189
|
+
? entry.imported.name
|
|
190
|
+
: entry.imported.value) === 'defineConfig',
|
|
191
|
+
)
|
|
192
|
+
if (name === 'zyzz' || name === 'zyzz/default' || native) {
|
|
178
193
|
authored.add(filename)
|
|
179
194
|
return true
|
|
180
195
|
}
|
|
@@ -278,6 +278,14 @@ export function compile<
|
|
|
278
278
|
output[`${property}Right`] = right!
|
|
279
279
|
output[`${property}Bottom`] = bottom!
|
|
280
280
|
output[`${property}Left`] = left!
|
|
281
|
+
} else if (property === 'flex') {
|
|
282
|
+
Object.assign(output, Scalar.flex(value, options, path))
|
|
283
|
+
} else if (property === 'inset') {
|
|
284
|
+
const values =
|
|
285
|
+
typeof value === 'string' ? Calculation.parts(value) : [value]
|
|
286
|
+
const [top, right = top, bottom = top, left = right] =
|
|
287
|
+
values.map((value) => convert('offset', value, options, path))
|
|
288
|
+
Object.assign(output, { top, right, bottom, left })
|
|
281
289
|
} else if (
|
|
282
290
|
property === 'borderColor' ||
|
|
283
291
|
property === 'borderWidth'
|
|
@@ -613,15 +621,26 @@ type Length =
|
|
|
613
621
|
| `${number}px`
|
|
614
622
|
| `${number}rem`
|
|
615
623
|
| `${'c' | 'C'}${'a' | 'A'}${'l' | 'L'}${'c' | 'C'}(${string})`
|
|
616
|
-
type Box =
|
|
617
|
-
|
|
|
618
|
-
| `${
|
|
619
|
-
| `${
|
|
620
|
-
| Box4
|
|
624
|
+
type Box<item extends string | number = Length> =
|
|
625
|
+
| item
|
|
626
|
+
| `${item} ${item}`
|
|
627
|
+
| `${item} ${item} ${item}`
|
|
628
|
+
| Box4<item>
|
|
621
629
|
// Distribute the first item to keep case-insensitive calculations below the template expansion limit.
|
|
622
|
-
type Box4<
|
|
623
|
-
|
|
624
|
-
|
|
630
|
+
type Box4<
|
|
631
|
+
item extends string | number,
|
|
632
|
+
first extends string | number = item,
|
|
633
|
+
> = first extends unknown ? `${first} ${item} ${item} ${item}` : never
|
|
634
|
+
type FlexBasis = Length | `${number}%` | 'auto'
|
|
635
|
+
type Flex =
|
|
636
|
+
| number
|
|
637
|
+
| 'initial'
|
|
638
|
+
| 'none'
|
|
639
|
+
| FlexBasis
|
|
640
|
+
| `${number} ${number | FlexBasis}`
|
|
641
|
+
| `${FlexBasis} ${number}`
|
|
642
|
+
| `${number} ${number} ${FlexBasis}`
|
|
643
|
+
| `${FlexBasis} ${number} ${number}`
|
|
625
644
|
type Weight =
|
|
626
645
|
| 'bold'
|
|
627
646
|
| 'normal'
|
|
@@ -666,7 +685,11 @@ type Atom<kind> = kind extends readonly string[]
|
|
|
666
685
|
? Length | number
|
|
667
686
|
: kind extends 'box' | 'boxSigned'
|
|
668
687
|
? Box
|
|
669
|
-
:
|
|
688
|
+
: kind extends 'boxOffset'
|
|
689
|
+
? Box<Length | `${number}%`>
|
|
690
|
+
: kind extends 'flex'
|
|
691
|
+
? Flex
|
|
692
|
+
: Length
|
|
670
693
|
|
|
671
694
|
type Reference<property extends keyof typeof properties> = {
|
|
672
695
|
[group in Token.Group]: property extends Token.Properties<group>
|
|
@@ -5,6 +5,7 @@ import NativeZyzz from './NativeZyzz.js'
|
|
|
5
5
|
import * as React from 'react'
|
|
6
6
|
import * as ReactNative from 'react-native'
|
|
7
7
|
import * as Store from './Store.js'
|
|
8
|
+
import * as Styles from './Styles.js'
|
|
8
9
|
import * as Subscription from '../react.js'
|
|
9
10
|
import * as Viewport from './Viewport.js'
|
|
10
11
|
|
|
@@ -22,7 +23,72 @@ export function defineConfig<const options extends Config.create.Options = {}>(
|
|
|
22
23
|
/** Provides selected variables, appearance, and automatic native dimensions. */
|
|
23
24
|
export const Provider = dimensions(Subscription.Provider)
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Compiler-inserted subscription for resolving native style applications.
|
|
28
|
+
* Outside a Provider, selections use default variables, the light scheme, and window dimensions.
|
|
29
|
+
*/
|
|
30
|
+
export function useStyles(): Subscription.useStyles.ReturnType {
|
|
31
|
+
const owner = React.useContext(Store.context)
|
|
32
|
+
const [fallback] = React.useState(() =>
|
|
33
|
+
Store.create({
|
|
34
|
+
colorScheme: 'light',
|
|
35
|
+
viewport: ReactNative.Dimensions.get('window'),
|
|
36
|
+
}),
|
|
37
|
+
)
|
|
38
|
+
React.useLayoutEffect(() => {
|
|
39
|
+
if (owner) return
|
|
40
|
+
const update = () =>
|
|
41
|
+
fallback.update({
|
|
42
|
+
colorScheme: 'light',
|
|
43
|
+
viewport: ReactNative.Dimensions.get('window'),
|
|
44
|
+
})
|
|
45
|
+
// Dimensions can change between render and commit.
|
|
46
|
+
update()
|
|
47
|
+
const subscription = ReactNative.Dimensions.addEventListener(
|
|
48
|
+
'change',
|
|
49
|
+
update,
|
|
50
|
+
)
|
|
51
|
+
return () => subscription.remove()
|
|
52
|
+
}, [fallback, owner])
|
|
53
|
+
|
|
54
|
+
return Styles.use(owner ?? fallback)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { useVars } from '../react.js'
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolves compiled style props for function components using React updates.
|
|
61
|
+
* @param Component - Function, memo, or ref-forwarding component receiving native styles.
|
|
62
|
+
* @param options - Additional style-bearing prop names.
|
|
63
|
+
* @returns A component with the original call signature and ref. Type parameters remain when `styleProps` is omitted.
|
|
64
|
+
* @throws For invalid style prop names.
|
|
65
|
+
*/
|
|
66
|
+
export function withStyles<
|
|
67
|
+
props extends object,
|
|
68
|
+
result extends React.ReactNode,
|
|
69
|
+
>(
|
|
70
|
+
Component: (props: props) => result,
|
|
71
|
+
options?: NoInfer<Subscription.withStyles.Options<(props: props) => result>>,
|
|
72
|
+
): (props: props) => result
|
|
73
|
+
/**
|
|
74
|
+
* Resolves compiled style props for class and host components using React updates.
|
|
75
|
+
* @param Component - Class component or host element type receiving native styles.
|
|
76
|
+
* @param options - Additional style-bearing prop names.
|
|
77
|
+
* @returns A component preserving the original props and instance ref.
|
|
78
|
+
* @throws For invalid style prop names.
|
|
79
|
+
*/
|
|
80
|
+
export function withStyles<const component extends React.ElementType>(
|
|
81
|
+
Component: component,
|
|
82
|
+
options?: Subscription.withStyles.Options<component>,
|
|
83
|
+
): React.ForwardRefExoticComponent<React.ComponentPropsWithRef<component>>
|
|
84
|
+
export function withStyles(
|
|
85
|
+
Component: React.ElementType,
|
|
86
|
+
options: Subscription.withStyles.Options<
|
|
87
|
+
(props: Readonly<Record<string, unknown>>) => React.ReactNode
|
|
88
|
+
> = {},
|
|
89
|
+
): React.ElementType {
|
|
90
|
+
return Styles.wrap(Component, options, useStyles)
|
|
91
|
+
}
|
|
26
92
|
|
|
27
93
|
function dimensions<props extends Subscription.Provider.Props>(
|
|
28
94
|
Component: React.FunctionComponent<props>,
|
|
@@ -48,6 +48,7 @@ export const properties = {
|
|
|
48
48
|
columnGap: 'length',
|
|
49
49
|
direction: ['ltr', 'rtl'],
|
|
50
50
|
display: ['contents', 'flex', 'none'],
|
|
51
|
+
flex: 'flex',
|
|
51
52
|
flexBasis: 'size',
|
|
52
53
|
flexDirection: ['column', 'column-reverse', 'row', 'row-reverse'],
|
|
53
54
|
flexGrow: 'number',
|
|
@@ -60,6 +61,7 @@ export const properties = {
|
|
|
60
61
|
fontWeight: 'weight',
|
|
61
62
|
gap: 'length',
|
|
62
63
|
height: 'size',
|
|
64
|
+
inset: 'boxOffset',
|
|
63
65
|
justifyContent: [
|
|
64
66
|
'center',
|
|
65
67
|
'flex-end',
|
|
@@ -206,18 +208,87 @@ export function convert(
|
|
|
206
208
|
return value
|
|
207
209
|
fail('unsupported_value', 'Invalid native percentage.', path)
|
|
208
210
|
}
|
|
209
|
-
if (kind === '
|
|
211
|
+
if (kind === 'flex') {
|
|
212
|
+
flex(value, options, path)
|
|
213
|
+
return 0
|
|
214
|
+
}
|
|
215
|
+
if (kind === 'box' || kind === 'boxOffset' || kind === 'boxSigned') {
|
|
210
216
|
const values =
|
|
211
217
|
typeof value === 'string' ? Calculation.parts(value) : [value]
|
|
212
218
|
if (!values.length || values.length > 4)
|
|
213
219
|
fail('unsupported_value', 'Use one to four scalar lengths.', path)
|
|
214
220
|
for (const value of values)
|
|
215
|
-
|
|
221
|
+
if (kind === 'boxOffset') convert('offset', value, options, path)
|
|
222
|
+
else length(value, options, kind === 'boxSigned', path)
|
|
216
223
|
return 0
|
|
217
224
|
}
|
|
218
225
|
return length(value, options, kind === 'signed' || kind === 'offset', path)
|
|
219
226
|
}
|
|
220
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Expands the CSS `flex` shorthand to native longhands with CSS semantics.
|
|
230
|
+
* Omitted factors default to 1 and an omitted basis to 0, unlike the React Native `flex` prop.
|
|
231
|
+
*/
|
|
232
|
+
export function flex(
|
|
233
|
+
value: number | string,
|
|
234
|
+
options: Options,
|
|
235
|
+
path: readonly string[],
|
|
236
|
+
): flex.ReturnType {
|
|
237
|
+
const keyword = typeof value === 'string' ? value.trim() : undefined
|
|
238
|
+
if (keyword === 'none')
|
|
239
|
+
return { flexBasis: 'auto', flexGrow: 0, flexShrink: 0 }
|
|
240
|
+
if (keyword === 'auto')
|
|
241
|
+
return { flexBasis: 'auto', flexGrow: 1, flexShrink: 1 }
|
|
242
|
+
if (keyword === 'initial')
|
|
243
|
+
return { flexBasis: 'auto', flexGrow: 0, flexShrink: 1 }
|
|
244
|
+
|
|
245
|
+
const parts = typeof value === 'string' ? Calculation.parts(value) : [value]
|
|
246
|
+
const kinds: ('basis' | 'factor')[] = []
|
|
247
|
+
for (const part of parts) {
|
|
248
|
+
const numeric =
|
|
249
|
+
typeof part === 'number' ||
|
|
250
|
+
/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(part)
|
|
251
|
+
// A unitless zero after two factors is the basis, as in CSS.
|
|
252
|
+
const basis =
|
|
253
|
+
!numeric || (kinds.join(' ') === 'factor factor' && Number(part) === 0)
|
|
254
|
+
kinds.push(basis ? 'basis' : 'factor')
|
|
255
|
+
}
|
|
256
|
+
// Factors stay adjacent, and the basis may precede or follow them.
|
|
257
|
+
if (
|
|
258
|
+
![
|
|
259
|
+
'basis',
|
|
260
|
+
'basis factor',
|
|
261
|
+
'basis factor factor',
|
|
262
|
+
'factor',
|
|
263
|
+
'factor basis',
|
|
264
|
+
'factor factor',
|
|
265
|
+
'factor factor basis',
|
|
266
|
+
].includes(kinds.join(' '))
|
|
267
|
+
)
|
|
268
|
+
fail('unsupported_value', 'Invalid flex shorthand.', path)
|
|
269
|
+
|
|
270
|
+
const factors = parts
|
|
271
|
+
.filter((_, index) => kinds[index] === 'factor')
|
|
272
|
+
.map((part) => convert('number', Number(part), options, path) as number)
|
|
273
|
+
const basis = parts.find((_, index) => kinds[index] === 'basis')
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
flexBasis: basis === undefined ? 0 : convert('size', basis, options, path),
|
|
277
|
+
flexGrow: factors[0] ?? 1,
|
|
278
|
+
flexShrink: factors[1] ?? 1,
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Native longhands produced by the `flex` shorthand. */
|
|
283
|
+
export declare namespace flex {
|
|
284
|
+
/** Grow and shrink factors with a converted basis. */
|
|
285
|
+
type ReturnType = {
|
|
286
|
+
readonly flexBasis: number | string
|
|
287
|
+
readonly flexGrow: number
|
|
288
|
+
readonly flexShrink: number
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
221
292
|
/** Converts a portable length to logical units. */
|
|
222
293
|
export function length(
|
|
223
294
|
value: number | string,
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** Resolves compiled style applications for consumers outside native view bindings. @module */
|
|
2
|
+
import * as NativeContext from '../../runtime/NativeContext.js'
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import type { useStyles } from '../react.js'
|
|
5
|
+
import type * as Store from './Store.js'
|
|
6
|
+
|
|
7
|
+
/** Subscribes to a selection and returns its style resolvers. */
|
|
8
|
+
export function use(
|
|
9
|
+
store: ReturnType<typeof Store.create>,
|
|
10
|
+
): useStyles.ReturnType {
|
|
11
|
+
const value = React.useSyncExternalStore(
|
|
12
|
+
store.subscribe,
|
|
13
|
+
store.getSnapshot,
|
|
14
|
+
store.getSnapshot,
|
|
15
|
+
)
|
|
16
|
+
return React.useMemo(
|
|
17
|
+
() => ({
|
|
18
|
+
props: (props) =>
|
|
19
|
+
props && Object.hasOwn(props, 'style')
|
|
20
|
+
? { ...props, style: NativeContext.resolve(props.style, value) }
|
|
21
|
+
: props,
|
|
22
|
+
style: <style>(style: style, input?: unknown) =>
|
|
23
|
+
NativeContext.resolve(style, value, input) as useStyles.Style<style>,
|
|
24
|
+
}),
|
|
25
|
+
[value],
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Wraps a component so its style props resolve through an entrypoint's selection hook.
|
|
31
|
+
* @throws For invalid style prop names.
|
|
32
|
+
*/
|
|
33
|
+
export function wrap<const component extends React.ElementType>(
|
|
34
|
+
Component: component,
|
|
35
|
+
options: wrap.Options,
|
|
36
|
+
useStyles: () => useStyles.ReturnType,
|
|
37
|
+
) {
|
|
38
|
+
const additional = options.styleProps ?? []
|
|
39
|
+
if (
|
|
40
|
+
!Array.isArray(additional) ||
|
|
41
|
+
additional.some(
|
|
42
|
+
(name) =>
|
|
43
|
+
typeof name !== 'string' || !name || name === 'key' || name === 'ref',
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
throw new Error(
|
|
47
|
+
'withStyles requires style prop names excluding key and ref.',
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
const names = [...new Set(['style', 'contentContainerStyle', ...additional])]
|
|
51
|
+
const Wrapped = React.forwardRef<
|
|
52
|
+
React.ComponentRef<component>,
|
|
53
|
+
React.ComponentPropsWithoutRef<component>
|
|
54
|
+
>((props, ref) => {
|
|
55
|
+
const selected = useStyles()
|
|
56
|
+
const resolved: Record<string, unknown> = { ...props, ref }
|
|
57
|
+
for (const name of names)
|
|
58
|
+
if (Object.hasOwn(props, name))
|
|
59
|
+
resolved[name] = selected.style(
|
|
60
|
+
(props as Record<string, unknown>)[name],
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return React.createElement(Component, resolved)
|
|
64
|
+
})
|
|
65
|
+
Wrapped.displayName = `withStyles(${typeof Component === 'string' ? Component : Component.displayName || Component.name || 'Component'})`
|
|
66
|
+
|
|
67
|
+
return Wrapped
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Style-bearing prop names accepted by a wrapper. */
|
|
71
|
+
export declare namespace wrap {
|
|
72
|
+
/** Entrypoints narrow these names to the wrapped component's props. */
|
|
73
|
+
type Options = {
|
|
74
|
+
/** Additional style props declared by the wrapped component. */
|
|
75
|
+
readonly styleProps?: readonly string[] | undefined
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
/** React subscriptions for compiled native styles and variable values. @module */
|
|
2
2
|
import * as Config from '../Config.js'
|
|
3
|
-
import * as NativeContext from '../runtime/NativeContext.js'
|
|
3
|
+
import type * as NativeContext from '../runtime/NativeContext.js'
|
|
4
4
|
import * as NativeVars from '../runtime/NativeVars.js'
|
|
5
5
|
import * as React from 'react'
|
|
6
6
|
import type * as Vars from '../Vars.js'
|
|
7
7
|
import * as Store from './internal/Store.js'
|
|
8
|
+
import * as Styles from './internal/Styles.js'
|
|
8
9
|
import * as Viewport from './internal/Viewport.js'
|
|
9
10
|
|
|
10
|
-
const unbound = {
|
|
11
|
-
getSnapshot: () => undefined,
|
|
12
|
-
subscribe: () => () => {},
|
|
13
|
-
}
|
|
14
|
-
|
|
15
11
|
/** Creates native authoring helpers and a Provider typed to the configured variables. */
|
|
16
12
|
export function defineConfig<const options extends Config.create.Options = {}>(
|
|
17
13
|
options: options & Parameters<typeof Config.create<options>>[0] = {} as never,
|
|
@@ -113,15 +109,39 @@ export declare namespace Provider {
|
|
|
113
109
|
}
|
|
114
110
|
}
|
|
115
111
|
|
|
116
|
-
/**
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Compiler-inserted subscription for resolving native style applications.
|
|
114
|
+
* Outside a Provider, selections use default variables and the light scheme.
|
|
115
|
+
*/
|
|
116
|
+
export function useStyles(): useStyles.ReturnType {
|
|
117
|
+
const [fallback] = React.useState(() =>
|
|
118
|
+
Store.create({ colorScheme: 'light' }),
|
|
123
119
|
)
|
|
124
|
-
return React.
|
|
120
|
+
return Styles.use(React.useContext(Store.context) ?? fallback)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Resolver types for Provider-selected native styles. */
|
|
124
|
+
export declare namespace useStyles {
|
|
125
|
+
/** Resolvers bound to the nearest Provider selection. */
|
|
126
|
+
type ReturnType = {
|
|
127
|
+
/** Resolves the `style` prop of applied props and preserves other props. */
|
|
128
|
+
readonly props: <
|
|
129
|
+
props extends Readonly<Record<string, unknown>> | null | undefined,
|
|
130
|
+
>(
|
|
131
|
+
props: props,
|
|
132
|
+
) => props
|
|
133
|
+
/** Resolves an applied style, native style, style array, or style callback. */
|
|
134
|
+
readonly style: <style>(style: style, input?: unknown) => Style<style>
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Resolved style type. Caller-owned values keep their types. Native applications
|
|
138
|
+
* always carry `style`, so an applied style drops the `undefined` allowed by the shared props type.
|
|
139
|
+
*/
|
|
140
|
+
type Style<style> = [
|
|
141
|
+
style extends object ? (string extends keyof style ? style : never) : never,
|
|
142
|
+
] extends [never]
|
|
143
|
+
? style
|
|
144
|
+
: Exclude<style, undefined>
|
|
125
145
|
}
|
|
126
146
|
|
|
127
147
|
/** Compiler fallback for native view bindings when no native adapter is installed. */
|
|
@@ -171,46 +191,37 @@ export function useVars(
|
|
|
171
191
|
}
|
|
172
192
|
|
|
173
193
|
/**
|
|
174
|
-
* Resolves compiled style props for components using React updates.
|
|
175
|
-
* @param Component - Function,
|
|
194
|
+
* Resolves compiled style props for function components using React updates.
|
|
195
|
+
* @param Component - Function, memo, or ref-forwarding component receiving native styles.
|
|
196
|
+
* @param options - Additional style-bearing prop names.
|
|
197
|
+
* @returns A component with the original call signature and ref. Type parameters remain when `styleProps` is omitted.
|
|
198
|
+
* @throws For invalid style prop names.
|
|
199
|
+
*/
|
|
200
|
+
export function withStyles<
|
|
201
|
+
props extends object,
|
|
202
|
+
result extends React.ReactNode,
|
|
203
|
+
>(
|
|
204
|
+
Component: (props: props) => result,
|
|
205
|
+
options?: NoInfer<withStyles.Options<(props: props) => result>>,
|
|
206
|
+
): (props: props) => result
|
|
207
|
+
/**
|
|
208
|
+
* Resolves compiled style props for class and host components using React updates.
|
|
209
|
+
* @param Component - Class component or host element type receiving native styles.
|
|
176
210
|
* @param options - Additional style-bearing prop names.
|
|
177
|
-
* @returns A component preserving the original props and ref.
|
|
178
|
-
* @throws For invalid style prop names
|
|
211
|
+
* @returns A component preserving the original props and instance ref.
|
|
212
|
+
* @throws For invalid style prop names.
|
|
179
213
|
*/
|
|
180
214
|
export function withStyles<const component extends React.ElementType>(
|
|
181
215
|
Component: component,
|
|
182
|
-
options
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
)
|
|
192
|
-
throw new Error(
|
|
193
|
-
'withStyles requires style prop names excluding key and ref.',
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
const names = [...new Set(['style', 'contentContainerStyle', ...additional])]
|
|
197
|
-
const Wrapped = React.forwardRef<
|
|
198
|
-
React.ComponentRef<component>,
|
|
199
|
-
React.ComponentPropsWithoutRef<component>
|
|
200
|
-
>((props, ref) => {
|
|
201
|
-
const selected = useStyles()
|
|
202
|
-
const resolved: Record<string, unknown> = { ...props, ref }
|
|
203
|
-
for (const name of names)
|
|
204
|
-
if (Object.hasOwn(props, name))
|
|
205
|
-
resolved[name] = selected.style(
|
|
206
|
-
(props as Record<string, unknown>)[name],
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
return React.createElement(Component, resolved)
|
|
210
|
-
})
|
|
211
|
-
Wrapped.displayName = `withStyles(${typeof Component === 'string' ? Component : Component.displayName || Component.name || 'Component'})`
|
|
212
|
-
|
|
213
|
-
return Wrapped
|
|
216
|
+
options?: withStyles.Options<component>,
|
|
217
|
+
): React.ForwardRefExoticComponent<React.ComponentPropsWithRef<component>>
|
|
218
|
+
export function withStyles(
|
|
219
|
+
Component: React.ElementType,
|
|
220
|
+
options: withStyles.Options<
|
|
221
|
+
(props: Readonly<Record<string, unknown>>) => React.ReactNode
|
|
222
|
+
> = {},
|
|
223
|
+
): React.ElementType {
|
|
224
|
+
return Styles.wrap(Component, options, useStyles)
|
|
214
225
|
}
|
|
215
226
|
|
|
216
227
|
/** Style-bearing component properties resolved by the wrapper. */
|
|
@@ -226,14 +237,3 @@ export declare namespace withStyles {
|
|
|
226
237
|
| undefined
|
|
227
238
|
}
|
|
228
239
|
}
|
|
229
|
-
|
|
230
|
-
function styles(value: NativeContext.Context | undefined) {
|
|
231
|
-
return {
|
|
232
|
-
props: (props: Record<string, unknown> | null | undefined) =>
|
|
233
|
-
props && Object.hasOwn(props, 'style')
|
|
234
|
-
? { ...props, style: NativeContext.resolve(props.style, value) }
|
|
235
|
-
: props,
|
|
236
|
-
style: (style: unknown, input?: unknown) =>
|
|
237
|
-
NativeContext.resolve(style, value, input),
|
|
238
|
-
}
|
|
239
|
-
}
|
|
@@ -205,6 +205,13 @@ export function create(
|
|
|
205
205
|
[`${property}Bottom`]: bottom,
|
|
206
206
|
[`${property}Left`]: left,
|
|
207
207
|
})
|
|
208
|
+
} else if (property === 'inset') {
|
|
209
|
+
const parts =
|
|
210
|
+
typeof value === 'string' ? Calculation.parts(value) : [value]
|
|
211
|
+
const [top, right = top, bottom = top, left = right] = parts.map(
|
|
212
|
+
(part) => Scalar.convert('offset', part, options, [property]),
|
|
213
|
+
)
|
|
214
|
+
Object.assign(output, { top, right, bottom, left })
|
|
208
215
|
} else if (property === 'borderColor' || property === 'borderWidth') {
|
|
209
216
|
for (const side of ['Top', 'Right', 'Bottom', 'Left'])
|
|
210
217
|
output[
|