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
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/** Publishes scoped native selections to Reanimated shared values. @module */
|
|
2
|
+
import * as NativeContext from '../runtime/NativeContext.js'
|
|
3
|
+
import * as NativeVars from '../runtime/NativeVars.js'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
import * as ReactNative from 'react-native'
|
|
6
|
+
import * as Reanimated from 'react-native-reanimated'
|
|
7
|
+
import * as Store from './internal/Store.js'
|
|
8
|
+
import type * as StyleSheet from './StyleSheet.js'
|
|
9
|
+
import type * as Vars from '../Vars.js'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Reads a numeric or color property from a selected, composed native style.
|
|
13
|
+
* @param appliedStyle - Applied native style, including arrays and caller overrides.
|
|
14
|
+
* @param property - Numeric or color property to publish.
|
|
15
|
+
* @returns A shared value updated on style input and Provider selection changes.
|
|
16
|
+
* @throws For unresolved bindings, missing properties, or unsupported property values.
|
|
17
|
+
*/
|
|
18
|
+
export function useAnimatedStyleValue<
|
|
19
|
+
const property extends useAnimatedStyleValue.Property,
|
|
20
|
+
>(
|
|
21
|
+
appliedStyle: StyleSheet.StyleProp<object>,
|
|
22
|
+
property: property,
|
|
23
|
+
): Reanimated.SharedValue<useAnimatedStyleValue.Value<property>> {
|
|
24
|
+
const store = React.useContext(Store.context)
|
|
25
|
+
const read = React.useCallback(() => {
|
|
26
|
+
const resolved = NativeContext.resolve(appliedStyle, store?.getSnapshot())
|
|
27
|
+
const style = ReactNative.StyleSheet.flatten(
|
|
28
|
+
resolved as ReactNative.StyleProp<ReactNative.ViewStyle>,
|
|
29
|
+
)
|
|
30
|
+
const value = style?.[property as keyof ReactNative.ViewStyle]
|
|
31
|
+
const color = property === 'color' || property.endsWith('Color')
|
|
32
|
+
if (
|
|
33
|
+
color
|
|
34
|
+
? typeof value !== 'string' || ReactNative.processColor(value) == null
|
|
35
|
+
: typeof value !== 'number' || !Number.isFinite(value)
|
|
36
|
+
)
|
|
37
|
+
throw new Error(
|
|
38
|
+
`useAnimatedStyleValue requires a numeric or color value for ${property}.`,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
return value as useAnimatedStyleValue.Value<property>
|
|
42
|
+
}, [appliedStyle, property, store])
|
|
43
|
+
|
|
44
|
+
return useSelection(read, store)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Properties supported by scalar style animation bindings. */
|
|
48
|
+
export declare namespace useAnimatedStyleValue {
|
|
49
|
+
/** Numeric native properties and native color properties. */
|
|
50
|
+
type Property = {
|
|
51
|
+
[key in keyof StyleSheet.NativeStyle]-?: key extends
|
|
52
|
+
| 'color'
|
|
53
|
+
| `${string}Color`
|
|
54
|
+
? key
|
|
55
|
+
: Extract<StyleSheet.NativeStyle[key], number> extends never
|
|
56
|
+
? never
|
|
57
|
+
: key
|
|
58
|
+
}[keyof StyleSheet.NativeStyle] &
|
|
59
|
+
string
|
|
60
|
+
/** Color strings or finite numeric selections, excluding layout strings. */
|
|
61
|
+
type Value<property extends Property> = property extends
|
|
62
|
+
| 'color'
|
|
63
|
+
| `${string}Color`
|
|
64
|
+
? string
|
|
65
|
+
: number
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Publishes compiled native variables without subscribing the component to React updates.
|
|
70
|
+
* @param variables - Statically linked variables from the owning configuration.
|
|
71
|
+
* @param select - Optional JS selector, with results compared using Object.is.
|
|
72
|
+
* @returns A shared value containing the selected serializable native values.
|
|
73
|
+
* @throws For a missing Provider, unsupported variable reads, or nonserializable selections.
|
|
74
|
+
*/
|
|
75
|
+
export function useAnimatedVars<const variables extends Vars.Definition>(
|
|
76
|
+
variables: variables,
|
|
77
|
+
select?: undefined,
|
|
78
|
+
): Reanimated.SharedValue<NativeVars.Values<Vars.Extract<variables>>>
|
|
79
|
+
/** Publishes a selected variable value to the UI thread. */
|
|
80
|
+
export function useAnimatedVars<
|
|
81
|
+
const variables extends Vars.Definition,
|
|
82
|
+
selected extends useAnimatedVars.Value,
|
|
83
|
+
>(
|
|
84
|
+
variables: variables,
|
|
85
|
+
select: (values: NativeVars.Values<Vars.Extract<variables>>) => selected,
|
|
86
|
+
): Reanimated.SharedValue<selected>
|
|
87
|
+
export function useAnimatedVars(
|
|
88
|
+
variables: Vars.Definition,
|
|
89
|
+
select: (values: never) => unknown = (values) => values,
|
|
90
|
+
) {
|
|
91
|
+
const store = React.useContext(Store.context)
|
|
92
|
+
if (!store) throw new Error('useAnimatedVars requires a Zyzz Provider.')
|
|
93
|
+
|
|
94
|
+
const read = React.useCallback(
|
|
95
|
+
() => select(NativeVars.read(variables, store.getSnapshot()) as never),
|
|
96
|
+
[select, store, variables],
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return useSelection(read, store)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Serializable selections shared with animation worklets. */
|
|
103
|
+
export declare namespace useAnimatedVars {
|
|
104
|
+
/** Plain native values, arrays, and objects without functions or class instances. */
|
|
105
|
+
type Value =
|
|
106
|
+
| boolean
|
|
107
|
+
| null
|
|
108
|
+
| number
|
|
109
|
+
| readonly Value[]
|
|
110
|
+
| string
|
|
111
|
+
| undefined
|
|
112
|
+
| { readonly [key: string]: Value }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function useSelection<value>(
|
|
116
|
+
read: () => value,
|
|
117
|
+
store: ReturnType<typeof Store.create> | undefined,
|
|
118
|
+
): Reanimated.SharedValue<value> {
|
|
119
|
+
const initial = read()
|
|
120
|
+
serializable(initial)
|
|
121
|
+
const previous = React.useRef(initial)
|
|
122
|
+
const shared = Reanimated.useSharedValue(initial)
|
|
123
|
+
|
|
124
|
+
React.useLayoutEffect(() => {
|
|
125
|
+
const update = () => {
|
|
126
|
+
const next = read()
|
|
127
|
+
if (Object.is(previous.current, next)) return
|
|
128
|
+
serializable(next)
|
|
129
|
+
shared.set(next)
|
|
130
|
+
previous.current = next
|
|
131
|
+
}
|
|
132
|
+
update()
|
|
133
|
+
return store?.subscribe(update)
|
|
134
|
+
}, [read, shared, store])
|
|
135
|
+
|
|
136
|
+
return shared
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function serializable(value: unknown, ancestors = new Set<object>()): void {
|
|
140
|
+
if (value == null || typeof value === 'string' || typeof value === 'boolean')
|
|
141
|
+
return
|
|
142
|
+
if (typeof value === 'number' && Number.isFinite(value)) return
|
|
143
|
+
if (
|
|
144
|
+
typeof value !== 'object' ||
|
|
145
|
+
(Array.isArray(value)
|
|
146
|
+
? Object.getPrototypeOf(value) !== Array.prototype
|
|
147
|
+
: Object.getPrototypeOf(value) !== Object.prototype &&
|
|
148
|
+
Object.getPrototypeOf(value) !== null)
|
|
149
|
+
)
|
|
150
|
+
throw new Error(
|
|
151
|
+
'Animated selections require finite, serializable native values.',
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
if (ancestors.has(value))
|
|
155
|
+
throw new Error('Animated selections cannot contain cycles.')
|
|
156
|
+
if (Object.getOwnPropertySymbols(value).length)
|
|
157
|
+
throw new Error('Animated selections cannot contain symbol properties.')
|
|
158
|
+
|
|
159
|
+
ancestors.add(value)
|
|
160
|
+
for (const entry of Object.values(value)) serializable(entry, ancestors)
|
|
161
|
+
ancestors.delete(value)
|
|
162
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
/** Defers precompiled native selection until a
|
|
1
|
+
/** Defers precompiled native selection until a consumer supplies its scoped selection. @module */
|
|
2
2
|
import type * as Native from './Native.js'
|
|
3
|
+
import * as Media from './internal/NativeMedia.js'
|
|
3
4
|
|
|
4
5
|
const binding = Symbol('zyzz.native.context')
|
|
6
|
+
const dependency = Symbol('zyzz.native.dependency')
|
|
5
7
|
|
|
6
8
|
/** Render-local set and resolved appearance. */
|
|
7
9
|
export type Context = {
|
|
@@ -9,6 +11,8 @@ export type Context = {
|
|
|
9
11
|
readonly colorScheme: 'dark' | 'light'
|
|
10
12
|
/** Named compiled set; omission uses each configuration default. */
|
|
11
13
|
readonly set?: string | undefined
|
|
14
|
+
/** Logical window dimensions supplied by the native adapter. */
|
|
15
|
+
readonly viewport?: Media.Viewport | undefined
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
type Callable = (input: never) => Native.Props<object>
|
|
@@ -25,6 +29,9 @@ export function create<const tables extends Tables>(
|
|
|
25
29
|
Object.keys(tables).length === 1 && defaultVars === 'default'
|
|
26
30
|
? tables.default
|
|
27
31
|
: undefined
|
|
32
|
+
const selections = new Set(
|
|
33
|
+
Object.values(tables).flatMap((table) => [table.dark, table.light]),
|
|
34
|
+
)
|
|
28
35
|
|
|
29
36
|
function select(context: Context, input?: never) {
|
|
30
37
|
const set = context.set ?? defaultVars
|
|
@@ -32,22 +39,12 @@ export function create<const tables extends Tables>(
|
|
|
32
39
|
if (!table) throw new Error(`Unknown native set: ${set}.`)
|
|
33
40
|
return table[context.colorScheme](input!).style
|
|
34
41
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
const defaults = props()
|
|
43
|
-
Object.freeze(defaults.style)
|
|
44
|
-
Object.freeze(defaults)
|
|
45
|
-
const callable = (input?: never) =>
|
|
46
|
-
input === undefined ? defaults : props(input)
|
|
47
|
-
Object.defineProperty(callable, binding, {
|
|
48
|
-
value: select,
|
|
49
|
-
})
|
|
50
|
-
return callable as tables[keyof tables]['light']
|
|
42
|
+
return bind(select, (context) => {
|
|
43
|
+
const table = tables[context.set ?? defaultVars] ?? fallback
|
|
44
|
+
if (!table)
|
|
45
|
+
throw new Error(`Unknown native set: ${context.set ?? defaultVars}.`)
|
|
46
|
+
return selections.size === 1 ? [] : [table[context.colorScheme]]
|
|
47
|
+
}) as tables[keyof tables]['light']
|
|
51
48
|
}
|
|
52
49
|
|
|
53
50
|
/** Resolves generated bindings and arrays while preserving caller-owned native objects. */
|
|
@@ -72,3 +69,77 @@ export function resolve(
|
|
|
72
69
|
return (...args: unknown[]) => resolve(value(...args), context)
|
|
73
70
|
return value
|
|
74
71
|
}
|
|
72
|
+
|
|
73
|
+
/** Selects a precompiled media alternative before applying its scoped styling. */
|
|
74
|
+
export function responsive<
|
|
75
|
+
const profiles extends Readonly<Record<string, Callable>>,
|
|
76
|
+
>(
|
|
77
|
+
queries: readonly Media.Query[],
|
|
78
|
+
profiles: profiles,
|
|
79
|
+
): profiles[keyof profiles] {
|
|
80
|
+
return bind(
|
|
81
|
+
(context, input) => {
|
|
82
|
+
const profile = profiles[Media.select(queries, context.viewport)]
|
|
83
|
+
if (!profile) throw new Error('Native media alternative is missing.')
|
|
84
|
+
return resolve(profile, context, input)
|
|
85
|
+
},
|
|
86
|
+
(context) => {
|
|
87
|
+
const profile = profiles[Media.select(queries, context.viewport)]
|
|
88
|
+
return [profile, ...key(profile, context)]
|
|
89
|
+
},
|
|
90
|
+
) as unknown as profiles[keyof profiles]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Retains a compiled application and its input until a view selects its context. */
|
|
94
|
+
export function application(value: unknown, input?: unknown): unknown {
|
|
95
|
+
return {
|
|
96
|
+
[binding]: (context: Context) => resolve(value, context, input),
|
|
97
|
+
[dependency]: (context: Context) => [
|
|
98
|
+
...key(value, context),
|
|
99
|
+
...key(input, context),
|
|
100
|
+
],
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Returns compiler-owned alternatives consumed by a native style application. */
|
|
105
|
+
export function key(value: unknown, context: Context): readonly unknown[] {
|
|
106
|
+
if (Array.isArray(value)) return value.flatMap((entry) => key(entry, context))
|
|
107
|
+
if (
|
|
108
|
+
value &&
|
|
109
|
+
(typeof value === 'object' || typeof value === 'function') &&
|
|
110
|
+
dependency in value
|
|
111
|
+
) {
|
|
112
|
+
const read = value[dependency]
|
|
113
|
+
if (typeof read === 'function') return read(context)
|
|
114
|
+
}
|
|
115
|
+
if (value && typeof value === 'object' && 'style' in value)
|
|
116
|
+
return key(value.style, context)
|
|
117
|
+
return []
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function bind(
|
|
121
|
+
select: (context: Context, input?: never) => unknown,
|
|
122
|
+
read: (context: Context) => readonly unknown[],
|
|
123
|
+
) {
|
|
124
|
+
function props(input?: never) {
|
|
125
|
+
return {
|
|
126
|
+
style: {
|
|
127
|
+
[binding]: (context: Context) => select(context, input),
|
|
128
|
+
[dependency]: (context: Context) => [
|
|
129
|
+
...read(context),
|
|
130
|
+
...key(input, context),
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const defaults = props()
|
|
136
|
+
Object.freeze(defaults.style)
|
|
137
|
+
Object.freeze(defaults)
|
|
138
|
+
const callable = (input?: never) =>
|
|
139
|
+
input === undefined ? defaults : props(input)
|
|
140
|
+
Object.defineProperties(callable, {
|
|
141
|
+
[binding]: { value: select },
|
|
142
|
+
[dependency]: { value: read },
|
|
143
|
+
})
|
|
144
|
+
return callable
|
|
145
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Selects ordered native fragments and applies runtime scalar payloads. @module */
|
|
2
2
|
import * as Scalar from '../react-native/internal/Scalar.js'
|
|
3
|
+
import * as Calculation from '../react-native/internal/Calculation.js'
|
|
3
4
|
import type * as StyleSheet from '../react-native/StyleSheet.js'
|
|
4
5
|
import * as Native from './Native.js'
|
|
5
6
|
import type * as Recipe from './Recipe.js'
|
|
@@ -45,14 +46,14 @@ export type Program = {
|
|
|
45
46
|
readonly parts: readonly (
|
|
46
47
|
| string
|
|
47
48
|
| number
|
|
48
|
-
| { readonly slot: string }
|
|
49
|
+
| { readonly slot: string; readonly number?: true }
|
|
49
50
|
)[]
|
|
50
51
|
}
|
|
51
52
|
)[]
|
|
52
53
|
}[]
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
/**
|
|
56
|
+
/** Applies generated rules with bounded static caching and fresh scalar results. */
|
|
56
57
|
export function create(
|
|
57
58
|
options: create.Options,
|
|
58
59
|
): Callable<Record<string, unknown>> {
|
|
@@ -63,6 +64,10 @@ export function create(
|
|
|
63
64
|
Object.freeze(value)
|
|
64
65
|
}
|
|
65
66
|
for (const style of Object.values(options.styles)) freeze(style)
|
|
67
|
+
const cache =
|
|
68
|
+
Object.keys(options.program.slots).length || options.payloads?.length
|
|
69
|
+
? undefined
|
|
70
|
+
: new Map<string, StyleSheet.NativeStyle>()
|
|
66
71
|
|
|
67
72
|
return (input = {}) => {
|
|
68
73
|
const bindings: Record<string, string | number> = Object.create(null)
|
|
@@ -98,7 +103,9 @@ export function create(
|
|
|
98
103
|
for (const [axis, choices] of Object.entries(options.axes)) {
|
|
99
104
|
let value = Object.hasOwn(input, axis) ? input[axis] : undefined
|
|
100
105
|
if (value === undefined) {
|
|
101
|
-
value = options.defaults
|
|
106
|
+
value = Object.hasOwn(options.defaults, axis)
|
|
107
|
+
? options.defaults[axis]
|
|
108
|
+
: undefined
|
|
102
109
|
if (value != null && Object.hasOwn(options.defaultPayloads ?? {}, axis))
|
|
103
110
|
value = { [String(value)]: options.defaultPayloads![axis] }
|
|
104
111
|
}
|
|
@@ -107,7 +114,8 @@ export function create(
|
|
|
107
114
|
continue
|
|
108
115
|
}
|
|
109
116
|
let payload: unknown
|
|
110
|
-
|
|
117
|
+
const scoped = typeof value === 'object' && !Array.isArray(value)
|
|
118
|
+
if (scoped) {
|
|
111
119
|
const entries = Object.entries(value)
|
|
112
120
|
if (entries.length !== 1)
|
|
113
121
|
throw new Native.SelectionError(
|
|
@@ -128,12 +136,16 @@ export function create(
|
|
|
128
136
|
(entry) => entry.axis === axis && entry.choice === choice,
|
|
129
137
|
)
|
|
130
138
|
if (definition) bind(definition.slots[0]!, payload)
|
|
131
|
-
else if (
|
|
139
|
+
else if (scoped)
|
|
132
140
|
throw new Native.SelectionError(
|
|
133
141
|
`Static native choice does not accept a payload: ${axis}.`,
|
|
134
142
|
)
|
|
135
143
|
}
|
|
136
144
|
|
|
145
|
+
const key = cache ? JSON.stringify(Object.values(selected)) : undefined
|
|
146
|
+
const cached = key === undefined ? undefined : cache?.get(key)
|
|
147
|
+
if (cached) return { style: input.style ? [cached, input.style] : cached }
|
|
148
|
+
|
|
137
149
|
const output: Record<string, unknown> = {}
|
|
138
150
|
let lineHeight: string | number | undefined
|
|
139
151
|
for (const rule of options.program.rules) {
|
|
@@ -155,9 +167,15 @@ export function create(
|
|
|
155
167
|
if (style.lineHeight !== undefined) lineHeight = undefined
|
|
156
168
|
continue
|
|
157
169
|
}
|
|
158
|
-
const values = step.parts.map((part) =>
|
|
159
|
-
typeof part
|
|
160
|
-
|
|
170
|
+
const values = step.parts.map((part) => {
|
|
171
|
+
if (typeof part !== 'object') return part
|
|
172
|
+
const value = bindings[part.slot]
|
|
173
|
+
if (value !== undefined && part.number && typeof value !== 'number')
|
|
174
|
+
throw new Native.SelectionError(
|
|
175
|
+
'Native numeric bindings require numbers.',
|
|
176
|
+
)
|
|
177
|
+
return value
|
|
178
|
+
})
|
|
161
179
|
if (values.some((value) => value === undefined))
|
|
162
180
|
throw new Native.SelectionError(
|
|
163
181
|
'Native binding program references an unbound slot.',
|
|
@@ -176,7 +194,7 @@ export function create(
|
|
|
176
194
|
)
|
|
177
195
|
if (property === 'padding' || property === 'margin') {
|
|
178
196
|
const parts =
|
|
179
|
-
typeof value === 'string' ?
|
|
197
|
+
typeof value === 'string' ? Calculation.parts(value) : [value]
|
|
180
198
|
const [top, right = top, bottom = top, left = right] = parts.map(
|
|
181
199
|
(part) =>
|
|
182
200
|
Scalar.length(part, options, property === 'margin', [property]),
|
|
@@ -187,6 +205,13 @@ export function create(
|
|
|
187
205
|
[`${property}Bottom`]: bottom,
|
|
188
206
|
[`${property}Left`]: left,
|
|
189
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 })
|
|
190
215
|
} else if (property === 'borderColor' || property === 'borderWidth') {
|
|
191
216
|
for (const side of ['Top', 'Right', 'Bottom', 'Left'])
|
|
192
217
|
output[
|
|
@@ -227,6 +252,13 @@ export function create(
|
|
|
227
252
|
])
|
|
228
253
|
}
|
|
229
254
|
const style = output as StyleSheet.NativeStyle
|
|
255
|
+
if (cache && key !== undefined) {
|
|
256
|
+
Object.freeze(style)
|
|
257
|
+
// Bound retained selections without limiting the recipe's choice space.
|
|
258
|
+
if (cache.size >= 256) cache.delete(cache.keys().next().value!)
|
|
259
|
+
cache.set(key, style)
|
|
260
|
+
}
|
|
261
|
+
|
|
230
262
|
return { style: input.style ? [style, input.style] : style }
|
|
231
263
|
}
|
|
232
264
|
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/** Selects finite native variants from precompiled fragments. @module */
|
|
2
|
+
import * as Native from './Native.js'
|
|
3
|
+
import type * as StyleSheet from '../react-native/StyleSheet.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Binds finite selection data without including dynamic value converters.
|
|
7
|
+
* @param options - Compiler-owned axes, defaults, ordered rules, and native fragments.
|
|
8
|
+
* @returns A callable preserving finite choices and caller-owned native overrides.
|
|
9
|
+
* @throws {Native.SelectionError} For invalid selections, missing fragments, or invalid line heights.
|
|
10
|
+
*/
|
|
11
|
+
export function create<
|
|
12
|
+
const axes extends Readonly<Record<string, readonly string[]>>,
|
|
13
|
+
>(options: create.Options<axes>): Native.Callable<axes> {
|
|
14
|
+
const axes = Object.entries(options.axes)
|
|
15
|
+
const rules = options.rules.map((rule) => ({
|
|
16
|
+
matches: rule.matches.map(([name, choices]) => {
|
|
17
|
+
const axis = axes.findIndex(([key]) => key === name)
|
|
18
|
+
return [
|
|
19
|
+
axis,
|
|
20
|
+
choices.map((choice) => axes[axis]![1].indexOf(choice)),
|
|
21
|
+
] as const
|
|
22
|
+
}),
|
|
23
|
+
steps: rule.steps.map((step) => {
|
|
24
|
+
if (typeof step === 'number') return step
|
|
25
|
+
const style = options.styles[step]
|
|
26
|
+
if (!style)
|
|
27
|
+
throw new Native.SelectionError(
|
|
28
|
+
'Native binding program is missing a static style.',
|
|
29
|
+
)
|
|
30
|
+
freeze(style)
|
|
31
|
+
return style
|
|
32
|
+
}),
|
|
33
|
+
}))
|
|
34
|
+
const cache = new Map<string, StyleSheet.NativeStyle>()
|
|
35
|
+
|
|
36
|
+
return (input = {}) => {
|
|
37
|
+
for (const key of Object.keys(input))
|
|
38
|
+
if (key !== 'style' && !Object.hasOwn(options.axes, key))
|
|
39
|
+
throw new Native.SelectionError(`Unknown native recipe input: ${key}.`)
|
|
40
|
+
const selected: number[] = []
|
|
41
|
+
let key = ''
|
|
42
|
+
const values: Readonly<Record<string, unknown>> = input
|
|
43
|
+
for (const [axis, choices] of axes) {
|
|
44
|
+
const supplied = Object.hasOwn(values, axis) ? values[axis] : undefined
|
|
45
|
+
const value =
|
|
46
|
+
supplied === undefined
|
|
47
|
+
? Object.hasOwn(options.defaults, axis)
|
|
48
|
+
? options.defaults[axis]
|
|
49
|
+
: undefined
|
|
50
|
+
: supplied
|
|
51
|
+
if (
|
|
52
|
+
value !== null &&
|
|
53
|
+
typeof value === 'object' &&
|
|
54
|
+
!Array.isArray(value)
|
|
55
|
+
) {
|
|
56
|
+
const entries = Object.keys(value)
|
|
57
|
+
if (entries.length !== 1)
|
|
58
|
+
throw new Native.SelectionError(
|
|
59
|
+
'Native payloads require exactly one choice.',
|
|
60
|
+
)
|
|
61
|
+
if (choices.includes(entries[0]!))
|
|
62
|
+
throw new Native.SelectionError(
|
|
63
|
+
`Static native choice does not accept a payload: ${axis}.`,
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
const choice =
|
|
67
|
+
value == null
|
|
68
|
+
? choices.length
|
|
69
|
+
: typeof value === 'string' || typeof value === 'boolean'
|
|
70
|
+
? choices.indexOf(String(value))
|
|
71
|
+
: -1
|
|
72
|
+
if (choice < 0)
|
|
73
|
+
throw new Native.SelectionError(
|
|
74
|
+
`Unknown native recipe choice for ${axis}.`,
|
|
75
|
+
)
|
|
76
|
+
selected.push(choice)
|
|
77
|
+
key += `${choice},`
|
|
78
|
+
}
|
|
79
|
+
let style = cache.get(key)
|
|
80
|
+
if (!style) {
|
|
81
|
+
const output: Record<string, unknown> = {}
|
|
82
|
+
let lineHeight: number | undefined
|
|
83
|
+
for (const rule of rules) {
|
|
84
|
+
if (
|
|
85
|
+
!rule.matches.every(([axis, choices]) =>
|
|
86
|
+
choices.includes(selected[axis]!),
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
continue
|
|
90
|
+
for (const step of rule.steps) {
|
|
91
|
+
if (typeof step === 'number') lineHeight = step
|
|
92
|
+
else {
|
|
93
|
+
Object.assign(output, step)
|
|
94
|
+
if (step.lineHeight !== undefined) lineHeight = undefined
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (lineHeight !== undefined) {
|
|
99
|
+
if (
|
|
100
|
+
!Number.isFinite(lineHeight) ||
|
|
101
|
+
lineHeight < 0 ||
|
|
102
|
+
typeof output.fontSize !== 'number'
|
|
103
|
+
)
|
|
104
|
+
throw new Native.SelectionError(
|
|
105
|
+
'Numeric lineHeight requires an explicit fontSize and a nonnegative finite multiplier.',
|
|
106
|
+
)
|
|
107
|
+
output.lineHeight = lineHeight * output.fontSize
|
|
108
|
+
if (!Number.isFinite(output.lineHeight))
|
|
109
|
+
throw new Native.SelectionError(
|
|
110
|
+
'Converted lineHeight must be finite.',
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
style = Object.freeze(output) as StyleSheet.NativeStyle
|
|
114
|
+
// Retain a bounded working set even for recipes with many independent axes.
|
|
115
|
+
if (cache.size >= 256) cache.delete(cache.keys().next().value!)
|
|
116
|
+
cache.set(key, style)
|
|
117
|
+
}
|
|
118
|
+
return { style: input.style ? [style, input.style] : style }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Compiler-owned finite metadata and converted native fragments. */
|
|
123
|
+
export declare namespace create {
|
|
124
|
+
/** Numeric steps are line-height multipliers resolved against the final font size. */
|
|
125
|
+
type Options<
|
|
126
|
+
axes extends Readonly<Record<string, readonly string[]>> = Readonly<
|
|
127
|
+
Record<string, readonly string[]>
|
|
128
|
+
>,
|
|
129
|
+
> = Native.create.Options<axes> & {
|
|
130
|
+
/** Ordered rules containing static fragment names and line-height multipliers. */
|
|
131
|
+
readonly rules: readonly {
|
|
132
|
+
/** Axis names and allowed choices, all of which must match. */
|
|
133
|
+
readonly matches: readonly (readonly [string, readonly string[]])[]
|
|
134
|
+
/** Fragment names or line-height multipliers applied in order. */
|
|
135
|
+
readonly steps: readonly (string | number)[]
|
|
136
|
+
}[]
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function freeze(value: object): void {
|
|
141
|
+
if (Object.isFrozen(value)) return
|
|
142
|
+
for (const child of Object.values(value))
|
|
143
|
+
if (child && typeof child === 'object') freeze(child)
|
|
144
|
+
Object.freeze(value)
|
|
145
|
+
}
|