@astryxdesign/native 0.7.4-canary.720bb10 → 0.7.5-canary.04434e7
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/README.md +116 -4
- package/apple/Sources/AstryxNative/Generated/AstryxBase.swift +1 -1
- package/apple/Sources/AstryxNative/Generated/AstryxNode.swift +69 -0
- package/apple/Sources/AstryxNative/Generated/AstryxWire.swift +67 -0
- package/apple/Sources/AstryxNative/Generated/Badge.swift +89 -0
- package/apple/Sources/AstryxNative/Generated/Button.swift +154 -0
- package/apple/Sources/AstryxNative/Generated/Card.swift +117 -0
- package/apple/Sources/AstryxNative/Generated/Divider.swift +97 -0
- package/apple/Sources/AstryxNative/Generated/HStack.swift +308 -0
- package/apple/Sources/AstryxNative/Generated/Heading.swift +169 -0
- package/apple/Sources/AstryxNative/Generated/ProgressBar.swift +144 -0
- package/apple/Sources/AstryxNative/Generated/Selector.swift +133 -9
- package/apple/Sources/AstryxNative/Generated/Switch.swift +202 -0
- package/apple/Sources/AstryxNative/Generated/Text.swift +172 -0
- package/apple/Sources/AstryxNative/Generated/TextInput.swift +243 -0
- package/apple/Sources/AstryxNative/Generated/VStack.swift +308 -0
- package/apple/Sources/AstryxNative/Logic/AstryxLogicHost.swift +102 -0
- package/apple/Tests/AstryxNativeTests/ControlPropsTests.swift +29 -0
- package/apple/Tests/AstryxNativeTests/LogicHostTests.swift +85 -0
- package/apple/Tests/AstryxNativeTests/WireTests.swift +53 -0
- package/dist/components/Badge.d.ts +10 -0
- package/dist/components/Badge.d.ts.map +1 -0
- package/dist/components/Badge.js +7 -0
- package/dist/components/Button.d.ts +10 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +15 -0
- package/dist/components/Card.d.ts +10 -0
- package/dist/components/Card.d.ts.map +1 -0
- package/dist/components/Card.js +7 -0
- package/dist/components/Divider.d.ts +10 -0
- package/dist/components/Divider.d.ts.map +1 -0
- package/dist/components/Divider.js +6 -0
- package/dist/components/HStack.d.ts +10 -0
- package/dist/components/HStack.d.ts.map +1 -0
- package/dist/components/HStack.js +9 -0
- package/dist/components/Heading.d.ts +10 -0
- package/dist/components/Heading.d.ts.map +1 -0
- package/dist/components/Heading.js +14 -0
- package/dist/components/ProgressBar.d.ts +10 -0
- package/dist/components/ProgressBar.d.ts.map +1 -0
- package/dist/components/ProgressBar.js +5 -0
- package/dist/components/Switch.d.ts +10 -0
- package/dist/components/Switch.d.ts.map +1 -0
- package/dist/components/Switch.js +6 -0
- package/dist/components/Text.d.ts +10 -0
- package/dist/components/Text.d.ts.map +1 -0
- package/dist/components/Text.js +13 -0
- package/dist/components/TextInput.d.ts +10 -0
- package/dist/components/TextInput.d.ts.map +1 -0
- package/dist/components/TextInput.js +9 -0
- package/dist/components/VStack.d.ts +10 -0
- package/dist/components/VStack.d.ts.map +1 -0
- package/dist/components/VStack.js +9 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +25 -1
- package/dist/components/stackShared.d.ts +9 -0
- package/dist/components/stackShared.d.ts.map +1 -0
- package/dist/components/stackShared.js +4 -0
- package/dist/contract/fromComponentDoc.d.ts +44 -5
- package/dist/contract/fromComponentDoc.d.ts.map +1 -1
- package/dist/contract/fromComponentDoc.js +47 -4
- package/dist/contract/generateSwift.d.ts.map +1 -1
- package/dist/contract/generateSwift.js +193 -10
- package/dist/contract/generateTypeScript.d.ts.map +1 -1
- package/dist/contract/generateTypeScript.js +69 -9
- package/dist/contract/index.d.ts +7 -3
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +2 -1
- package/dist/contract/parseDocType.d.ts +5 -2
- package/dist/contract/parseDocType.d.ts.map +1 -1
- package/dist/contract/parseDocType.js +34 -5
- package/dist/contract/rules.d.ts +5 -2
- package/dist/contract/rules.d.ts.map +1 -1
- package/dist/contract/rules.js +8 -4
- package/dist/contract/types.d.ts +11 -0
- package/dist/contract/types.d.ts.map +1 -1
- package/dist/logic/connectBridge.d.ts +29 -0
- package/dist/logic/connectBridge.d.ts.map +1 -0
- package/dist/logic/connectBridge.js +32 -0
- package/dist/logic/createLogicCore.d.ts +44 -0
- package/dist/logic/createLogicCore.d.ts.map +1 -0
- package/dist/logic/createLogicCore.js +40 -0
- package/dist/logic/index.d.ts +12 -0
- package/dist/logic/index.d.ts.map +1 -0
- package/dist/logic/index.js +10 -0
- package/dist/types/android.d.ts +136 -2
- package/dist/types/core.d.ts +150 -0
- package/dist/types/ios.d.ts +136 -2
- package/dist/types/macos.d.ts +136 -2
- package/dist/types/shared.d.ts +136 -2
- package/dist/types/windows.d.ts +136 -2
- package/package.json +10 -4
- package/src/components/Badge.ts +18 -0
- package/src/components/Button.ts +26 -0
- package/src/components/Card.ts +18 -0
- package/src/components/Divider.ts +17 -0
- package/src/components/HStack.ts +20 -0
- package/src/components/Heading.ts +25 -0
- package/src/components/ProgressBar.ts +16 -0
- package/src/components/Switch.ts +17 -0
- package/src/components/Text.ts +24 -0
- package/src/components/TextInput.ts +20 -0
- package/src/components/VStack.ts +20 -0
- package/src/components/index.ts +25 -1
- package/src/components/stackShared.ts +14 -0
- package/src/contract/contracts.test.ts +58 -0
- package/src/contract/fromComponentDoc.test.ts +97 -0
- package/src/contract/fromComponentDoc.ts +106 -9
- package/src/contract/generateSwift.ts +223 -10
- package/src/contract/generateTypeScript.ts +83 -10
- package/src/contract/index.ts +13 -4
- package/src/contract/parseDocType.test.ts +45 -0
- package/src/contract/parseDocType.ts +41 -6
- package/src/contract/rules.ts +11 -4
- package/src/contract/types.ts +6 -0
- package/src/logic/connectBridge.ts +72 -0
- package/src/logic/createLogicCore.ts +97 -0
- package/src/logic/index.ts +19 -0
- package/src/logic/logic.test.ts +160 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// Generated by @astryxdesign/native. Do not edit.
|
|
2
|
+
public enum VStackGap: Double, CaseIterable, Codable, Sendable {
|
|
3
|
+
case v0 = 0
|
|
4
|
+
case v0_5 = 0.5
|
|
5
|
+
case v1 = 1
|
|
6
|
+
case v1_5 = 1.5
|
|
7
|
+
case v2 = 2
|
|
8
|
+
case v3 = 3
|
|
9
|
+
case v4 = 4
|
|
10
|
+
case v5 = 5
|
|
11
|
+
case v6 = 6
|
|
12
|
+
case v8 = 8
|
|
13
|
+
case v10 = 10
|
|
14
|
+
}
|
|
15
|
+
public enum VStackPadding: Double, CaseIterable, Codable, Sendable {
|
|
16
|
+
case v0 = 0
|
|
17
|
+
case v0_5 = 0.5
|
|
18
|
+
case v1 = 1
|
|
19
|
+
case v1_5 = 1.5
|
|
20
|
+
case v2 = 2
|
|
21
|
+
case v3 = 3
|
|
22
|
+
case v4 = 4
|
|
23
|
+
case v5 = 5
|
|
24
|
+
case v6 = 6
|
|
25
|
+
case v8 = 8
|
|
26
|
+
case v10 = 10
|
|
27
|
+
}
|
|
28
|
+
public enum VStackPaddingInline: Double, CaseIterable, Codable, Sendable {
|
|
29
|
+
case v0 = 0
|
|
30
|
+
case v0_5 = 0.5
|
|
31
|
+
case v1 = 1
|
|
32
|
+
case v1_5 = 1.5
|
|
33
|
+
case v2 = 2
|
|
34
|
+
case v3 = 3
|
|
35
|
+
case v4 = 4
|
|
36
|
+
case v5 = 5
|
|
37
|
+
case v6 = 6
|
|
38
|
+
case v8 = 8
|
|
39
|
+
case v10 = 10
|
|
40
|
+
}
|
|
41
|
+
public enum VStackPaddingInlineStart: Double, CaseIterable, Codable, Sendable {
|
|
42
|
+
case v0 = 0
|
|
43
|
+
case v0_5 = 0.5
|
|
44
|
+
case v1 = 1
|
|
45
|
+
case v1_5 = 1.5
|
|
46
|
+
case v2 = 2
|
|
47
|
+
case v3 = 3
|
|
48
|
+
case v4 = 4
|
|
49
|
+
case v5 = 5
|
|
50
|
+
case v6 = 6
|
|
51
|
+
case v8 = 8
|
|
52
|
+
case v10 = 10
|
|
53
|
+
}
|
|
54
|
+
public enum VStackPaddingInlineEnd: Double, CaseIterable, Codable, Sendable {
|
|
55
|
+
case v0 = 0
|
|
56
|
+
case v0_5 = 0.5
|
|
57
|
+
case v1 = 1
|
|
58
|
+
case v1_5 = 1.5
|
|
59
|
+
case v2 = 2
|
|
60
|
+
case v3 = 3
|
|
61
|
+
case v4 = 4
|
|
62
|
+
case v5 = 5
|
|
63
|
+
case v6 = 6
|
|
64
|
+
case v8 = 8
|
|
65
|
+
case v10 = 10
|
|
66
|
+
}
|
|
67
|
+
public enum VStackPaddingBlock: Double, CaseIterable, Codable, Sendable {
|
|
68
|
+
case v0 = 0
|
|
69
|
+
case v0_5 = 0.5
|
|
70
|
+
case v1 = 1
|
|
71
|
+
case v1_5 = 1.5
|
|
72
|
+
case v2 = 2
|
|
73
|
+
case v3 = 3
|
|
74
|
+
case v4 = 4
|
|
75
|
+
case v5 = 5
|
|
76
|
+
case v6 = 6
|
|
77
|
+
case v8 = 8
|
|
78
|
+
case v10 = 10
|
|
79
|
+
}
|
|
80
|
+
public enum VStackPaddingBlockStart: Double, CaseIterable, Codable, Sendable {
|
|
81
|
+
case v0 = 0
|
|
82
|
+
case v0_5 = 0.5
|
|
83
|
+
case v1 = 1
|
|
84
|
+
case v1_5 = 1.5
|
|
85
|
+
case v2 = 2
|
|
86
|
+
case v3 = 3
|
|
87
|
+
case v4 = 4
|
|
88
|
+
case v5 = 5
|
|
89
|
+
case v6 = 6
|
|
90
|
+
case v8 = 8
|
|
91
|
+
case v10 = 10
|
|
92
|
+
}
|
|
93
|
+
public enum VStackPaddingBlockEnd: Double, CaseIterable, Codable, Sendable {
|
|
94
|
+
case v0 = 0
|
|
95
|
+
case v0_5 = 0.5
|
|
96
|
+
case v1 = 1
|
|
97
|
+
case v1_5 = 1.5
|
|
98
|
+
case v2 = 2
|
|
99
|
+
case v3 = 3
|
|
100
|
+
case v4 = 4
|
|
101
|
+
case v5 = 5
|
|
102
|
+
case v6 = 6
|
|
103
|
+
case v8 = 8
|
|
104
|
+
case v10 = 10
|
|
105
|
+
}
|
|
106
|
+
public enum VStackHAlign: String, CaseIterable, Codable, Sendable {
|
|
107
|
+
case start = "start"
|
|
108
|
+
case center = "center"
|
|
109
|
+
case end = "end"
|
|
110
|
+
case stretch = "stretch"
|
|
111
|
+
}
|
|
112
|
+
public enum VStackVAlign: String, CaseIterable, Codable, Sendable {
|
|
113
|
+
case start = "start"
|
|
114
|
+
case center = "center"
|
|
115
|
+
case end = "end"
|
|
116
|
+
case between = "between"
|
|
117
|
+
case around = "around"
|
|
118
|
+
case evenly = "evenly"
|
|
119
|
+
}
|
|
120
|
+
public enum VStackJustify: String, CaseIterable, Codable, Sendable {
|
|
121
|
+
case start = "start"
|
|
122
|
+
case center = "center"
|
|
123
|
+
case end = "end"
|
|
124
|
+
case between = "between"
|
|
125
|
+
case around = "around"
|
|
126
|
+
case evenly = "evenly"
|
|
127
|
+
}
|
|
128
|
+
public enum VStackAlign: String, CaseIterable, Codable, Sendable {
|
|
129
|
+
case start = "start"
|
|
130
|
+
case center = "center"
|
|
131
|
+
case end = "end"
|
|
132
|
+
case stretch = "stretch"
|
|
133
|
+
}
|
|
134
|
+
public enum VStackWrap: String, CaseIterable, Codable, Sendable {
|
|
135
|
+
case nowrap = "nowrap"
|
|
136
|
+
case wrap = "wrap"
|
|
137
|
+
case wrapReverse = "wrap-reverse"
|
|
138
|
+
}
|
|
139
|
+
#if os(iOS)
|
|
140
|
+
public struct VStackProps {
|
|
141
|
+
/// Base props attach to this part on every platform.
|
|
142
|
+
public static let baseTarget: AstryxBaseTarget = .container
|
|
143
|
+
public var base: AstryxBaseProps
|
|
144
|
+
public var gap: VStackGap?
|
|
145
|
+
public var padding: VStackPadding?
|
|
146
|
+
public var paddingInline: VStackPaddingInline?
|
|
147
|
+
public var paddingInlineStart: VStackPaddingInlineStart?
|
|
148
|
+
public var paddingInlineEnd: VStackPaddingInlineEnd?
|
|
149
|
+
public var paddingBlock: VStackPaddingBlock?
|
|
150
|
+
public var paddingBlockStart: VStackPaddingBlockStart?
|
|
151
|
+
public var paddingBlockEnd: VStackPaddingBlockEnd?
|
|
152
|
+
public var isScrollable: Bool?
|
|
153
|
+
public var hAlign: VStackHAlign?
|
|
154
|
+
public var vAlign: VStackVAlign?
|
|
155
|
+
public var justify: VStackJustify?
|
|
156
|
+
public var align: VStackAlign?
|
|
157
|
+
public var wrap: VStackWrap?
|
|
158
|
+
public init(gap: VStackGap? = nil, padding: VStackPadding? = nil, paddingInline: VStackPaddingInline? = nil, paddingInlineStart: VStackPaddingInlineStart? = nil, paddingInlineEnd: VStackPaddingInlineEnd? = nil, paddingBlock: VStackPaddingBlock? = nil, paddingBlockStart: VStackPaddingBlockStart? = nil, paddingBlockEnd: VStackPaddingBlockEnd? = nil, isScrollable: Bool? = nil, hAlign: VStackHAlign? = nil, vAlign: VStackVAlign? = nil, justify: VStackJustify? = nil, align: VStackAlign? = nil, wrap: VStackWrap? = nil, base: AstryxBaseProps = AstryxBaseProps()) {
|
|
159
|
+
self.gap = gap
|
|
160
|
+
self.padding = padding
|
|
161
|
+
self.paddingInline = paddingInline
|
|
162
|
+
self.paddingInlineStart = paddingInlineStart
|
|
163
|
+
self.paddingInlineEnd = paddingInlineEnd
|
|
164
|
+
self.paddingBlock = paddingBlock
|
|
165
|
+
self.paddingBlockStart = paddingBlockStart
|
|
166
|
+
self.paddingBlockEnd = paddingBlockEnd
|
|
167
|
+
self.isScrollable = isScrollable
|
|
168
|
+
self.hAlign = hAlign
|
|
169
|
+
self.vAlign = vAlign
|
|
170
|
+
self.justify = justify
|
|
171
|
+
self.align = align
|
|
172
|
+
self.wrap = wrap
|
|
173
|
+
self.base = base
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
public struct VStackNode: Decodable, Equatable, Sendable {
|
|
177
|
+
public struct Props: Decodable, Equatable, Sendable {
|
|
178
|
+
public var gap: VStackGap?
|
|
179
|
+
public var padding: VStackPadding?
|
|
180
|
+
public var paddingInline: VStackPaddingInline?
|
|
181
|
+
public var paddingInlineStart: VStackPaddingInlineStart?
|
|
182
|
+
public var paddingInlineEnd: VStackPaddingInlineEnd?
|
|
183
|
+
public var paddingBlock: VStackPaddingBlock?
|
|
184
|
+
public var paddingBlockStart: VStackPaddingBlockStart?
|
|
185
|
+
public var paddingBlockEnd: VStackPaddingBlockEnd?
|
|
186
|
+
public var isScrollable: Bool?
|
|
187
|
+
public var hAlign: VStackHAlign?
|
|
188
|
+
public var vAlign: VStackVAlign?
|
|
189
|
+
public var justify: VStackJustify?
|
|
190
|
+
public var align: VStackAlign?
|
|
191
|
+
public var wrap: VStackWrap?
|
|
192
|
+
public var children: [AstryxNode]?
|
|
193
|
+
}
|
|
194
|
+
public let key: String
|
|
195
|
+
public let base: AstryxBaseProps?
|
|
196
|
+
public let props: Props
|
|
197
|
+
/// The callbacks the core listens to, by prop name.
|
|
198
|
+
public let events: [String]?
|
|
199
|
+
|
|
200
|
+
public func listens(_ event: String) -> Bool { events?.contains(event) ?? false }
|
|
201
|
+
|
|
202
|
+
/// The contract a VStack view takes; its callbacks send actions to the core.
|
|
203
|
+
public func viewProps(send: @escaping @Sendable (AstryxAction) -> Void) -> VStackProps {
|
|
204
|
+
return VStackProps(
|
|
205
|
+
gap: props.gap,
|
|
206
|
+
padding: props.padding,
|
|
207
|
+
paddingInline: props.paddingInline,
|
|
208
|
+
paddingInlineStart: props.paddingInlineStart,
|
|
209
|
+
paddingInlineEnd: props.paddingInlineEnd,
|
|
210
|
+
paddingBlock: props.paddingBlock,
|
|
211
|
+
paddingBlockStart: props.paddingBlockStart,
|
|
212
|
+
paddingBlockEnd: props.paddingBlockEnd,
|
|
213
|
+
isScrollable: props.isScrollable,
|
|
214
|
+
hAlign: props.hAlign,
|
|
215
|
+
vAlign: props.vAlign,
|
|
216
|
+
justify: props.justify,
|
|
217
|
+
align: props.align,
|
|
218
|
+
wrap: props.wrap,
|
|
219
|
+
base: base ?? AstryxBaseProps()
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
#endif
|
|
224
|
+
#if os(macOS)
|
|
225
|
+
public struct VStackProps {
|
|
226
|
+
/// Base props attach to this part on every platform.
|
|
227
|
+
public static let baseTarget: AstryxBaseTarget = .container
|
|
228
|
+
public var base: AstryxBaseProps
|
|
229
|
+
public var gap: VStackGap?
|
|
230
|
+
public var padding: VStackPadding?
|
|
231
|
+
public var paddingInline: VStackPaddingInline?
|
|
232
|
+
public var paddingInlineStart: VStackPaddingInlineStart?
|
|
233
|
+
public var paddingInlineEnd: VStackPaddingInlineEnd?
|
|
234
|
+
public var paddingBlock: VStackPaddingBlock?
|
|
235
|
+
public var paddingBlockStart: VStackPaddingBlockStart?
|
|
236
|
+
public var paddingBlockEnd: VStackPaddingBlockEnd?
|
|
237
|
+
public var isScrollable: Bool?
|
|
238
|
+
public var hAlign: VStackHAlign?
|
|
239
|
+
public var vAlign: VStackVAlign?
|
|
240
|
+
public var justify: VStackJustify?
|
|
241
|
+
public var align: VStackAlign?
|
|
242
|
+
public var wrap: VStackWrap?
|
|
243
|
+
public init(gap: VStackGap? = nil, padding: VStackPadding? = nil, paddingInline: VStackPaddingInline? = nil, paddingInlineStart: VStackPaddingInlineStart? = nil, paddingInlineEnd: VStackPaddingInlineEnd? = nil, paddingBlock: VStackPaddingBlock? = nil, paddingBlockStart: VStackPaddingBlockStart? = nil, paddingBlockEnd: VStackPaddingBlockEnd? = nil, isScrollable: Bool? = nil, hAlign: VStackHAlign? = nil, vAlign: VStackVAlign? = nil, justify: VStackJustify? = nil, align: VStackAlign? = nil, wrap: VStackWrap? = nil, base: AstryxBaseProps = AstryxBaseProps()) {
|
|
244
|
+
self.gap = gap
|
|
245
|
+
self.padding = padding
|
|
246
|
+
self.paddingInline = paddingInline
|
|
247
|
+
self.paddingInlineStart = paddingInlineStart
|
|
248
|
+
self.paddingInlineEnd = paddingInlineEnd
|
|
249
|
+
self.paddingBlock = paddingBlock
|
|
250
|
+
self.paddingBlockStart = paddingBlockStart
|
|
251
|
+
self.paddingBlockEnd = paddingBlockEnd
|
|
252
|
+
self.isScrollable = isScrollable
|
|
253
|
+
self.hAlign = hAlign
|
|
254
|
+
self.vAlign = vAlign
|
|
255
|
+
self.justify = justify
|
|
256
|
+
self.align = align
|
|
257
|
+
self.wrap = wrap
|
|
258
|
+
self.base = base
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
public struct VStackNode: Decodable, Equatable, Sendable {
|
|
262
|
+
public struct Props: Decodable, Equatable, Sendable {
|
|
263
|
+
public var gap: VStackGap?
|
|
264
|
+
public var padding: VStackPadding?
|
|
265
|
+
public var paddingInline: VStackPaddingInline?
|
|
266
|
+
public var paddingInlineStart: VStackPaddingInlineStart?
|
|
267
|
+
public var paddingInlineEnd: VStackPaddingInlineEnd?
|
|
268
|
+
public var paddingBlock: VStackPaddingBlock?
|
|
269
|
+
public var paddingBlockStart: VStackPaddingBlockStart?
|
|
270
|
+
public var paddingBlockEnd: VStackPaddingBlockEnd?
|
|
271
|
+
public var isScrollable: Bool?
|
|
272
|
+
public var hAlign: VStackHAlign?
|
|
273
|
+
public var vAlign: VStackVAlign?
|
|
274
|
+
public var justify: VStackJustify?
|
|
275
|
+
public var align: VStackAlign?
|
|
276
|
+
public var wrap: VStackWrap?
|
|
277
|
+
public var children: [AstryxNode]?
|
|
278
|
+
}
|
|
279
|
+
public let key: String
|
|
280
|
+
public let base: AstryxBaseProps?
|
|
281
|
+
public let props: Props
|
|
282
|
+
/// The callbacks the core listens to, by prop name.
|
|
283
|
+
public let events: [String]?
|
|
284
|
+
|
|
285
|
+
public func listens(_ event: String) -> Bool { events?.contains(event) ?? false }
|
|
286
|
+
|
|
287
|
+
/// The contract a VStack view takes; its callbacks send actions to the core.
|
|
288
|
+
public func viewProps(send: @escaping @Sendable (AstryxAction) -> Void) -> VStackProps {
|
|
289
|
+
return VStackProps(
|
|
290
|
+
gap: props.gap,
|
|
291
|
+
padding: props.padding,
|
|
292
|
+
paddingInline: props.paddingInline,
|
|
293
|
+
paddingInlineStart: props.paddingInlineStart,
|
|
294
|
+
paddingInlineEnd: props.paddingInlineEnd,
|
|
295
|
+
paddingBlock: props.paddingBlock,
|
|
296
|
+
paddingBlockStart: props.paddingBlockStart,
|
|
297
|
+
paddingBlockEnd: props.paddingBlockEnd,
|
|
298
|
+
isScrollable: props.isScrollable,
|
|
299
|
+
hAlign: props.hAlign,
|
|
300
|
+
vAlign: props.vAlign,
|
|
301
|
+
justify: props.justify,
|
|
302
|
+
align: props.align,
|
|
303
|
+
wrap: props.wrap,
|
|
304
|
+
base: base ?? AstryxBaseProps()
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
#endif
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
//
|
|
3
|
+
// The host side of arm C (AST-082 FR12): a TypeScript logic core runs in a
|
|
4
|
+
// JavaScript engine inside the app and native views draw its tree. Its shape
|
|
5
|
+
// follows the ClientHost of EJ Campbell's astryx-swift spike
|
|
6
|
+
// (github.com/ejc3/astryx-swift, MIT), which runs the core in Hermes. This host
|
|
7
|
+
// uses JavaScriptCore, part of the platform SDK, so the package needs nothing
|
|
8
|
+
// else; a Hermes host speaks the same bridge.
|
|
9
|
+
|
|
10
|
+
#if os(iOS) || os(macOS)
|
|
11
|
+
import Foundation
|
|
12
|
+
import JavaScriptCore
|
|
13
|
+
import Observation
|
|
14
|
+
|
|
15
|
+
/// Runs a logic core bundle and carries its tree to native views and their
|
|
16
|
+
/// actions back, as the JSON `@astryxdesign/native/logic`'s `connectBridge`
|
|
17
|
+
/// speaks.
|
|
18
|
+
///
|
|
19
|
+
/// The bundle defines a global function, `astryxStart` by default, that takes
|
|
20
|
+
/// the host (`connected(version)` and `changed(json)`) and returns the bridge
|
|
21
|
+
/// (`getSnapshot()` and `dispatch(json)`).
|
|
22
|
+
@MainActor
|
|
23
|
+
@Observable
|
|
24
|
+
public final class AstryxLogicHost {
|
|
25
|
+
/// The bridge version this host speaks (`BRIDGE_VERSION` in TypeScript).
|
|
26
|
+
public static let bridgeVersion = 1
|
|
27
|
+
|
|
28
|
+
/// The current tree; nil before the first snapshot and once the host fails.
|
|
29
|
+
public private(set) var root: AstryxNode?
|
|
30
|
+
/// Why the core stopped or a snapshot could not be read.
|
|
31
|
+
public private(set) var failure: String?
|
|
32
|
+
|
|
33
|
+
@ObservationIgnored private let context: JSContext
|
|
34
|
+
@ObservationIgnored private var bridge: JSValue?
|
|
35
|
+
|
|
36
|
+
public init(bundle source: String, entry: String = "astryxStart") {
|
|
37
|
+
context = JSContext()
|
|
38
|
+
context.exceptionHandler = { [weak self] _, exception in
|
|
39
|
+
let message = exception?.toString() ?? "An unknown JavaScript error"
|
|
40
|
+
MainActor.assumeIsolated { self?.fail(message) }
|
|
41
|
+
}
|
|
42
|
+
context.evaluateScript(source)
|
|
43
|
+
guard failure == nil else { return }
|
|
44
|
+
guard let start = context.objectForKeyedSubscript(entry), start.isObject else {
|
|
45
|
+
fail("The bundle defines no \(entry) function")
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
let host = JSValue(newObjectIn: context)
|
|
49
|
+
let connected: @convention(block) (Int) -> Void = { [weak self] version in
|
|
50
|
+
MainActor.assumeIsolated {
|
|
51
|
+
if version != Self.bridgeVersion {
|
|
52
|
+
self?.fail("The core speaks bridge \(version); this host speaks \(Self.bridgeVersion)")
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
let changed: @convention(block) (String) -> Void = { [weak self] json in
|
|
57
|
+
MainActor.assumeIsolated { self?.receive(json) }
|
|
58
|
+
}
|
|
59
|
+
host?.setObject(connected, forKeyedSubscript: "connected" as NSString)
|
|
60
|
+
host?.setObject(changed, forKeyedSubscript: "changed" as NSString)
|
|
61
|
+
bridge = start.call(withArguments: [host as Any])
|
|
62
|
+
guard failure == nil, let snapshot = bridge?.invokeMethod("getSnapshot", withArguments: []),
|
|
63
|
+
snapshot.isString, let json = snapshot.toString() else {
|
|
64
|
+
if failure == nil { fail("The bridge returned no snapshot") }
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
receive(json)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// Sends a view's action to the core: `node.viewProps(send: host.send)`.
|
|
71
|
+
/// Views call it on the main thread, where it dispatches at once; from any
|
|
72
|
+
/// other thread it dispatches on the main actor.
|
|
73
|
+
public nonisolated func send(_ action: AstryxAction) {
|
|
74
|
+
if Thread.isMainThread {
|
|
75
|
+
MainActor.assumeIsolated { dispatch(action) }
|
|
76
|
+
} else {
|
|
77
|
+
Task { @MainActor in self.dispatch(action) }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private func dispatch(_ action: AstryxAction) {
|
|
82
|
+
guard failure == nil, let bridge else { return }
|
|
83
|
+
bridge.invokeMethod("dispatch", withArguments: [action.json])
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private func receive(_ json: String) {
|
|
87
|
+
// A failed host stays failed: a later tree does not bring it back.
|
|
88
|
+
guard failure == nil else { return }
|
|
89
|
+
do {
|
|
90
|
+
root = try JSONDecoder().decode(AstryxNode.self, from: Data(json.utf8))
|
|
91
|
+
} catch {
|
|
92
|
+
fail("The core sent a tree this binding cannot read: \(error)")
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// Stops the host: no tree to draw and no actions sent.
|
|
97
|
+
private func fail(_ message: String) {
|
|
98
|
+
failure = message
|
|
99
|
+
root = nil
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
#endif
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import Testing
|
|
4
|
+
@testable import AstryxNative
|
|
5
|
+
|
|
6
|
+
@Test func valueCallbacksCarryTheValueOnly() {
|
|
7
|
+
var received: Bool?
|
|
8
|
+
let props = SwitchProps(label: "Wi-Fi", value: false, onChange: { received = $0 })
|
|
9
|
+
props.onChange?(true)
|
|
10
|
+
#expect(received == true)
|
|
11
|
+
#expect(SwitchProps.baseTarget == .root)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@Test func textSlotsAndNumberSetsAreTyped() {
|
|
15
|
+
let heading = HeadingProps(level: .v2, children: "Orders")
|
|
16
|
+
#expect(heading.level.rawValue == 2)
|
|
17
|
+
#expect(heading.children == "Orders")
|
|
18
|
+
let badge = BadgeProps(variant: .success, label: "Paid")
|
|
19
|
+
#expect(badge.label == "Paid")
|
|
20
|
+
let divider = DividerProps(label: "Or")
|
|
21
|
+
#expect(divider.label == "Or")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Test func aButtonPressTakesNoArguments() {
|
|
25
|
+
var pressed = 0
|
|
26
|
+
let button = ButtonProps(label: "Pay", onClick: { pressed += 1 })
|
|
27
|
+
button.onClick?()
|
|
28
|
+
#expect(pressed == 1)
|
|
29
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import Testing
|
|
4
|
+
@testable import AstryxNative
|
|
5
|
+
|
|
6
|
+
/// A logic core with the bridge `@astryxdesign/native/logic` exposes, written
|
|
7
|
+
/// out by hand so the Swift tests need no build step.
|
|
8
|
+
private let settingsBundle = """
|
|
9
|
+
function astryxStart(host) {
|
|
10
|
+
var state = {wifi: true};
|
|
11
|
+
function view() {
|
|
12
|
+
return {component: 'VStack', key: 'root', props: {children: [
|
|
13
|
+
{component: 'Switch', key: 'wifi', props: {label: 'Wi-Fi', value: state.wifi}, events: ['onChange']},
|
|
14
|
+
{component: 'Text', key: 'status', props: {children: state.wifi ? 'Connected' : 'Off'}}
|
|
15
|
+
]}};
|
|
16
|
+
}
|
|
17
|
+
host.connected(1);
|
|
18
|
+
return {
|
|
19
|
+
getSnapshot: function () { return JSON.stringify(view()); },
|
|
20
|
+
dispatch: function (json) {
|
|
21
|
+
var action = JSON.parse(json);
|
|
22
|
+
if (action.key === 'wifi' && action.event === 'onChange') {
|
|
23
|
+
state = {wifi: action.args[0]};
|
|
24
|
+
host.changed(JSON.stringify(view()));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
@MainActor
|
|
32
|
+
@Test func aViewsActionReachesTheCoreAndTheNewTreeComesBack() throws {
|
|
33
|
+
let host = AstryxLogicHost(bundle: settingsBundle)
|
|
34
|
+
#expect(host.failure == nil)
|
|
35
|
+
guard case .vStack(let stack) = host.root, case .switch(let wifi) = stack.props.children?[0] else {
|
|
36
|
+
Issue.record("unexpected first tree")
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
#expect(wifi.props.value == true)
|
|
40
|
+
|
|
41
|
+
// The view calls its onChange; the action goes to the core.
|
|
42
|
+
let props = wifi.viewProps(send: host.send)
|
|
43
|
+
props.onChange?(false)
|
|
44
|
+
|
|
45
|
+
guard case .vStack(let next) = host.root, case .text(let status) = next.props.children?[1] else {
|
|
46
|
+
Issue.record("unexpected next tree")
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
#expect(status.props.children == "Off")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@MainActor
|
|
53
|
+
@Test func aLaterErrorClearsTheTreeAndStopsActions() throws {
|
|
54
|
+
let bundle = settingsBundle + "\nvar fragile = astryxStart; astryxStart = function (h) { var b = fragile(h); var d = b.dispatch; b.dispatch = function (j) { if (JSON.parse(j).args[0] === false) throw new Error('core broke'); d(j); }; return b; };"
|
|
55
|
+
let host = AstryxLogicHost(bundle: bundle)
|
|
56
|
+
guard case .vStack(let stack) = host.root, case .switch(let wifi) = stack.props.children?[0] else {
|
|
57
|
+
Issue.record("unexpected first tree")
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
wifi.viewProps(send: host.send).onChange?(false)
|
|
61
|
+
#expect(host.failure?.contains("core broke") == true)
|
|
62
|
+
#expect(host.root == nil)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@MainActor
|
|
66
|
+
@Test func aTreeAfterAFailureIsIgnored() {
|
|
67
|
+
let bundle = """
|
|
68
|
+
function astryxStart(h) {
|
|
69
|
+
h.connected(2);
|
|
70
|
+
h.changed(JSON.stringify({component: 'Text', key: 'late', props: {children: 'Too late'}}));
|
|
71
|
+
return {getSnapshot: function () { return '{}'; }, dispatch: function () {}};
|
|
72
|
+
}
|
|
73
|
+
"""
|
|
74
|
+
let host = AstryxLogicHost(bundle: bundle)
|
|
75
|
+
#expect(host.failure?.contains("bridge 2") == true)
|
|
76
|
+
#expect(host.root == nil)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@MainActor
|
|
80
|
+
@Test func aBundleWithoutTheEntryOrWithAnErrorFails() {
|
|
81
|
+
#expect(AstryxLogicHost(bundle: "var x = 1;").failure == "The bundle defines no astryxStart function")
|
|
82
|
+
#expect(AstryxLogicHost(bundle: "throw new Error('boom');").failure?.contains("boom") == true)
|
|
83
|
+
let wrongVersion = AstryxLogicHost(bundle: "function astryxStart(h) { h.connected(2); return {getSnapshot: function () { return '{}'; }}; }")
|
|
84
|
+
#expect(wrongVersion.failure?.contains("bridge 2") == true)
|
|
85
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
import Testing
|
|
5
|
+
@testable import AstryxNative
|
|
6
|
+
|
|
7
|
+
private let tree = """
|
|
8
|
+
{"component": "VStack", "key": "root", "props": {"gap": 2, "children": [
|
|
9
|
+
{"component": "Heading", "key": "title", "props": {"level": 2, "children": "Settings"}},
|
|
10
|
+
{"component": "Switch", "key": "wifi", "props": {"label": "Wi-Fi", "value": true}, "events": ["onChange"]},
|
|
11
|
+
{"component": "Button", "key": "save", "base": {"id": "save"}, "props": {"label": "Save", "variant": "primary"}},
|
|
12
|
+
{"component": "Chart", "key": "later", "props": {}},
|
|
13
|
+
{"component": "Button", "key": "newer", "props": {"label": "Go", "variant": "glass"}}
|
|
14
|
+
]}}
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
private final class Sent: @unchecked Sendable {
|
|
18
|
+
var actions: [AstryxAction] = []
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Test func decodesATreeByComponentName() throws {
|
|
22
|
+
let node = try JSONDecoder().decode(AstryxNode.self, from: Data(tree.utf8))
|
|
23
|
+
guard case .vStack(let stack) = node else { Issue.record("not a VStack"); return }
|
|
24
|
+
#expect(stack.props.gap == .v2)
|
|
25
|
+
let children = stack.props.children ?? []
|
|
26
|
+
#expect(children.map(\.key) == ["title", "wifi", "save", "later", "newer"])
|
|
27
|
+
guard case .heading(let heading) = children[0] else { Issue.record("not a Heading"); return }
|
|
28
|
+
#expect(heading.props.level == .v2)
|
|
29
|
+
#expect(heading.props.children == "Settings")
|
|
30
|
+
#expect(children[3] == .unsupported(component: "Chart", key: "later"))
|
|
31
|
+
// A value this binding's enum lacks makes only that node unsupported.
|
|
32
|
+
#expect(children[4] == .unsupported(component: "Button", key: "newer"))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Test func callbacksTheCoreListensToSendActions() throws {
|
|
36
|
+
let node = try JSONDecoder().decode(AstryxNode.self, from: Data(tree.utf8))
|
|
37
|
+
guard case .vStack(let stack) = node,
|
|
38
|
+
case .switch(let wifi) = stack.props.children?[1],
|
|
39
|
+
case .button(let save) = stack.props.children?[2] else {
|
|
40
|
+
Issue.record("unexpected tree")
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
let sent = Sent()
|
|
44
|
+
let switchProps = wifi.viewProps { sent.actions.append($0) }
|
|
45
|
+
switchProps.onChange?(false)
|
|
46
|
+
#expect(sent.actions == [AstryxAction(key: "wifi", component: "Switch", event: "onChange", args: [.bool(false)])])
|
|
47
|
+
#expect(sent.actions[0].json == #"{"args":[false],"component":"Switch","event":"onChange","key":"wifi"}"#)
|
|
48
|
+
|
|
49
|
+
// The core did not listen to the button's press, so the view gets no callback.
|
|
50
|
+
let buttonProps = save.viewProps { sent.actions.append($0) }
|
|
51
|
+
#expect(buttonProps.onClick == nil)
|
|
52
|
+
#expect(buttonProps.base.id == "save")
|
|
53
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file components/Badge.ts
|
|
3
|
+
* @input None
|
|
4
|
+
* @output What @astryxdesign/native adds to the core Badge contract
|
|
5
|
+
* @position Native additions read by fromComponentDoc together with core's
|
|
6
|
+
* Badge documentation
|
|
7
|
+
*/
|
|
8
|
+
import type { NativeAdditions } from '../contract/fromComponentDoc.js';
|
|
9
|
+
export declare const badge: NativeAdditions;
|
|
10
|
+
//# sourceMappingURL=Badge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../src/components/Badge.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,iCAAiC,CAAC;AAErE,eAAO,MAAM,KAAK,EAAE,eAKnB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file components/Button.ts
|
|
3
|
+
* @input None
|
|
4
|
+
* @output What @astryxdesign/native adds to the core Button contract
|
|
5
|
+
* @position Native additions read by fromComponentDoc together with core's
|
|
6
|
+
* Button documentation
|
|
7
|
+
*/
|
|
8
|
+
import type { NativeAdditions } from '../contract/fromComponentDoc.js';
|
|
9
|
+
export declare const button: NativeAdditions;
|
|
10
|
+
//# sourceMappingURL=Button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../src/components/Button.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,iCAAiC,CAAC;AAErE,eAAO,MAAM,MAAM,EAAE,eAapB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
export const button = {
|
|
3
|
+
name: 'Button',
|
|
4
|
+
baseTarget: 'root',
|
|
5
|
+
callbacks: ['onClick'],
|
|
6
|
+
sameMeaning: { onClick: 'a press, as in Compose' },
|
|
7
|
+
webOnly: {
|
|
8
|
+
type: 'HTML form submission type',
|
|
9
|
+
name: 'HTML form field name',
|
|
10
|
+
form: 'HTML form owner',
|
|
11
|
+
target: 'HTML link target',
|
|
12
|
+
rel: 'HTML link relationship',
|
|
13
|
+
isIconOnly: 'needs an icon, which has no core slot yet',
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file components/Card.ts
|
|
3
|
+
* @input None
|
|
4
|
+
* @output What @astryxdesign/native adds to the core Card contract
|
|
5
|
+
* @position Native additions read by fromComponentDoc together with core's
|
|
6
|
+
* Card documentation
|
|
7
|
+
*/
|
|
8
|
+
import type { NativeAdditions } from '../contract/fromComponentDoc.js';
|
|
9
|
+
export declare const card: NativeAdditions;
|
|
10
|
+
//# sourceMappingURL=Card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../src/components/Card.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,iCAAiC,CAAC;AAErE,eAAO,MAAM,IAAI,EAAE,eAKlB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file components/Divider.ts
|
|
3
|
+
* @input None
|
|
4
|
+
* @output What @astryxdesign/native adds to the core Divider contract
|
|
5
|
+
* @position Native additions read by fromComponentDoc together with core's
|
|
6
|
+
* Divider documentation
|
|
7
|
+
*/
|
|
8
|
+
import type { NativeAdditions } from '../contract/fromComponentDoc.js';
|
|
9
|
+
export declare const divider: NativeAdditions;
|
|
10
|
+
//# sourceMappingURL=Divider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Divider.d.ts","sourceRoot":"","sources":["../../src/components/Divider.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,iCAAiC,CAAC;AAErE,eAAO,MAAM,OAAO,EAAE,eAIrB,CAAC"}
|