@symbiote-native/vue 0.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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -0
  3. package/build/components/activity-indicator/index.android.d.ts +4 -0
  4. package/build/components/activity-indicator/index.android.js +10 -0
  5. package/build/components/activity-indicator/index.d.ts +1 -0
  6. package/build/components/activity-indicator/index.ios.d.ts +4 -0
  7. package/build/components/activity-indicator/index.ios.js +10 -0
  8. package/build/components/activity-indicator/index.js +4 -0
  9. package/build/components/activity-indicator/shared.d.ts +8 -0
  10. package/build/components/activity-indicator/shared.js +48 -0
  11. package/build/components/button.d.ts +14 -0
  12. package/build/components/button.js +59 -0
  13. package/build/components/drawer-layout-android/index.android.d.ts +8 -0
  14. package/build/components/drawer-layout-android/index.android.js +144 -0
  15. package/build/components/drawer-layout-android/index.d.ts +8 -0
  16. package/build/components/drawer-layout-android/index.js +36 -0
  17. package/build/components/drawer-layout-android/shared.d.ts +18 -0
  18. package/build/components/drawer-layout-android/shared.js +6 -0
  19. package/build/components/flat-list/index.d.ts +88 -0
  20. package/build/components/flat-list/index.js +189 -0
  21. package/build/components/image-background.d.ts +9 -0
  22. package/build/components/image-background.js +114 -0
  23. package/build/components/image.d.ts +9 -0
  24. package/build/components/image.js +99 -0
  25. package/build/components/input-accessory-view.d.ts +10 -0
  26. package/build/components/input-accessory-view.js +41 -0
  27. package/build/components/keyboard-avoiding-view.d.ts +18 -0
  28. package/build/components/keyboard-avoiding-view.js +121 -0
  29. package/build/components/modal.d.ts +29 -0
  30. package/build/components/modal.js +134 -0
  31. package/build/components/pressable.d.ts +58 -0
  32. package/build/components/pressable.js +221 -0
  33. package/build/components/refresh-control.d.ts +20 -0
  34. package/build/components/refresh-control.js +53 -0
  35. package/build/components/safe-area-view.d.ts +8 -0
  36. package/build/components/safe-area-view.js +33 -0
  37. package/build/components/scroll-view/index.android.d.ts +4 -0
  38. package/build/components/scroll-view/index.android.js +74 -0
  39. package/build/components/scroll-view/index.d.ts +1 -0
  40. package/build/components/scroll-view/index.ios.d.ts +4 -0
  41. package/build/components/scroll-view/index.ios.js +15 -0
  42. package/build/components/scroll-view/index.js +4 -0
  43. package/build/components/scroll-view/shared.d.ts +96 -0
  44. package/build/components/scroll-view/shared.js +297 -0
  45. package/build/components/scroll-view/sticky-header.d.ts +7 -0
  46. package/build/components/scroll-view/sticky-header.js +195 -0
  47. package/build/components/section-list/index.d.ts +39 -0
  48. package/build/components/section-list/index.js +77 -0
  49. package/build/components/switch/index.android.d.ts +12 -0
  50. package/build/components/switch/index.android.js +13 -0
  51. package/build/components/switch/index.d.ts +2 -0
  52. package/build/components/switch/index.ios.d.ts +12 -0
  53. package/build/components/switch/index.ios.js +11 -0
  54. package/build/components/switch/index.js +5 -0
  55. package/build/components/switch/shared.d.ts +23 -0
  56. package/build/components/switch/shared.js +129 -0
  57. package/build/components/text-input/index.d.ts +24 -0
  58. package/build/components/text-input/index.js +257 -0
  59. package/build/components/touchable-native-feedback.d.ts +24 -0
  60. package/build/components/touchable-native-feedback.js +75 -0
  61. package/build/components/touchable.d.ts +44 -0
  62. package/build/components/touchable.js +150 -0
  63. package/build/components/virtualized-list/index.d.ts +121 -0
  64. package/build/components/virtualized-list/index.js +687 -0
  65. package/build/components/virtualized-section-list/index.d.ts +96 -0
  66. package/build/components/virtualized-section-list/index.js +160 -0
  67. package/build/components.d.ts +51 -0
  68. package/build/components.js +26 -0
  69. package/build/composables/use-color-scheme.d.ts +3 -0
  70. package/build/composables/use-color-scheme.js +24 -0
  71. package/build/composables/use-window-dimensions.d.ts +3 -0
  72. package/build/composables/use-window-dimensions.js +34 -0
  73. package/build/create-tunnel.d.ts +9 -0
  74. package/build/create-tunnel.js +37 -0
  75. package/build/descriptor-to-vue.d.ts +3 -0
  76. package/build/descriptor-to-vue.js +14 -0
  77. package/build/host-instance/index.d.ts +2 -0
  78. package/build/host-instance/index.js +34 -0
  79. package/build/index.d.ts +67 -0
  80. package/build/index.js +90 -0
  81. package/build/modules/animated/create-animated-component.d.ts +4 -0
  82. package/build/modules/animated/create-animated-component.js +169 -0
  83. package/build/modules/animated/index.d.ts +55 -0
  84. package/build/modules/animated/index.js +66 -0
  85. package/build/modules/app-registry/index.d.ts +7 -0
  86. package/build/modules/app-registry/index.js +32 -0
  87. package/build/modules/status-bar.d.ts +17 -0
  88. package/build/modules/status-bar.js +65 -0
  89. package/build/render.d.ts +7 -0
  90. package/build/render.js +43 -0
  91. package/build/renderer.d.ts +4 -0
  92. package/build/renderer.js +129 -0
  93. package/build/runtime-helpers.d.ts +28 -0
  94. package/build/runtime-helpers.js +68 -0
  95. package/build/utils/component-helpers.d.ts +2 -0
  96. package/build/utils/component-helpers.js +13 -0
  97. package/build/utils/model-binding.d.ts +4 -0
  98. package/build/utils/model-binding.js +17 -0
  99. package/build/utils/normalize-attrs.d.ts +1 -0
  100. package/build/utils/normalize-attrs.js +30 -0
  101. package/build/utils/slots-to-render-props.d.ts +4 -0
  102. package/build/utils/slots-to-render-props.js +18 -0
  103. package/metro-css-parser.cjs +12 -0
  104. package/package.json +54 -0
@@ -0,0 +1,96 @@
1
+ import { type VNode } from '@vue/runtime-core';
2
+ import { type ISection, type ISeparatorProps, type ISeparators, type IVirtualizedSectionListHandle } from '@symbiote-native/components';
3
+ import { type IStyleProp, type ISymbioteEvent, type IViewStyle } from '@symbiote-native/engine';
4
+ export type { ISection } from '@symbiote-native/components';
5
+ export type { IVirtualizedSectionListHandle };
6
+ export interface IVirtualizedSectionListProps<ItemT> {
7
+ sections: ReadonlyArray<ISection<ItemT>>;
8
+ keyExtractor?: (item: ItemT, index: number) => string;
9
+ stickySectionHeadersEnabled?: boolean;
10
+ extraData?: unknown;
11
+ onEndReachedThreshold?: number;
12
+ onStartReachedThreshold?: number;
13
+ refreshing?: boolean | null;
14
+ progressViewOffset?: number;
15
+ initialNumToRender?: number;
16
+ initialScrollIndex?: number;
17
+ maxToRenderPerBatch?: number;
18
+ updateCellsBatchingPeriod?: number;
19
+ windowSize?: number;
20
+ inverted?: boolean;
21
+ maintainVisibleContentPosition?: {
22
+ minIndexForVisible: number;
23
+ autoscrollToTopThreshold?: number;
24
+ };
25
+ onScroll?: (event: ISymbioteEvent) => void;
26
+ onScrollBeginDrag?: (event: ISymbioteEvent) => void;
27
+ onScrollEndDrag?: (event: ISymbioteEvent) => void;
28
+ onMomentumScrollBegin?: (event: ISymbioteEvent) => void;
29
+ onMomentumScrollEnd?: (event: ISymbioteEvent) => void;
30
+ scrollEventThrottle?: number;
31
+ keyboardShouldPersistTaps?: boolean | 'always' | 'never' | 'handled';
32
+ keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
33
+ style?: IStyleProp<IViewStyle>;
34
+ contentContainerStyle?: IStyleProp<IViewStyle>;
35
+ [key: string]: unknown;
36
+ }
37
+ export type IVirtualizedSectionListSlots<ItemT> = {
38
+ item: (info: {
39
+ item: ItemT;
40
+ index: number;
41
+ section: ISection<ItemT>;
42
+ separators: ISeparators;
43
+ }) => VNode[] | VNode;
44
+ sectionHeader?: (info: {
45
+ section: ISection<ItemT>;
46
+ }) => VNode[] | VNode;
47
+ sectionFooter?: (info: {
48
+ section: ISection<ItemT>;
49
+ }) => VNode[] | VNode;
50
+ sectionSeparator?: () => VNode[] | VNode;
51
+ separator?: (props: ISeparatorProps<ItemT>) => VNode[] | VNode;
52
+ header?: () => VNode[] | VNode;
53
+ footer?: () => VNode[] | VNode;
54
+ empty?: () => VNode[] | VNode;
55
+ };
56
+ export type IVirtualizedSectionListEmits = {
57
+ endReached: (info: {
58
+ distanceFromEnd: number;
59
+ }) => void;
60
+ startReached: (info: {
61
+ distanceFromStart: number;
62
+ }) => void;
63
+ refresh: () => void;
64
+ };
65
+ export declare const VirtualizedSectionList: new <ItemT>(props: (IVirtualizedSectionListProps<ItemT> & ({} | {
66
+ onRefresh?: (() => any) | undefined;
67
+ onEndReached?: ((info: {
68
+ distanceFromEnd: number;
69
+ }) => any) | undefined;
70
+ onStartReached?: ((info: {
71
+ distanceFromStart: number;
72
+ }) => any) | undefined;
73
+ })) & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps) => import("vue").CreateComponentPublicInstanceWithMixins<IVirtualizedSectionListProps<ItemT> & ({} | {
74
+ onRefresh?: (() => any) | undefined;
75
+ onEndReached?: ((info: {
76
+ distanceFromEnd: number;
77
+ }) => any) | undefined;
78
+ onStartReached?: ((info: {
79
+ distanceFromStart: number;
80
+ }) => any) | undefined;
81
+ }), {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} | IVirtualizedSectionListEmits, import("vue").PublicProps, {}, false, {}, import("vue").SlotsType<IVirtualizedSectionListSlots<ItemT>>, {}, {}, string, {}, any, import("vue").ComponentProvideOptions, {
82
+ P: {};
83
+ B: {};
84
+ D: {};
85
+ C: {};
86
+ M: {};
87
+ Defaults: {};
88
+ }, (IVirtualizedSectionListProps<ItemT> & {}) | (IVirtualizedSectionListProps<ItemT> & {
89
+ onRefresh?: (() => any) | undefined;
90
+ onEndReached?: ((info: {
91
+ distanceFromEnd: number;
92
+ }) => any) | undefined;
93
+ onStartReached?: ((info: {
94
+ distanceFromStart: number;
95
+ }) => any) | undefined;
96
+ }), {}, {}, {}, {}, {}>;
@@ -0,0 +1,160 @@
1
+ // VirtualizedSectionList, the Vue wrapper that flattens sections into one virtualized stream
2
+ // over VirtualizedList. Each section contributes a header row, its item rows, then a footer row
3
+ // (RN counts 2 per section); the flattened tagged sequence is fed to VirtualizedList as one list,
4
+ // so headers/items/footers are all windowed by the same machinery. The flattening, entry keying,
5
+ // separator-item unwrap, and scrollToLocation mapping are shared from @symbiote-native/components; this
6
+ // file wires Vue lifecycle (typed-prop inputs + handle re-expose + the per-entry render dispatch).
7
+ // The Vue twin of the React adapter's VirtualizedSectionList.
8
+ //
9
+ // Typed-emits generic component (mirrors FlatList / VirtualizedList): a GENERIC setup function
10
+ // `<ItemT,>(props: IVirtualizedSectionListProps<ItemT>, ctx: ICtx<IVirtualizedSectionListEmits>)`
11
+ // so the section inputs (sections/renderItem/renderSectionHeader/…) infer ItemT at the call site.
12
+ // For that, those inputs are read from typed `props` (declared in the runtime `props` array); the
13
+ // VirtualizedList passthrough tail (refreshing/style/raw scroll/…) rides through `$attrs` onto the
14
+ // inner list. The three adapter-synthesized events (endReached/startReached/refresh — the exact set
15
+ // React's section list exposes) stay GATED on listener presence: each emit bridge is wired ONLY
16
+ // when the consumer listens (read off the instance vnode props), so the inner VirtualizedList keeps
17
+ // building RefreshControl / computing edge-reached strictly on demand.
18
+ import { defineComponent, getCurrentInstance, h, shallowRef, } from '@vue/runtime-core';
19
+ import { flattenSections, scrollLocationToFlatIndex, sectionEntryKey, unwrapEntryItem, } from '@symbiote-native/components';
20
+ import { Platform, dlog, } from '@symbiote-native/engine';
21
+ import { VirtualizedList } from '../virtualized-list';
22
+ import { normalizeVueAttrs } from '../../utils/normalize-attrs';
23
+ // VirtualizedList is a generic component (generic construct signature), which h()'s overloads can't
24
+ // resolve. Drive it through a loose functional-component handle (generic-component h() limitation).
25
+ const VirtualizedListHost = VirtualizedList;
26
+ // The typed inputs VSL reads off `props`; everything else (the VirtualizedList passthrough tail and
27
+ // raw scroll) falls through $attrs onto the inner list. Listed for the runtime `props` declaration
28
+ // (keyof can't derive it: the index signature widens keyof to `string`). The three emit events are
29
+ // deliberately absent (declared as emits below).
30
+ const PROP_KEYS = ['sections', 'keyExtractor', 'stickySectionHeadersEnabled'];
31
+ const EMIT_KEYS = ['endReached', 'startReached', 'refresh'];
32
+ function isRecord(value) {
33
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
34
+ }
35
+ function isVirtualizedListHandle(value) {
36
+ return isRecord(value) && typeof value.scrollToOffset === 'function';
37
+ }
38
+ function buildSectionDelegate(getInner, getHeaderIndices) {
39
+ return {
40
+ scrollToLocation: (params) => {
41
+ const flatIndex = scrollLocationToFlatIndex(getHeaderIndices(), params.sectionIndex, params.itemIndex);
42
+ if (flatIndex === undefined) {
43
+ dlog(`Vue VirtualizedSectionList scrollToLocation: section ${params.sectionIndex} out of range`);
44
+ return;
45
+ }
46
+ dlog(`Vue VirtualizedSectionList scrollToLocation section=${params.sectionIndex} ` +
47
+ `item=${params.itemIndex} -> flat ${flatIndex}`);
48
+ getInner()?.scrollToIndex({
49
+ index: flatIndex,
50
+ viewOffset: params.viewOffset,
51
+ viewPosition: params.viewPosition,
52
+ animated: params.animated,
53
+ });
54
+ },
55
+ flashScrollIndicators: () => getInner()?.flashScrollIndicators(),
56
+ getNativeScrollRef: () => getInner()?.getNativeScrollRef() ?? null,
57
+ getScrollableNode: () => getInner()?.getScrollableNode() ?? null,
58
+ getScrollResponder: () => getInner()?.getScrollResponder() ?? null,
59
+ getScrollNode: () => getInner()?.getScrollNode() ?? null,
60
+ recordInteraction: () => getInner()?.recordInteraction(),
61
+ };
62
+ }
63
+ export const VirtualizedSectionList = defineComponent((props, { attrs, expose, emit, slots, }) => {
64
+ const inner = shallowRef(null);
65
+ const setInner = (instance) => {
66
+ inner.value = isVirtualizedListHandle(instance) ? instance : null;
67
+ };
68
+ // headerIndices change each render (sections may change); the delegate reads them lazily.
69
+ let headerIndices = [];
70
+ expose(buildSectionDelegate(() => inner.value, () => headerIndices));
71
+ // The three section-list events are emits, so Vue strips their onX listeners from $attrs. Detect
72
+ // listener presence off the instance's own vnode props (what the parent passed), exactly like
73
+ // FlatList/VirtualizedList, so an emit bridge is wired ONLY when the consumer actually listens
74
+ // (preserving the inner list's RefreshControl / edge-reached gating).
75
+ const instance = getCurrentInstance();
76
+ const listens = (onName) => {
77
+ const vnodeProps = instance?.vnode.props;
78
+ return vnodeProps != null && typeof vnodeProps[onName] === 'function';
79
+ };
80
+ return () => {
81
+ const sections = Array.isArray(props.sections)
82
+ ? props.sections
83
+ : [];
84
+ const keyExtractor = props.keyExtractor;
85
+ // The passthrough tail: declared props + declared emits' onX are already removed from $attrs,
86
+ // so what's left is the VirtualizedList passthrough (kebab-folded for the inner list).
87
+ const forwarded = normalizeVueAttrs(attrs);
88
+ const { entries, headerIndices: indices } = flattenSections(sections, slots.sectionSeparator !== undefined);
89
+ headerIndices = indices;
90
+ // RN sticks section headers by default only on iOS; Android does not unless asked.
91
+ const stickyDefault = Platform.OS === 'ios';
92
+ const stickyEnabled = props.stickySectionHeadersEnabled ?? stickyDefault;
93
+ const stickyHeaderIndices = stickyEnabled ? indices : undefined;
94
+ dlog(`Vue VirtualizedSectionList: ${sections.length} sections flattened to ${entries.length} entries`);
95
+ // The inner list's #item slot: dispatch each flattened entry to the matching consumer slot,
96
+ // returning its VNode[] (empty when the consumer left that slot off).
97
+ const renderEntry = (info) => {
98
+ const entry = info.item;
99
+ if (entry.kind === 'header') {
100
+ return slots.sectionHeader ? slots.sectionHeader({ section: entry.section }) : [];
101
+ }
102
+ if (entry.kind === 'footer') {
103
+ return slots.sectionFooter ? slots.sectionFooter({ section: entry.section }) : [];
104
+ }
105
+ if (entry.kind === 'section-separator') {
106
+ return slots.sectionSeparator ? slots.sectionSeparator() : [];
107
+ }
108
+ return slots.item
109
+ ? slots.item({
110
+ item: entry.item,
111
+ index: entry.itemIndex,
112
+ section: entry.section,
113
+ separators: info.separators,
114
+ })
115
+ : [];
116
+ };
117
+ // The user's #separator is typed on ItemT, but the inner stream is the entry wrapper; unwrap
118
+ // each entry back to its ItemT (shared unwrapEntryItem) before the inner list invokes it.
119
+ const entrySeparatorSlot = slots.separator === undefined
120
+ ? undefined
121
+ : (entryProps) => slots.separator({
122
+ ...entryProps,
123
+ leadingItem: unwrapEntryItem(entryProps.leadingItem),
124
+ trailingItem: unwrapEntryItem(entryProps.trailingItem),
125
+ });
126
+ const entryKeyExtractor = (entry, index) => sectionEntryKey(entry, index, keyExtractor);
127
+ // The three synthesized events become inner-VL handlers ONLY when listened, so the inner list
128
+ // keeps gating (no parasitic RefreshControl / edge-reached work for an unlistened event).
129
+ const endReached = listens('onEndReached')
130
+ ? (eventInfo) => emit('endReached', eventInfo)
131
+ : undefined;
132
+ const startReached = listens('onStartReached')
133
+ ? (eventInfo) => emit('startReached', eventInfo)
134
+ : undefined;
135
+ const refresh = listens('onRefresh') ? () => emit('refresh') : undefined;
136
+ return h(VirtualizedListHost, {
137
+ ...forwarded,
138
+ ref: setInner,
139
+ data: entries,
140
+ getItem: (_source, index) => entries[index],
141
+ getItemCount: () => entries.length,
142
+ keyExtractor: entryKeyExtractor,
143
+ stickyHeaderIndices,
144
+ onEndReached: endReached,
145
+ onStartReached: startReached,
146
+ onRefresh: refresh,
147
+ }, {
148
+ item: renderEntry,
149
+ separator: entrySeparatorSlot,
150
+ header: slots.header,
151
+ footer: slots.footer,
152
+ empty: slots.empty,
153
+ });
154
+ };
155
+ }, {
156
+ name: 'VirtualizedSectionList',
157
+ inheritAttrs: false,
158
+ props: PROP_KEYS,
159
+ emits: EMIT_KEYS,
160
+ });
@@ -0,0 +1,51 @@
1
+ import { type FunctionalComponent, type VNodeRef } from '@vue/runtime-core';
2
+ import type { ISymbioteEvent, IClassNameValue, IStyleProp, ITextStyle, IViewStyle } from '@symbiote-native/engine';
3
+ import type { IAccessibilityProps, IAriaProps, IResponderProps } from '@symbiote-native/components';
4
+ export interface IViewProps extends IAccessibilityProps, IAriaProps, IResponderProps {
5
+ style?: IStyleProp<IViewStyle>;
6
+ class?: IClassNameValue;
7
+ onPress?: (event: ISymbioteEvent) => void;
8
+ onPressIn?: (event: ISymbioteEvent) => void;
9
+ onPressOut?: (event: ISymbioteEvent) => void;
10
+ onLayout?: (event: ISymbioteEvent) => void;
11
+ onFocus?: (event: ISymbioteEvent) => void;
12
+ onBlur?: (event: ISymbioteEvent) => void;
13
+ pointerEvents?: 'auto' | 'none' | 'box-none' | 'box-only';
14
+ hitSlop?: number | {
15
+ top?: number;
16
+ left?: number;
17
+ bottom?: number;
18
+ right?: number;
19
+ };
20
+ id?: string;
21
+ focusable?: boolean;
22
+ collapsable?: boolean;
23
+ removeClippedSubviews?: boolean;
24
+ renderToHardwareTextureAndroid?: boolean;
25
+ shouldRasterizeIOS?: boolean;
26
+ needsOffscreenAlphaCompositing?: boolean;
27
+ ref?: VNodeRef;
28
+ key?: string | number | symbol;
29
+ }
30
+ export interface ITextProps extends IAccessibilityProps, IAriaProps {
31
+ style?: IStyleProp<ITextStyle>;
32
+ class?: IClassNameValue;
33
+ onPress?: (event: ISymbioteEvent) => void;
34
+ onLongPress?: (event: ISymbioteEvent) => void;
35
+ onPressIn?: (event: ISymbioteEvent) => void;
36
+ onPressOut?: (event: ISymbioteEvent) => void;
37
+ onLayout?: (event: ISymbioteEvent) => void;
38
+ onTextLayout?: (event: ISymbioteEvent) => void;
39
+ numberOfLines?: number;
40
+ ellipsizeMode?: 'head' | 'middle' | 'tail' | 'clip';
41
+ selectable?: boolean;
42
+ adjustsFontSizeToFit?: boolean;
43
+ minimumFontScale?: number;
44
+ allowFontScaling?: boolean;
45
+ maxFontSizeMultiplier?: number | null;
46
+ selectionColor?: string;
47
+ ref?: VNodeRef;
48
+ key?: string | number | symbol;
49
+ }
50
+ export declare const View: FunctionalComponent<IViewProps, {}, any, {}>;
51
+ export declare const Text: FunctionalComponent<ITextProps, {}, any, {}>;
@@ -0,0 +1,26 @@
1
+ // Host primitives for app code. Thin FUNCTIONAL components over the intrinsic tags the
2
+ // renderer maps to Fabric: `inheritAttrs: false` + a manual attr spread passes every
3
+ // prop and `@event` (onX) straight through to patchProp -> routeProp. The full prop
4
+ // surface (typed ViewProps/TextProps, a11y folding) arrives with @symbiote-native/components.
5
+ //
6
+ // FUNCTIONAL, not a stateful defineComponent: a functional component has no instance, so
7
+ // a template/function ref on it falls through to its single root host element (the raw
8
+ // SymbioteNode), exactly like React's functional View hands back its host instance. A
9
+ // stateful component's ref would resolve to a useless component proxy, which is why
10
+ // createAnimatedComponent (which captures the host node via that ref) needs the fall-through.
11
+ import { h } from '@vue/runtime-core';
12
+ import { normalizeVueAttrs } from './utils/normalize-attrs';
13
+ function hostComponent(intrinsic, name) {
14
+ // A functional component's ctx is Omit<SetupContext, 'expose'> (no instance to expose); let the
15
+ // FunctionalComponent target infer the param types rather than annotate SetupContext.
16
+ // normalizeVueAttrs folds kebab template props (:accessibility-label) to the RN camelCase contract.
17
+ const component = (_props, { slots, attrs }) => h(intrinsic, normalizeVueAttrs(attrs), slots.default !== undefined ? slots.default() : undefined);
18
+ component.displayName = name;
19
+ component.inheritAttrs = false;
20
+ return component;
21
+ }
22
+ export const View = hostComponent('symbiote-view', 'View');
23
+ export const Text = hostComponent('symbiote-text', 'Text');
24
+ // Image is NOT a bare host primitive: it needs the shared fold (source/src/srcSet resolution,
25
+ // width/height → style, alt → accessibility) + the Image statics, so it lives in ./image as a
26
+ // functional component over renderImage. View/Text stay bare; they forward attrs verbatim.
@@ -0,0 +1,3 @@
1
+ import { type Ref } from '@vue/runtime-core';
2
+ import { type IColorSchemeName } from '@symbiote-native/engine';
3
+ export declare function useColorScheme(): Ref<IColorSchemeName | null>;
@@ -0,0 +1,24 @@
1
+ // useColorScheme, the Vue twin of React's hook. It subscribes a component to the device
2
+ // color scheme and updates a reactive ref on change, over the now-core Appearance module
3
+ // (@symbiote-native/engine). Idiomatic Vue: returns a Ref the template unwraps, seeded from the
4
+ // current scheme and kept fresh by an Appearance change listener torn down on unmount.
5
+ // React's useColorScheme returns the bare value via useSyncExternalStore; the underlying
6
+ // Appearance module is shared verbatim; only the lifecycle differs.
7
+ import { ref, onMounted, onUnmounted } from '@vue/runtime-core';
8
+ import { Appearance, } from '@symbiote-native/engine';
9
+ export function useColorScheme() {
10
+ // A plain ref: the value is a string|null, not an engine node, so no shallowRef needed.
11
+ const colorScheme = ref(Appearance.getColorScheme());
12
+ let subscription;
13
+ onMounted(() => {
14
+ // Re-read on mount in case the scheme changed between setup() and mount.
15
+ colorScheme.value = Appearance.getColorScheme();
16
+ subscription = Appearance.addChangeListener(preferences => {
17
+ colorScheme.value = preferences.colorScheme;
18
+ });
19
+ });
20
+ onUnmounted(() => {
21
+ subscription?.remove();
22
+ });
23
+ return colorScheme;
24
+ }
@@ -0,0 +1,3 @@
1
+ import { type Ref } from '@vue/runtime-core';
2
+ import { type IDisplayMetrics } from '@symbiote-native/engine';
3
+ export declare function useWindowDimensions(): Ref<IDisplayMetrics>;
@@ -0,0 +1,34 @@
1
+ // useWindowDimensions, the Vue twin of React's hook, over the now-core Dimensions module
2
+ // (@symbiote-native/engine). It seeds from Dimensions.get('window'), subscribes to 'change', and
3
+ // re-checks once after subscribing to close the gap between the setup-time get and the
4
+ // mount-time listener. Only a real window-metric change updates the ref. React returns the
5
+ // bare value via useState; the underlying Dimensions module is shared verbatim; only the
6
+ // lifecycle differs (returns a Ref the template unwraps).
7
+ import { ref, onMounted, onUnmounted } from '@vue/runtime-core';
8
+ import { Dimensions, } from '@symbiote-native/engine';
9
+ export function useWindowDimensions() {
10
+ // A plain ref: the value is a metrics record (plain data), not an engine node.
11
+ const dimensions = ref(Dimensions.get('window'));
12
+ let subscription;
13
+ onMounted(() => {
14
+ const handleChange = (window) => {
15
+ const current = dimensions.value;
16
+ if (current.width !== window.width ||
17
+ current.height !== window.height ||
18
+ current.scale !== window.scale ||
19
+ current.fontScale !== window.fontScale) {
20
+ dimensions.value = window;
21
+ }
22
+ };
23
+ subscription = Dimensions.addEventListener('change', (set) => {
24
+ handleChange(set.window);
25
+ });
26
+ // We may have missed an update between the setup-time `get` and subscribing here;
27
+ // re-check now. If nothing changed, the equality guard filters the no-op.
28
+ handleChange(Dimensions.get('window'));
29
+ });
30
+ onUnmounted(() => {
31
+ subscription?.remove();
32
+ });
33
+ return dimensions;
34
+ }
@@ -0,0 +1,9 @@
1
+ import { defineComponent } from 'vue';
2
+ export interface ITunnel {
3
+ /** Register the default slot's content under the tunnel — mount this anywhere, any surface. */
4
+ In: ReturnType<typeof defineComponent>;
5
+ /** Renders everything currently tunneled in, in registration order. Mount this in the
6
+ * component that should actually paint the content. */
7
+ Out: ReturnType<typeof defineComponent>;
8
+ }
9
+ export declare function createTunnel(): ITunnel;
@@ -0,0 +1,37 @@
1
+ // createTunnel — the Vue twin of the React adapter's create-tunnel.ts (see its header, plus
2
+ // the react-adapter-portal / vue-adapter-directives skills, for the full "why not just
3
+ // extend Teleport across surfaces" rationale). `Out` lives in whichever component should
4
+ // PAINT the content; its own reactive read makes THAT surface's normal render/commit pick it
5
+ // up — no cross-surface reach-in, no rootTag lookup, works whether In and Out share a
6
+ // surface or not.
7
+ //
8
+ // Both In and Out are COMPONENTS, not composables: a composable can't accept template
9
+ // markup (slots only exist on components), so app code writes ordinary <template> —
10
+ // <tunnel.In>…</tunnel.In> / <tunnel.Out /> — exactly like the existing Teleport wrapper.
11
+ // h()/render functions stay inside THIS file; they never leak into app-level SFC script.
12
+ import { defineComponent, onUnmounted, reactive } from 'vue';
13
+ export function createTunnel() {
14
+ const items = reactive(new Map());
15
+ let nextId = 0;
16
+ const In = defineComponent({
17
+ name: 'TunnelIn',
18
+ setup(_props, { slots }) {
19
+ const id = nextId++;
20
+ onUnmounted(() => items.delete(id));
21
+ // Runs on every render of In — Vue's own reactivity picks up the Map mutation (the
22
+ // slot function itself re-reads whatever reactive state it closes over), no manual
23
+ // notify needed, unlike the React twin's useSyncExternalStore.
24
+ return () => {
25
+ items.set(id, slots.default ?? (() => []));
26
+ return null;
27
+ };
28
+ },
29
+ });
30
+ const Out = defineComponent({
31
+ name: 'TunnelOut',
32
+ setup() {
33
+ return () => Array.from(items.values()).flatMap(slot => slot());
34
+ },
35
+ });
36
+ return { In, Out };
37
+ }
@@ -0,0 +1,3 @@
1
+ import { type VNode } from '@vue/runtime-core';
2
+ import type { IDescriptor } from '@symbiote-native/components';
3
+ export declare function descriptorToVue(node: IDescriptor): VNode;
@@ -0,0 +1,14 @@
1
+ // The descriptor→element bridge for Vue. A render function in @symbiote-native/components returns
2
+ // a framework-agnostic `Descriptor` tree; this maps it onto Vue vnodes via h(). The host
3
+ // vnode (`symbiote-view`, `symbiote-activity-indicator`, …) flows on through the Vue custom
4
+ // renderer → engine → Fabric, exactly like a hand-written h('symbiote-view'). The React
5
+ // twin is `adapters/react/src/descriptor-to-react.ts`.
6
+ import { h } from '@vue/runtime-core';
7
+ export function descriptorToVue(node) {
8
+ // String type → host element (the Vue renderer's createElement → descriptorFor maps it to
9
+ // a Fabric name); array children, since these are host elements, not slotted components.
10
+ return h(node.type, { ...node.props, key: node.key }, node.children.map(toChild));
11
+ }
12
+ function toChild(child) {
13
+ return typeof child === 'string' ? child : descriptorToVue(child);
14
+ }
@@ -0,0 +1,2 @@
1
+ export type { IHostInstance } from '@symbiote-native/engine';
2
+ export declare function findNodeHandle(componentOrHandle: unknown): number | null;
@@ -0,0 +1,34 @@
1
+ // findNodeHandle, the Vue adapter twin of adapters/react/src/host-instance.ts. RN's
2
+ // "ref/instance -> native reactTag" lookup, the seam imperative-interop libraries
3
+ // (reanimated, gesture-handler, react-navigation) reach through. A Vue template/function ref
4
+ // to a symbiote host element falls through to the raw engine SymbioteNode, and the Vue
5
+ // renderer grafts the public-instance API onto it (toPublicInstance in renderer.ts), so the
6
+ // node a ref receives carries measure / setNativeProps / focus exactly like React's
7
+ // getPublicInstance. IHostInstance is therefore that grafted node — re-exported from the
8
+ // engine so React and Vue share one definition.
9
+ //
10
+ // The node -> tag resolution itself belongs to the engine (getNativeTag, keyed on the raw
11
+ // node in the commit mirror); this only adapts the Vue-shaped inputs onto it. Accepts, in
12
+ // RN's idempotent spirit: a bare number (passes through), an engine host node (a ref's
13
+ // .value), a Vue Ref carrying either (unwrapped), or null / undefined (-> null). An
14
+ // uncommitted or unknown input has no tag yet and surfaces as null.
15
+ import { isRef, toRaw } from '@vue/runtime-core';
16
+ import { getNativeTag, isSymbioteNode, dlog } from '@symbiote-native/engine';
17
+ export function findNodeHandle(componentOrHandle) {
18
+ const candidate = isRef(componentOrHandle) ? componentOrHandle.value : componentOrHandle;
19
+ if (candidate === null || candidate === undefined)
20
+ return null;
21
+ if (typeof candidate === 'number')
22
+ return candidate;
23
+ // A plain ref() would have wrapped the node in a reactive Proxy; the engine mirror is
24
+ // keyed on the RAW node, so recover it before the lookup or getNativeTag misses. See
25
+ // the vue-adapter-reactivity skill.
26
+ const node = toRaw(candidate);
27
+ if (isSymbioteNode(node)) {
28
+ const tag = getNativeTag(node) ?? null;
29
+ dlog(`findNodeHandle: component=${node.component} tag=${tag}`);
30
+ return tag;
31
+ }
32
+ dlog('findNodeHandle: input did not resolve to a symbiote host node');
33
+ return null;
34
+ }
@@ -0,0 +1,67 @@
1
+ export { mount, unmount } from './render';
2
+ export { createTunnel, type ITunnel } from './create-tunnel';
3
+ export { View, Text } from './components';
4
+ export type { IAccessibilityProps, IAriaProps, IAccessibilityRole, IRole, IAccessibilityStateValue, IAccessibilityValue, IAccessibilityActionInfo, IResponderProps, } from '@symbiote-native/components';
5
+ export { findNodeHandle } from './host-instance';
6
+ export type { IHostInstance } from './host-instance';
7
+ export { Image, setImageSourceResolver } from './components/image';
8
+ export type { IImageProps, IImageSource, IImageSourceProp, IResizeMode, IImageSize, IImageCacheStatus, } from './components/image';
9
+ export { ActivityIndicator } from './components/activity-indicator';
10
+ export type { IActivityIndicatorProps } from './components/activity-indicator';
11
+ export { Switch } from './components/switch';
12
+ export type { ISwitchProps, ISwitchTrackColor } from './components/switch';
13
+ export { ScrollView } from './components/scroll-view';
14
+ export type { IScrollViewProps, IScrollViewEmits, IScrollViewHandle, } from './components/scroll-view';
15
+ export { Pressable } from './components/pressable';
16
+ export type { IPressableProps, IPressableSlots, IPressState, IPressableAndroidRippleConfig, } from './components/pressable';
17
+ export { TouchableOpacity, TouchableHighlight, TouchableWithoutFeedback, } from './components/touchable';
18
+ export type { ITouchableOpacityProps, ITouchableHighlightProps, ITouchableWithoutFeedbackProps, } from './components/touchable';
19
+ export { TouchableNativeFeedback } from './components/touchable-native-feedback';
20
+ export type { INativeFeedbackBackground, IThemeAttrBackground, IRippleBackground, ITouchableNativeFeedbackProps, } from './components/touchable-native-feedback';
21
+ export { Button } from './components/button';
22
+ export type { IButtonProps } from './components/button';
23
+ export { TextInput } from './components/text-input';
24
+ export type { ITextInputProps, ITextInputHandle } from './components/text-input';
25
+ export { VirtualizedList } from './components/virtualized-list';
26
+ export type { IVirtualizedListProps, IVirtualizedListSlots, IVirtualizedListEmits, IVirtualizedListHandle, ISeparators, ISeparatorProps, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, ICellLayout, } from './components/virtualized-list';
27
+ export { FlatList } from './components/flat-list';
28
+ export type { IFlatListProps, IFlatListSlots, IFlatListEmits, IFlatListHandle, } from './components/flat-list';
29
+ export { VirtualizedSectionList } from './components/virtualized-section-list';
30
+ export type { IVirtualizedSectionListProps, IVirtualizedSectionListSlots, IVirtualizedSectionListEmits, IVirtualizedSectionListHandle, } from './components/virtualized-section-list';
31
+ export { SectionList } from './components/section-list';
32
+ export type { ISection, ISectionListProps, ISectionListSlots, ISectionListEmits, ISectionListHandle, } from './components/section-list';
33
+ export { SafeAreaView } from './components/safe-area-view';
34
+ export type { ISafeAreaViewProps } from './components/safe-area-view';
35
+ export { RefreshControl } from './components/refresh-control';
36
+ export type { IRefreshControlProps, IRefreshControlEmits } from './components/refresh-control';
37
+ export { descriptorToVue } from './descriptor-to-vue';
38
+ export { normalizeVueAttrs } from './utils/normalize-attrs';
39
+ export { resolveModelValue, emitModelUpdate } from './utils/model-binding';
40
+ export { createSymbioteRenderer } from './renderer';
41
+ export { Animated, createAnimatedComponent } from './modules/animated';
42
+ export { ImageBackground } from './components/image-background';
43
+ export type { IImageBackgroundProps } from './components/image-background';
44
+ export { InputAccessoryView } from './components/input-accessory-view';
45
+ export type { IInputAccessoryViewProps } from './components/input-accessory-view';
46
+ export { Modal } from './components/modal';
47
+ export type { IModalProps, IModalEmits, IModalAnimationType, IModalPresentationStyle, IModalOrientation, IModalOrientationChangeEvent, } from './components/modal';
48
+ export { KeyboardAvoidingView } from './components/keyboard-avoiding-view';
49
+ export type { IKeyboardAvoidingBehavior, IKeyboardAvoidingViewProps, IKeyboardAvoidingViewEmits, } from './components/keyboard-avoiding-view';
50
+ export { StatusBar } from './modules/status-bar';
51
+ export type { IStatusBarProps, IStatusBarStyle } from './modules/status-bar';
52
+ export { AppRegistry, setHostRegistrar } from './modules/app-registry';
53
+ export type { IComponentProvider, IAppParameters, IRunnable, IHostRegistrar, IWrapperComponentProvider, IRegistry, IHeadlessTask, ITaskProvider, ITaskCanceller, ITaskCancelProvider, } from './modules/app-registry';
54
+ export { useColorScheme } from './composables/use-color-scheme';
55
+ export { useWindowDimensions } from './composables/use-window-dimensions';
56
+ export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, AccessibilityInfo, LayoutAnimation, } from '@symbiote-native/engine';
57
+ export type { IAlertType, IAlertButtonStyle, IAlertButton, IAlertButtons, IAlertOptions, IShareContent, IShareOptions, IShareAction, IActionSheetIOSOptions, IShareActionSheetIOSOptions, IShareActionSheetError, IUrlEvent, II18nManagerConstants, IDisplayMetrics, IDisplayMetricsAndroid, IDimensionsPayload, IDimensionsSet, IDimensionsKey, IDimensionsChangeListener, IDimensionsStatic, IColorSchemeName, IColorSchemePreference, IAppStateStatus, IAppStateEvent, IKeyboardEventName, IKeyboardEvent, IKeyboardMetrics, IBackPressEventName, IBackPressHandler, IPermission, IPermissionStatus, IRationale, IAccessibilityChangeEvent, IAccessibilityChangeEventName, IAccessibilityChangeEventHandler, IAccessibilityAnnouncementFinishedEvent, IAnnounceForAccessibilityOptions, IAccessibilityEventType, ILayoutAnimationType, ILayoutAnimationProperty, ILayoutAnimationConfig, ILayoutAnimationAnim, } from '@symbiote-native/engine';
58
+ export { Platform, StyleSheet, processColor, setColorProcessor, dlog, isDebug, } from '@symbiote-native/engine';
59
+ export type { ISymbioteEvent, ISymbioteNode, IRootTag } from '@symbiote-native/engine';
60
+ export type { IViewStyle, ITextStyle, IFlexAlign, IFlexJustify, IPlatformStatic, IPlatformOSType, IPlatformConstantsIOS, IPlatformConstantsAndroid, IPlatformSelectSpec, } from '@symbiote-native/engine';
61
+ export { setNativeViewConfigSource } from '@symbiote-native/engine';
62
+ export type { INativeViewConfig, INativeViewConfigSource } from '@symbiote-native/engine';
63
+ export { PixelRatio, PanResponder, PlatformColor, DynamicColorIOS, InteractionManager, } from '@symbiote-native/engine';
64
+ export type { IPixelRatioStatic } from '@symbiote-native/engine';
65
+ export type { IPanResponderGestureState, IPanResponderCallbacks, IGestureResponderHandlers, IPanResponderInstance, } from '@symbiote-native/engine';
66
+ export type { IColorValue, IOpaqueColorValue, IDynamicColorIOSTuple, } from '@symbiote-native/engine';
67
+ export type { IInteractionEvent, ISimpleTask, IPromiseTask, ITask, IHandle, } from '@symbiote-native/engine';