@symbiote-native/vue 0.3.8 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/components/flat-list/index.js +18 -34
- package/build/components/image-background.js +4 -14
- package/build/components/image.js +9 -20
- package/build/components/keyboard-avoiding-view.js +10 -19
- package/build/components/modal/index.d.ts +4 -4
- package/build/components/pressable.js +24 -31
- package/build/components/refresh-control.js +12 -18
- package/build/components/scroll-view/index.android.js +21 -33
- package/build/components/scroll-view/shared.js +60 -110
- package/build/components/scroll-view/sticky-header.js +41 -59
- package/build/components/section-list/index.js +12 -19
- package/build/components/switch/shared.js +17 -29
- package/build/components/text-input/index.js +29 -49
- package/build/components/virtualized-list/index.js +20 -3
- package/build/components/virtualized-section-list/index.js +17 -25
- package/build/components.js +1 -3
- package/build/host-instance/index.js +2 -4
- package/build/index.d.ts +2 -1
- package/build/index.js +11 -45
- package/build/modules/animated/create-animated-component.js +34 -97
- package/build/modules/animated/index.js +13 -17
- package/build/modules/app-registry/index.js +7 -11
- package/build/modules/status-bar.js +8 -15
- package/build/renderer/index.js +11 -6
- package/build/runtime-helpers/index.js +16 -26
- package/metro-css-parser.cjs +9 -14
- package/metro-vue-transformer.cjs +112 -135
- package/package.json +4 -4
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
// Switch, the Vue lifecycle half. The logic (
|
|
2
|
-
//
|
|
3
|
-
// @symbiote-native/components/view, both shared verbatim with
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// twin of the React adapter's useReducer + useLayoutEffect + dispatchViewCommand.
|
|
1
|
+
// Switch, the Vue lifecycle half. The logic (lastNativeReport reducer, valueFromChange, the
|
|
2
|
+
// snap-back decision) lives in @symbiote-native/components/state and the render in
|
|
3
|
+
// @symbiote-native/components/view, both shared verbatim with React. Vue supplies the
|
|
4
|
+
// reactivity: a ref holds what native last reported, a function ref grabs the host node, and a
|
|
5
|
+
// post-flush watch snaps native back when the parent rejects a toggle.
|
|
7
6
|
//
|
|
8
|
-
// Inputs arrive as attrs (untyped), so each is narrowed with a runtime guard
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// and crash Android's folly::dynamic.
|
|
7
|
+
// Inputs arrive as attrs (untyped), so each is narrowed with a runtime guard, never a cast.
|
|
8
|
+
// onValueChange MUST be stripped from the forwarded attrs: it is not a ViewConfig event, so
|
|
9
|
+
// leaking it would reach Fabric as a function prop and crash Android's folly::dynamic.
|
|
12
10
|
import { defineComponent, ref, shallowRef, watch } from '@vue/runtime-core';
|
|
13
11
|
import { renderSwitch, switchReducer, createInitialSwitchState, shouldSnapBack, valueFromChange, } from '@symbiote-native/components';
|
|
14
12
|
import { dispatchViewCommand, isSymbioteNode, dlog, } from '@symbiote-native/engine';
|
|
@@ -24,7 +22,6 @@ function asString(value) {
|
|
|
24
22
|
function isBoolean(value) {
|
|
25
23
|
return typeof value === 'boolean';
|
|
26
24
|
}
|
|
27
|
-
// trackColor arrives untyped; keep only the string false/true entries the render fn reads.
|
|
28
25
|
function normalizeTrackColor(value) {
|
|
29
26
|
if (!isRecord(value))
|
|
30
27
|
return undefined;
|
|
@@ -36,12 +33,11 @@ function normalizeTrackColor(value) {
|
|
|
36
33
|
return trackColor;
|
|
37
34
|
}
|
|
38
35
|
// The render fn's style param is a plain object; array/registered styles degrade to undefined
|
|
39
|
-
// (the engine flattens those on its own).
|
|
36
|
+
// (the engine flattens those on its own).
|
|
40
37
|
function isViewStyleObject(value) {
|
|
41
38
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
42
39
|
}
|
|
43
|
-
//
|
|
44
|
-
// testID, …) forwards onto the host node. onValueChange is pure JS and must never reach Fabric.
|
|
40
|
+
// Everything else forwards onto the host node. onValueChange is pure JS and must never reach Fabric.
|
|
45
41
|
const HANDLED_ATTRS = [
|
|
46
42
|
'value',
|
|
47
43
|
'modelValue',
|
|
@@ -62,22 +58,15 @@ function forwardAttrs(attrs) {
|
|
|
62
58
|
}
|
|
63
59
|
export function createSwitch(platform) {
|
|
64
60
|
return defineComponent((_props, { attrs: rawAttrs, emit }) => {
|
|
65
|
-
// shallowRef, NOT ref:
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
// keyed on the raw node, so every imperative command (dispatchViewCommand / measure /
|
|
69
|
-
// setNativeProps) would miss on the Proxy and silently no-op. shallowRef stores the raw
|
|
70
|
-
// node untouched. General rule for this adapter: host nodes live in shallowRef / markRaw.
|
|
61
|
+
// shallowRef, NOT ref: a plain ref() would hand back a reactive Proxy the engine's WeakMap
|
|
62
|
+
// mirror doesn't recognize, so every imperative command would silently no-op. General rule
|
|
63
|
+
// for this adapter: host nodes live in shallowRef / markRaw.
|
|
71
64
|
const nodeRef = shallowRef(null);
|
|
72
65
|
const state = ref(createInitialSwitchState());
|
|
73
|
-
// The host node our Vue renderer creates IS an engine SymbioteNode; Vue hands it to this
|
|
74
|
-
// function ref on mount. A stable function ref (defined once) isn't re-invoked per patch.
|
|
75
66
|
const setNodeRef = (el) => {
|
|
76
67
|
nodeRef.value = isSymbioteNode(el) ? el : null;
|
|
77
68
|
};
|
|
78
69
|
const handleChange = (event) => {
|
|
79
|
-
// Vue-facing event is emitted here; the host still receives this internal onChange
|
|
80
|
-
// handler so the controlled snap-back state stays synchronized with native.
|
|
81
70
|
const next = valueFromChange(event);
|
|
82
71
|
dlog(`Switch onChange value=${String(next)} eventCount=${String(event.nativeEvent.eventCount)}`);
|
|
83
72
|
if (next === undefined)
|
|
@@ -86,11 +75,10 @@ export function createSwitch(platform) {
|
|
|
86
75
|
emitModelUpdate(emit, next);
|
|
87
76
|
state.value = switchReducer(state.value, { type: 'native-reported', value: next });
|
|
88
77
|
};
|
|
89
|
-
// Snap-back: when native reported a value the parent rejected
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
// before the command reads its Fabric handle.
|
|
78
|
+
// Snap-back: when native reported a value the parent rejected, command the JS value back
|
|
79
|
+
// down (the controlled-Switch correction RN does via SwitchCommands.setValue). The
|
|
80
|
+
// decision is shared with React (shouldSnapBack); only the command name is platform-
|
|
81
|
+
// imperative. flush:'post' so the node is committed before the command reads its Fabric handle.
|
|
94
82
|
watch(() => ({
|
|
95
83
|
fabricValue: resolveModelValue(rawAttrs, isBoolean) === true,
|
|
96
84
|
switchState: state.value,
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
// TextInput, the Vue lifecycle half. The folds/maps (value->text,
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// and expose() wires the imperative handle. The Vue twin of the React useState/useRef +
|
|
8
|
-
// useLayoutEffect + useImperativeHandle.
|
|
1
|
+
// TextInput, the Vue lifecycle half. The folds/maps (value->text, W3C/alias resolution) and the
|
|
2
|
+
// controlled-write predicate live in @symbiote-native/components/state, the render in
|
|
3
|
+
// @symbiote-native/components/view, both shared verbatim with React. Vue supplies only the
|
|
4
|
+
// reactivity: a shallowRef holds the host node, a ref holds the acknowledged event count, setup-
|
|
5
|
+
// scope `let`s hold the last native text + focus flag, a post-flush watch runs the
|
|
6
|
+
// controlled-write command, and expose() wires the imperative handle.
|
|
9
7
|
//
|
|
10
|
-
// Inputs arrive as attrs (untyped), so each is narrowed with a runtime guard
|
|
11
|
-
// onValueChange MUST be stripped from the forwarded attrs: it is not a ViewConfig event
|
|
12
|
-
//
|
|
13
|
-
// crash Android's folly::dynamic. The imperative module (blurTextInput / setInput*) is imported
|
|
14
|
-
// from @symbiote-native/engine, the same framework-agnostic singleton both adapters share.
|
|
8
|
+
// Inputs arrive as attrs (untyped), so each is narrowed with a runtime guard, never a cast.
|
|
9
|
+
// onValueChange MUST be stripped from the forwarded attrs: it is not a ViewConfig event, so
|
|
10
|
+
// leaking it would reach Fabric as a function prop and crash Android's folly::dynamic.
|
|
15
11
|
import { defineComponent, onBeforeUnmount, ref, shallowRef, watch } from '@vue/runtime-core';
|
|
16
12
|
import { resolveAccessibilityProps, resolveTextInputProps, renderTextInput, foldText, textFromChange, eventCountFromChange, shouldCommandText, INITIAL_EVENT_COUNT, SELECTION_NONE, } from '@symbiote-native/components';
|
|
17
13
|
import { dispatchViewCommand, isSymbioteNode, dlog, blurTextInput, setInputFocused, setInputBlurred, whenCommitted, } from '@symbiote-native/engine';
|
|
@@ -30,7 +26,6 @@ function isString(value) {
|
|
|
30
26
|
function asBoolean(value) {
|
|
31
27
|
return typeof value === 'boolean' ? value : undefined;
|
|
32
28
|
}
|
|
33
|
-
// selection arrives untyped; keep only the numeric start/end the render fn + controlled write read.
|
|
34
29
|
function normalizeSelection(value) {
|
|
35
30
|
if (!isRecord(value))
|
|
36
31
|
return undefined;
|
|
@@ -41,9 +36,7 @@ function normalizeSelection(value) {
|
|
|
41
36
|
selection.end = value.end;
|
|
42
37
|
return selection;
|
|
43
38
|
}
|
|
44
|
-
//
|
|
45
|
-
// everything else (placeholder, secureTextEntry, the remaining native events, accessibility,
|
|
46
|
-
// testID, style…) forwards onto the host node. onFocus/onBlur are re-supplied as our wrapped
|
|
39
|
+
// Everything else forwards onto the host node. onFocus/onBlur are re-supplied as our wrapped
|
|
47
40
|
// handlers; onValueChange is pure JS and must never reach Fabric.
|
|
48
41
|
const HANDLED_ATTRS = [
|
|
49
42
|
'value',
|
|
@@ -80,38 +73,30 @@ function forwardAttrs(attrs) {
|
|
|
80
73
|
return result;
|
|
81
74
|
}
|
|
82
75
|
export const TextInput = defineComponent((_props, { attrs: rawAttrs, emit, expose }) => {
|
|
83
|
-
// shallowRef, NOT ref:
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
// (dispatchViewCommand / focus / blur / setTextAndSelection) would miss and silently no-op.
|
|
87
|
-
// Same rule applies to the Switch / ScrollView host node.
|
|
76
|
+
// shallowRef, NOT ref: a plain ref() would hand back a reactive Proxy the engine's WeakMap
|
|
77
|
+
// mirror doesn't recognize, so every imperative command would silently no-op. Same rule as
|
|
78
|
+
// the Switch / ScrollView host node.
|
|
88
79
|
const nodeRef = shallowRef(null);
|
|
89
80
|
const setNodeRef = (el) => {
|
|
90
81
|
nodeRef.value = isSymbioteNode(el) ? el : null;
|
|
91
82
|
};
|
|
92
|
-
// The count native last acknowledged. A ref so
|
|
93
|
-
//
|
|
94
|
-
// it so native's eventLag lands on 0.
|
|
83
|
+
// The count native last acknowledged. A ref so render echoes it as mostRecentEventCount; the
|
|
84
|
+
// controlled write commands it so native's eventLag lands on 0.
|
|
95
85
|
const mostRecentEventCount = ref(INITIAL_EVENT_COUNT);
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
// divergence and must NOT re-command). A setup-scope `let`: no render needed when it changes.
|
|
86
|
+
// Seeded from the mount-time value (the `text` prop already carries it down via createNode,
|
|
87
|
+
// so the FIRST controlled value is not a divergence and must NOT re-command).
|
|
99
88
|
let lastNativeText = foldText(resolveModelValue(rawAttrs, isString), asString(rawAttrs.defaultValue));
|
|
100
|
-
//
|
|
101
|
-
// synchronous focus getter (RN's TextInputState holds the same).
|
|
89
|
+
// Mirrored from focus/blur events for isFocused(): native exposes no synchronous focus getter.
|
|
102
90
|
let focused = false;
|
|
103
|
-
//
|
|
104
|
-
// not re-focus.
|
|
91
|
+
// Guard so a later node identity change does not re-focus.
|
|
105
92
|
let autoFocused = false;
|
|
106
93
|
const handleChange = (event) => {
|
|
107
|
-
//
|
|
108
|
-
// (+ `eventCount`). iOS and Android Fabric can key these differently, so log the actual shape.
|
|
94
|
+
// iOS and Android Fabric can key the change payload differently, so log the actual shape.
|
|
109
95
|
dlog(`TextInput change keys=[${Object.keys(event.nativeEvent).join(',')}] ` +
|
|
110
96
|
`text=${JSON.stringify(event.nativeEvent.text)} count=${JSON.stringify(event.nativeEvent.eventCount)}`);
|
|
111
97
|
const text = textFromChange(event);
|
|
112
98
|
if (text !== undefined) {
|
|
113
|
-
// Record the text first,
|
|
114
|
-
// stands for.
|
|
99
|
+
// Record the text first, so the count never runs ahead of the text it stands for.
|
|
115
100
|
lastNativeText = text;
|
|
116
101
|
emit('valueChange', text, event);
|
|
117
102
|
emitModelUpdate(emit, text);
|
|
@@ -136,10 +121,8 @@ export const TextInput = defineComponent((_props, { attrs: rawAttrs, emit, expos
|
|
|
136
121
|
emit('blur', event);
|
|
137
122
|
};
|
|
138
123
|
// Controlled write: when JS-side `value` diverges from what native reported, command the new
|
|
139
|
-
// text down with the acknowledged count
|
|
140
|
-
// keystrokes.
|
|
141
|
-
// a change). flush:'post' so the engine has committed the node before the command reads its
|
|
142
|
-
// Fabric handle; the predicate makes it a no-op on mount (value === the seed).
|
|
124
|
+
// text down with the acknowledged count - a plain prop re-push would race the user's
|
|
125
|
+
// keystrokes. flush:'post' so the node is committed before the command reads its Fabric handle.
|
|
143
126
|
watch(() => resolveModelValue(rawAttrs, isString), value => {
|
|
144
127
|
const node = nodeRef.value;
|
|
145
128
|
if (node === null)
|
|
@@ -158,11 +141,10 @@ export const TextInput = defineComponent((_props, { attrs: rawAttrs, emit, expos
|
|
|
158
141
|
]);
|
|
159
142
|
lastNativeText = value;
|
|
160
143
|
}, { flush: 'post' });
|
|
161
|
-
// autoFocus is driven in JS
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
// assigns the tag. Cancelled on unmount so an un-committed pending focus can't leak.
|
|
144
|
+
// autoFocus is driven in JS: once the node first commits, command `focus` down once. Under
|
|
145
|
+
// Vue's async-batched commit the node has no Fabric tag yet at post-flush, so
|
|
146
|
+
// whenCommitted defers it to the commit that assigns the tag. Cancelled on unmount so an
|
|
147
|
+
// un-committed pending focus can't leak.
|
|
166
148
|
let cancelAutoFocus;
|
|
167
149
|
watch(nodeRef, node => {
|
|
168
150
|
if (autoFocused || node === null || rawAttrs.autoFocus !== true)
|
|
@@ -172,10 +154,8 @@ export const TextInput = defineComponent((_props, { attrs: rawAttrs, emit, expos
|
|
|
172
154
|
cancelAutoFocus = whenCommitted(node, () => dispatchViewCommand(node, 'focus', []));
|
|
173
155
|
}, { flush: 'post' });
|
|
174
156
|
onBeforeUnmount(() => cancelAutoFocus?.());
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
// view commands; clear and setSelection reuse setTextAndSelection (the same stale-safe path as
|
|
178
|
-
// a controlled write) echoing the acknowledged event count.
|
|
157
|
+
// Methods read nodeRef.value / the count LIVE, so no stale capture. clear/setSelection reuse
|
|
158
|
+
// setTextAndSelection, the same path as a controlled write.
|
|
179
159
|
expose({
|
|
180
160
|
focus: () => {
|
|
181
161
|
const node = nodeRef.value;
|
|
@@ -477,8 +477,9 @@ export const VirtualizedList = defineComponent((props, { attrs, expose, emit, sl
|
|
|
477
477
|
? INVERTED_X_STYLE
|
|
478
478
|
: INVERTED_Y_STYLE
|
|
479
479
|
: undefined;
|
|
480
|
-
|
|
481
|
-
|
|
480
|
+
// Wraps one cell identically whether it's the force-mounted sticky cell ahead of the
|
|
481
|
+
// window or an in-window cell from the loop below — same measuring View, same key shape.
|
|
482
|
+
const pushCell = (cell) => {
|
|
482
483
|
const item = p.getItem(p.data, cell.index);
|
|
483
484
|
const content = p.renderItem?.({
|
|
484
485
|
item,
|
|
@@ -490,8 +491,24 @@ export const VirtualizedList = defineComponent((props, { attrs, expose, emit, sl
|
|
|
490
491
|
onLayout: makeCellMeasure(cell.index),
|
|
491
492
|
style: cellInverted,
|
|
492
493
|
}, [content]));
|
|
494
|
+
};
|
|
495
|
+
// The nearest sticky section header below the window (RN _ensureClosestStickyHeader):
|
|
496
|
+
// force-mounted so it never gets destroyed/recreated while pinned off-window. Rendered
|
|
497
|
+
// ahead of the main window loop, with its own gap spacer to the window's first cell.
|
|
498
|
+
if (plan.forcedStickyCell !== undefined) {
|
|
499
|
+
pushCell(plan.forcedStickyCell);
|
|
500
|
+
if (plan.gapExtent > EMPTY_OFFSET) {
|
|
501
|
+
children.push(h('symbiote-view', {
|
|
502
|
+
key: 'spacer-gap',
|
|
503
|
+
style: p.horizontal ? { width: plan.gapExtent } : { height: plan.gapExtent },
|
|
504
|
+
}));
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
for (let cellPos = 0; cellPos < plan.cells.length; cellPos += 1) {
|
|
508
|
+
const cell = plan.cells[cellPos];
|
|
509
|
+
pushCell(cell);
|
|
493
510
|
if (cell.index < m.last) {
|
|
494
|
-
const separator = renderSeparatorElement(p.itemSeparatorComponent,
|
|
511
|
+
const separator = renderSeparatorElement(p.itemSeparatorComponent, p.getItem(p.data, cell.index), p.getItem(p.data, cell.index + 1), separatorOverrides.get(cell.index));
|
|
495
512
|
if (separator !== undefined) {
|
|
496
513
|
children.push(h('symbiote-view', { key: `sep-${cell.key}` }, [separator]));
|
|
497
514
|
}
|
|
@@ -1,32 +1,26 @@
|
|
|
1
1
|
// VirtualizedSectionList, the Vue wrapper that flattens sections into one virtualized stream
|
|
2
|
-
// over VirtualizedList
|
|
3
|
-
// (RN counts 2 per section)
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// The Vue twin of the React adapter's VirtualizedSectionList.
|
|
2
|
+
// over VirtualizedList: each section contributes a header row, its item rows, then a footer row
|
|
3
|
+
// (RN counts 2 per section), and the flattened tagged sequence is windowed by the same
|
|
4
|
+
// machinery as one list. Flattening, entry keying, separator-item unwrap, and scrollToLocation
|
|
5
|
+
// mapping are shared from @symbiote-native/components; this file wires Vue lifecycle
|
|
6
|
+
// (typed-prop inputs + handle re-expose + per-entry render dispatch).
|
|
8
7
|
//
|
|
9
|
-
// Typed-emits generic component (mirrors FlatList
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
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.
|
|
8
|
+
// Typed-emits generic component (mirrors FlatList/VirtualizedList): a GENERIC setup function so
|
|
9
|
+
// section inputs infer ItemT at the call site. Those inputs are read from typed `props`
|
|
10
|
+
// (declared in the runtime `props` array); the VirtualizedList passthrough tail rides through
|
|
11
|
+
// $attrs onto the inner list. The three adapter-synthesized events (endReached/startReached/
|
|
12
|
+
// refresh) stay GATED on listener presence so the inner VirtualizedList keeps building
|
|
13
|
+
// RefreshControl / computing edge-reached strictly on demand.
|
|
18
14
|
import { defineComponent, getCurrentInstance, h, shallowRef, } from '@vue/runtime-core';
|
|
19
15
|
import { flattenSections, resolveStickySectionHeaders, scrollLocationToFlatIndex, sectionEntryKey, unwrapEntryItem, } from '@symbiote-native/components';
|
|
20
16
|
import { Platform, dlog, } from '@symbiote-native/engine';
|
|
21
17
|
import { VirtualizedList } from '../virtualized-list/index.js';
|
|
22
18
|
import { normalizeVueAttrs } from '../../utils/normalize-attrs.js';
|
|
23
|
-
// VirtualizedList
|
|
24
|
-
//
|
|
19
|
+
// VirtualizedList's generic construct signature can't be resolved by h()'s overloads, so drive
|
|
20
|
+
// it through a loose functional-component handle instead.
|
|
25
21
|
const VirtualizedListHost = VirtualizedList;
|
|
26
|
-
//
|
|
27
|
-
//
|
|
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).
|
|
22
|
+
// Listed for the runtime `props` declaration (keyof can't derive it: the index signature widens
|
|
23
|
+
// keyof to `string`). The three emit events are deliberately absent (declared as emits below).
|
|
30
24
|
const PROP_KEYS = ['sections', 'keyExtractor', 'stickySectionHeadersEnabled'];
|
|
31
25
|
const EMIT_KEYS = ['endReached', 'startReached', 'refresh'];
|
|
32
26
|
function isRecord(value) {
|
|
@@ -68,10 +62,8 @@ export const VirtualizedSectionList = defineComponent((props, { attrs, expose, e
|
|
|
68
62
|
// headerIndices change each render (sections may change); the delegate reads them lazily.
|
|
69
63
|
let headerIndices = [];
|
|
70
64
|
expose(buildSectionDelegate(() => inner.value, () => headerIndices));
|
|
71
|
-
//
|
|
72
|
-
//
|
|
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).
|
|
65
|
+
// Emits are stripped from $attrs by Vue; detect listener presence off the instance's own
|
|
66
|
+
// vnode props instead, so an emit bridge is wired ONLY when the consumer actually listens.
|
|
75
67
|
const instance = getCurrentInstance();
|
|
76
68
|
const listens = (onName) => {
|
|
77
69
|
const vnodeProps = instance?.vnode.props;
|
package/build/components.js
CHANGED
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
import { h } from '@vue/runtime-core';
|
|
12
12
|
import { normalizeVueAttrs } from './utils/normalize-attrs.js';
|
|
13
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
14
|
// normalizeVueAttrs folds kebab template props (:accessibility-label) to the RN camelCase contract.
|
|
17
15
|
const component = (_props, { slots, attrs }) => h(intrinsic, normalizeVueAttrs(attrs), slots.default !== undefined ? slots.default() : undefined);
|
|
18
16
|
component.displayName = name;
|
|
@@ -22,5 +20,5 @@ function hostComponent(intrinsic, name) {
|
|
|
22
20
|
export const View = hostComponent('symbiote-view', 'View');
|
|
23
21
|
export const Text = hostComponent('symbiote-text', 'Text');
|
|
24
22
|
// Image is NOT a bare host primitive: it needs the shared fold (source/src/srcSet resolution,
|
|
25
|
-
// width/height
|
|
23
|
+
// width/height -> style, alt -> accessibility) + the Image statics, so it lives in ./image as a
|
|
26
24
|
// functional component over renderImage. View/Text stay bare; they forward attrs verbatim.
|
|
@@ -8,10 +8,8 @@
|
|
|
8
8
|
// engine so React and Vue share one definition.
|
|
9
9
|
//
|
|
10
10
|
// The node -> tag resolution itself belongs to the engine (getNativeTag, keyed on the raw
|
|
11
|
-
// node in the commit mirror); this only
|
|
12
|
-
//
|
|
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.
|
|
11
|
+
// node in the commit mirror); this only unwraps the Vue-shaped inputs (a Ref or a bare node)
|
|
12
|
+
// onto it. An uncommitted or unknown input has no tag yet and surfaces as null.
|
|
15
13
|
import { isRef, toRaw } from '@vue/runtime-core';
|
|
16
14
|
import { getNativeTag, isSymbioteNode, dlog } from '@symbiote-native/engine';
|
|
17
15
|
export function findNodeHandle(componentOrHandle) {
|
package/build/index.d.ts
CHANGED
|
@@ -58,8 +58,9 @@ export type { IAlertType, IAlertButtonStyle, IAlertButton, IAlertButtons, IAlert
|
|
|
58
58
|
export { Platform, StyleSheet, processColor, setColorProcessor, dlog, isDebug, } from '@symbiote-native/engine';
|
|
59
59
|
export type { ISymbioteEvent, ISymbioteNode, IRootTag } from '@symbiote-native/engine';
|
|
60
60
|
export type { IViewStyle, ITextStyle, IFlexAlign, IFlexJustify, IPlatformStatic, IPlatformOSType, IPlatformConstantsIOS, IPlatformConstantsAndroid, IPlatformSelectSpec, } from '@symbiote-native/engine';
|
|
61
|
-
export { setNativeViewConfigSource } from '@symbiote-native/engine';
|
|
61
|
+
export { setNativeViewConfigSource, setDeviceEventSource } from '@symbiote-native/engine';
|
|
62
62
|
export type { INativeViewConfig, INativeViewConfigSource } from '@symbiote-native/engine';
|
|
63
|
+
export type { IEnterKeyHint, IInputMode, ISubmitBehavior, ITextInputSelection, IImageStatics, } from '@symbiote-native/components';
|
|
63
64
|
export { PixelRatio, PanResponder, PlatformColor, DynamicColorIOS, InteractionManager, } from '@symbiote-native/engine';
|
|
64
65
|
export type { IPixelRatioStatic } from '@symbiote-native/engine';
|
|
65
66
|
export type { IPanResponderGestureState, IPanResponderCallbacks, IGestureResponderHandlers, IPanResponderInstance, } from '@symbiote-native/engine';
|
package/build/index.js
CHANGED
|
@@ -2,78 +2,43 @@
|
|
|
2
2
|
// each RendererOptions call onto the engine's mutation API; all Fabric clone-on-write
|
|
3
3
|
// lives in the engine, shared with every other adapter. App code names only @symbiote-native/vue.
|
|
4
4
|
export { mount, unmount } from './render.js';
|
|
5
|
-
//
|
|
6
|
-
// see create-tunnel.ts for why).
|
|
5
|
+
// Teleport stays same-surface-only by design - see create-tunnel.ts for why.
|
|
7
6
|
export { createTunnel } from './create-tunnel/index.js';
|
|
8
7
|
export { View, Text } from './components.js';
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// through to it; imperative methods live on each component's expose() handle).
|
|
8
|
+
// IHostInstance is the raw engine node (Vue host refs fall through to it; imperative methods
|
|
9
|
+
// live on each component's expose() handle).
|
|
12
10
|
export { findNodeHandle } from './host-instance/index.js';
|
|
13
|
-
// Image. Full parity with React: source/src/srcSet resolution, the width/height → style fold,
|
|
14
|
-
// alt → accessibility, and the Image statics (getSize / prefetch / queryCache / …) are shared
|
|
15
|
-
// verbatim from @symbiote-native/components via renderImage; Vue supplies only the functional bridge.
|
|
16
11
|
export { Image, setImageSourceResolver } from './components/image.js';
|
|
17
|
-
// First component off the shared @symbiote-native/components layer: render-only, drives the
|
|
18
|
-
// agnostic renderActivityIndicator through descriptorToVue. Proof the layer is reusable:
|
|
19
|
-
// the render fn is shared verbatim with React; Vue supplies only the bridge.
|
|
20
12
|
export { ActivityIndicator } from './components/activity-indicator';
|
|
21
|
-
// First component to bring the state half into Vue (the lastNativeReport reducer + the
|
|
22
|
-
// snap-back watch); render shared verbatim with React, Vue supplies the reactive lifecycle.
|
|
23
13
|
export { Switch } from './components/switch';
|
|
24
|
-
// ScrollView. Full parity: vertical + horizontal, every pass-through prop, the
|
|
25
|
-
// synthesized onContentSizeChange, the imperative handle via expose() + shallowRef, RefreshControl
|
|
26
|
-
// (iOS sibling / Android wrap), and sticky headers (the scroll AnimatedValue,
|
|
27
|
-
// the headerLayoutYs cross-talk, and the per-header Animated.View wrap). The pure math (intrinsics,
|
|
28
|
-
// decelerationRate, content-size dedupe, the handle, the sticky interpolation) is shared verbatim
|
|
29
|
-
// with React from @symbiote-native/components.
|
|
30
14
|
export { ScrollView } from './components/scroll-view';
|
|
31
|
-
// Pressable family. Full parity with React (the 3-layer split): the press state machine + render
|
|
32
|
-
// decisions are shared in @symbiote-native/components; Vue supplies the reactivity + descriptor bridge.
|
|
33
15
|
export { Pressable } from './components/pressable.js';
|
|
34
16
|
export { TouchableOpacity, TouchableHighlight, TouchableWithoutFeedback, } from './components/touchable.js';
|
|
35
17
|
export { TouchableNativeFeedback } from './components/touchable-native-feedback.js';
|
|
36
18
|
export { Button } from './components/button.js';
|
|
37
|
-
// TextInput. Full parity with React (the 3-layer split): the value/selection folds + the
|
|
38
|
-
// controlled-write predicate live in @symbiote-native/components; Vue supplies the reactivity
|
|
39
|
-
// (shallowRef host node, post-flush watch) + the imperative handle via expose().
|
|
40
19
|
export { TextInput } from './components/text-input/index.js';
|
|
41
|
-
// VirtualizedList family. Full parity with React: the windowing math (visible range, cell
|
|
42
|
-
// keys, viewability token diffing, edge-reached, the list plan) is shared in
|
|
43
|
-
// @symbiote-native/components; Vue supplies the reactive lifecycle + the per-cell element.
|
|
44
20
|
export { VirtualizedList } from './components/virtualized-list/index.js';
|
|
45
21
|
export { FlatList } from './components/flat-list/index.js';
|
|
46
22
|
export { VirtualizedSectionList } from './components/virtualized-section-list/index.js';
|
|
47
23
|
export { SectionList } from './components/section-list/index.js';
|
|
48
|
-
//
|
|
49
|
-
// Android-wrap platform split. RefreshControl hosts the wrapped scroll view via its default slot.
|
|
24
|
+
// RefreshControl hosts the wrapped scroll view via its default slot.
|
|
50
25
|
export { SafeAreaView } from './components/safe-area-view.js';
|
|
51
26
|
export { RefreshControl } from './components/refresh-control.js';
|
|
52
27
|
export { descriptorToVue } from './descriptor-to-vue.js';
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
28
|
+
// Exported so an external wrapper package (e.g. @symbiote-native/slider/vue over a third-party
|
|
29
|
+
// native view) can fold its incoming attrs/v-model through the SAME transform rather than
|
|
30
|
+
// reimplementing it.
|
|
56
31
|
export { normalizeVueAttrs } from './utils/normalize-attrs.js';
|
|
57
|
-
// resolveModelValue/emitModelUpdate: the v-model helper every
|
|
58
|
-
// controlled-value component uses. Exported for the same external-package reason as
|
|
59
|
-
// normalizeVueAttrs above (e.g. @symbiote-native/slider/vue).
|
|
60
32
|
export { resolveModelValue, emitModelUpdate } from './utils/model-binding.js';
|
|
61
33
|
export { createSymbioteRenderer } from './renderer/index.js';
|
|
62
|
-
// Animated: Animated.View/Text/Image + the lazy Animated.ScrollView over the
|
|
63
|
-
// Vue primitives, with the value graph / easing / drivers spread from @symbiote-native/engine. The wrap
|
|
64
|
-
// mechanism (createAnimatedComponent) is the Vue twin of React's; the pure leaves live in the
|
|
65
|
-
// engine.
|
|
66
34
|
export { Animated, createAnimatedComponent } from './modules/animated/index.js';
|
|
67
|
-
// Visual components off the shared @symbiote-native/components layer (wave: simple-visual).
|
|
68
35
|
export { ImageBackground } from './components/image-background.js';
|
|
69
36
|
export { InputAccessoryView } from './components/input-accessory-view.js';
|
|
70
37
|
export { Modal } from './components/modal/index.js';
|
|
71
|
-
// KeyboardAvoidingView: full parity via the shared inset math; Vue owns the Keyboard subscription.
|
|
72
38
|
export { KeyboardAvoidingView } from './components/keyboard-avoiding-view.js';
|
|
73
|
-
// StatusBar: declarative component + the shared imperative API.
|
|
74
39
|
export { StatusBar } from './modules/status-bar.js';
|
|
75
|
-
//
|
|
76
|
-
//
|
|
40
|
+
// RN's app entry point over `mount`. setHostRegistrar wires RN's own registrar so the native
|
|
41
|
+
// Fabric host finds our runnable by app key.
|
|
77
42
|
export { AppRegistry, setHostRegistrar } from './modules/app-registry/index.js';
|
|
78
43
|
// Vue composables over the core device-state modules.
|
|
79
44
|
export { useColorScheme } from './composables/use-color-scheme.js';
|
|
@@ -84,7 +49,8 @@ export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Setting
|
|
|
84
49
|
export { Platform, StyleSheet, processColor, setColorProcessor, dlog, isDebug, } from '@symbiote-native/engine';
|
|
85
50
|
// Wired once by the app entry on a real host (like setColorProcessor): hands the engine
|
|
86
51
|
// RN's ViewConfig registry so third-party Fabric views auto-derive their metadata.
|
|
87
|
-
|
|
52
|
+
// setDeviceEventSource is the third seam of the same set and travels with them.
|
|
53
|
+
export { setNativeViewConfigSource, setDeviceEventSource } from '@symbiote-native/engine';
|
|
88
54
|
// Pure utilities that moved to the engine (single source, both adapters re-export):
|
|
89
55
|
// PixelRatio + PanResponder, plus the color builders and the interaction scheduler.
|
|
90
56
|
export { PixelRatio, PanResponder, PlatformColor, DynamicColorIOS, InteractionManager, } from '@symbiote-native/engine';
|