zyzz 0.0.21 → 0.0.22
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 +204 -12
- 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 +64 -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/NativeBindings.d.ts +24 -26
- package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
- package/dist/compiler/internal/NativeBindings.js +61 -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 +157 -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 +22 -1
- package/dist/compiler/internal/Themes.d.ts.map +1 -1
- package/dist/compiler/internal/Themes.js +118 -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 +179 -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 +3 -2
- package/dist/react-native/StyleSheet.d.ts.map +1 -1
- package/dist/react-native/StyleSheet.js +17 -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 +191 -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.map +1 -1
- package/dist/react-native/internal/Scalar.js +9 -2
- 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/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 +74 -11
- package/dist/react-native/react.d.ts.map +1 -1
- package/dist/react-native/react.js +111 -12
- 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 +32 -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 +305 -28
- package/src/babel/index.ts +21 -3
- package/src/compiler/Graph.ts +75 -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/NativeBindings.ts +98 -15
- package/src/compiler/internal/NativeMedia.ts +342 -0
- package/src/compiler/internal/NativeVars.ts +228 -0
- package/src/compiler/internal/PackedStyles.ts +4 -0
- package/src/compiler/internal/Themes.ts +187 -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 +235 -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 +28 -44
- 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 +231 -0
- package/src/react-native/internal/NativeZyzz.ts +20 -0
- package/src/react-native/internal/Scalar.ts +13 -2
- package/src/react-native/internal/Store.ts +78 -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 +211 -19
- package/src/react-native/reanimated.ts +162 -0
- package/src/runtime/NativeContext.ts +88 -17
- package/src/runtime/NativeDynamic.ts +35 -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),
|
|
@@ -606,12 +607,21 @@ type Merged<style> = {
|
|
|
606
607
|
type StyleArray<style extends object> = Array<
|
|
607
608
|
style | Falsy | readonly (style | Falsy)[] | StyleArray<style>
|
|
608
609
|
>
|
|
609
|
-
type Length =
|
|
610
|
+
type Length =
|
|
611
|
+
| 0
|
|
612
|
+
| '0'
|
|
613
|
+
| `${number}px`
|
|
614
|
+
| `${number}rem`
|
|
615
|
+
| `${'c' | 'C'}${'a' | 'A'}${'l' | 'L'}${'c' | 'C'}(${string})`
|
|
610
616
|
type Box =
|
|
611
617
|
| Length
|
|
612
618
|
| `${Length} ${Length}`
|
|
613
619
|
| `${Length} ${Length} ${Length}`
|
|
614
|
-
|
|
|
620
|
+
| Box4
|
|
621
|
+
// Distribute the first item to keep case-insensitive calculations below the template expansion limit.
|
|
622
|
+
type Box4<first extends string | number = Length> = first extends unknown
|
|
623
|
+
? `${first} ${Length} ${Length} ${Length}`
|
|
624
|
+
: never
|
|
615
625
|
type Weight =
|
|
616
626
|
| 'bold'
|
|
617
627
|
| 'normal'
|
|
@@ -757,50 +767,24 @@ function resolve(
|
|
|
757
767
|
scheme: ColorScheme,
|
|
758
768
|
path: readonly string[],
|
|
759
769
|
resolution: { schemeIndependent: boolean },
|
|
770
|
+
options: compile.Options,
|
|
760
771
|
): number | string {
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
772
|
+
try {
|
|
773
|
+
return Tokens.resolve(value, {
|
|
774
|
+
...options,
|
|
775
|
+
metadata,
|
|
776
|
+
colorScheme: scheme,
|
|
777
|
+
resolution,
|
|
778
|
+
})
|
|
779
|
+
} catch (error) {
|
|
766
780
|
fail(
|
|
767
|
-
|
|
768
|
-
|
|
781
|
+
error instanceof Tokens.UnsupportedError
|
|
782
|
+
? 'unsupported_feature'
|
|
783
|
+
: 'unsupported_value',
|
|
784
|
+
(error as Error).message,
|
|
769
785
|
path,
|
|
770
786
|
)
|
|
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
787
|
}
|
|
803
|
-
return resolved
|
|
804
788
|
}
|
|
805
789
|
|
|
806
790
|
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,231 @@
|
|
|
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 Subscription from '../react.js'
|
|
9
|
+
import * as Viewport from './Viewport.js'
|
|
10
|
+
|
|
11
|
+
/** Creates native authoring helpers and a Provider with automatic window subscriptions. */
|
|
12
|
+
export function defineConfig<const options extends Config.create.Options = {}>(
|
|
13
|
+
options: options & Parameters<typeof Config.create<options>>[0] = {} as never,
|
|
14
|
+
): Subscription.defineConfig.ReturnType<options> {
|
|
15
|
+
const config = Subscription.defineConfig<options>(options)
|
|
16
|
+
return Object.freeze({
|
|
17
|
+
...config,
|
|
18
|
+
Provider: dimensions(config.Provider),
|
|
19
|
+
}) as Subscription.defineConfig.ReturnType<options>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Provides selected variables, appearance, and automatic native dimensions. */
|
|
23
|
+
export const Provider = dimensions(Subscription.Provider)
|
|
24
|
+
|
|
25
|
+
export { useStyles, useVars, withStyles } from '../react.js'
|
|
26
|
+
|
|
27
|
+
function dimensions<props extends Subscription.Provider.Props>(
|
|
28
|
+
Component: React.FunctionComponent<props>,
|
|
29
|
+
): React.FunctionComponent<props> {
|
|
30
|
+
return function WindowProvider(props: props) {
|
|
31
|
+
const window = ReactNative.useWindowDimensions()
|
|
32
|
+
const viewport = React.useMemo(
|
|
33
|
+
() => ({ height: window.height, width: window.width }),
|
|
34
|
+
[window.height, window.width],
|
|
35
|
+
)
|
|
36
|
+
return React.createElement(
|
|
37
|
+
Viewport.context.Provider,
|
|
38
|
+
{ value: viewport },
|
|
39
|
+
React.createElement(Component, props),
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Compiler-only binding path, with React subscriptions for Expo Go. */
|
|
45
|
+
export const useNativeStyles = useBindings
|
|
46
|
+
|
|
47
|
+
type Instance = {
|
|
48
|
+
readonly __internalInstanceHandle?:
|
|
49
|
+
| {
|
|
50
|
+
readonly stateNode?: { readonly node?: object | undefined } | undefined
|
|
51
|
+
}
|
|
52
|
+
| undefined
|
|
53
|
+
readonly getNativeScrollRef?: (() => Instance) | undefined
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function useBindings() {
|
|
57
|
+
const owner = React.useContext(Store.context)
|
|
58
|
+
const [fallback] = React.useState(() =>
|
|
59
|
+
Store.create({
|
|
60
|
+
colorScheme: 'light',
|
|
61
|
+
viewport: ReactNative.Dimensions.get('window'),
|
|
62
|
+
}),
|
|
63
|
+
)
|
|
64
|
+
const store = owner ?? fallback
|
|
65
|
+
const [, render] = React.useReducer((count: number) => count + 1, 0)
|
|
66
|
+
const [refs] = React.useState(
|
|
67
|
+
() =>
|
|
68
|
+
new Map<
|
|
69
|
+
Instance,
|
|
70
|
+
{
|
|
71
|
+
active: boolean
|
|
72
|
+
ref: React.Ref<Instance> | undefined
|
|
73
|
+
release: () => void
|
|
74
|
+
}
|
|
75
|
+
>(),
|
|
76
|
+
)
|
|
77
|
+
React.useLayoutEffect(() => {
|
|
78
|
+
for (const [instance, entry] of refs) {
|
|
79
|
+
if (entry.active) continue
|
|
80
|
+
entry.release()
|
|
81
|
+
refs.delete(instance)
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
React.useLayoutEffect(
|
|
85
|
+
() => () => {
|
|
86
|
+
for (const entry of refs.values()) entry.release()
|
|
87
|
+
refs.clear()
|
|
88
|
+
},
|
|
89
|
+
[refs],
|
|
90
|
+
)
|
|
91
|
+
return {
|
|
92
|
+
style: NativeContext.application,
|
|
93
|
+
view: (props: Record<string, unknown>) => {
|
|
94
|
+
const snapshot = store.getSnapshot()
|
|
95
|
+
const selected = NativeContext.resolve(props.style, snapshot)
|
|
96
|
+
const initial = NativeContext.key(props.style, snapshot)
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
...props,
|
|
100
|
+
ref: (value: Instance | null) => {
|
|
101
|
+
const ref = props.ref as React.Ref<Instance> | undefined
|
|
102
|
+
if (!value) return
|
|
103
|
+
let entry = refs.get(value)
|
|
104
|
+
if (entry && entry.ref !== ref) {
|
|
105
|
+
entry.release()
|
|
106
|
+
refs.delete(value)
|
|
107
|
+
entry = undefined
|
|
108
|
+
}
|
|
109
|
+
if (!entry) {
|
|
110
|
+
const release = typeof ref === 'function' ? ref(value) : undefined
|
|
111
|
+
if (ref && typeof ref !== 'function') ref.current = value
|
|
112
|
+
entry = {
|
|
113
|
+
active: true,
|
|
114
|
+
ref,
|
|
115
|
+
release: () => {
|
|
116
|
+
if (typeof release === 'function') release()
|
|
117
|
+
else if (typeof ref === 'function') ref(null)
|
|
118
|
+
else if (ref) ref.current = null
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
refs.set(value, entry)
|
|
122
|
+
}
|
|
123
|
+
entry.active = true
|
|
124
|
+
const attached = entry
|
|
125
|
+
|
|
126
|
+
const instance = value.getNativeScrollRef?.() ?? value
|
|
127
|
+
const node = instance.__internalInstanceHandle?.stateNode?.node
|
|
128
|
+
const detach = (() => {
|
|
129
|
+
if (!initial.length && typeof selected !== 'function')
|
|
130
|
+
return () => {}
|
|
131
|
+
if (!NativeZyzz || !node || !selective(selected)) {
|
|
132
|
+
const unsubscribe = store.subscribe(render)
|
|
133
|
+
if (store.getSnapshot() !== snapshot) render()
|
|
134
|
+
return unsubscribe
|
|
135
|
+
}
|
|
136
|
+
const id = NativeZyzz!.attach(node, nativeProps(selected))
|
|
137
|
+
let previous = initial
|
|
138
|
+
const read = (context: NativeContext.Context) => {
|
|
139
|
+
const keys = NativeContext.key(props.style, context)
|
|
140
|
+
if (
|
|
141
|
+
keys.length === previous.length &&
|
|
142
|
+
keys.every((key, index) => Object.is(key, previous[index]))
|
|
143
|
+
)
|
|
144
|
+
return { commit: () => {} }
|
|
145
|
+
const selected = NativeContext.resolve(props.style, context)
|
|
146
|
+
if (!selective(selected)) return { commit: render }
|
|
147
|
+
return {
|
|
148
|
+
commit: () => {
|
|
149
|
+
previous = keys
|
|
150
|
+
},
|
|
151
|
+
patch: {
|
|
152
|
+
id,
|
|
153
|
+
props: nativeProps(selected),
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Context can change between render and committing the host ref.
|
|
158
|
+
const current = read(store.getSnapshot())
|
|
159
|
+
if (current.patch) write([current.patch])
|
|
160
|
+
current.commit()
|
|
161
|
+
const unbind = store.bind(read, write)
|
|
162
|
+
return () => {
|
|
163
|
+
unbind()
|
|
164
|
+
NativeZyzz!.detach(id)
|
|
165
|
+
}
|
|
166
|
+
})()
|
|
167
|
+
const update = () =>
|
|
168
|
+
fallback.update({
|
|
169
|
+
colorScheme: 'light',
|
|
170
|
+
viewport: ReactNative.Dimensions.get('window'),
|
|
171
|
+
})
|
|
172
|
+
const contextual =
|
|
173
|
+
initial.length > 0 || typeof selected === 'function'
|
|
174
|
+
const subscription =
|
|
175
|
+
owner || !contextual
|
|
176
|
+
? undefined
|
|
177
|
+
: ReactNative.Dimensions.addEventListener('change', update)
|
|
178
|
+
if (!owner && contextual) update()
|
|
179
|
+
return () => {
|
|
180
|
+
detach()
|
|
181
|
+
subscription?.remove()
|
|
182
|
+
attached.active = false
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
style: selected,
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function selective(style: unknown): boolean {
|
|
192
|
+
if (typeof style === 'function') return false
|
|
193
|
+
const value = ReactNative.StyleSheet.flatten(
|
|
194
|
+
style as ReactNative.StyleProp<ReactNative.ViewStyle>,
|
|
195
|
+
)
|
|
196
|
+
// Let React Native apply its processors to caller-owned representations.
|
|
197
|
+
// Only color properties are processed by the selective writer below.
|
|
198
|
+
return (
|
|
199
|
+
!value ||
|
|
200
|
+
![
|
|
201
|
+
'aspectRatio',
|
|
202
|
+
'backgroundImage',
|
|
203
|
+
'backgroundPosition',
|
|
204
|
+
'backgroundRepeat',
|
|
205
|
+
'backgroundSize',
|
|
206
|
+
'boxShadow',
|
|
207
|
+
'experimental_backgroundImage',
|
|
208
|
+
'filter',
|
|
209
|
+
'fontVariant',
|
|
210
|
+
'transform',
|
|
211
|
+
'transformOrigin',
|
|
212
|
+
].some((property) => property in value)
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function nativeProps(style: unknown): Record<string, unknown> {
|
|
217
|
+
const flattened =
|
|
218
|
+
ReactNative.StyleSheet.flatten(
|
|
219
|
+
style as ReactNative.StyleProp<ReactNative.ViewStyle>,
|
|
220
|
+
) ?? {}
|
|
221
|
+
const result: Record<string, unknown> = { ...flattened }
|
|
222
|
+
for (const [name, value] of Object.entries(result)) {
|
|
223
|
+
if (name === 'color' || name.endsWith('Color'))
|
|
224
|
+
result[name] = ReactNative.processColor(value as ReactNative.ColorValue)
|
|
225
|
+
}
|
|
226
|
+
return result
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function write(patches: readonly Store.Patch[]) {
|
|
230
|
+
NativeZyzz!.update(patches)
|
|
231
|
+
}
|
|
@@ -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')
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Converts portable scalar values without source parsing or device state. @module */
|
|
2
2
|
import * as Color from './Color.js'
|
|
3
|
+
import * as Calculation from './Calculation.js'
|
|
3
4
|
|
|
4
5
|
/** Portable property domains shared by static and dynamic native conversion. */
|
|
5
6
|
export const properties = {
|
|
@@ -207,7 +208,7 @@ export function convert(
|
|
|
207
208
|
}
|
|
208
209
|
if (kind === 'box' || kind === 'boxSigned') {
|
|
209
210
|
const values =
|
|
210
|
-
typeof value === 'string' ?
|
|
211
|
+
typeof value === 'string' ? Calculation.parts(value) : [value]
|
|
211
212
|
if (!values.length || values.length > 4)
|
|
212
213
|
fail('unsupported_value', 'Use one to four scalar lengths.', path)
|
|
213
214
|
for (const value of values)
|
|
@@ -225,9 +226,19 @@ export function length(
|
|
|
225
226
|
path: readonly string[],
|
|
226
227
|
): number {
|
|
227
228
|
if (value === 0 || value === '0') return 0
|
|
229
|
+
if (typeof value === 'string' && /^\s*calc\(/i.test(value)) {
|
|
230
|
+
const result = Calculation.length(value.trim(), options)
|
|
231
|
+
if (!signed && result < 0)
|
|
232
|
+
fail(
|
|
233
|
+
'unsupported_value',
|
|
234
|
+
'Converted length is outside the native property domain.',
|
|
235
|
+
path,
|
|
236
|
+
)
|
|
237
|
+
return result
|
|
238
|
+
}
|
|
228
239
|
const match =
|
|
229
240
|
typeof value === 'string' &&
|
|
230
|
-
/^([+-]?(?:\d+(?:\.\d+)?|\.\d+))(px|rem)$/.exec(value)
|
|
241
|
+
/^([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:[eE][+-]?\d+)?)(px|rem)$/.exec(value)
|
|
231
242
|
if (!match)
|
|
232
243
|
fail(
|
|
233
244
|
'unsupported_value',
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** Owns scoped React subscriptions and committed native style readers. @module */
|
|
2
|
+
import type * as NativeContext from '../../runtime/NativeContext.js'
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
|
|
5
|
+
/** Internal view update payload shared with the native bridge. */
|
|
6
|
+
export type Patch = {
|
|
7
|
+
/** Runtime-local mounted view identifier. */
|
|
8
|
+
readonly id: number
|
|
9
|
+
/** Processed native style properties. */
|
|
10
|
+
readonly props: Record<string, unknown>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type Selection = {
|
|
14
|
+
readonly commit: () => void
|
|
15
|
+
readonly patch?: Patch | undefined
|
|
16
|
+
}
|
|
17
|
+
type Reader = (value: NativeContext.Context) => Selection
|
|
18
|
+
type Writer = (patches: readonly Patch[]) => void
|
|
19
|
+
|
|
20
|
+
/** Nearest configuration-owned selection. */
|
|
21
|
+
export const context = React.createContext<
|
|
22
|
+
ReturnType<typeof create> | undefined
|
|
23
|
+
>(undefined)
|
|
24
|
+
|
|
25
|
+
/** Creates subscriptions owned by a Provider or a provider-free style consumer. */
|
|
26
|
+
export function create(initial: NativeContext.Context) {
|
|
27
|
+
let snapshot = Object.freeze(initial)
|
|
28
|
+
const bindings = new Map<Reader, Writer>()
|
|
29
|
+
const listeners = new Set<() => void>()
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
bind(read: Reader, write: Writer) {
|
|
33
|
+
bindings.set(read, write)
|
|
34
|
+
return () => {
|
|
35
|
+
bindings.delete(read)
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
getSnapshot: () => snapshot,
|
|
39
|
+
subscribe(listener: () => void) {
|
|
40
|
+
const entry = () => listener()
|
|
41
|
+
listeners.add(entry)
|
|
42
|
+
return () => {
|
|
43
|
+
listeners.delete(entry)
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
update(value: NativeContext.Context) {
|
|
47
|
+
if (
|
|
48
|
+
snapshot.colorScheme === value.colorScheme &&
|
|
49
|
+
snapshot.set === value.set &&
|
|
50
|
+
snapshot.viewport?.height === value.viewport?.height &&
|
|
51
|
+
snapshot.viewport?.width === value.viewport?.width
|
|
52
|
+
)
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
// Resolve every binding before applying a scope change that can fail.
|
|
56
|
+
const selections: Selection[] = []
|
|
57
|
+
const batches = new Map<Writer, Patch[]>()
|
|
58
|
+
for (const [read, write] of bindings) {
|
|
59
|
+
const selection = read(value)
|
|
60
|
+
selections.push(selection)
|
|
61
|
+
if (!selection.patch) continue
|
|
62
|
+
let batch = batches.get(write)
|
|
63
|
+
if (!batch) {
|
|
64
|
+
batch = []
|
|
65
|
+
batches.set(write, batch)
|
|
66
|
+
}
|
|
67
|
+
batch.push(selection.patch)
|
|
68
|
+
}
|
|
69
|
+
for (const [write, patches] of batches) write(patches)
|
|
70
|
+
|
|
71
|
+
snapshot = Object.freeze(value)
|
|
72
|
+
for (const selection of selections) selection.commit()
|
|
73
|
+
// Subscriptions added during notification wait for the next change.
|
|
74
|
+
for (const listener of Array.from(listeners))
|
|
75
|
+
if (listeners.has(listener)) listener()
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
}
|