@use-voltra/core 2.0.0-rc.4 → 2.1.0
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/build/cjs/index.js +1 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/payload/short-names.js +4 -0
- package/build/cjs/payload/short-names.js.map +1 -1
- package/build/cjs/widget-environment.js +26 -0
- package/build/cjs/widget-environment.js.map +1 -0
- package/build/esm/index.js +1 -0
- package/build/esm/index.js.map +1 -1
- package/build/esm/payload/short-names.js +4 -0
- package/build/esm/payload/short-names.js.map +1 -1
- package/build/esm/widget-environment.js +22 -0
- package/build/esm/widget-environment.js.map +1 -0
- package/build/types/index.d.ts +1 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/payload/short-names.d.ts.map +1 -1
- package/build/types/widget-environment.d.ts +85 -0
- package/build/types/widget-environment.d.ts.map +1 -0
- package/package.json +2 -1
- package/src/index.ts +6 -0
- package/src/jsx/createVoltraComponent.ts +36 -0
- package/src/payload/short-names.ts +379 -0
- package/src/payload.ts +23 -0
- package/src/renderer/context-registry.ts +40 -0
- package/src/renderer/dispatcher.ts +105 -0
- package/src/renderer/element-registry.ts +69 -0
- package/src/renderer/flatten-styles.ts +24 -0
- package/src/renderer/index.ts +14 -0
- package/src/renderer/render-cache.ts +27 -0
- package/src/renderer/renderer.ts +491 -0
- package/src/renderer/stylesheet-registry.ts +54 -0
- package/src/renderer/types.ts +5 -0
- package/src/types.ts +14 -0
- package/src/widget-environment.ts +114 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// 🤖 AUTO-GENERATED from data/components.json
|
|
3
|
+
// DO NOT EDIT MANUALLY - Changes will be overwritten
|
|
4
|
+
// Schema version: 1.0.0
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Unified mapping from full names to short names
|
|
8
|
+
* Used for props and style properties
|
|
9
|
+
*/
|
|
10
|
+
export const NAME_TO_SHORT: Record<string, string> = {
|
|
11
|
+
absolutePosition: 'ap',
|
|
12
|
+
accentedRenderingMode: 'arm',
|
|
13
|
+
alignItems: 'ai',
|
|
14
|
+
alignment: 'al',
|
|
15
|
+
alignSelf: 'as',
|
|
16
|
+
alpha: 'a',
|
|
17
|
+
aspectRatio: 'ar',
|
|
18
|
+
assetName: 'an',
|
|
19
|
+
background: 'bkg',
|
|
20
|
+
backgroundColor: 'bg',
|
|
21
|
+
backgroundImage: 'bgi',
|
|
22
|
+
backgroundStyle: 'bgs',
|
|
23
|
+
base64: 'b64',
|
|
24
|
+
border: 'bd',
|
|
25
|
+
borderColor: 'bc',
|
|
26
|
+
borderRadius: 'br',
|
|
27
|
+
borderWidth: 'bw',
|
|
28
|
+
bottom: 'b',
|
|
29
|
+
buttonStyle: 'bs',
|
|
30
|
+
chartScrollableAxes: 'csa',
|
|
31
|
+
checked: 'chk',
|
|
32
|
+
checkedColor: 'chc',
|
|
33
|
+
clipped: 'clip',
|
|
34
|
+
color: 'c',
|
|
35
|
+
colorFilter: 'cf',
|
|
36
|
+
colors: 'cls',
|
|
37
|
+
columns: 'cols',
|
|
38
|
+
contentAlignment: 'ca',
|
|
39
|
+
contentColor: 'cc',
|
|
40
|
+
contentDescription: 'cdesc',
|
|
41
|
+
contentScale: 'csc',
|
|
42
|
+
cornerRadius: 'cr',
|
|
43
|
+
countDown: 'cd',
|
|
44
|
+
currentValueLabel: 'cvl',
|
|
45
|
+
deepLinkUrl: 'dlu',
|
|
46
|
+
defaultValue: 'dv',
|
|
47
|
+
destination: 'dest',
|
|
48
|
+
direction: 'dir',
|
|
49
|
+
dither: 'dth',
|
|
50
|
+
durationMs: 'dur',
|
|
51
|
+
enabled: 'en',
|
|
52
|
+
endAtMs: 'end',
|
|
53
|
+
endPoint: 'ep',
|
|
54
|
+
fallback: 'flb',
|
|
55
|
+
fillMaxHeight: 'fmh',
|
|
56
|
+
fillMaxWidth: 'fmw',
|
|
57
|
+
fixedSizeHorizontal: 'fsh',
|
|
58
|
+
fixedSizeVertical: 'fsv',
|
|
59
|
+
flex: 'fl',
|
|
60
|
+
flexBasis: 'fb',
|
|
61
|
+
flexDirection: 'fdir',
|
|
62
|
+
flexGrow: 'fg',
|
|
63
|
+
flexGrowWidth: 'fgw',
|
|
64
|
+
flexShrink: 'fk',
|
|
65
|
+
font: 'fnt',
|
|
66
|
+
fontFamily: 'ff',
|
|
67
|
+
fontSize: 'fs',
|
|
68
|
+
fontVariant: 'fvar',
|
|
69
|
+
fontWeight: 'fw',
|
|
70
|
+
foregroundStyle: 'fgs',
|
|
71
|
+
foregroundStyleScale: 'fss',
|
|
72
|
+
frame: 'f',
|
|
73
|
+
gap: 'g',
|
|
74
|
+
gaugeStyle: 'gs',
|
|
75
|
+
glassEffect: 'ge',
|
|
76
|
+
height: 'h',
|
|
77
|
+
horizontalAlignment: 'halig',
|
|
78
|
+
horizontalPadding: 'hp',
|
|
79
|
+
icon: 'ic',
|
|
80
|
+
iconColor: 'icc',
|
|
81
|
+
id: 'id',
|
|
82
|
+
italic: 'it',
|
|
83
|
+
justifyContent: 'jc',
|
|
84
|
+
kerning: 'kern',
|
|
85
|
+
label: 'lbl',
|
|
86
|
+
layout: 'ly',
|
|
87
|
+
layoutPriority: 'lp',
|
|
88
|
+
left: 'l',
|
|
89
|
+
legendVisibility: 'lgv',
|
|
90
|
+
letterSpacing: 'ls',
|
|
91
|
+
lineHeight: 'lh',
|
|
92
|
+
lineLimit: 'll',
|
|
93
|
+
lineSpacing: 'lsp',
|
|
94
|
+
lineWidth: 'lw',
|
|
95
|
+
margin: 'm',
|
|
96
|
+
marginBottom: 'mb',
|
|
97
|
+
marginHorizontal: 'mh',
|
|
98
|
+
marginLeft: 'ml',
|
|
99
|
+
marginRight: 'mr',
|
|
100
|
+
marginTop: 'mt',
|
|
101
|
+
marginVertical: 'mv',
|
|
102
|
+
marks: 'mrk',
|
|
103
|
+
maskElement: 'me',
|
|
104
|
+
maxHeight: 'maxh',
|
|
105
|
+
maximumValue: 'max',
|
|
106
|
+
maximumValueLabel: 'maxl',
|
|
107
|
+
maxLines: 'mxl',
|
|
108
|
+
maxWidth: 'maxw',
|
|
109
|
+
minHeight: 'minh',
|
|
110
|
+
minimumValue: 'min',
|
|
111
|
+
minimumValueLabel: 'minvl',
|
|
112
|
+
minLength: 'minl',
|
|
113
|
+
minWidth: 'minw',
|
|
114
|
+
monospacedDigit: 'md',
|
|
115
|
+
multilineTextAlignment: 'mta',
|
|
116
|
+
name: 'n',
|
|
117
|
+
numberOfLines: 'nol',
|
|
118
|
+
offset: 'off',
|
|
119
|
+
offsetX: 'ox',
|
|
120
|
+
offsetY: 'oy',
|
|
121
|
+
opacity: 'op',
|
|
122
|
+
overflow: 'ov',
|
|
123
|
+
padding: 'pad',
|
|
124
|
+
paddingBottom: 'pb',
|
|
125
|
+
paddingHorizontal: 'ph',
|
|
126
|
+
paddingLeft: 'pl',
|
|
127
|
+
paddingRight: 'pr',
|
|
128
|
+
paddingTop: 'pt',
|
|
129
|
+
paddingVertical: 'pv',
|
|
130
|
+
position: 'pos',
|
|
131
|
+
progress: 'prg',
|
|
132
|
+
progressColor: 'pc',
|
|
133
|
+
resizeMode: 'rm',
|
|
134
|
+
right: 'r',
|
|
135
|
+
rotationEffect: 're',
|
|
136
|
+
scale: 'sc',
|
|
137
|
+
scaleEffect: 'sce',
|
|
138
|
+
shadow: 'sh',
|
|
139
|
+
shadowColor: 'shc',
|
|
140
|
+
shadowOffset: 'sho',
|
|
141
|
+
shadowOpacity: 'shop',
|
|
142
|
+
shadowRadius: 'shr',
|
|
143
|
+
showHours: 'shrs',
|
|
144
|
+
size: 'sz',
|
|
145
|
+
smallCaps: 'smc',
|
|
146
|
+
source: 'src',
|
|
147
|
+
spacing: 'sp',
|
|
148
|
+
startAtMs: 'start',
|
|
149
|
+
startIcon: 'sic',
|
|
150
|
+
startPoint: 'stp',
|
|
151
|
+
stops: 'sts',
|
|
152
|
+
strikethrough: 'st',
|
|
153
|
+
style: 's',
|
|
154
|
+
systemImage: 'si',
|
|
155
|
+
text: 'txt',
|
|
156
|
+
textAlign: 'ta',
|
|
157
|
+
textColor: 'txc',
|
|
158
|
+
textDecorationLine: 'tdl',
|
|
159
|
+
textStyle: 'ts',
|
|
160
|
+
textTemplates: 'tt',
|
|
161
|
+
thumb: 'th',
|
|
162
|
+
thumbCheckedColor: 'thc',
|
|
163
|
+
thumbUncheckedColor: 'thu',
|
|
164
|
+
tint: 'tnt',
|
|
165
|
+
tintColor: 'tc',
|
|
166
|
+
title: 'ttl',
|
|
167
|
+
top: 't',
|
|
168
|
+
trackCheckedColor: 'trkc',
|
|
169
|
+
trackColor: 'trc',
|
|
170
|
+
trackUncheckedColor: 'trku',
|
|
171
|
+
transform: 'tf',
|
|
172
|
+
type: 'typ',
|
|
173
|
+
uncheckedColor: 'uchc',
|
|
174
|
+
underline: 'ul',
|
|
175
|
+
value: 'v',
|
|
176
|
+
verticalAlignment: 'valig',
|
|
177
|
+
weight: 'wt',
|
|
178
|
+
width: 'w',
|
|
179
|
+
xAxisGridStyle: 'xgs',
|
|
180
|
+
xAxisVisibility: 'xav',
|
|
181
|
+
yAxisGridStyle: 'ygs',
|
|
182
|
+
yAxisVisibility: 'yav',
|
|
183
|
+
zIndex: 'zi',
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Reverse mapping from short names to full names
|
|
188
|
+
*/
|
|
189
|
+
export const SHORT_TO_NAME: Record<string, string> = {
|
|
190
|
+
ap: 'absolutePosition',
|
|
191
|
+
arm: 'accentedRenderingMode',
|
|
192
|
+
ai: 'alignItems',
|
|
193
|
+
al: 'alignment',
|
|
194
|
+
as: 'alignSelf',
|
|
195
|
+
a: 'alpha',
|
|
196
|
+
ar: 'aspectRatio',
|
|
197
|
+
an: 'assetName',
|
|
198
|
+
bkg: 'background',
|
|
199
|
+
bg: 'backgroundColor',
|
|
200
|
+
bgi: 'backgroundImage',
|
|
201
|
+
bgs: 'backgroundStyle',
|
|
202
|
+
b64: 'base64',
|
|
203
|
+
bd: 'border',
|
|
204
|
+
bc: 'borderColor',
|
|
205
|
+
br: 'borderRadius',
|
|
206
|
+
bw: 'borderWidth',
|
|
207
|
+
b: 'bottom',
|
|
208
|
+
bs: 'buttonStyle',
|
|
209
|
+
csa: 'chartScrollableAxes',
|
|
210
|
+
chk: 'checked',
|
|
211
|
+
chc: 'checkedColor',
|
|
212
|
+
clip: 'clipped',
|
|
213
|
+
c: 'color',
|
|
214
|
+
cf: 'colorFilter',
|
|
215
|
+
cls: 'colors',
|
|
216
|
+
cols: 'columns',
|
|
217
|
+
ca: 'contentAlignment',
|
|
218
|
+
cc: 'contentColor',
|
|
219
|
+
cdesc: 'contentDescription',
|
|
220
|
+
csc: 'contentScale',
|
|
221
|
+
cr: 'cornerRadius',
|
|
222
|
+
cd: 'countDown',
|
|
223
|
+
cvl: 'currentValueLabel',
|
|
224
|
+
dlu: 'deepLinkUrl',
|
|
225
|
+
dv: 'defaultValue',
|
|
226
|
+
dest: 'destination',
|
|
227
|
+
dir: 'direction',
|
|
228
|
+
dth: 'dither',
|
|
229
|
+
dur: 'durationMs',
|
|
230
|
+
en: 'enabled',
|
|
231
|
+
end: 'endAtMs',
|
|
232
|
+
ep: 'endPoint',
|
|
233
|
+
flb: 'fallback',
|
|
234
|
+
fmh: 'fillMaxHeight',
|
|
235
|
+
fmw: 'fillMaxWidth',
|
|
236
|
+
fsh: 'fixedSizeHorizontal',
|
|
237
|
+
fsv: 'fixedSizeVertical',
|
|
238
|
+
fl: 'flex',
|
|
239
|
+
fb: 'flexBasis',
|
|
240
|
+
fdir: 'flexDirection',
|
|
241
|
+
fg: 'flexGrow',
|
|
242
|
+
fgw: 'flexGrowWidth',
|
|
243
|
+
fk: 'flexShrink',
|
|
244
|
+
fnt: 'font',
|
|
245
|
+
ff: 'fontFamily',
|
|
246
|
+
fs: 'fontSize',
|
|
247
|
+
fvar: 'fontVariant',
|
|
248
|
+
fw: 'fontWeight',
|
|
249
|
+
fgs: 'foregroundStyle',
|
|
250
|
+
fss: 'foregroundStyleScale',
|
|
251
|
+
f: 'frame',
|
|
252
|
+
g: 'gap',
|
|
253
|
+
gs: 'gaugeStyle',
|
|
254
|
+
ge: 'glassEffect',
|
|
255
|
+
h: 'height',
|
|
256
|
+
halig: 'horizontalAlignment',
|
|
257
|
+
hp: 'horizontalPadding',
|
|
258
|
+
ic: 'icon',
|
|
259
|
+
icc: 'iconColor',
|
|
260
|
+
id: 'id',
|
|
261
|
+
it: 'italic',
|
|
262
|
+
jc: 'justifyContent',
|
|
263
|
+
kern: 'kerning',
|
|
264
|
+
lbl: 'label',
|
|
265
|
+
ly: 'layout',
|
|
266
|
+
lp: 'layoutPriority',
|
|
267
|
+
l: 'left',
|
|
268
|
+
lgv: 'legendVisibility',
|
|
269
|
+
ls: 'letterSpacing',
|
|
270
|
+
lh: 'lineHeight',
|
|
271
|
+
ll: 'lineLimit',
|
|
272
|
+
lsp: 'lineSpacing',
|
|
273
|
+
lw: 'lineWidth',
|
|
274
|
+
m: 'margin',
|
|
275
|
+
mb: 'marginBottom',
|
|
276
|
+
mh: 'marginHorizontal',
|
|
277
|
+
ml: 'marginLeft',
|
|
278
|
+
mr: 'marginRight',
|
|
279
|
+
mt: 'marginTop',
|
|
280
|
+
mv: 'marginVertical',
|
|
281
|
+
mrk: 'marks',
|
|
282
|
+
me: 'maskElement',
|
|
283
|
+
maxh: 'maxHeight',
|
|
284
|
+
max: 'maximumValue',
|
|
285
|
+
maxl: 'maximumValueLabel',
|
|
286
|
+
mxl: 'maxLines',
|
|
287
|
+
maxw: 'maxWidth',
|
|
288
|
+
minh: 'minHeight',
|
|
289
|
+
min: 'minimumValue',
|
|
290
|
+
minvl: 'minimumValueLabel',
|
|
291
|
+
minl: 'minLength',
|
|
292
|
+
minw: 'minWidth',
|
|
293
|
+
md: 'monospacedDigit',
|
|
294
|
+
mta: 'multilineTextAlignment',
|
|
295
|
+
n: 'name',
|
|
296
|
+
nol: 'numberOfLines',
|
|
297
|
+
off: 'offset',
|
|
298
|
+
ox: 'offsetX',
|
|
299
|
+
oy: 'offsetY',
|
|
300
|
+
op: 'opacity',
|
|
301
|
+
ov: 'overflow',
|
|
302
|
+
pad: 'padding',
|
|
303
|
+
pb: 'paddingBottom',
|
|
304
|
+
ph: 'paddingHorizontal',
|
|
305
|
+
pl: 'paddingLeft',
|
|
306
|
+
pr: 'paddingRight',
|
|
307
|
+
pt: 'paddingTop',
|
|
308
|
+
pv: 'paddingVertical',
|
|
309
|
+
pos: 'position',
|
|
310
|
+
prg: 'progress',
|
|
311
|
+
pc: 'progressColor',
|
|
312
|
+
rm: 'resizeMode',
|
|
313
|
+
r: 'right',
|
|
314
|
+
re: 'rotationEffect',
|
|
315
|
+
sc: 'scale',
|
|
316
|
+
sce: 'scaleEffect',
|
|
317
|
+
sh: 'shadow',
|
|
318
|
+
shc: 'shadowColor',
|
|
319
|
+
sho: 'shadowOffset',
|
|
320
|
+
shop: 'shadowOpacity',
|
|
321
|
+
shr: 'shadowRadius',
|
|
322
|
+
shrs: 'showHours',
|
|
323
|
+
sz: 'size',
|
|
324
|
+
smc: 'smallCaps',
|
|
325
|
+
src: 'source',
|
|
326
|
+
sp: 'spacing',
|
|
327
|
+
start: 'startAtMs',
|
|
328
|
+
sic: 'startIcon',
|
|
329
|
+
stp: 'startPoint',
|
|
330
|
+
sts: 'stops',
|
|
331
|
+
st: 'strikethrough',
|
|
332
|
+
s: 'style',
|
|
333
|
+
si: 'systemImage',
|
|
334
|
+
txt: 'text',
|
|
335
|
+
ta: 'textAlign',
|
|
336
|
+
txc: 'textColor',
|
|
337
|
+
tdl: 'textDecorationLine',
|
|
338
|
+
ts: 'textStyle',
|
|
339
|
+
tt: 'textTemplates',
|
|
340
|
+
th: 'thumb',
|
|
341
|
+
thc: 'thumbCheckedColor',
|
|
342
|
+
thu: 'thumbUncheckedColor',
|
|
343
|
+
tnt: 'tint',
|
|
344
|
+
tc: 'tintColor',
|
|
345
|
+
ttl: 'title',
|
|
346
|
+
t: 'top',
|
|
347
|
+
trkc: 'trackCheckedColor',
|
|
348
|
+
trc: 'trackColor',
|
|
349
|
+
trku: 'trackUncheckedColor',
|
|
350
|
+
tf: 'transform',
|
|
351
|
+
typ: 'type',
|
|
352
|
+
uchc: 'uncheckedColor',
|
|
353
|
+
ul: 'underline',
|
|
354
|
+
v: 'value',
|
|
355
|
+
valig: 'verticalAlignment',
|
|
356
|
+
wt: 'weight',
|
|
357
|
+
w: 'width',
|
|
358
|
+
xgs: 'xAxisGridStyle',
|
|
359
|
+
xav: 'xAxisVisibility',
|
|
360
|
+
ygs: 'yAxisGridStyle',
|
|
361
|
+
yav: 'yAxisVisibility',
|
|
362
|
+
zi: 'zIndex',
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Shorten a name using the unified mapping
|
|
367
|
+
* Returns the original name if no short form exists
|
|
368
|
+
*/
|
|
369
|
+
export function shorten(name: string): string {
|
|
370
|
+
return NAME_TO_SHORT[name] ?? name
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Expand a short name back to the full name
|
|
375
|
+
* Returns the original value if no expansion exists
|
|
376
|
+
*/
|
|
377
|
+
export function expand(short: string): string {
|
|
378
|
+
return SHORT_TO_NAME[short] ?? short
|
|
379
|
+
}
|
package/src/payload.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const MAX_ACTIVITYKIT_BYTES = 4096
|
|
2
|
+
const EFFECTIVE_JSON_BUDGET = 3345
|
|
3
|
+
|
|
4
|
+
const utf8ByteLength = (s: string): number => {
|
|
5
|
+
try {
|
|
6
|
+
if (typeof TextEncoder !== 'undefined') return new TextEncoder().encode(s).length
|
|
7
|
+
} catch {}
|
|
8
|
+
try {
|
|
9
|
+
return encodeURI(s).replace(/%[A-F\d]{2}/gi, 'X').length
|
|
10
|
+
} catch {
|
|
11
|
+
return s.length
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const ensurePayloadWithinBudget = (compressedPayload: string): void => {
|
|
16
|
+
const bytes = utf8ByteLength(compressedPayload)
|
|
17
|
+
|
|
18
|
+
if (bytes > EFFECTIVE_JSON_BUDGET) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`[Voltra] Compressed payload size ${bytes}B exceeds safe budget ${EFFECTIVE_JSON_BUDGET}B (ActivityKit hard cap ${MAX_ACTIVITYKIT_BYTES}B). You need to reduce the complexity of your UI.`
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Context } from 'react'
|
|
2
|
+
|
|
3
|
+
declare module 'react' {
|
|
4
|
+
interface Context<T> {
|
|
5
|
+
_currentValue: T
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type ContextRegistry = {
|
|
10
|
+
pushProvider: <T>(context: Context<T>, value: T) => void
|
|
11
|
+
popProvider: <T>(context: Context<T>) => void
|
|
12
|
+
readContext: <T>(context: Context<T>) => T
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const getContextRegistry = (): ContextRegistry => {
|
|
16
|
+
const contextMap = new Map()
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
pushProvider: <T>(context: Context<T>, value: T) => {
|
|
20
|
+
const stack = contextMap.get(context) || []
|
|
21
|
+
stack.push(value)
|
|
22
|
+
contextMap.set(context, stack)
|
|
23
|
+
},
|
|
24
|
+
popProvider: <T>(context: Context<T>) => {
|
|
25
|
+
const stack = contextMap.get(context)
|
|
26
|
+
if (stack) {
|
|
27
|
+
stack.pop()
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
readContext: <T>(context: Context<T>) => {
|
|
31
|
+
const stack = contextMap.get(context)
|
|
32
|
+
|
|
33
|
+
if (stack && stack.length > 0) {
|
|
34
|
+
return stack[stack.length - 1]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return context._currentValue
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import React, { Context, ReactDispatcher, ReactHooksDispatcher } from 'react'
|
|
2
|
+
|
|
3
|
+
import { ContextRegistry } from './context-registry.js'
|
|
4
|
+
|
|
5
|
+
const REACT_CONTEXT_TYPE = Symbol.for('react.context')
|
|
6
|
+
const REACT_MEMO_CACHE_SENTINEL = Symbol.for('react.memo_cache_sentinel')
|
|
7
|
+
|
|
8
|
+
declare module 'react' {
|
|
9
|
+
type HookFn = (...args: any[]) => any
|
|
10
|
+
|
|
11
|
+
export type ReactHooksDispatcher = {
|
|
12
|
+
useState: HookFn
|
|
13
|
+
useReducer: HookFn
|
|
14
|
+
useEffect: HookFn
|
|
15
|
+
useLayoutEffect: HookFn
|
|
16
|
+
useInsertionEffect: HookFn
|
|
17
|
+
useCallback: HookFn
|
|
18
|
+
useMemo: HookFn
|
|
19
|
+
useRef: HookFn
|
|
20
|
+
useContext: HookFn
|
|
21
|
+
useId: HookFn
|
|
22
|
+
useImperativeHandle: HookFn
|
|
23
|
+
useDebugValue: HookFn
|
|
24
|
+
useDeferredValue: HookFn
|
|
25
|
+
useTransition: HookFn
|
|
26
|
+
useSyncExternalStore: HookFn
|
|
27
|
+
use: HookFn
|
|
28
|
+
useActionState: HookFn
|
|
29
|
+
useOptimistic: HookFn
|
|
30
|
+
useEffectEvent: HookFn
|
|
31
|
+
useMemoCache: HookFn
|
|
32
|
+
useCacheRefresh: HookFn
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type ReactDispatcher = {
|
|
36
|
+
H: ReactHooksDispatcher
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE: ReactDispatcher
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const getHooksDispatcher = (registry: ContextRegistry): ReactHooksDispatcher => ({
|
|
43
|
+
useContext: <T>(context: Context<T>) => registry.readContext(context),
|
|
44
|
+
use: <T>(usable: React.Usable<T>): T => {
|
|
45
|
+
if (
|
|
46
|
+
usable !== null &&
|
|
47
|
+
typeof usable === 'object' &&
|
|
48
|
+
(usable as { $$typeof?: symbol }).$$typeof === REACT_CONTEXT_TYPE
|
|
49
|
+
) {
|
|
50
|
+
return registry.readContext(usable as unknown as Context<T>)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (
|
|
54
|
+
usable !== null &&
|
|
55
|
+
(typeof usable === 'object' || typeof usable === 'function') &&
|
|
56
|
+
typeof (usable as { then?: unknown }).then === 'function'
|
|
57
|
+
) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
'use() with promises is not supported in Voltra. Async data fetching is not available in this synchronous renderer.'
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
throw new Error(`An unsupported type was passed to use(): ${String(usable)}`)
|
|
64
|
+
},
|
|
65
|
+
useState: <S>(initial?: S | (() => S)) => [
|
|
66
|
+
typeof initial === 'function' ? (initial as () => S)() : initial,
|
|
67
|
+
() => {},
|
|
68
|
+
],
|
|
69
|
+
useReducer: <S, I, A extends React.AnyActionArg>(
|
|
70
|
+
_: (prevState: S, ...args: A) => S,
|
|
71
|
+
initialArg: I,
|
|
72
|
+
init?: (i: I) => S
|
|
73
|
+
): [S, React.ActionDispatch<A>] => {
|
|
74
|
+
const state = init ? init(initialArg) : initialArg
|
|
75
|
+
return [state as S, () => {}]
|
|
76
|
+
},
|
|
77
|
+
useMemo: (factory) => factory(),
|
|
78
|
+
useCallback: (cb) => cb,
|
|
79
|
+
useRef: (initial) => ({ current: initial }),
|
|
80
|
+
useEffect: () => {},
|
|
81
|
+
useLayoutEffect: () => {},
|
|
82
|
+
useInsertionEffect: () => {},
|
|
83
|
+
useId: () => Math.random().toString(36).substr(2, 9),
|
|
84
|
+
useDebugValue: () => {},
|
|
85
|
+
useImperativeHandle: () => {},
|
|
86
|
+
useDeferredValue: <T>(value: T) => value,
|
|
87
|
+
useTransition: () => [false, (func: () => void) => func()],
|
|
88
|
+
useSyncExternalStore: (_, getSnapshot) => getSnapshot(),
|
|
89
|
+
useActionState: <S>(_: unknown, initialState: S, _permalink?: string) =>
|
|
90
|
+
[initialState, () => {}, false] as [S, () => void, boolean],
|
|
91
|
+
useOptimistic: <T>(passthrough: T) => [passthrough, () => {}] as [T, (action: unknown) => void],
|
|
92
|
+
useEffectEvent: <T extends Function>(callback: T): T => callback,
|
|
93
|
+
useMemoCache: (size: number) => {
|
|
94
|
+
const data = new Array<unknown>(size)
|
|
95
|
+
for (let i = 0; i < size; i++) {
|
|
96
|
+
data[i] = REACT_MEMO_CACHE_SENTINEL
|
|
97
|
+
}
|
|
98
|
+
return data
|
|
99
|
+
},
|
|
100
|
+
useCacheRefresh: () => () => {},
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
export const getReactCurrentDispatcher = (): ReactDispatcher => {
|
|
104
|
+
return React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE
|
|
105
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { VoltraNodeJson } from '../types.js'
|
|
4
|
+
|
|
5
|
+
export type ElementRegistry = {
|
|
6
|
+
isRegistered: (element: ReactNode) => number | undefined
|
|
7
|
+
register: (element: ReactNode, rendered: VoltraNodeJson) => number
|
|
8
|
+
getElements: () => VoltraNodeJson[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const createElementRegistry = (): ElementRegistry => {
|
|
12
|
+
const elementToIndex = new Map<ReactNode, number>()
|
|
13
|
+
const elements: VoltraNodeJson[] = []
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
isRegistered: (element: ReactNode): number | undefined => {
|
|
17
|
+
return elementToIndex.get(element)
|
|
18
|
+
},
|
|
19
|
+
register: (element: ReactNode, rendered: VoltraNodeJson): number => {
|
|
20
|
+
const index = elements.length
|
|
21
|
+
elementToIndex.set(element, index)
|
|
22
|
+
elements.push(rendered)
|
|
23
|
+
return index
|
|
24
|
+
},
|
|
25
|
+
getElements: (): VoltraNodeJson[] => elements,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const preScanForDuplicates = (element: ReactNode): Set<ReactNode> => {
|
|
30
|
+
const seen = new Set<ReactNode>()
|
|
31
|
+
const duplicates = new Set<ReactNode>()
|
|
32
|
+
|
|
33
|
+
const scan = (node: ReactNode): void => {
|
|
34
|
+
if (node === null || node === undefined || typeof node !== 'object') {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (Array.isArray(node)) {
|
|
39
|
+
for (const child of node) {
|
|
40
|
+
scan(child)
|
|
41
|
+
}
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (seen.has(node)) {
|
|
46
|
+
duplicates.add(node)
|
|
47
|
+
} else {
|
|
48
|
+
seen.add(node)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if ('props' in node) {
|
|
52
|
+
const props = (node as { props: Record<string, unknown> }).props
|
|
53
|
+
if (props) {
|
|
54
|
+
if (props.children !== undefined) {
|
|
55
|
+
scan(props.children as ReactNode)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (const [key, value] of Object.entries(props)) {
|
|
59
|
+
if (key !== 'children' && value && typeof value === 'object' && 'type' in value && 'props' in value) {
|
|
60
|
+
scan(value as ReactNode)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
scan(element)
|
|
68
|
+
return duplicates
|
|
69
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type StyleProp<T> = T | T[] | null | undefined | false
|
|
2
|
+
|
|
3
|
+
type FlatStyleProp<T> = T extends (infer U)[] ? FlatStyleProp<U> : T
|
|
4
|
+
|
|
5
|
+
export const flattenStyle = <T extends StyleProp<unknown>>(style: T | null | undefined): FlatStyleProp<T> | null => {
|
|
6
|
+
if (!style) {
|
|
7
|
+
return null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!Array.isArray(style)) {
|
|
11
|
+
return style as FlatStyleProp<T>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const result: Record<string, unknown> = {}
|
|
15
|
+
for (let i = 0, styleLength = style.length; i < styleLength; ++i) {
|
|
16
|
+
const computedStyle = flattenStyle(style[i])
|
|
17
|
+
if (computedStyle) {
|
|
18
|
+
for (const key in computedStyle) {
|
|
19
|
+
result[key] = computedStyle[key]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return result as FlatStyleProp<T>
|
|
24
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { type ContextRegistry, getContextRegistry } from './context-registry.js'
|
|
2
|
+
export { getHooksDispatcher, getReactCurrentDispatcher } from './dispatcher.js'
|
|
3
|
+
export { createElementRegistry, type ElementRegistry, preScanForDuplicates } from './element-registry.js'
|
|
4
|
+
export { flattenStyle } from './flatten-styles.js'
|
|
5
|
+
export { getRenderCache, type RenderCache } from './render-cache.js'
|
|
6
|
+
export {
|
|
7
|
+
type ComponentRegistry,
|
|
8
|
+
createVoltraRenderer,
|
|
9
|
+
renderVariantToJson,
|
|
10
|
+
transformProps,
|
|
11
|
+
VOLTRA_PAYLOAD_VERSION,
|
|
12
|
+
} from './renderer.js'
|
|
13
|
+
export { createStylesheetRegistry, type StylesheetRegistry } from './stylesheet-registry.js'
|
|
14
|
+
export type { VoltraVariantRenderer } from './types.js'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { VoltraNodeJson } from '../types.js'
|
|
4
|
+
import { VoltraVariantRenderer } from './types.js'
|
|
5
|
+
|
|
6
|
+
export type RenderCache = {
|
|
7
|
+
getOrRender: VoltraVariantRenderer
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const getRenderCache = (renderer: VoltraVariantRenderer): RenderCache => {
|
|
11
|
+
const renderCache = new Map<ReactNode, VoltraNodeJson>()
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
getOrRender: (node: ReactNode): VoltraNodeJson => {
|
|
15
|
+
const cached = renderCache.get(node)
|
|
16
|
+
|
|
17
|
+
if (cached) {
|
|
18
|
+
return cached
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const result = renderer(node)
|
|
22
|
+
renderCache.set(node, result)
|
|
23
|
+
|
|
24
|
+
return result
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
}
|