zelune 1.6.2 → 1.6.3

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.
@@ -1,71 +1,8 @@
1
- import React, { useRef } from 'react';
2
- import * as RNW from 'react-native-web';
3
- import { withNativeWind } from './nativewindShim';
4
-
5
- export * from 'react-native-web';
6
-
7
- // Wrap core React Native primitives with NativeWind className -> style support
8
- export const View = withNativeWind(RNW.View as unknown as React.ComponentType<Record<string, unknown>>, 'View');
9
- export const Text = withNativeWind(RNW.Text as unknown as React.ComponentType<Record<string, unknown>>, 'Text');
10
- export const Pressable = withNativeWind(
11
- RNW.Pressable as unknown as React.ComponentType<Record<string, unknown>>,
12
- 'Pressable'
13
- );
14
- export const TouchableOpacity = withNativeWind(
15
- RNW.TouchableOpacity as unknown as React.ComponentType<Record<string, unknown>>,
16
- 'TouchableOpacity'
17
- );
18
- export const TouchableHighlight = withNativeWind(
19
- RNW.TouchableHighlight as unknown as React.ComponentType<Record<string, unknown>>,
20
- 'TouchableHighlight'
21
- );
22
- export const TouchableWithoutFeedback = withNativeWind(
23
- RNW.TouchableWithoutFeedback as unknown as React.ComponentType<Record<string, unknown>>,
24
- 'TouchableWithoutFeedback'
25
- );
26
- export const ScrollView = withNativeWind(
27
- RNW.ScrollView as unknown as React.ComponentType<Record<string, unknown>>,
28
- 'ScrollView'
29
- );
30
- export const FlatList = withNativeWind(
31
- RNW.FlatList as unknown as React.ComponentType<Record<string, unknown>>,
32
- 'FlatList'
33
- );
34
- export const SectionList = withNativeWind(
35
- RNW.SectionList as unknown as React.ComponentType<Record<string, unknown>>,
36
- 'SectionList'
37
- );
38
- export const KeyboardAvoidingView = withNativeWind(
39
- RNW.KeyboardAvoidingView as unknown as React.ComponentType<Record<string, unknown>>,
40
- 'KeyboardAvoidingView'
41
- );
42
- export const TextInput = withNativeWind(
43
- RNW.TextInput as unknown as React.ComponentType<Record<string, unknown>>,
44
- 'TextInput'
45
- );
46
- export const Image = withNativeWind(
47
- RNW.Image as unknown as React.ComponentType<Record<string, unknown>>,
48
- 'Image'
49
- );
50
- export const ImageBackground = withNativeWind(
51
- RNW.ImageBackground as unknown as React.ComponentType<Record<string, unknown>>,
52
- 'ImageBackground'
53
- );
54
- export const SafeAreaView = withNativeWind(
55
- RNW.SafeAreaView as unknown as React.ComponentType<Record<string, unknown>>,
56
- 'SafeAreaView'
57
- );
58
- export const ActivityIndicator = withNativeWind(
59
- RNW.ActivityIndicator as unknown as React.ComponentType<Record<string, unknown>>,
60
- 'ActivityIndicator'
61
- );
62
- export const Modal = withNativeWind(
63
- RNW.Modal as unknown as React.ComponentType<Record<string, unknown>>,
64
- 'Modal'
65
- );
1
+ import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
2
+ import { resolveTailwindClassNameToStyle } from './nativewindShim';
3
+
4
+ export type RnStyle = Record<string, unknown> | RnStyle[] | null | undefined | false | number;
66
5
 
67
- // Pin mobile artboard dimensions (393x852 iPhone 15 Pro) so Dimensions.get('window')
68
- // and useWindowDimensions() never return the desktop browser's 1440x900 window size.
69
6
  const ARTBOARD_DIMENSIONS = {
70
7
  width: 393,
71
8
  height: 852,
@@ -73,6 +10,1169 @@ const ARTBOARD_DIMENSIONS = {
73
10
  fontScale: 1,
74
11
  };
75
12
 
13
+ export function flattenStyle(styleInput: RnStyle): Record<string, unknown> {
14
+ const result: Record<string, unknown> = {};
15
+ function walk(item: RnStyle) {
16
+ if (!item || typeof item !== 'object') return;
17
+ if (Array.isArray(item)) {
18
+ for (let i = 0; i < item.length; i++) {
19
+ walk(item[i]);
20
+ }
21
+ return;
22
+ }
23
+ for (const [k, v] of Object.entries(item)) {
24
+ if (v !== undefined) {
25
+ result[k] = v;
26
+ }
27
+ }
28
+ }
29
+ walk(styleInput);
30
+ return result;
31
+ }
32
+
33
+ function formatTransformArray(transforms: unknown[]): string {
34
+ const parts: string[] = [];
35
+ for (const item of transforms) {
36
+ if (!item || typeof item !== 'object') continue;
37
+ for (const [key, rawVal] of Object.entries(item as Record<string, unknown>)) {
38
+ if (rawVal === undefined || rawVal === null) continue;
39
+ if (
40
+ (key.startsWith('translate') || key === 'perspective') &&
41
+ typeof rawVal === 'number'
42
+ ) {
43
+ parts.push(`${key}(${rawVal}px)`);
44
+ } else {
45
+ parts.push(`${key}(${String(rawVal)})`);
46
+ }
47
+ }
48
+ }
49
+ return parts.join(' ');
50
+ }
51
+
52
+ function applyAlphaToColor(color: string, opacity: number): string {
53
+ const clamped = Math.max(0, Math.min(1, opacity));
54
+ const hexMatch = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color.trim());
55
+ if (hexMatch) {
56
+ let hex = hexMatch[1];
57
+ if (hex.length === 3) {
58
+ hex = hex
59
+ .split('')
60
+ .map((c) => c + c)
61
+ .join('');
62
+ }
63
+ const r = Number.parseInt(hex.slice(0, 2), 16);
64
+ const g = Number.parseInt(hex.slice(2, 4), 16);
65
+ const b = Number.parseInt(hex.slice(4, 6), 16);
66
+ return `rgba(${r}, ${g}, ${b}, ${Number(clamped.toFixed(3))})`;
67
+ }
68
+ return color;
69
+ }
70
+
71
+ export function toDomStyle(
72
+ styleInput: RnStyle,
73
+ baseDefaults: Record<string, unknown> = {},
74
+ className?: string
75
+ ): React.CSSProperties {
76
+ const twStyle = className ? resolveTailwindClassNameToStyle(className) : null;
77
+ const flat = flattenStyle([baseDefaults, twStyle, styleInput]);
78
+ const out: Record<string, unknown> = {};
79
+
80
+ for (const [key, val] of Object.entries(flat)) {
81
+ if (val === undefined || val === null) continue;
82
+ switch (key) {
83
+ case 'paddingHorizontal':
84
+ if (flat.paddingLeft === undefined) out.paddingLeft = val;
85
+ if (flat.paddingRight === undefined) out.paddingRight = val;
86
+ break;
87
+ case 'paddingVertical':
88
+ if (flat.paddingTop === undefined) out.paddingTop = val;
89
+ if (flat.paddingBottom === undefined) out.paddingBottom = val;
90
+ break;
91
+ case 'paddingStart':
92
+ if (flat.paddingLeft === undefined) out.paddingLeft = val;
93
+ break;
94
+ case 'paddingEnd':
95
+ if (flat.paddingRight === undefined) out.paddingRight = val;
96
+ break;
97
+ case 'marginHorizontal':
98
+ if (flat.marginLeft === undefined) out.marginLeft = val;
99
+ if (flat.marginRight === undefined) out.marginRight = val;
100
+ break;
101
+ case 'marginVertical':
102
+ if (flat.marginTop === undefined) out.marginTop = val;
103
+ if (flat.marginBottom === undefined) out.marginBottom = val;
104
+ break;
105
+ case 'marginStart':
106
+ if (flat.marginLeft === undefined) out.marginLeft = val;
107
+ break;
108
+ case 'marginEnd':
109
+ if (flat.marginRight === undefined) out.marginRight = val;
110
+ break;
111
+ case 'borderStartWidth':
112
+ if (flat.borderLeftWidth === undefined) out.borderLeftWidth = val;
113
+ break;
114
+ case 'borderEndWidth':
115
+ if (flat.borderRightWidth === undefined) out.borderRightWidth = val;
116
+ break;
117
+ case 'borderTopStartRadius':
118
+ if (flat.borderTopLeftRadius === undefined) out.borderTopLeftRadius = val;
119
+ break;
120
+ case 'borderTopEndRadius':
121
+ if (flat.borderTopRightRadius === undefined) out.borderTopRightRadius = val;
122
+ break;
123
+ case 'borderBottomStartRadius':
124
+ if (flat.borderBottomLeftRadius === undefined) out.borderBottomLeftRadius = val;
125
+ break;
126
+ case 'borderBottomEndRadius':
127
+ if (flat.borderBottomRightRadius === undefined) out.borderBottomRightRadius = val;
128
+ break;
129
+ case 'lineHeight':
130
+ out.lineHeight = typeof val === 'number' ? `${val}px` : val;
131
+ break;
132
+ case 'flex':
133
+ if (typeof val === 'number') {
134
+ if (val > 0) {
135
+ if (flat.flexGrow === undefined) out.flexGrow = val;
136
+ if (flat.flexShrink === undefined) out.flexShrink = 1;
137
+ if (flat.flexBasis === undefined) out.flexBasis = '0%';
138
+ } else if (val === 0) {
139
+ if (flat.flexGrow === undefined) out.flexGrow = 0;
140
+ if (flat.flexShrink === undefined) out.flexShrink = 0;
141
+ }
142
+ } else {
143
+ out.flex = val;
144
+ }
145
+ break;
146
+ case 'transform':
147
+ if (Array.isArray(val)) {
148
+ const cssTransform = formatTransformArray(val);
149
+ if (cssTransform) out.transform = cssTransform;
150
+ } else if (typeof val === 'string') {
151
+ out.transform = val;
152
+ }
153
+ break;
154
+ case 'textDecorationLine':
155
+ out.textDecoration = val;
156
+ break;
157
+ case 'includeFontPadding':
158
+ case 'textAlignVertical':
159
+ case 'tintColor':
160
+ case 'resizeMode':
161
+ case 'overlayColor':
162
+ case 'shadowColor':
163
+ case 'shadowOffset':
164
+ case 'shadowOpacity':
165
+ case 'shadowRadius':
166
+ case 'elevation':
167
+ break;
168
+ default:
169
+ out[key] = val;
170
+ break;
171
+ }
172
+ }
173
+
174
+ // Ensure React Native borders render solid by default when borderWidth > 0
175
+ const hasBorderWidth =
176
+ (typeof out.borderWidth === 'number' && out.borderWidth > 0) ||
177
+ (typeof out.borderTopWidth === 'number' && out.borderTopWidth > 0) ||
178
+ (typeof out.borderRightWidth === 'number' && out.borderRightWidth > 0) ||
179
+ (typeof out.borderBottomWidth === 'number' && out.borderBottomWidth > 0) ||
180
+ (typeof out.borderLeftWidth === 'number' && out.borderLeftWidth > 0);
181
+
182
+ if (hasBorderWidth) {
183
+ if (!out.borderStyle) out.borderStyle = 'solid';
184
+ if (
185
+ !out.borderColor &&
186
+ !out.borderTopColor &&
187
+ !out.borderRightColor &&
188
+ !out.borderBottomColor &&
189
+ !out.borderLeftColor
190
+ ) {
191
+ out.borderColor = 'transparent';
192
+ }
193
+ }
194
+
195
+ // Synthesize CSS boxShadow from React Native shadow* / elevation props
196
+ if (!out.boxShadow) {
197
+ const shadowOpacity = typeof flat.shadowOpacity === 'number' ? flat.shadowOpacity : 0;
198
+ const elevation = typeof flat.elevation === 'number' ? flat.elevation : 0;
199
+ if (shadowOpacity > 0) {
200
+ const offset = (flat.shadowOffset as { width?: number; height?: number } | undefined) ?? {};
201
+ const ox = typeof offset.width === 'number' ? offset.width : 0;
202
+ const oy = typeof offset.height === 'number' ? offset.height : 2;
203
+ const radius = typeof flat.shadowRadius === 'number' ? flat.shadowRadius : 4;
204
+ const rawColor = typeof flat.shadowColor === 'string' ? flat.shadowColor : '#000000';
205
+ out.boxShadow = `${ox}px ${oy}px ${radius}px ${applyAlphaToColor(rawColor, shadowOpacity)}`;
206
+ } else if (elevation > 0) {
207
+ const oy = Math.max(1, Math.round(elevation * 0.5));
208
+ const blur = Math.max(2, Math.round(elevation * 1.5));
209
+ out.boxShadow = `0px ${oy}px ${blur}px rgba(15, 23, 42, 0.08)`;
210
+ }
211
+ }
212
+
213
+ return out as React.CSSProperties;
214
+ }
215
+
216
+ function camelToKebab(str: string): string {
217
+ return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
218
+ }
219
+
220
+ function extractDomAttributes(props: Record<string, unknown>): Record<string, unknown> {
221
+ const domAttrs: Record<string, unknown> = {};
222
+ for (const [k, v] of Object.entries(props)) {
223
+ if (k.startsWith('data-') || k.startsWith('aria-')) {
224
+ domAttrs[k] = v;
225
+ }
226
+ }
227
+ const dataSet = props.dataSet;
228
+ if (dataSet && typeof dataSet === 'object') {
229
+ for (const [dk, dv] of Object.entries(dataSet as Record<string, unknown>)) {
230
+ if (dv !== undefined && dv !== null) {
231
+ const attrName = `data-${camelToKebab(dk)}`;
232
+ if (!(attrName in domAttrs)) {
233
+ domAttrs[attrName] = String(dv);
234
+ }
235
+ }
236
+ }
237
+ }
238
+ if (typeof props.nativeID === 'string') {
239
+ domAttrs.id = props.nativeID;
240
+ } else if (typeof props.id === 'string') {
241
+ domAttrs.id = props.id;
242
+ }
243
+ if (typeof props.testID === 'string') {
244
+ domAttrs['data-testid'] = props.testID;
245
+ }
246
+ if (typeof props.accessibilityLabel === 'string') {
247
+ domAttrs['aria-label'] = props.accessibilityLabel;
248
+ }
249
+ if (typeof props.accessibilityRole === 'string') {
250
+ domAttrs.role = props.accessibilityRole === ' header' ? 'heading' : props.accessibilityRole;
251
+ }
252
+ return domAttrs;
253
+ }
254
+
255
+ const VIEW_BASE_STYLE: Record<string, unknown> = {
256
+ display: 'flex',
257
+ flexDirection: 'column',
258
+ alignItems: 'stretch',
259
+ flexShrink: 0,
260
+ boxSizing: 'border-box',
261
+ position: 'relative',
262
+ minWidth: 0,
263
+ minHeight: 0,
264
+ };
265
+
266
+ const TEXT_BASE_STYLE: Record<string, unknown> = {
267
+ display: 'inline',
268
+ boxSizing: 'border-box',
269
+ color: '#0F172A',
270
+ fontFamily:
271
+ 'var(--font-sans, "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif)',
272
+ fontSize: 14,
273
+ whiteSpace: 'pre-wrap',
274
+ wordWrap: 'break-word',
275
+ margin: 0,
276
+ padding: 0,
277
+ };
278
+
279
+ function useSimulatedLayout(onLayout: unknown) {
280
+ const firedRef = useRef(false);
281
+ useEffect(() => {
282
+ if (!firedRef.current && typeof onLayout === 'function') {
283
+ firedRef.current = true;
284
+ try {
285
+ onLayout({
286
+ nativeEvent: {
287
+ layout: { x: 0, y: 0, width: ARTBOARD_DIMENSIONS.width, height: 48 },
288
+ },
289
+ });
290
+ } catch {
291
+ // Ignore user layout handler errors
292
+ }
293
+ }
294
+ }, [onLayout]);
295
+ }
296
+
297
+ export const View = forwardRef<HTMLDivElement, Record<string, unknown>>(function View(props, ref) {
298
+ useSimulatedLayout(props.onLayout);
299
+ const domAttrs = extractDomAttributes(props);
300
+ const style = toDomStyle(
301
+ props.style as RnStyle,
302
+ VIEW_BASE_STYLE,
303
+ props.className as string | undefined
304
+ );
305
+ if (props.pointerEvents === 'none') {
306
+ style.pointerEvents = 'none';
307
+ } else if (props.pointerEvents === 'box-none') {
308
+ style.pointerEvents = 'auto';
309
+ }
310
+ return React.createElement(
311
+ 'div',
312
+ {
313
+ ...domAttrs,
314
+ ref,
315
+ style,
316
+ onClick: typeof props.onClick === 'function' ? props.onClick : undefined,
317
+ },
318
+ props.children as React.ReactNode
319
+ );
320
+ });
321
+ View.displayName = 'View';
322
+
323
+ export const Text = forwardRef<HTMLSpanElement, Record<string, unknown>>(function Text(
324
+ props,
325
+ ref
326
+ ) {
327
+ useSimulatedLayout(props.onLayout);
328
+ const domAttrs = extractDomAttributes(props);
329
+ const style = toDomStyle(
330
+ props.style as RnStyle,
331
+ TEXT_BASE_STYLE,
332
+ props.className as string | undefined
333
+ );
334
+
335
+ const numberOfLines =
336
+ typeof props.numberOfLines === 'number' && props.numberOfLines > 0
337
+ ? props.numberOfLines
338
+ : undefined;
339
+ if (numberOfLines === 1) {
340
+ style.overflow = 'hidden';
341
+ style.textOverflow = 'ellipsis';
342
+ style.whiteSpace = 'nowrap';
343
+ style.maxWidth = '100%';
344
+ } else if (numberOfLines && numberOfLines > 1) {
345
+ style.display = '-webkit-box';
346
+ style.WebkitLineClamp = numberOfLines;
347
+ style.WebkitBoxOrient = 'vertical';
348
+ style.overflow = 'hidden';
349
+ }
350
+
351
+ if (props.selectable === false) {
352
+ style.userSelect = 'none';
353
+ }
354
+ const onPress = typeof props.onPress === 'function' ? (props.onPress as () => void) : undefined;
355
+ if (onPress) {
356
+ style.cursor = 'pointer';
357
+ }
358
+
359
+ return React.createElement(
360
+ 'span',
361
+ {
362
+ ...domAttrs,
363
+ ref,
364
+ style,
365
+ onClick: onPress
366
+ ? (e: React.MouseEvent) => {
367
+ onPress();
368
+ void e;
369
+ }
370
+ : undefined,
371
+ },
372
+ props.children as React.ReactNode
373
+ );
374
+ });
375
+ Text.displayName = 'Text';
376
+
377
+ function createInteractiveComponent(displayName: string, defaultActiveOpacity = 1) {
378
+ const Comp = forwardRef<HTMLDivElement, Record<string, unknown>>(function InteractiveView(
379
+ props,
380
+ ref
381
+ ) {
382
+ const [pressed, setPressed] = useState(false);
383
+ useSimulatedLayout(props.onLayout);
384
+ const disabled = Boolean(props.disabled);
385
+ const domAttrs = extractDomAttributes(props);
386
+
387
+ const rawStyle =
388
+ typeof props.style === 'function'
389
+ ? (props.style as (state: { pressed: boolean; hovered: boolean; focused: boolean }) => RnStyle)({
390
+ pressed,
391
+ hovered: false,
392
+ focused: false,
393
+ })
394
+ : (props.style as RnStyle);
395
+
396
+ const style = toDomStyle(rawStyle, VIEW_BASE_STYLE, props.className as string | undefined);
397
+ style.cursor = disabled ? 'default' : 'pointer';
398
+ style.userSelect = 'none';
399
+
400
+ const activeOpacity =
401
+ typeof props.activeOpacity === 'number' ? props.activeOpacity : defaultActiveOpacity;
402
+ if (pressed && !disabled && activeOpacity < 1) {
403
+ style.opacity = activeOpacity;
404
+ }
405
+
406
+ const onPress = typeof props.onPress === 'function' ? (props.onPress as (e: unknown) => void) : undefined;
407
+ const onPressIn =
408
+ typeof props.onPressIn === 'function' ? (props.onPressIn as (e: unknown) => void) : undefined;
409
+ const onPressOut =
410
+ typeof props.onPressOut === 'function' ? (props.onPressOut as (e: unknown) => void) : undefined;
411
+
412
+ const resolvedChildren =
413
+ typeof props.children === 'function'
414
+ ? (props.children as (state: { pressed: boolean }) => React.ReactNode)({ pressed })
415
+ : (props.children as React.ReactNode);
416
+
417
+ return React.createElement(
418
+ 'div',
419
+ {
420
+ ...domAttrs,
421
+ ref,
422
+ role: (domAttrs.role as string | undefined) ?? 'button',
423
+ 'aria-disabled': disabled || undefined,
424
+ style,
425
+ onMouseDown: (e: React.MouseEvent) => {
426
+ if (disabled) return;
427
+ setPressed(true);
428
+ onPressIn?.(e);
429
+ },
430
+ onMouseUp: (e: React.MouseEvent) => {
431
+ if (disabled) return;
432
+ setPressed(false);
433
+ onPressOut?.(e);
434
+ },
435
+ onMouseLeave: () => {
436
+ if (pressed) setPressed(false);
437
+ },
438
+ onClick: (e: React.MouseEvent) => {
439
+ if (disabled) return;
440
+ onPress?.(e);
441
+ },
442
+ },
443
+ resolvedChildren
444
+ );
445
+ });
446
+ Comp.displayName = displayName;
447
+ return Comp;
448
+ }
449
+
450
+ export const Pressable = createInteractiveComponent('Pressable', 1);
451
+ export const TouchableOpacity = createInteractiveComponent('TouchableOpacity', 0.78);
452
+ export const TouchableHighlight = createInteractiveComponent('TouchableHighlight', 0.85);
453
+ export const TouchableWithoutFeedback = createInteractiveComponent('TouchableWithoutFeedback', 1);
454
+ export const TouchableNativeFeedback = createInteractiveComponent('TouchableNativeFeedback', 0.85);
455
+
456
+ export const ScrollView = forwardRef<unknown, Record<string, unknown>>(function ScrollView(
457
+ props,
458
+ ref
459
+ ) {
460
+ const domRef = useRef<HTMLDivElement | null>(null);
461
+ useSimulatedLayout(props.onLayout);
462
+
463
+ useImperativeHandle(ref, () => ({
464
+ scrollTo: (opts?: { x?: number; y?: number; animated?: boolean }) => {
465
+ if (domRef.current && opts) {
466
+ domRef.current.scrollTo({
467
+ left: opts.x ?? 0,
468
+ top: opts.y ?? 0,
469
+ behavior: opts.animated ? 'smooth' : 'auto',
470
+ });
471
+ }
472
+ },
473
+ scrollToEnd: (opts?: { animated?: boolean }) => {
474
+ if (domRef.current) {
475
+ domRef.current.scrollTo({
476
+ top: domRef.current.scrollHeight,
477
+ left: domRef.current.scrollWidth,
478
+ behavior: opts?.animated ? 'smooth' : 'auto',
479
+ });
480
+ }
481
+ },
482
+ flashScrollIndicators: () => undefined,
483
+ getScrollResponder: () => ({}),
484
+ getInnerViewNode: () => domRef.current,
485
+ }));
486
+
487
+ const horizontal = Boolean(props.horizontal);
488
+ const domAttrs = extractDomAttributes(props);
489
+
490
+ const outerStyle = toDomStyle(
491
+ props.style as RnStyle,
492
+ {
493
+ ...VIEW_BASE_STYLE,
494
+ flexGrow: 1,
495
+ flexShrink: 1,
496
+ overflowX: horizontal ? 'auto' : 'hidden',
497
+ overflowY: horizontal ? 'hidden' : 'auto',
498
+ WebkitOverflowScrolling: 'touch',
499
+ scrollbarWidth:
500
+ props.showsVerticalScrollIndicator === false ||
501
+ props.showsHorizontalScrollIndicator === false
502
+ ? 'none'
503
+ : undefined,
504
+ },
505
+ props.className as string | undefined
506
+ );
507
+
508
+ const innerStyle = toDomStyle(
509
+ props.contentContainerStyle as RnStyle,
510
+ {
511
+ ...VIEW_BASE_STYLE,
512
+ flexDirection: horizontal ? 'row' : 'column',
513
+ minWidth: horizontal ? 'max-content' : undefined,
514
+ },
515
+ props.contentContainerClassName as string | undefined
516
+ );
517
+
518
+ return React.createElement(
519
+ 'div',
520
+ {
521
+ ...domAttrs,
522
+ ref: domRef,
523
+ style: outerStyle,
524
+ onScroll: typeof props.onScroll === 'function' ? props.onScroll : undefined,
525
+ },
526
+ React.createElement('div', { style: innerStyle }, props.children as React.ReactNode)
527
+ );
528
+ });
529
+ ScrollView.displayName = 'ScrollView';
530
+
531
+ function renderSlotComponent(slot: unknown): React.ReactNode {
532
+ if (!slot) return null;
533
+ if (React.isValidElement(slot)) return slot;
534
+ if (typeof slot === 'function') {
535
+ const Comp = slot as React.ComponentType;
536
+ return React.createElement(Comp);
537
+ }
538
+ return null;
539
+ }
540
+
541
+ export const FlatList = forwardRef<unknown, Record<string, unknown>>(function FlatList(props, ref) {
542
+ const data = Array.isArray(props.data) ? (props.data as unknown[]) : [];
543
+ const renderItem =
544
+ typeof props.renderItem === 'function'
545
+ ? (props.renderItem as (info: {
546
+ item: unknown;
547
+ index: number;
548
+ separators: {
549
+ highlight: () => void;
550
+ unhighlight: () => void;
551
+ updateProps: () => void;
552
+ };
553
+ }) => React.ReactNode)
554
+ : null;
555
+ const keyExtractor =
556
+ typeof props.keyExtractor === 'function'
557
+ ? (props.keyExtractor as (item: unknown, index: number) => string)
558
+ : (item: unknown, index: number) => {
559
+ if (item && typeof item === 'object' && 'id' in item) {
560
+ return String((item as Record<string, unknown>).id);
561
+ }
562
+ if (item && typeof item === 'object' && 'key' in item) {
563
+ return String((item as Record<string, unknown>).key);
564
+ }
565
+ return String(index);
566
+ };
567
+
568
+ const numColumns =
569
+ typeof props.numColumns === 'number' && props.numColumns > 1 ? props.numColumns : 1;
570
+ const separators = {
571
+ highlight: () => undefined,
572
+ unhighlight: () => undefined,
573
+ updateProps: () => undefined,
574
+ };
575
+
576
+ const renderedItems: React.ReactNode[] = [];
577
+ if (data.length === 0) {
578
+ renderedItems.push(
579
+ React.createElement(
580
+ React.Fragment,
581
+ { key: '__empty' },
582
+ renderSlotComponent(props.ListEmptyComponent)
583
+ )
584
+ );
585
+ } else if (renderItem) {
586
+ if (numColumns > 1) {
587
+ for (let i = 0; i < data.length; i += numColumns) {
588
+ const rowSlice = data.slice(i, i + numColumns);
589
+ const rowStyle = toDomStyle(props.columnWrapperStyle as RnStyle, {
590
+ ...VIEW_BASE_STYLE,
591
+ flexDirection: 'row',
592
+ });
593
+ renderedItems.push(
594
+ React.createElement(
595
+ 'div',
596
+ { key: `row_${i}`, style: rowStyle },
597
+ rowSlice.map((item, colIdx) => {
598
+ const idx = i + colIdx;
599
+ return React.createElement(
600
+ React.Fragment,
601
+ { key: keyExtractor(item, idx) },
602
+ renderItem({ item, index: idx, separators })
603
+ );
604
+ })
605
+ )
606
+ );
607
+ }
608
+ } else {
609
+ data.forEach((item, index) => {
610
+ if (index > 0 && props.ItemSeparatorComponent) {
611
+ renderedItems.push(
612
+ React.createElement(
613
+ React.Fragment,
614
+ { key: `sep_${index}` },
615
+ renderSlotComponent(props.ItemSeparatorComponent)
616
+ )
617
+ );
618
+ }
619
+ renderedItems.push(
620
+ React.createElement(
621
+ React.Fragment,
622
+ { key: keyExtractor(item, index) },
623
+ renderItem({ item, index, separators })
624
+ )
625
+ );
626
+ });
627
+ }
628
+ }
629
+
630
+ return React.createElement(
631
+ ScrollView,
632
+ { ...props, ref },
633
+ renderSlotComponent(props.ListHeaderComponent),
634
+ ...renderedItems,
635
+ renderSlotComponent(props.ListFooterComponent)
636
+ );
637
+ });
638
+ FlatList.displayName = 'FlatList';
639
+
640
+ export const SectionList = forwardRef<unknown, Record<string, unknown>>(function SectionList(
641
+ props,
642
+ ref
643
+ ) {
644
+ const sections = Array.isArray(props.sections)
645
+ ? (props.sections as Array<{ data?: unknown[]; [k: string]: unknown }>)
646
+ : [];
647
+ const renderItem =
648
+ typeof props.renderItem === 'function'
649
+ ? (props.renderItem as (info: {
650
+ item: unknown;
651
+ index: number;
652
+ section: unknown;
653
+ separators: Record<string, () => void>;
654
+ }) => React.ReactNode)
655
+ : null;
656
+ const renderSectionHeader =
657
+ typeof props.renderSectionHeader === 'function'
658
+ ? (props.renderSectionHeader as (info: { section: unknown }) => React.ReactNode)
659
+ : null;
660
+ const renderSectionFooter =
661
+ typeof props.renderSectionFooter === 'function'
662
+ ? (props.renderSectionFooter as (info: { section: unknown }) => React.ReactNode)
663
+ : null;
664
+
665
+ const separators = {
666
+ highlight: () => undefined,
667
+ unhighlight: () => undefined,
668
+ updateProps: () => undefined,
669
+ };
670
+
671
+ const children: React.ReactNode[] = [];
672
+ if (sections.length === 0) {
673
+ children.push(
674
+ React.createElement(
675
+ React.Fragment,
676
+ { key: '__empty' },
677
+ renderSlotComponent(props.ListEmptyComponent)
678
+ )
679
+ );
680
+ } else {
681
+ sections.forEach((section, sIdx) => {
682
+ if ( renderSectionHeader) {
683
+ children.push(
684
+ React.createElement(
685
+ React.Fragment,
686
+ { key: `sec_hdr_${sIdx}` },
687
+ renderSectionHeader({ section })
688
+ )
689
+ );
690
+ }
691
+ const items = Array.isArray(section?.data) ? section.data : [];
692
+ items.forEach((item, iIdx) => {
693
+ if (iIdx > 0 && props.ItemSeparatorComponent) {
694
+ children.push(
695
+ React.createElement(
696
+ React.Fragment,
697
+ { key: `sec_${sIdx}_sep_${iIdx}` },
698
+ renderSlotComponent(props.ItemSeparatorComponent)
699
+ )
700
+ );
701
+ }
702
+ if (renderItem) {
703
+ children.push(
704
+ React.createElement(
705
+ React.Fragment,
706
+ { key: `sec_${sIdx}_item_${iIdx}` },
707
+ renderItem({ item, index: iIdx, section, separators })
708
+ )
709
+ );
710
+ }
711
+ });
712
+ if (renderSectionFooter) {
713
+ children.push(
714
+ React.createElement(
715
+ React.Fragment,
716
+ { key: `sec_ftr_${sIdx}` },
717
+ renderSectionFooter({ section })
718
+ )
719
+ );
720
+ }
721
+ });
722
+ }
723
+
724
+ return React.createElement(
725
+ ScrollView,
726
+ { ...props, ref },
727
+ renderSlotComponent(props.ListHeaderComponent),
728
+ ...children,
729
+ renderSlotComponent(props.ListFooterComponent)
730
+ );
731
+ });
732
+ SectionList.displayName = 'SectionList';
733
+
734
+ export const VirtualizedList = FlatList;
735
+
736
+ export const SafeAreaView = forwardRef<HTMLDivElement, Record<string, unknown>>(
737
+ function SafeAreaView(props, ref) {
738
+ return React.createElement(View, {
739
+ ...props,
740
+ ref,
741
+ style: [{ flex: 1 }, props.style as RnStyle],
742
+ });
743
+ }
744
+ );
745
+ SafeAreaView.displayName = 'SafeAreaView';
746
+
747
+ export const KeyboardAvoidingView = forwardRef<HTMLDivElement, Record<string, unknown>>(
748
+ function KeyboardAvoidingView(props, ref) {
749
+ return React.createElement(View, {
750
+ ...props,
751
+ ref,
752
+ style: [{ flex: 1 }, props.style as RnStyle],
753
+ });
754
+ }
755
+ );
756
+ KeyboardAvoidingView.displayName = 'KeyboardAvoidingView';
757
+
758
+ export const TextInput = forwardRef<unknown, Record<string, unknown>>(function TextInput(
759
+ props,
760
+ ref
761
+ ) {
762
+ const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);
763
+ useSimulatedLayout(props.onLayout);
764
+
765
+ useImperativeHandle(ref, () => ({
766
+ focus: () => inputRef.current?.focus(),
767
+ blur: () => inputRef.current?.blur(),
768
+ clear: () => {
769
+ if (inputRef.current) inputRef.current.value = '';
770
+ },
771
+ isFocused: () =>
772
+ typeof document !== 'undefined' && document.activeElement === inputRef.current,
773
+ }));
774
+
775
+ const domAttrs = extractDomAttributes(props);
776
+ const style = toDomStyle(
777
+ props.style as RnStyle,
778
+ {
779
+ boxSizing: 'border-box',
780
+ fontFamily:
781
+ 'var(--font-sans, "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif)',
782
+ fontSize: 14,
783
+ color: '#0F172A',
784
+ backgroundColor: 'transparent',
785
+ borderWidth: 0,
786
+ outline: 'none',
787
+ padding: 0,
788
+ margin: 0,
789
+ },
790
+ props.className as string | undefined
791
+ );
792
+
793
+ const multiline = Boolean(props.multiline);
794
+ const editable = props.editable !== false;
795
+ const onChangeText =
796
+ typeof props.onChangeText === 'function'
797
+ ? (props.onChangeText as (text: string) => void)
798
+ : undefined;
799
+ const onChange =
800
+ typeof props.onChange === 'function' ? (props.onChange as (e: unknown) => void) : undefined;
801
+ const onSubmitEditing =
802
+ typeof props.onSubmitEditing === 'function'
803
+ ? (props.onSubmitEditing as (e: unknown) => void)
804
+ : undefined;
805
+
806
+ const commonProps: Record<string, unknown> = {
807
+ ...domAttrs,
808
+ ref: inputRef,
809
+ style,
810
+ placeholder: typeof props.placeholder === 'string' ? props.placeholder : undefined,
811
+ readOnly: !editable,
812
+ disabled: !editable,
813
+ maxLength: typeof props.maxLength === 'number' ? props.maxLength : undefined,
814
+ onFocus: typeof props.onFocus === 'function' ? props.onFocus : undefined,
815
+ onBlur: typeof props.onBlur === 'function' ? props.onBlur : undefined,
816
+ onChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
817
+ onChange?.({ nativeEvent: { text: e.target.value } });
818
+ onChangeText?.(e.target.value);
819
+ },
820
+ onKeyDown: (e: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
821
+ if (!multiline && e.key === 'Enter' && onSubmitEditing) {
822
+ onSubmitEditing({ nativeEvent: { text: e.currentTarget.value } });
823
+ }
824
+ },
825
+ };
826
+
827
+ if (props.value !== undefined) {
828
+ commonProps.value = String(props.value ?? '');
829
+ } else if (props.defaultValue !== undefined) {
830
+ commonProps.defaultValue = String(props.defaultValue ?? '');
831
+ }
832
+
833
+ if (multiline) {
834
+ return React.createElement('textarea', {
835
+ ...commonProps,
836
+ rows: typeof props.numberOfLines === 'number' ? props.numberOfLines : 3,
837
+ });
838
+ }
839
+
840
+ return React.createElement('input', {
841
+ ...commonProps,
842
+ type: props.secureTextEntry ? 'password' : 'text',
843
+ });
844
+ });
845
+ TextInput.displayName = 'TextInput';
846
+
847
+ function resolveImageUri(source: unknown): string {
848
+ if (typeof source === 'string') return source;
849
+ if (Array.isArray(source) && source[0] && typeof source[0] === 'object') {
850
+ return String((source[0] as Record<string, unknown>).uri ?? '');
851
+ }
852
+ if (source && typeof source === 'object') {
853
+ return String((source as Record<string, unknown>).uri ?? '');
854
+ }
855
+ return '';
856
+ }
857
+
858
+ const BaseImage = forwardRef<HTMLDivElement, Record<string, unknown>>(function Image(props, ref) {
859
+ useSimulatedLayout(props.onLayout);
860
+ const domAttrs = extractDomAttributes(props);
861
+ const uri = resolveImageUri(props.source ?? props.src);
862
+ const flatStyle = flattenStyle(props.style as RnStyle);
863
+ const resizeMode = String(
864
+ props.resizeMode ?? flatStyle.resizeMode ?? 'cover'
865
+ ) as React.CSSProperties['objectFit'];
866
+
867
+ const style = toDomStyle(
868
+ props.style as RnStyle,
869
+ {
870
+ ...VIEW_BASE_STYLE,
871
+ overflow: 'hidden',
872
+ },
873
+ props.className as string | undefined
874
+ );
875
+
876
+ return React.createElement(
877
+ 'div',
878
+ {
879
+ ...domAttrs,
880
+ ref,
881
+ style,
882
+ },
883
+ uri
884
+ ? React.createElement('img', {
885
+ src: uri,
886
+ alt: (props.accessibilityLabel as string | undefined) ?? '',
887
+ draggable: false,
888
+ style: {
889
+ width: '100%',
890
+ height: '100%',
891
+ objectFit: resizeMode === 'stretch' ? 'fill' : resizeMode,
892
+ display: 'block',
893
+ },
894
+ onLoad: typeof props.onLoad === 'function' ? props.onLoad : undefined,
895
+ onError: typeof props.onError === 'function' ? props.onError : undefined,
896
+ })
897
+ : null
898
+ );
899
+ });
900
+ BaseImage.displayName = 'Image';
901
+
902
+ export const Image = Object.assign(BaseImage, {
903
+ getSize: (
904
+ _uri: string,
905
+ success?: (width: number, height: number) => void
906
+ ) => {
907
+ success?.(393, 240);
908
+ },
909
+ getSizeWithHeaders: (
910
+ _uri: string,
911
+ _headers: Record<string, string>,
912
+ success?: (width: number, height: number) => void
913
+ ) => {
914
+ success?.(393, 240);
915
+ },
916
+ prefetch: async () => true,
917
+ abortPrefetch: () => undefined,
918
+ queryCache: async () => ({}),
919
+ resolveAssetSource: (source: unknown) => ({
920
+ uri: resolveImageUri(source),
921
+ width: 393,
922
+ height: 240,
923
+ scale: 1,
924
+ }),
925
+ });
926
+
927
+ export const ImageBackground = forwardRef<HTMLDivElement, Record<string, unknown>>(
928
+ function ImageBackground(props, ref) {
929
+ const domAttrs = extractDomAttributes(props);
930
+ const uri = resolveImageUri(props.source);
931
+ const style = toDomStyle(
932
+ props.style as RnStyle,
933
+ {
934
+ ...VIEW_BASE_STYLE,
935
+ overflow: 'hidden',
936
+ },
937
+ props.className as string | undefined
938
+ );
939
+ const imageStyle = toDomStyle(props.imageStyle as RnStyle, {
940
+ position: 'absolute',
941
+ top: 0,
942
+ left: 0,
943
+ right: 0,
944
+ bottom: 0,
945
+ width: '100%',
946
+ height: '100%',
947
+ });
948
+
949
+ return React.createElement(
950
+ 'div',
951
+ { ...domAttrs, ref, style },
952
+ uri
953
+ ? React.createElement('img', {
954
+ src: uri,
955
+ alt: '',
956
+ draggable: false,
957
+ style: {
958
+ ...imageStyle,
959
+ objectFit: 'cover',
960
+ pointerEvents: 'none',
961
+ },
962
+ })
963
+ : null,
964
+ props.children as React.ReactNode
965
+ );
966
+ }
967
+ );
968
+ ImageBackground.displayName = 'ImageBackground';
969
+
970
+ export const ActivityIndicator = forwardRef<HTMLDivElement, Record<string, unknown>>(
971
+ function ActivityIndicator(props, ref) {
972
+ if (props.animating === false) return null;
973
+ const domAttrs = extractDomAttributes(props);
974
+ const rawSize = props.size;
975
+ const pxSize = rawSize === 'large' ? 32 : typeof rawSize === 'number' ? rawSize : 20;
976
+ const color = typeof props.color === 'string' ? props.color : '#6366F1';
977
+ const style = toDomStyle(
978
+ props.style as RnStyle,
979
+ {
980
+ ...VIEW_BASE_STYLE,
981
+ alignItems: 'center',
982
+ justifyContent: 'center',
983
+ },
984
+ props.className as string | undefined
985
+ );
986
+
987
+ return React.createElement(
988
+ 'div',
989
+ { ...domAttrs, ref, role: 'progressbar', style },
990
+ React.createElement('div', {
991
+ style: {
992
+ width: pxSize,
993
+ height: pxSize,
994
+ borderRadius: '50%',
995
+ border: `2.5px solid ${applyAlphaToColor(color, 0.25)}`,
996
+ borderTopColor: color,
997
+ boxSizing: 'border-box',
998
+ },
999
+ })
1000
+ );
1001
+ }
1002
+ );
1003
+ ActivityIndicator.displayName = 'ActivityIndicator';
1004
+
1005
+ export const Switch = forwardRef<HTMLButtonElement, Record<string, unknown>>(function Switch(
1006
+ props,
1007
+ ref
1008
+ ) {
1009
+ const domAttrs = extractDomAttributes(props);
1010
+ const value = Boolean(props.value);
1011
+ const disabled = Boolean(props.disabled);
1012
+ const trackColor = (props.trackColor as { false?: string; true?: string } | undefined) ?? {};
1013
+ const activeBg = trackColor.true ?? '#6366F1';
1014
+ const inactiveBg = trackColor.false ?? '#CBD5E1';
1015
+ const thumbColor = typeof props.thumbColor === 'string' ? props.thumbColor : '#FFFFFF';
1016
+ const onValueChange =
1017
+ typeof props.onValueChange === 'function'
1018
+ ? (props.onValueChange as (next: boolean) => void)
1019
+ : undefined;
1020
+
1021
+ const style = toDomStyle(
1022
+ props.style as RnStyle,
1023
+ {
1024
+ width: 44,
1025
+ height: 24,
1026
+ borderRadius: 12,
1027
+ backgroundColor: value ? activeBg : inactiveBg,
1028
+ padding: 2,
1029
+ border: 'none',
1030
+ cursor: disabled ? 'default' : 'pointer',
1031
+ display: 'inline-flex',
1032
+ alignItems: 'center',
1033
+ justifyContent: value ? 'flex-end' : 'flex-start',
1034
+ boxSizing: 'border-box',
1035
+ opacity: disabled ? 0.5 : 1,
1036
+ },
1037
+ props.className as string | undefined
1038
+ );
1039
+
1040
+ return React.createElement(
1041
+ 'button',
1042
+ {
1043
+ ...domAttrs,
1044
+ ref,
1045
+ type: 'button',
1046
+ role: 'switch',
1047
+ 'aria-checked': value,
1048
+ disabled,
1049
+ style,
1050
+ onClick: () => {
1051
+ if (!disabled) onValueChange?.(!value);
1052
+ },
1053
+ },
1054
+ React.createElement('span', {
1055
+ style: {
1056
+ width: 20,
1057
+ height: 20,
1058
+ borderRadius: 10,
1059
+ backgroundColor: thumbColor,
1060
+ display: 'block',
1061
+ boxShadow: '0 1px 2px rgba(0,0,0,0.2)',
1062
+ },
1063
+ })
1064
+ );
1065
+ });
1066
+ Switch.displayName = 'Switch';
1067
+
1068
+ export const Modal: React.FC<Record<string, unknown>> = (props) => {
1069
+ if (props.visible !== true) return null;
1070
+ const domAttrs = extractDomAttributes(props);
1071
+ return React.createElement(
1072
+ 'div',
1073
+ {
1074
+ ...domAttrs,
1075
+ style: {
1076
+ position: 'absolute',
1077
+ top: 0,
1078
+ left: 0,
1079
+ right: 0,
1080
+ bottom: 0,
1081
+ zIndex: 100,
1082
+ display: 'flex',
1083
+ flexDirection: 'column',
1084
+ },
1085
+ },
1086
+ props.children as React.ReactNode
1087
+ );
1088
+ };
1089
+ Modal.displayName = 'Modal';
1090
+
1091
+ export const Button: React.FC<{
1092
+ title?: string;
1093
+ onPress?: () => void;
1094
+ color?: string;
1095
+ disabled?: boolean;
1096
+ testID?: string;
1097
+ }> = ({ title = 'Button', onPress, color = '#6366F1', disabled = false, testID }) =>
1098
+ React.createElement(
1099
+ TouchableOpacity,
1100
+ {
1101
+ onPress,
1102
+ disabled,
1103
+ testID,
1104
+ style: {
1105
+ backgroundColor: disabled ? '#94A3B8' : color,
1106
+ paddingVertical: 10,
1107
+ paddingHorizontal: 16,
1108
+ borderRadius: 8,
1109
+ alignItems: 'center',
1110
+ justifyContent: 'center',
1111
+ },
1112
+ },
1113
+ React.createElement(
1114
+ Text,
1115
+ { style: { color: '#FFFFFF', fontWeight: '600', fontSize: 14 } },
1116
+ title
1117
+ )
1118
+ );
1119
+
1120
+ export const RefreshControl: React.FC<Record<string, unknown>> = () => null;
1121
+
1122
+ export const StatusBar = Object.assign(
1123
+ function StatusBar(): React.ReactElement | null {
1124
+ return null;
1125
+ },
1126
+ {
1127
+ currentHeight: 0,
1128
+ setBarStyle: () => undefined,
1129
+ setBackgroundColor: () => undefined,
1130
+ setHidden: () => undefined,
1131
+ setTranslucent: () => undefined,
1132
+ setNetworkActivityIndicatorVisible: () => undefined,
1133
+ pushStackEntry: () => ({}),
1134
+ popStackEntry: () => undefined,
1135
+ replaceStackEntry: () => ({}),
1136
+ }
1137
+ );
1138
+
1139
+ export const StyleSheet = {
1140
+ create: <T extends Record<string, unknown>>(styles: T): T => styles,
1141
+ flatten: flattenStyle,
1142
+ compose: (s1: unknown, s2: unknown) => [s1, s2],
1143
+ hairlineWidth: 1,
1144
+ absoluteFillObject: {
1145
+ position: 'absolute' as const,
1146
+ left: 0,
1147
+ right: 0,
1148
+ top: 0,
1149
+ bottom: 0,
1150
+ },
1151
+ absoluteFill: {
1152
+ position: 'absolute' as const,
1153
+ left: 0,
1154
+ right: 0,
1155
+ top: 0,
1156
+ bottom: 0,
1157
+ },
1158
+ setStyleAttributePreprocessor: () => undefined,
1159
+ };
1160
+
1161
+ export const Platform = {
1162
+ OS: 'ios' as const,
1163
+ Version: '17.0',
1164
+ isPad: false,
1165
+ isTV: false,
1166
+ isTesting: false,
1167
+ Constants: {
1168
+ reactNativeVersion: { major: 0, minor: 76, patch: 0 },
1169
+ osVersion: '17.0',
1170
+ systemName: 'iOS',
1171
+ },
1172
+ select: <T,>(spec: Record<string, T>): T =>
1173
+ spec.ios ?? spec.default ?? spec.web ?? spec.native ?? spec.android ?? Object.values(spec)[0],
1174
+ };
1175
+
76
1176
  export const Dimensions = {
77
1177
  get: (_dim?: 'window' | 'screen' | string) => ARTBOARD_DIMENSIONS,
78
1178
  set: () => undefined,
@@ -84,18 +1184,332 @@ export function useWindowDimensions() {
84
1184
  return ARTBOARD_DIMENSIONS;
85
1185
  }
86
1186
 
87
- export function useAnimatedValue(initialValue = 0): RNW.Animated.Value {
88
- const ref = useRef<RNW.Animated.Value | null>(null);
1187
+ export const PixelRatio = {
1188
+ get: () => 3,
1189
+ getFontScale: () => 1,
1190
+ getPixelSizeForLayoutSize: (size: number) => Math.round(size * 3),
1191
+ roundToNearestPixel: (size: number) => Math.round(size * 3) / 3,
1192
+ startDetecting: () => undefined,
1193
+ };
1194
+
1195
+ export class AnimatedValue {
1196
+ private _value: number;
1197
+ constructor(value = 0) {
1198
+ this._value = value;
1199
+ }
1200
+ setValue(value: number) {
1201
+ this._value = value;
1202
+ }
1203
+ setOffset(_offset: number) {}
1204
+ flattenOffset() {}
1205
+ extractOffset() {}
1206
+ addListener(_cb: (state: { value: number }) => void): string {
1207
+ return 'listener_0';
1208
+ }
1209
+ removeListener(_id: string) {}
1210
+ removeAllListeners() {}
1211
+ stopAnimation(cb?: (value: number) => void) {
1212
+ cb?.(this._value);
1213
+ }
1214
+ resetAnimation(cb?: (value: number) => void) {
1215
+ cb?.(this._value);
1216
+ }
1217
+ interpolate(config: { inputRange?: number[]; outputRange?: Array<number | string> }) {
1218
+ const out = config?.outputRange;
1219
+ if (Array.isArray(out) && out.length > 0) {
1220
+ return out[0];
1221
+ }
1222
+ return this._value;
1223
+ }
1224
+ __getValue() {
1225
+ return this._value;
1226
+ }
1227
+ }
1228
+
1229
+ export class AnimatedValueXY {
1230
+ x: AnimatedValue;
1231
+ y: AnimatedValue;
1232
+ constructor(value?: { x?: number; y?: number }) {
1233
+ this.x = new AnimatedValue(value?.x ?? 0);
1234
+ this.y = new AnimatedValue(value?.y ?? 0);
1235
+ }
1236
+ setValue(value: { x: number; y: number }) {
1237
+ this.x.setValue(value.x);
1238
+ this.y.setValue(value.y);
1239
+ }
1240
+ setOffset(_offset: { x: number; y: number }) {}
1241
+ flattenOffset() {}
1242
+ extractOffset() {}
1243
+ stopAnimation(cb?: (value: { x: number; y: number }) => void) {
1244
+ cb?.({ x: this.x.__getValue(), y: this.y.__getValue() });
1245
+ }
1246
+ resetAnimation(cb?: (value: { x: number; y: number }) => void) {
1247
+ cb?.({ x: this.x.__getValue(), y: this.y.__getValue() });
1248
+ }
1249
+ getLayout() {
1250
+ return { left: this.x.__getValue(), top: this.y.__getValue() };
1251
+ }
1252
+ getTranslateTransform() {
1253
+ return [{ translateX: this.x.__getValue() }, { translateY: this.y.__getValue() }];
1254
+ }
1255
+ }
1256
+
1257
+ function createNoopCompositeAnimation() {
1258
+ return {
1259
+ start: (cb?: (result: { finished: boolean }) => void) => {
1260
+ cb?.({ finished: true });
1261
+ },
1262
+ stop: () => undefined,
1263
+ reset: () => undefined,
1264
+ };
1265
+ }
1266
+
1267
+ export function createAnimatedComponent<P extends object>(Component: React.ComponentType<P>) {
1268
+ return Component;
1269
+ }
1270
+
1271
+ export const Animated = {
1272
+ Value: AnimatedValue,
1273
+ ValueXY: AnimatedValueXY,
1274
+ View,
1275
+ Text,
1276
+ Image,
1277
+ ScrollView,
1278
+ FlatList,
1279
+ SectionList,
1280
+ createAnimatedComponent,
1281
+ timing: (_value: unknown, _config: unknown) => createNoopCompositeAnimation(),
1282
+ spring: (_value: unknown, _config: unknown) => createNoopCompositeAnimation(),
1283
+ decay: (_value: unknown, _config: unknown) => createNoopCompositeAnimation(),
1284
+ parallel: (_animations: unknown[]) => createNoopCompositeAnimation(),
1285
+ sequence: (_animations: unknown[]) => createNoopCompositeAnimation(),
1286
+ stagger: (_delay: number, _animations: unknown[]) => createNoopCompositeAnimation(),
1287
+ loop: (_animation: unknown) => createNoopCompositeAnimation(),
1288
+ delay: (_ms: number) => createNoopCompositeAnimation(),
1289
+ event: () => () => undefined,
1290
+ add: (a: unknown) => a,
1291
+ subtract: (a: unknown) => a,
1292
+ divide: (a: unknown) => a,
1293
+ multiply: (a: unknown) => a,
1294
+ modulo: (a: unknown) => a,
1295
+ diffClamp: (a: unknown) => a,
1296
+ };
1297
+
1298
+ export function useAnimatedValue(initialValue = 0): AnimatedValue {
1299
+ const ref = useRef<AnimatedValue | null>(null);
89
1300
  if (!ref.current) {
90
- ref.current = new RNW.Animated.Value(initialValue);
1301
+ ref.current = new AnimatedValue(initialValue);
91
1302
  }
92
1303
  return ref.current;
93
1304
  }
94
1305
 
1306
+ export const Easing = {
1307
+ step0: (n: number) => (n > 0 ? 1 : 0),
1308
+ step1: (n: number) => (n >= 1 ? 1 : 0),
1309
+ linear: (t: number) => t,
1310
+ ease: (t: number) => t,
1311
+ quad: (t: number) => t * t,
1312
+ cubic: (t: number) => t * t * t,
1313
+ poly: () => (t: number) => t,
1314
+ sin: (t: number) => t,
1315
+ circle: (t: number) => t,
1316
+ exp: (t: number) => t,
1317
+ elastic: () => (t: number) => t,
1318
+ back: () => (t: number) => t,
1319
+ bounce: (t: number) => t,
1320
+ bezier: () => (t: number) => t,
1321
+ in: (fn: (t: number) => number) => fn,
1322
+ out: (fn: (t: number) => number) => fn,
1323
+ inOut: (fn: (t: number) => number) => fn,
1324
+ };
1325
+
1326
+ export const Alert = {
1327
+ alert: (_title?: string, _message?: string, _buttons?: unknown[]) => undefined,
1328
+ prompt: (_title?: string, _message?: string, _callbackOrButtons?: unknown) => undefined,
1329
+ };
1330
+
1331
+ export const Linking = {
1332
+ openURL: async (_url: string) => true,
1333
+ canOpenURL: async (_url: string) => true,
1334
+ openSettings: async () => undefined,
1335
+ getInitialURL: async () => null,
1336
+ sendIntent: async () => undefined,
1337
+ addEventListener: () => ({ remove: () => undefined }),
1338
+ removeEventListener: () => undefined,
1339
+ };
1340
+
1341
+ export const Share = {
1342
+ share: async () => ({ action: 'sharedAction' }),
1343
+ sharedAction: 'sharedAction',
1344
+ dismissedAction: 'dismissedAction',
1345
+ };
1346
+
1347
+ export const Vibration = {
1348
+ vibrate: () => undefined,
1349
+ cancel: () => undefined,
1350
+ };
1351
+
1352
+ export const Keyboard = {
1353
+ dismiss: () => undefined,
1354
+ isVisible: () => false,
1355
+ metrics: () => undefined,
1356
+ addListener: () => ({ remove: () => undefined }),
1357
+ removeListener: () => undefined,
1358
+ removeAllListeners: () => undefined,
1359
+ };
1360
+
1361
+ export const BackHandler = {
1362
+ exitApp: () => undefined,
1363
+ addEventListener: () => ({ remove: () => undefined }),
1364
+ removeEventListener: () => undefined,
1365
+ };
1366
+
1367
+ export const AppState = {
1368
+ currentState: 'active',
1369
+ isAvailable: true,
1370
+ addEventListener: () => ({ remove: () => undefined }),
1371
+ removeEventListener: () => undefined,
1372
+ };
1373
+
1374
+ export const Appearance = {
1375
+ getColorScheme: () => 'light' as const,
1376
+ setColorScheme: () => undefined,
1377
+ addChangeListener: () => ({ remove: () => undefined }),
1378
+ };
1379
+
1380
+ export function useColorScheme(): 'light' | 'dark' {
1381
+ return 'light';
1382
+ }
1383
+
1384
+ export const PanResponder = {
1385
+ create: () => ({ panHandlers: {} }),
1386
+ };
1387
+
1388
+ export const LayoutAnimation = {
1389
+ configureNext: (_config: unknown, onAnimationDidEnd?: () => void) => {
1390
+ onAnimationDidEnd?.();
1391
+ },
1392
+ create: () => ({}),
1393
+ Types: {
1394
+ spring: 'spring',
1395
+ linear: 'linear',
1396
+ easeInEaseOut: 'easeInEaseOut',
1397
+ easeIn: 'easeIn',
1398
+ easeOut: 'easeOut',
1399
+ keyboard: 'keyboard',
1400
+ },
1401
+ Properties: {
1402
+ opacity: 'opacity',
1403
+ scaleX: 'scaleX',
1404
+ scaleY: 'scaleY',
1405
+ scaleXY: 'scaleXY',
1406
+ },
1407
+ Presets: {
1408
+ easeInEaseOut: {},
1409
+ linear: {},
1410
+ spring: {},
1411
+ },
1412
+ };
1413
+
1414
+ export const UIManager = {
1415
+ setLayoutAnimationEnabledExperimental: () => undefined,
1416
+ measure: (
1417
+ _node: unknown,
1418
+ callback?: (x: number, y: number, w: number, h: number, px: number, py: number) => void
1419
+ ) => callback?.(0, 0, 393, 48, 0, 0),
1420
+ measureInWindow: (
1421
+ _node: unknown,
1422
+ callback?: (x: number, y: number, w: number, h: number) => void
1423
+ ) => callback?.(0, 0, 393, 48),
1424
+ measureLayout: () => undefined,
1425
+ dispatchViewManagerCommand: () => undefined,
1426
+ getViewManagerConfig: () => ({}),
1427
+ };
1428
+
1429
+ export const InteractionManager = {
1430
+ runAfterInteractions: (task?: () => void) => {
1431
+ task?.();
1432
+ return {
1433
+ then: (cb?: () => unknown) => Promise.resolve().then(cb),
1434
+ done: (cb?: () => unknown) => cb?.(),
1435
+ cancel: () => undefined,
1436
+ };
1437
+ },
1438
+ createInteractionHandle: () => 1,
1439
+ clearInteractionHandle: () => undefined,
1440
+ setDeadline: () => undefined,
1441
+ };
1442
+
1443
+ export class NativeEventEmitter {
1444
+ addListener() {
1445
+ return { remove: () => undefined };
1446
+ }
1447
+ removeAllListeners() {}
1448
+ removeSubscription() {}
1449
+ }
1450
+
1451
+ export const DeviceEventEmitter = new NativeEventEmitter();
1452
+ export const NativeAppEventEmitter = new NativeEventEmitter();
1453
+
1454
+ export const NativeModules: Record<string, Record<string, unknown>> = {
1455
+ StatusBarManager: { HEIGHT: 0, getHeight: (cb?: (h: { height: number }) => void) => cb?.({ height: 0 }) },
1456
+ PlatformConstants: { forceTouchAvailable: false, interfaceIdiom: 'phone' },
1457
+ SourceCode: { scriptURL: '' },
1458
+ UIManager,
1459
+ };
1460
+
1461
+ export const I18nManager = {
1462
+ isRTL: false,
1463
+ doLeftAndRightSwapInRTL: true,
1464
+ allowRTL: () => undefined,
1465
+ forceRTL: () => undefined,
1466
+ swapLeftAndRightInRTL: () => undefined,
1467
+ getConstants: () => ({ isRTL: false, doLeftAndRightSwapInRTL: true }),
1468
+ };
1469
+
1470
+ export const AccessibilityInfo = {
1471
+ isBoldTextEnabled: async () => false,
1472
+ isGrayscaleEnabled: async () => false,
1473
+ isInvertColorsEnabled: async () => false,
1474
+ isReduceMotionEnabled: async () => false,
1475
+ isReduceTransparencyEnabled: async () => false,
1476
+ isScreenReaderEnabled: async () => false,
1477
+ addEventListener: () => ({ remove: () => undefined }),
1478
+ announceForAccessibility: () => undefined,
1479
+ setAccessibilityFocus: () => undefined,
1480
+ };
1481
+
1482
+ export const LogBox = {
1483
+ ignoreLogs: () => undefined,
1484
+ ignoreAllLogs: () => undefined,
1485
+ install: () => undefined,
1486
+ uninstall: () => undefined,
1487
+ };
1488
+
1489
+ export const YellowBox = {
1490
+ ignoreWarnings: () => undefined,
1491
+ };
1492
+
1493
+ export function findNodeHandle(_componentOrHandle: unknown): number | null {
1494
+ return null;
1495
+ }
1496
+
1497
+ export function processColor(color: unknown): unknown {
1498
+ return color;
1499
+ }
1500
+
95
1501
  export const PermissionsAndroid = {
96
- PERMISSIONS: new Proxy({} as Record<string, string>, {
97
- get: (_target, prop) => String(prop),
98
- }),
1502
+ PERMISSIONS: {
1503
+ CAMERA: 'android.permission.CAMERA',
1504
+ ACCESS_FINE_LOCATION: 'android.permission.ACCESS_FINE_LOCATION',
1505
+ ACCESS_COARSE_LOCATION: 'android.permission.ACCESS_COARSE_LOCATION',
1506
+ READ_EXTERNAL_STORAGE: 'android.permission.READ_EXTERNAL_STORAGE',
1507
+ WRITE_EXTERNAL_STORAGE: 'android.permission.WRITE_EXTERNAL_STORAGE',
1508
+ READ_MEDIA_IMAGES: 'android.permission.READ_MEDIA_IMAGES',
1509
+ POST_NOTIFICATIONS: 'android.permission.POST_NOTIFICATIONS',
1510
+ RECORD_AUDIO: 'android.permission.RECORD_AUDIO',
1511
+ READ_CONTACTS: 'android.permission.READ_CONTACTS',
1512
+ },
99
1513
  RESULTS: {
100
1514
  GRANTED: 'granted',
101
1515
  DENIED: 'denied',
@@ -130,7 +1544,7 @@ export const DrawerLayoutAndroid: React.FC<{
130
1544
  children?: React.ReactNode;
131
1545
  style?: unknown;
132
1546
  }> = ({ children, style }) =>
133
- React.createElement(RNW.View, { style: [{ flex: 1 }, style] as never }, children);
1547
+ React.createElement(View, { style: [{ flex: 1 }, style] as never }, children);
134
1548
 
135
1549
  export const TurboModuleRegistry = {
136
1550
  get: () => ({}),
@@ -141,7 +1555,7 @@ export function requireNativeComponent(viewName: string) {
141
1555
  const Comp: React.FC<{ children?: React.ReactNode; style?: unknown }> = ({
142
1556
  children,
143
1557
  style,
144
- }) => React.createElement(RNW.View, { style: style as never }, children);
1558
+ }) => React.createElement(View, { style: style as never }, children);
145
1559
  Comp.displayName = viewName;
146
1560
  return Comp;
147
1561
  }
@@ -187,54 +1601,74 @@ export const Settings = {
187
1601
  clearWatch: () => undefined,
188
1602
  };
189
1603
 
190
- const ReactNativeDefault = new Proxy(
191
- {
192
- ...RNW,
193
- View,
194
- Text,
195
- Pressable,
196
- TouchableOpacity,
197
- TouchableHighlight,
198
- TouchableWithoutFeedback,
199
- ScrollView,
200
- FlatList,
201
- SectionList,
202
- KeyboardAvoidingView,
203
- TextInput,
204
- Image,
205
- ImageBackground,
206
- SafeAreaView,
207
- ActivityIndicator,
208
- Modal,
209
- Dimensions,
210
- useWindowDimensions,
211
- useAnimatedValue,
212
- PermissionsAndroid,
213
- ToastAndroid,
214
- ActionSheetIOS,
215
- InputAccessoryView,
216
- DrawerLayoutAndroid,
217
- TurboModuleRegistry,
218
- requireNativeComponent,
219
- codegenNativeComponent,
220
- PlatformColor,
221
- DynamicColorIOS,
222
- DevSettings,
223
- Systrace,
224
- Networking,
225
- Settings,
226
- },
227
- {
228
- get(target, prop) {
229
- if (prop in target) {
230
- return target[prop as keyof typeof target];
231
- }
232
- if (typeof prop === 'string' && /^[A-Z]/.test(prop)) {
233
- return requireNativeComponent(prop);
234
- }
235
- return () => undefined;
236
- },
237
- }
238
- );
1604
+ const ReactNativeDefault = {
1605
+ View,
1606
+ Text,
1607
+ Pressable,
1608
+ TouchableOpacity,
1609
+ TouchableHighlight,
1610
+ TouchableWithoutFeedback,
1611
+ TouchableNativeFeedback,
1612
+ ScrollView,
1613
+ FlatList,
1614
+ SectionList,
1615
+ VirtualizedList,
1616
+ KeyboardAvoidingView,
1617
+ TextInput,
1618
+ Image,
1619
+ ImageBackground,
1620
+ SafeAreaView,
1621
+ ActivityIndicator,
1622
+ Switch,
1623
+ Modal,
1624
+ Button,
1625
+ RefreshControl,
1626
+ StatusBar,
1627
+ StyleSheet,
1628
+ Platform,
1629
+ Dimensions,
1630
+ useWindowDimensions,
1631
+ PixelRatio,
1632
+ Animated,
1633
+ useAnimatedValue,
1634
+ Easing,
1635
+ Alert,
1636
+ Linking,
1637
+ Share,
1638
+ Vibration,
1639
+ Keyboard,
1640
+ BackHandler,
1641
+ AppState,
1642
+ Appearance,
1643
+ useColorScheme,
1644
+ PanResponder,
1645
+ LayoutAnimation,
1646
+ UIManager,
1647
+ InteractionManager,
1648
+ NativeEventEmitter,
1649
+ DeviceEventEmitter,
1650
+ NativeAppEventEmitter,
1651
+ NativeModules,
1652
+ I18nManager,
1653
+ AccessibilityInfo,
1654
+ LogBox,
1655
+ YellowBox,
1656
+ findNodeHandle,
1657
+ processColor,
1658
+ PermissionsAndroid,
1659
+ ToastAndroid,
1660
+ ActionSheetIOS,
1661
+ InputAccessoryView,
1662
+ DrawerLayoutAndroid,
1663
+ TurboModuleRegistry,
1664
+ requireNativeComponent,
1665
+ codegenNativeComponent,
1666
+ PlatformColor,
1667
+ DynamicColorIOS,
1668
+ DevSettings,
1669
+ Systrace,
1670
+ Networking,
1671
+ Settings,
1672
+ };
239
1673
 
240
1674
  export default ReactNativeDefault;