zyzz 0.0.21 → 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/Zyzz.podspec +16 -0
- package/dist/Style.d.ts +3 -1
- package/dist/Style.d.ts.map +1 -1
- package/dist/Style.js.map +1 -1
- package/dist/babel/NativeJsx.d.ts.map +1 -1
- package/dist/babel/NativeJsx.js +217 -15
- package/dist/babel/NativeJsx.js.map +1 -1
- package/dist/babel/index.d.ts +6 -2
- package/dist/babel/index.d.ts.map +1 -1
- package/dist/babel/index.js +10 -1
- package/dist/babel/index.js.map +1 -1
- package/dist/compiler/Graph.d.ts.map +1 -1
- package/dist/compiler/Graph.js +107 -0
- package/dist/compiler/Graph.js.map +1 -1
- package/dist/compiler/Native.d.ts +6 -3
- package/dist/compiler/Native.d.ts.map +1 -1
- package/dist/compiler/Native.js +142 -24
- package/dist/compiler/Native.js.map +1 -1
- package/dist/compiler/Source.d.ts +2 -0
- package/dist/compiler/Source.d.ts.map +1 -1
- package/dist/compiler/Source.js +17 -5
- package/dist/compiler/Source.js.map +1 -1
- package/dist/compiler/internal/Configurations.d.ts +2 -0
- package/dist/compiler/internal/Configurations.d.ts.map +1 -1
- package/dist/compiler/internal/Configurations.js +21 -3
- package/dist/compiler/internal/Configurations.js.map +1 -1
- package/dist/compiler/internal/Contract.d.ts.map +1 -1
- package/dist/compiler/internal/Contract.js +75 -38
- package/dist/compiler/internal/Contract.js.map +1 -1
- package/dist/compiler/internal/Contributions.d.ts.map +1 -1
- package/dist/compiler/internal/Contributions.js +7 -1
- package/dist/compiler/internal/Contributions.js.map +1 -1
- package/dist/compiler/internal/Descriptors.d.ts.map +1 -1
- package/dist/compiler/internal/Descriptors.js +11 -2
- package/dist/compiler/internal/Descriptors.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 +24 -26
- package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
- package/dist/compiler/internal/NativeBindings.js +62 -8
- package/dist/compiler/internal/NativeBindings.js.map +1 -1
- package/dist/compiler/internal/NativeMedia.d.ts +14 -0
- package/dist/compiler/internal/NativeMedia.d.ts.map +1 -0
- package/dist/compiler/internal/NativeMedia.js +287 -0
- package/dist/compiler/internal/NativeMedia.js.map +1 -0
- package/dist/compiler/internal/NativeVars.d.ts +28 -0
- package/dist/compiler/internal/NativeVars.d.ts.map +1 -0
- package/dist/compiler/internal/NativeVars.js +160 -0
- package/dist/compiler/internal/NativeVars.js.map +1 -0
- package/dist/compiler/internal/PackedStyles.d.ts +2 -0
- package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
- package/dist/compiler/internal/PackedStyles.js +2 -0
- package/dist/compiler/internal/PackedStyles.js.map +1 -1
- package/dist/compiler/internal/Themes.d.ts +24 -1
- package/dist/compiler/internal/Themes.d.ts.map +1 -1
- package/dist/compiler/internal/Themes.js +120 -3
- package/dist/compiler/internal/Themes.js.map +1 -1
- package/dist/compiler/internal/Vars.d.ts +1 -1
- package/dist/compiler/internal/Vars.d.ts.map +1 -1
- package/dist/compiler/internal/Vars.js +7 -2
- package/dist/compiler/internal/Vars.js.map +1 -1
- package/dist/default.js.zyzz.json +1 -1
- package/dist/internal/Completion.d.ts +1 -1
- package/dist/internal/Completion.d.ts.map +1 -1
- package/dist/internal/Targets.d.ts.map +1 -1
- package/dist/internal/Targets.js +2 -0
- package/dist/internal/Targets.js.map +1 -1
- package/dist/internal/Token.d.ts +0 -2
- package/dist/internal/Token.d.ts.map +1 -1
- package/dist/internal/Token.js.map +1 -1
- package/dist/internal/Value.d.ts +7 -0
- package/dist/internal/Value.d.ts.map +1 -1
- package/dist/internal/Value.js.map +1 -1
- package/dist/metro/Graph.d.ts +22 -7
- package/dist/metro/Graph.d.ts.map +1 -1
- package/dist/metro/Graph.js +188 -42
- package/dist/metro/Graph.js.map +1 -1
- package/dist/metro/Watch.d.ts +28 -4
- package/dist/metro/Watch.d.ts.map +1 -1
- package/dist/metro/Watch.js +39 -7
- package/dist/metro/Watch.js.map +1 -1
- package/dist/metro/index.d.ts +15 -4
- package/dist/metro/index.d.ts.map +1 -1
- package/dist/metro/index.js +27 -0
- package/dist/metro/index.js.map +1 -1
- package/dist/metro/transformer.d.ts +5 -1
- package/dist/metro/transformer.d.ts.map +1 -1
- package/dist/metro/transformer.js +5 -3
- package/dist/metro/transformer.js.map +1 -1
- package/dist/react-native/StyleSheet.d.ts +8 -3
- package/dist/react-native/StyleSheet.d.ts.map +1 -1
- package/dist/react-native/StyleSheet.js +25 -34
- package/dist/react-native/StyleSheet.js.map +1 -1
- package/dist/react-native/index.d.ts +1 -1
- package/dist/react-native/index.d.ts.map +1 -1
- package/dist/react-native/index.js +1 -1
- package/dist/react-native/index.js.map +1 -1
- package/dist/react-native/index.native.d.ts +5 -0
- package/dist/react-native/index.native.js +20 -0
- package/dist/react-native/index.native.js.map +7 -0
- package/dist/react-native/internal/Calculation.d.ts +7 -0
- package/dist/react-native/internal/Calculation.d.ts.map +1 -0
- package/dist/react-native/internal/Calculation.js +134 -0
- package/dist/react-native/internal/Calculation.js.map +1 -0
- package/dist/react-native/internal/Device.js +218 -0
- package/dist/react-native/internal/Device.js.map +7 -0
- package/dist/react-native/internal/NativeZyzz.js +7 -0
- package/dist/react-native/internal/NativeZyzz.js.map +7 -0
- 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 +62 -4
- package/dist/react-native/internal/Scalar.js.map +1 -1
- package/dist/react-native/internal/Store.d.ts +32 -0
- package/dist/react-native/internal/Store.d.ts.map +1 -0
- package/dist/react-native/internal/Store.js +57 -0
- package/dist/react-native/internal/Store.js.map +1 -0
- 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/internal/Tokens.d.ts +21 -0
- package/dist/react-native/internal/Tokens.d.ts.map +1 -0
- package/dist/react-native/internal/Tokens.js +58 -0
- package/dist/react-native/internal/Tokens.js.map +1 -0
- package/dist/react-native/internal/Viewport.d.ts +6 -0
- package/dist/react-native/internal/Viewport.d.ts.map +1 -0
- package/dist/react-native/internal/Viewport.js +4 -0
- package/dist/react-native/internal/Viewport.js.map +1 -0
- package/dist/react-native/react.d.ts +102 -14
- package/dist/react-native/react.d.ts.map +1 -1
- package/dist/react-native/react.js +86 -18
- package/dist/react-native/react.js.map +1 -1
- package/dist/react-native/reanimated.d.ts +39 -0
- package/dist/react-native/reanimated.d.ts.map +1 -0
- package/dist/react-native/reanimated.js +76 -0
- package/dist/react-native/reanimated.js.map +1 -0
- package/dist/runtime/NativeContext.d.ts +10 -1
- package/dist/runtime/NativeContext.d.ts.map +1 -1
- package/dist/runtime/NativeContext.js +67 -14
- package/dist/runtime/NativeContext.js.map +1 -1
- package/dist/runtime/NativeDynamic.d.ts +3 -2
- package/dist/runtime/NativeDynamic.d.ts.map +1 -1
- package/dist/runtime/NativeDynamic.js +37 -7
- package/dist/runtime/NativeDynamic.js.map +1 -1
- package/dist/runtime/NativeStatic.d.ts +23 -0
- package/dist/runtime/NativeStatic.d.ts.map +1 -0
- package/dist/runtime/NativeStatic.js +106 -0
- package/dist/runtime/NativeStatic.js.map +1 -0
- package/dist/runtime/NativeVars.d.ts +53 -0
- package/dist/runtime/NativeVars.d.ts.map +1 -0
- package/dist/runtime/NativeVars.js +77 -0
- package/dist/runtime/NativeVars.js.map +1 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/internal/NativeMedia.d.ts +25 -0
- package/dist/runtime/internal/NativeMedia.d.ts.map +1 -0
- package/dist/runtime/internal/NativeMedia.js +36 -0
- package/dist/runtime/internal/NativeMedia.js.map +1 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +7 -35
- package/dist/vite/index.js.map +1 -1
- package/dist/web/Css.d.ts.map +1 -1
- package/dist/web/Css.js +31 -19
- package/dist/web/Css.js.map +1 -1
- package/dist/web/internal/Themes.d.ts +0 -2
- package/dist/web/internal/Themes.d.ts.map +1 -1
- package/dist/web/internal/Themes.js +30 -28
- package/dist/web/internal/Themes.js.map +1 -1
- package/dist/web/page.d.ts +2 -0
- package/dist/web/page.d.ts.map +1 -1
- package/native/NativeZyzz.cpp +215 -0
- package/native/NativeZyzz.h +52 -0
- package/native/ZyzzSpecJSI.h +65 -0
- package/native/android/CMakeLists.txt +8 -0
- package/native/android/ZyzzSpec-generated.cpp +50 -0
- package/native/android/ZyzzSpec.h +31 -0
- package/native/ios/ZyzzModuleProvider.mm +13 -0
- package/package.json +39 -1
- package/react-native.config.ts +14 -0
- package/src/Style.ts +11 -8
- package/src/babel/NativeJsx.ts +319 -33
- package/src/babel/index.ts +21 -3
- package/src/compiler/Graph.ts +126 -0
- package/src/compiler/Native.ts +158 -26
- package/src/compiler/Source.ts +32 -5
- package/src/compiler/internal/Configurations.ts +25 -3
- package/src/compiler/internal/Contract.ts +98 -50
- package/src/compiler/internal/Contributions.ts +7 -1
- package/src/compiler/internal/Descriptors.ts +12 -8
- package/src/compiler/internal/Dynamic.ts +4 -0
- package/src/compiler/internal/Expression.ts +2 -2
- package/src/compiler/internal/NativeBindings.ts +99 -15
- package/src/compiler/internal/NativeMedia.ts +342 -0
- package/src/compiler/internal/NativeVars.ts +231 -0
- package/src/compiler/internal/PackedStyles.ts +4 -0
- package/src/compiler/internal/Themes.ts +193 -2
- package/src/compiler/internal/Vars.ts +12 -1
- package/src/internal/Completion.ts +6 -3
- package/src/internal/Targets.ts +2 -0
- package/src/internal/Token.ts +0 -2
- package/src/internal/Value.ts +20 -0
- package/src/metro/Graph.ts +250 -50
- package/src/metro/Watch.ts +95 -10
- package/src/metro/index.ts +62 -4
- package/src/metro/transformer.ts +21 -11
- package/src/react-native/StyleSheet.ts +56 -49
- package/src/react-native/index.native.ts +10 -0
- package/src/react-native/index.ts +1 -1
- package/src/react-native/internal/Calculation.ts +159 -0
- package/src/react-native/internal/Device.ts +297 -0
- package/src/react-native/internal/NativeZyzz.ts +20 -0
- package/src/react-native/internal/Scalar.ts +86 -4
- package/src/react-native/internal/Store.ts +78 -0
- package/src/react-native/internal/Styles.ts +77 -0
- package/src/react-native/internal/Tokens.ts +85 -0
- package/src/react-native/internal/Viewport.ts +8 -0
- package/src/react-native/react.ts +218 -26
- package/src/react-native/reanimated.ts +162 -0
- package/src/runtime/NativeContext.ts +88 -17
- package/src/runtime/NativeDynamic.ts +42 -10
- package/src/runtime/NativeStatic.ts +145 -0
- package/src/runtime/NativeVars.ts +189 -0
- package/src/runtime/index.ts +6 -0
- package/src/runtime/internal/NativeMedia.ts +63 -0
- package/src/vite/index.ts +9 -42
- package/src/web/Css.ts +42 -27
- package/src/web/internal/Themes.ts +34 -57
- package/src/web/page.ts +2 -0
|
@@ -7,6 +7,8 @@ import type * as Native from '../internal/NativeProperties.js'
|
|
|
7
7
|
import * as Values from './internal/Values.js'
|
|
8
8
|
import * as Color from './internal/Color.js'
|
|
9
9
|
import * as Scalar from './internal/Scalar.js'
|
|
10
|
+
import * as Calculation from './internal/Calculation.js'
|
|
11
|
+
import * as Tokens from './internal/Tokens.js'
|
|
10
12
|
|
|
11
13
|
const properties = Scalar.properties
|
|
12
14
|
|
|
@@ -207,6 +209,7 @@ export function compile<
|
|
|
207
209
|
scheme,
|
|
208
210
|
path,
|
|
209
211
|
resolution,
|
|
212
|
+
options,
|
|
210
213
|
)
|
|
211
214
|
if (property === 'lineHeight') {
|
|
212
215
|
lineHeight = value
|
|
@@ -266,9 +269,7 @@ export function compile<
|
|
|
266
269
|
const converted = convert(kind, value, options, path)
|
|
267
270
|
if (property === 'padding' || property === 'margin') {
|
|
268
271
|
const values =
|
|
269
|
-
typeof value === 'string'
|
|
270
|
-
? value.trim().split(/\s+/)
|
|
271
|
-
: [value]
|
|
272
|
+
typeof value === 'string' ? Calculation.parts(value) : [value]
|
|
272
273
|
const [top, right = top, bottom = top, left = right] =
|
|
273
274
|
values.map((value) =>
|
|
274
275
|
length(value, options, property === 'margin', path),
|
|
@@ -277,6 +278,14 @@ export function compile<
|
|
|
277
278
|
output[`${property}Right`] = right!
|
|
278
279
|
output[`${property}Bottom`] = bottom!
|
|
279
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 })
|
|
280
289
|
} else if (
|
|
281
290
|
property === 'borderColor' ||
|
|
282
291
|
property === 'borderWidth'
|
|
@@ -606,12 +615,32 @@ type Merged<style> = {
|
|
|
606
615
|
type StyleArray<style extends object> = Array<
|
|
607
616
|
style | Falsy | readonly (style | Falsy)[] | StyleArray<style>
|
|
608
617
|
>
|
|
609
|
-
type Length =
|
|
610
|
-
|
|
611
|
-
|
|
|
612
|
-
| `${
|
|
613
|
-
| `${
|
|
614
|
-
| `${
|
|
618
|
+
type Length =
|
|
619
|
+
| 0
|
|
620
|
+
| '0'
|
|
621
|
+
| `${number}px`
|
|
622
|
+
| `${number}rem`
|
|
623
|
+
| `${'c' | 'C'}${'a' | 'A'}${'l' | 'L'}${'c' | 'C'}(${string})`
|
|
624
|
+
type Box<item extends string | number = Length> =
|
|
625
|
+
| item
|
|
626
|
+
| `${item} ${item}`
|
|
627
|
+
| `${item} ${item} ${item}`
|
|
628
|
+
| Box4<item>
|
|
629
|
+
// Distribute the first item to keep case-insensitive calculations below the template expansion limit.
|
|
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}`
|
|
615
644
|
type Weight =
|
|
616
645
|
| 'bold'
|
|
617
646
|
| 'normal'
|
|
@@ -656,7 +685,11 @@ type Atom<kind> = kind extends readonly string[]
|
|
|
656
685
|
? Length | number
|
|
657
686
|
: kind extends 'box' | 'boxSigned'
|
|
658
687
|
? Box
|
|
659
|
-
:
|
|
688
|
+
: kind extends 'boxOffset'
|
|
689
|
+
? Box<Length | `${number}%`>
|
|
690
|
+
: kind extends 'flex'
|
|
691
|
+
? Flex
|
|
692
|
+
: Length
|
|
660
693
|
|
|
661
694
|
type Reference<property extends keyof typeof properties> = {
|
|
662
695
|
[group in Token.Group]: property extends Token.Properties<group>
|
|
@@ -757,50 +790,24 @@ function resolve(
|
|
|
757
790
|
scheme: ColorScheme,
|
|
758
791
|
path: readonly string[],
|
|
759
792
|
resolution: { schemeIndependent: boolean },
|
|
793
|
+
options: compile.Options,
|
|
760
794
|
): number | string {
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
795
|
+
try {
|
|
796
|
+
return Tokens.resolve(value, {
|
|
797
|
+
...options,
|
|
798
|
+
metadata,
|
|
799
|
+
colorScheme: scheme,
|
|
800
|
+
resolution,
|
|
801
|
+
})
|
|
802
|
+
} catch (error) {
|
|
766
803
|
fail(
|
|
767
|
-
|
|
768
|
-
|
|
804
|
+
error instanceof Tokens.UnsupportedError
|
|
805
|
+
? 'unsupported_feature'
|
|
806
|
+
: 'unsupported_value',
|
|
807
|
+
(error as Error).message,
|
|
769
808
|
path,
|
|
770
809
|
)
|
|
771
|
-
const shared =
|
|
772
|
-
metadata &&
|
|
773
|
-
(metadata.contract === value.contract ||
|
|
774
|
-
(metadata.contract[Token.identity] !== undefined &&
|
|
775
|
-
metadata.contract[Token.identity] === value.contract[Token.identity]))
|
|
776
|
-
let resolved = shared
|
|
777
|
-
? Object.hasOwn(metadata.values, value.path)
|
|
778
|
-
? metadata.values[value.path]
|
|
779
|
-
: undefined
|
|
780
|
-
: value.value
|
|
781
|
-
if (resolved === undefined)
|
|
782
|
-
fail('unsupported_value', 'Theme is missing a live token.', path)
|
|
783
|
-
while (typeof resolved === 'object') {
|
|
784
|
-
if (Token.is(resolved)) {
|
|
785
|
-
resolved = resolved.value
|
|
786
|
-
continue
|
|
787
|
-
}
|
|
788
|
-
if (Token.isExpression(resolved))
|
|
789
|
-
fail(
|
|
790
|
-
'unsupported_feature',
|
|
791
|
-
'Composed variables require a web target.',
|
|
792
|
-
path,
|
|
793
|
-
)
|
|
794
|
-
if ('default' in resolved)
|
|
795
|
-
fail(
|
|
796
|
-
'unsupported_feature',
|
|
797
|
-
'Media-conditioned variables require a web target.',
|
|
798
|
-
path,
|
|
799
|
-
)
|
|
800
|
-
if (resolved.light !== resolved.dark) resolution.schemeIndependent = false
|
|
801
|
-
resolved = resolved[scheme]
|
|
802
810
|
}
|
|
803
|
-
return resolved
|
|
804
811
|
}
|
|
805
812
|
|
|
806
813
|
type TransformValues = {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Native authoring and React subscriptions with automatic device dimensions. @module */
|
|
2
|
+
export * as Host from './Host.js'
|
|
3
|
+
export * as StyleSheet from './StyleSheet.js'
|
|
4
|
+
export * as Variants from './Variants.js'
|
|
5
|
+
export {
|
|
6
|
+
defineConfig,
|
|
7
|
+
useStyles,
|
|
8
|
+
useVars,
|
|
9
|
+
withStyles,
|
|
10
|
+
} from './internal/Device.js'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Native compilation
|
|
1
|
+
/** Native compilation and explicit host lifecycle. @module */
|
|
2
2
|
/** Explicit native context and adapter lifecycle. */
|
|
3
3
|
export * as Host from './Host.js'
|
|
4
4
|
/** Native compilation and table selection. */
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/** Evaluates finite calc lengths using explicit native unit mappings. @module */
|
|
2
|
+
import type * as Scalar from './Scalar.js'
|
|
3
|
+
|
|
4
|
+
type Value = { amount: number; length: boolean }
|
|
5
|
+
|
|
6
|
+
/** Resolves number/length arithmetic, rejecting relative units and incompatible dimensions. */
|
|
7
|
+
export function length(input: string, options: Scalar.Options): number {
|
|
8
|
+
const pattern =
|
|
9
|
+
/\s*(?:(\d+(?:\.\d+)?|\.\d+)(?:[eE]([+-]?\d+))?(px|rem)?|(calc)|([()+*/-]))/giy
|
|
10
|
+
const positions: number[] = []
|
|
11
|
+
const tokens: (string | Value)[] = []
|
|
12
|
+
let offset = 0
|
|
13
|
+
while (offset < input.trimEnd().length) {
|
|
14
|
+
pattern.lastIndex = offset
|
|
15
|
+
const match = pattern.exec(input)
|
|
16
|
+
if (!match) throw new Error('Unsupported native calc expression.')
|
|
17
|
+
|
|
18
|
+
offset = pattern.lastIndex
|
|
19
|
+
positions.push(match.index + match[0].search(/\S/))
|
|
20
|
+
if (positions.length > 512)
|
|
21
|
+
throw new Error('Native calc exceeds 512 tokens.')
|
|
22
|
+
if (match[1] === undefined) {
|
|
23
|
+
tokens.push(match[4]?.toLowerCase() ?? match[5]!)
|
|
24
|
+
continue
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const unit = match[3]?.toLowerCase()
|
|
28
|
+
const scale =
|
|
29
|
+
unit === 'rem'
|
|
30
|
+
? options.units?.rem
|
|
31
|
+
: unit === 'px'
|
|
32
|
+
? (options.units?.px ?? 1)
|
|
33
|
+
: 1
|
|
34
|
+
if (scale === undefined)
|
|
35
|
+
throw new Error('Provide units.rem for rem lengths.')
|
|
36
|
+
|
|
37
|
+
tokens.push({
|
|
38
|
+
amount:
|
|
39
|
+
Number(`${match[1]}${match[2] === undefined ? '' : `e${match[2]}`}`) *
|
|
40
|
+
scale,
|
|
41
|
+
length: unit !== undefined,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let cursor = 0
|
|
46
|
+
let depth = 0
|
|
47
|
+
function atom(): Value {
|
|
48
|
+
if (++depth > 64)
|
|
49
|
+
throw new Error('Native calc exceeds 64 nested operations.')
|
|
50
|
+
try {
|
|
51
|
+
return readAtom()
|
|
52
|
+
} finally {
|
|
53
|
+
depth--
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function readAtom(): Value {
|
|
57
|
+
const token = tokens[cursor++]
|
|
58
|
+
if (token === '+' || token === '-') {
|
|
59
|
+
const value = atom()
|
|
60
|
+
return { ...value, amount: token === '-' ? -value.amount : value.amount }
|
|
61
|
+
}
|
|
62
|
+
if (token === 'calc' || token === '(') {
|
|
63
|
+
if (token === 'calc' && tokens[cursor++] !== '(')
|
|
64
|
+
throw new Error('Unsupported native calc expression.')
|
|
65
|
+
const value = sum()
|
|
66
|
+
if (tokens[cursor++] !== ')')
|
|
67
|
+
throw new Error('Unsupported native calc expression.')
|
|
68
|
+
return value
|
|
69
|
+
}
|
|
70
|
+
if (!token || typeof token === 'string')
|
|
71
|
+
throw new Error('Unsupported native calc expression.')
|
|
72
|
+
|
|
73
|
+
return token
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function product(): Value {
|
|
77
|
+
let value = atom()
|
|
78
|
+
while (tokens[cursor] === '*' || tokens[cursor] === '/') {
|
|
79
|
+
const operator = tokens[cursor++]
|
|
80
|
+
const right = atom()
|
|
81
|
+
if (
|
|
82
|
+
(operator === '*' && value.length && right.length) ||
|
|
83
|
+
(operator === '/' && (right.length || right.amount === 0))
|
|
84
|
+
)
|
|
85
|
+
throw new Error(
|
|
86
|
+
'Native calc requires compatible number and length operands.',
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
value = {
|
|
90
|
+
amount:
|
|
91
|
+
operator === '*'
|
|
92
|
+
? value.amount * right.amount
|
|
93
|
+
: value.amount / right.amount,
|
|
94
|
+
length: value.length || right.length,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return value
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function sum(): Value {
|
|
101
|
+
let value = product()
|
|
102
|
+
while (tokens[cursor] === '+' || tokens[cursor] === '-') {
|
|
103
|
+
const position = positions[cursor]!
|
|
104
|
+
if (
|
|
105
|
+
!/\s/.test(input[position - 1] ?? '') ||
|
|
106
|
+
!/\s/.test(input[position + 1] ?? '')
|
|
107
|
+
)
|
|
108
|
+
throw new Error('Unsupported native calc expression.')
|
|
109
|
+
|
|
110
|
+
const operator = tokens[cursor++]
|
|
111
|
+
const right = product()
|
|
112
|
+
if (value.length !== right.length)
|
|
113
|
+
throw new Error(
|
|
114
|
+
'Native calc requires compatible number and length operands.',
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
value = {
|
|
118
|
+
amount:
|
|
119
|
+
operator === '+'
|
|
120
|
+
? value.amount + right.amount
|
|
121
|
+
: value.amount - right.amount,
|
|
122
|
+
length: value.length,
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return value
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const value = atom()
|
|
129
|
+
if (
|
|
130
|
+
cursor !== tokens.length ||
|
|
131
|
+
!value.length ||
|
|
132
|
+
!Number.isFinite(value.amount)
|
|
133
|
+
)
|
|
134
|
+
throw new Error('Native calc must resolve to one finite px or rem length.')
|
|
135
|
+
|
|
136
|
+
return value.amount
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Splits shorthand lengths while preserving spaces within calculations. */
|
|
140
|
+
export function parts(input: string): readonly string[] {
|
|
141
|
+
const result: string[] = []
|
|
142
|
+
let depth = 0
|
|
143
|
+
let start = 0
|
|
144
|
+
const text = input.trim()
|
|
145
|
+
for (let index = 0; index < text.length; index++) {
|
|
146
|
+
const character = text[index]
|
|
147
|
+
if (character === '(') depth++
|
|
148
|
+
if (character === ')' && --depth < 0)
|
|
149
|
+
throw new Error('Unbalanced native calculation.')
|
|
150
|
+
if (depth > 64) throw new Error('Native calc exceeds 64 nested operations.')
|
|
151
|
+
if (depth === 0 && /\s/.test(character!)) {
|
|
152
|
+
if (index > start) result.push(text.slice(start, index))
|
|
153
|
+
start = index + 1
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (depth !== 0) throw new Error('Unbalanced native calculation.')
|
|
157
|
+
if (start < text.length) result.push(text.slice(start))
|
|
158
|
+
return result
|
|
159
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/** Supplies automatic window measurements for native React consumers. @module */
|
|
2
|
+
import type * as Config from '../../Config.js'
|
|
3
|
+
import * as NativeContext from '../../runtime/NativeContext.js'
|
|
4
|
+
import NativeZyzz from './NativeZyzz.js'
|
|
5
|
+
import * as React from 'react'
|
|
6
|
+
import * as ReactNative from 'react-native'
|
|
7
|
+
import * as Store from './Store.js'
|
|
8
|
+
import * as Styles from './Styles.js'
|
|
9
|
+
import * as Subscription from '../react.js'
|
|
10
|
+
import * as Viewport from './Viewport.js'
|
|
11
|
+
|
|
12
|
+
/** Creates native authoring helpers and a Provider with automatic window subscriptions. */
|
|
13
|
+
export function defineConfig<const options extends Config.create.Options = {}>(
|
|
14
|
+
options: options & Parameters<typeof Config.create<options>>[0] = {} as never,
|
|
15
|
+
): Subscription.defineConfig.ReturnType<options> {
|
|
16
|
+
const config = Subscription.defineConfig<options>(options)
|
|
17
|
+
return Object.freeze({
|
|
18
|
+
...config,
|
|
19
|
+
Provider: dimensions(config.Provider),
|
|
20
|
+
}) as Subscription.defineConfig.ReturnType<options>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Provides selected variables, appearance, and automatic native dimensions. */
|
|
24
|
+
export const Provider = dimensions(Subscription.Provider)
|
|
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
|
+
}
|
|
92
|
+
|
|
93
|
+
function dimensions<props extends Subscription.Provider.Props>(
|
|
94
|
+
Component: React.FunctionComponent<props>,
|
|
95
|
+
): React.FunctionComponent<props> {
|
|
96
|
+
return function WindowProvider(props: props) {
|
|
97
|
+
const window = ReactNative.useWindowDimensions()
|
|
98
|
+
const viewport = React.useMemo(
|
|
99
|
+
() => ({ height: window.height, width: window.width }),
|
|
100
|
+
[window.height, window.width],
|
|
101
|
+
)
|
|
102
|
+
return React.createElement(
|
|
103
|
+
Viewport.context.Provider,
|
|
104
|
+
{ value: viewport },
|
|
105
|
+
React.createElement(Component, props),
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Compiler-only binding path, with React subscriptions for Expo Go. */
|
|
111
|
+
export const useNativeStyles = useBindings
|
|
112
|
+
|
|
113
|
+
type Instance = {
|
|
114
|
+
readonly __internalInstanceHandle?:
|
|
115
|
+
| {
|
|
116
|
+
readonly stateNode?: { readonly node?: object | undefined } | undefined
|
|
117
|
+
}
|
|
118
|
+
| undefined
|
|
119
|
+
readonly getNativeScrollRef?: (() => Instance) | undefined
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function useBindings() {
|
|
123
|
+
const owner = React.useContext(Store.context)
|
|
124
|
+
const [fallback] = React.useState(() =>
|
|
125
|
+
Store.create({
|
|
126
|
+
colorScheme: 'light',
|
|
127
|
+
viewport: ReactNative.Dimensions.get('window'),
|
|
128
|
+
}),
|
|
129
|
+
)
|
|
130
|
+
const store = owner ?? fallback
|
|
131
|
+
const [, render] = React.useReducer((count: number) => count + 1, 0)
|
|
132
|
+
const [refs] = React.useState(
|
|
133
|
+
() =>
|
|
134
|
+
new Map<
|
|
135
|
+
Instance,
|
|
136
|
+
{
|
|
137
|
+
active: boolean
|
|
138
|
+
ref: React.Ref<Instance> | undefined
|
|
139
|
+
release: () => void
|
|
140
|
+
}
|
|
141
|
+
>(),
|
|
142
|
+
)
|
|
143
|
+
React.useLayoutEffect(() => {
|
|
144
|
+
for (const [instance, entry] of refs) {
|
|
145
|
+
if (entry.active) continue
|
|
146
|
+
entry.release()
|
|
147
|
+
refs.delete(instance)
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
React.useLayoutEffect(
|
|
151
|
+
() => () => {
|
|
152
|
+
for (const entry of refs.values()) entry.release()
|
|
153
|
+
refs.clear()
|
|
154
|
+
},
|
|
155
|
+
[refs],
|
|
156
|
+
)
|
|
157
|
+
return {
|
|
158
|
+
style: NativeContext.application,
|
|
159
|
+
view: (props: Record<string, unknown>) => {
|
|
160
|
+
const snapshot = store.getSnapshot()
|
|
161
|
+
const selected = NativeContext.resolve(props.style, snapshot)
|
|
162
|
+
const initial = NativeContext.key(props.style, snapshot)
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
...props,
|
|
166
|
+
ref: (value: Instance | null) => {
|
|
167
|
+
const ref = props.ref as React.Ref<Instance> | undefined
|
|
168
|
+
if (!value) return
|
|
169
|
+
let entry = refs.get(value)
|
|
170
|
+
if (entry && entry.ref !== ref) {
|
|
171
|
+
entry.release()
|
|
172
|
+
refs.delete(value)
|
|
173
|
+
entry = undefined
|
|
174
|
+
}
|
|
175
|
+
if (!entry) {
|
|
176
|
+
const release = typeof ref === 'function' ? ref(value) : undefined
|
|
177
|
+
if (ref && typeof ref !== 'function') ref.current = value
|
|
178
|
+
entry = {
|
|
179
|
+
active: true,
|
|
180
|
+
ref,
|
|
181
|
+
release: () => {
|
|
182
|
+
if (typeof release === 'function') release()
|
|
183
|
+
else if (typeof ref === 'function') ref(null)
|
|
184
|
+
else if (ref) ref.current = null
|
|
185
|
+
},
|
|
186
|
+
}
|
|
187
|
+
refs.set(value, entry)
|
|
188
|
+
}
|
|
189
|
+
entry.active = true
|
|
190
|
+
const attached = entry
|
|
191
|
+
|
|
192
|
+
const instance = value.getNativeScrollRef?.() ?? value
|
|
193
|
+
const node = instance.__internalInstanceHandle?.stateNode?.node
|
|
194
|
+
const detach = (() => {
|
|
195
|
+
if (!initial.length && typeof selected !== 'function')
|
|
196
|
+
return () => {}
|
|
197
|
+
if (!NativeZyzz || !node || !selective(selected)) {
|
|
198
|
+
const unsubscribe = store.subscribe(render)
|
|
199
|
+
if (store.getSnapshot() !== snapshot) render()
|
|
200
|
+
return unsubscribe
|
|
201
|
+
}
|
|
202
|
+
const id = NativeZyzz!.attach(node, nativeProps(selected))
|
|
203
|
+
let previous = initial
|
|
204
|
+
const read = (context: NativeContext.Context) => {
|
|
205
|
+
const keys = NativeContext.key(props.style, context)
|
|
206
|
+
if (
|
|
207
|
+
keys.length === previous.length &&
|
|
208
|
+
keys.every((key, index) => Object.is(key, previous[index]))
|
|
209
|
+
)
|
|
210
|
+
return { commit: () => {} }
|
|
211
|
+
const selected = NativeContext.resolve(props.style, context)
|
|
212
|
+
if (!selective(selected)) return { commit: render }
|
|
213
|
+
return {
|
|
214
|
+
commit: () => {
|
|
215
|
+
previous = keys
|
|
216
|
+
},
|
|
217
|
+
patch: {
|
|
218
|
+
id,
|
|
219
|
+
props: nativeProps(selected),
|
|
220
|
+
},
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Context can change between render and committing the host ref.
|
|
224
|
+
const current = read(store.getSnapshot())
|
|
225
|
+
if (current.patch) write([current.patch])
|
|
226
|
+
current.commit()
|
|
227
|
+
const unbind = store.bind(read, write)
|
|
228
|
+
return () => {
|
|
229
|
+
unbind()
|
|
230
|
+
NativeZyzz!.detach(id)
|
|
231
|
+
}
|
|
232
|
+
})()
|
|
233
|
+
const update = () =>
|
|
234
|
+
fallback.update({
|
|
235
|
+
colorScheme: 'light',
|
|
236
|
+
viewport: ReactNative.Dimensions.get('window'),
|
|
237
|
+
})
|
|
238
|
+
const contextual =
|
|
239
|
+
initial.length > 0 || typeof selected === 'function'
|
|
240
|
+
const subscription =
|
|
241
|
+
owner || !contextual
|
|
242
|
+
? undefined
|
|
243
|
+
: ReactNative.Dimensions.addEventListener('change', update)
|
|
244
|
+
if (!owner && contextual) update()
|
|
245
|
+
return () => {
|
|
246
|
+
detach()
|
|
247
|
+
subscription?.remove()
|
|
248
|
+
attached.active = false
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
style: selected,
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function selective(style: unknown): boolean {
|
|
258
|
+
if (typeof style === 'function') return false
|
|
259
|
+
const value = ReactNative.StyleSheet.flatten(
|
|
260
|
+
style as ReactNative.StyleProp<ReactNative.ViewStyle>,
|
|
261
|
+
)
|
|
262
|
+
// Let React Native apply its processors to caller-owned representations.
|
|
263
|
+
// Only color properties are processed by the selective writer below.
|
|
264
|
+
return (
|
|
265
|
+
!value ||
|
|
266
|
+
![
|
|
267
|
+
'aspectRatio',
|
|
268
|
+
'backgroundImage',
|
|
269
|
+
'backgroundPosition',
|
|
270
|
+
'backgroundRepeat',
|
|
271
|
+
'backgroundSize',
|
|
272
|
+
'boxShadow',
|
|
273
|
+
'experimental_backgroundImage',
|
|
274
|
+
'filter',
|
|
275
|
+
'fontVariant',
|
|
276
|
+
'transform',
|
|
277
|
+
'transformOrigin',
|
|
278
|
+
].some((property) => property in value)
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function nativeProps(style: unknown): Record<string, unknown> {
|
|
283
|
+
const flattened =
|
|
284
|
+
ReactNative.StyleSheet.flatten(
|
|
285
|
+
style as ReactNative.StyleProp<ReactNative.ViewStyle>,
|
|
286
|
+
) ?? {}
|
|
287
|
+
const result: Record<string, unknown> = { ...flattened }
|
|
288
|
+
for (const [name, value] of Object.entries(result)) {
|
|
289
|
+
if (name === 'color' || name.endsWith('Color'))
|
|
290
|
+
result[name] = ReactNative.processColor(value as ReactNative.ColorValue)
|
|
291
|
+
}
|
|
292
|
+
return result
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function write(patches: readonly Store.Patch[]) {
|
|
296
|
+
NativeZyzz!.update(patches)
|
|
297
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Private Fabric bridge for committed view bindings. @module */
|
|
2
|
+
import type { CodegenTypes, TurboModule } from 'react-native'
|
|
3
|
+
import { TurboModuleRegistry } from 'react-native'
|
|
4
|
+
|
|
5
|
+
/** Codegen contract, consumed only by the native adapter. */
|
|
6
|
+
export interface Spec extends TurboModule {
|
|
7
|
+
/** Links a mounted Fabric node and its rendered style. */
|
|
8
|
+
readonly attach: (
|
|
9
|
+
node: CodegenTypes.UnsafeObject,
|
|
10
|
+
props: CodegenTypes.UnsafeObject,
|
|
11
|
+
) => number
|
|
12
|
+
/** Releases a view binding. */
|
|
13
|
+
readonly detach: (id: number) => void
|
|
14
|
+
/** Reports live bindings and applied update batches. */
|
|
15
|
+
readonly inspect: () => CodegenTypes.UnsafeObject
|
|
16
|
+
/** Applies one batch of changed native styles. */
|
|
17
|
+
readonly update: (updates: ReadonlyArray<CodegenTypes.UnsafeObject>) => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default TurboModuleRegistry.get<Spec>('NativeZyzz')
|