@symbiote-native/vue 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @symbiote-native/vue
2
2
 
3
- The **Vue 3 adapter** for [SymbioteJS](../../README.md) — render real native iOS/Android views from
3
+ The **Vue 3 adapter** for [SymbioteNative](../../README.md) — render real native iOS/Android views from
4
4
  Vue, on the *same* untouched core as React, with React Native's own renderer never in the path. It
5
5
  is a `@vue/runtime-core` `createRenderer` whose nodeOps map each mutation onto the engine's
6
6
  four-call API; `@symbiote-native/engine` does the clone-on-write commit into Fabric.
@@ -10,11 +10,11 @@ mutation-oriented framework driving the already-validated engine.
10
10
 
11
11
  <div align="center">
12
12
 
13
- ![Vue 3 driving real native iOS views through SymbioteJS](../../assets/vue-demo.gif)
13
+ ![Vue 3 driving real native iOS views through SymbioteNative](../../assets/vue-demo.gif)
14
14
 
15
15
  </div>
16
16
 
17
- > New to SymbioteJS? The [root README](../../README.md) has the architecture.
17
+ > New to SymbioteNative? The [root README](../../README.md) has the architecture.
18
18
 
19
19
  ---
20
20
 
@@ -81,9 +81,9 @@ and each adapter supplies only its lifecycle (Vue's `ref`/`watch` + the descript
81
81
  The one deliberate gap is third-party **React component** packages such as
82
82
  `@react-native-community/slider` used directly. Their body calls React hooks off the React
83
83
  dispatcher, so they run only under the React adapter — under Vue the dispatcher is null and they
84
- throw. SymbioteJS makes the *native view* framework-agnostic, not the library's React *component*; such
84
+ throw. SymbioteNative makes the *native view* framework-agnostic, not the library's React *component*; such
85
85
  a view becomes reachable from Vue only through a thin wrapper over the same `createNode`-by-ViewConfig
86
- path SymbioteJS uses for its own primitives. `@symbiote-native/slider` (this repo's own wrapper around
86
+ path SymbioteNative uses for its own primitives. `@symbiote-native/slider` (this repo's own wrapper around
87
87
  `@react-native-community/slider`) *does* ship a real Vue build (`@symbiote-native/slider/vue`) through
88
88
  exactly that path — it's what makes this one third-party native view usable from Vue at all; any
89
89
  *other* React-only component package stays React-adapter-only until it gets the same treatment.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@symbiote-native/vue",
3
- "version": "0.1.0",
4
- "description": "SymbioteJS's Vue adapter — a @vue/runtime-core createRenderer driving real native iOS/Android views through the same engine as the React adapter.",
3
+ "version": "0.1.1",
4
+ "description": "SymbioteNative's Vue adapter — a @vue/runtime-core createRenderer driving real native iOS/Android views through the same engine as the React adapter.",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/OneEyed1366/symbiote-native.git",
@@ -36,9 +36,9 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@vue/runtime-core": "^3.5.13",
39
- "@symbiote-native/css-parser": "0.1.0",
40
- "@symbiote-native/engine": "0.1.0",
41
- "@symbiote-native/components": "0.1.0"
39
+ "@symbiote-native/components": "0.1.1",
40
+ "@symbiote-native/css-parser": "0.1.1",
41
+ "@symbiote-native/engine": "0.1.1"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "vue": ">=3.5.0",
@@ -1,8 +0,0 @@
1
- import type { IDrawerLayoutAndroidEmits, IDrawerLayoutAndroidProps } from './shared';
2
- export type { IDrawerPosition, IDrawerLockMode, IKeyboardDismissMode, IDrawerState, IDrawerSlideEvent, IDrawerLayoutAndroidEmits, IDrawerLayoutAndroidProps, IDrawerLayoutAndroidHandle, } from './shared';
3
- export declare const DrawerLayoutAndroid: import("vue").DefineSetupFnComponent<IDrawerLayoutAndroidProps, IDrawerLayoutAndroidEmits, {}, IDrawerLayoutAndroidProps & {
4
- onDrawerOpen?: (() => any) | undefined;
5
- onDrawerClose?: (() => any) | undefined;
6
- onDrawerSlide?: ((event: import("@symbiote/components").IDrawerSlideEvent) => any) | undefined;
7
- onDrawerStateChanged?: ((state: import("@symbiote/components").IDrawerState) => any) | undefined;
8
- }, import("vue").PublicProps>;
@@ -1,144 +0,0 @@
1
- // DrawerLayoutAndroid on Android: the real build, the Vue twin of the React adapter's
2
- // index.android.ts. AndroidDrawerLayout is an ordinary Fabric host node committing through the same
3
- // childSet as the rest of the tree (no per-library glue; shared derives the view's events from its
4
- // ViewConfig, so we render the RAW Fabric name 'AndroidDrawerLayout'). The platform-invariant math —
5
- // the host prop bag + the content/navigation wrapper styles, the slide/state event normalization,
6
- // the imperative open/close handle, and the view/command NAMES — lives in @symbiote/components,
7
- // shared verbatim with React; here Vue supplies only the lifecycle: a shallowRef holds the host node
8
- // (so the engine's mirror resolves it for the imperative commands), a `drawerOpened` ref gates the
9
- // navigation wrapper's pointerEvents, and expose() wires the imperative handle.
10
- //
11
- // Child order matches RN exactly: content FIRST, navigation SECOND ({childrenWrapper}{drawerViewWrapper}).
12
- // Content is the DEFAULT slot; the drawer is the `navigationView` slot (the Vue twin of React's
13
- // children / renderNavigationView). Inputs arrive as attrs (untyped), each narrowed with a runtime
14
- // guard rather than a cast. The onDrawer* handlers are wrapped (drawerOpened + normalization), so they
15
- // are stripped from the forwarded attrs; everything else (testID / accessibility / aria-*) folds via
16
- // resolveAccessibilityProps onto the host. Metro picks this file on an Android host; no Platform.OS read.
17
- // device-verify-pending: the AndroidDrawerLayout name + openDrawer/closeDrawer commands are
18
- // RN-source-confirmed, not yet exercised on a real Android host.
19
- import { defineComponent, h, ref, shallowRef } from '@vue/runtime-core';
20
- import { buildDrawerHandle, offsetFromSlide, resolveAccessibilityProps, resolveDrawerLayout, stateFromChange, } from '@symbiote/components';
21
- import { dlog, isSymbioteNode, } from '@symbiote/engine';
22
- import { View } from '../../components';
23
- import { normalizeVueAttrs } from '../../utils/normalize-attrs';
24
- function asNumber(value) {
25
- return typeof value === 'number' ? value : undefined;
26
- }
27
- function asString(value) {
28
- return typeof value === 'string' ? value : undefined;
29
- }
30
- function asDrawerPosition(value) {
31
- return value === 'left' || value === 'right' ? value : undefined;
32
- }
33
- function asDrawerLockMode(value) {
34
- return value === 'unlocked' || value === 'locked-closed' || value === 'locked-open'
35
- ? value
36
- : undefined;
37
- }
38
- function asKeyboardDismissMode(value) {
39
- return value === 'none' || value === 'on-drag' ? value : undefined;
40
- }
41
- function isStyleProp(value) {
42
- return typeof value === 'object' && value !== null;
43
- }
44
- // The typed fields the lifecycle consumes/re-emits (resolveDrawerLayout folds the drawer* props onto
45
- // the host) and the onDrawer* handlers (wrapped here, never forwarded raw); everything else (testID,
46
- // accessibility, aria-*) forwards onto the host via passthrough.
47
- const HANDLED_ATTRS = [
48
- 'drawerWidth',
49
- 'drawerPosition',
50
- 'drawerLockMode',
51
- 'keyboardDismissMode',
52
- 'drawerBackgroundColor',
53
- 'statusBarBackgroundColor',
54
- 'style',
55
- 'onDrawerOpen',
56
- 'onDrawerClose',
57
- 'onDrawerSlide',
58
- 'onDrawerStateChanged',
59
- ];
60
- function forwardAttrs(attrs) {
61
- const result = {};
62
- for (const key of Object.keys(attrs)) {
63
- if (!HANDLED_ATTRS.includes(key))
64
- result[key] = attrs[key];
65
- }
66
- return result;
67
- }
68
- export const DrawerLayoutAndroid = defineComponent((_props, { slots, attrs: rawAttrs, expose, emit }) => {
69
- // shallowRef, NOT ref: the engine node must be held by IDENTITY. A plain ref() runs the node
70
- // through Vue's toReactive(), handing back a reactive Proxy the engine's mirror (a WeakMap keyed
71
- // on the raw node) can't resolve, so openDrawer/closeDrawer would silently no-op. Same rule as
72
- // the Switch / ScrollView host node (see .claude/skills/vue-adapter-reactivity).
73
- const nodeRef = shallowRef(null);
74
- const setNodeRef = (el) => {
75
- nodeRef.value = isSymbioteNode(el) ? el : null;
76
- };
77
- // drawerOpened gates the navigation wrapper's pointerEvents (RN _onDrawerOpen/_onDrawerClose
78
- // setState): closed -> 'none' so the off-screen drawer never intercepts touches. A plain ref is
79
- // fine here — it holds a boolean, not an engine node; the shallowRef rule is only for the node.
80
- const drawerOpened = ref(false);
81
- // The imperative handle reads the node through a LAZY getter (() => nodeRef.value), not the node
82
- // captured once: it is null until the element commits. expose() makes it the value a parent ref
83
- // sees — the Vue twin of React's useImperativeHandle(ref, buildDrawerHandle(…)).
84
- expose(buildDrawerHandle(() => nodeRef.value));
85
- return () => {
86
- const attrs = normalizeVueAttrs(rawAttrs);
87
- const handleDrawerOpen = () => {
88
- dlog('Vue DrawerLayoutAndroid onDrawerOpen');
89
- drawerOpened.value = true;
90
- emit('drawerOpen');
91
- };
92
- const handleDrawerClose = () => {
93
- dlog('Vue DrawerLayoutAndroid onDrawerClose');
94
- drawerOpened.value = false;
95
- emit('drawerClose');
96
- };
97
- const handleDrawerSlide = (event) => {
98
- dlog('Vue DrawerLayoutAndroid onDrawerSlide');
99
- emit('drawerSlide', { offset: offsetFromSlide(event) });
100
- };
101
- const handleDrawerStateChanged = (event) => {
102
- emit('drawerStateChanged', stateFromChange(event));
103
- };
104
- const resolved = resolveDrawerLayout({
105
- drawerWidth: asNumber(attrs.drawerWidth),
106
- drawerPosition: asDrawerPosition(attrs.drawerPosition),
107
- drawerLockMode: asDrawerLockMode(attrs.drawerLockMode),
108
- keyboardDismissMode: asKeyboardDismissMode(attrs.keyboardDismissMode),
109
- drawerBackgroundColor: asString(attrs.drawerBackgroundColor),
110
- statusBarBackgroundColor: asString(attrs.statusBarBackgroundColor),
111
- drawerOpened: drawerOpened.value,
112
- style: isStyleProp(attrs.style) ? attrs.style : undefined,
113
- passthrough: resolveAccessibilityProps(forwardAttrs(attrs)),
114
- });
115
- // RN's mainSubview: content wrapped in an absolute full-screen box (the default slot).
116
- const content = h(View, { style: resolved.contentWrapperStyle }, () => slots.default !== undefined ? slots.default() : []);
117
- // RN's drawerSubview: the navigation view (the `navigationView` slot) wrapped, drawerWidth-wide,
118
- // painted with drawerBackgroundColor, untouchable until opened.
119
- const navigation = h(View, {
120
- style: resolved.navigationWrapperStyle,
121
- pointerEvents: resolved.navigationPointerEvents,
122
- }, () => (slots.navigationView !== undefined ? slots.navigationView() : []));
123
- dlog(`Vue DrawerLayoutAndroid render width=${String(asNumber(attrs.drawerWidth))} ` +
124
- `opened=${drawerOpened.value}`);
125
- // Child order matches RN exactly: content FIRST, navigation SECOND.
126
- return h(resolved.viewName, {
127
- ...resolved.hostProps,
128
- ref: setNodeRef,
129
- onDrawerOpen: handleDrawerOpen,
130
- onDrawerClose: handleDrawerClose,
131
- onDrawerSlide: handleDrawerSlide,
132
- onDrawerStateChanged: handleDrawerStateChanged,
133
- }, [content, navigation]);
134
- };
135
- }, {
136
- name: 'DrawerLayoutAndroid',
137
- inheritAttrs: false,
138
- emits: {
139
- drawerOpen: () => true,
140
- drawerClose: () => true,
141
- drawerSlide: (_event) => true,
142
- drawerStateChanged: (_state) => true,
143
- },
144
- });
@@ -1,8 +0,0 @@
1
- import type { IDrawerLayoutAndroidEmits, IDrawerLayoutAndroidProps } from './shared';
2
- export type { IDrawerPosition, IDrawerLockMode, IKeyboardDismissMode, IDrawerState, IDrawerSlideEvent, IDrawerLayoutAndroidEmits, IDrawerLayoutAndroidProps, IDrawerLayoutAndroidHandle, } from './shared';
3
- export declare const DrawerLayoutAndroid: import("vue").DefineSetupFnComponent<IDrawerLayoutAndroidProps, IDrawerLayoutAndroidEmits, {}, IDrawerLayoutAndroidProps & {
4
- onDrawerOpen?: (() => any) | undefined;
5
- onDrawerClose?: (() => any) | undefined;
6
- onDrawerSlide?: ((event: import("@symbiote/components").IDrawerSlideEvent) => any) | undefined;
7
- onDrawerStateChanged?: ((state: import("@symbiote/components").IDrawerState) => any) | undefined;
8
- }, import("vue").PublicProps>;
@@ -1,36 +0,0 @@
1
- // DrawerLayoutAndroid: base / off-Android fallback (the Vue twin of the React adapter's index.ts).
2
- // AndroidDrawerLayout is Android-only, so everywhere except an Android host — where Metro picks
3
- // index.android.ts — we render the content (the default slot) in a plain container and drop the
4
- // navigation slot (RN's DrawerLayoutAndroidFallback shape). The imperative open/close are silent
5
- // no-ops; there is no drawer to drive. The filename is the selector; no Platform.OS read. The Vue
6
- // barrel imports './drawer-layout-android', which resolves here under tsx/tsc and to the .android
7
- // file under Metro. See ADR 0019.
8
- import { defineComponent, h } from '@vue/runtime-core';
9
- import { dlog } from '@symbiote/engine';
10
- import { View } from '../../components';
11
- import { normalizeVueAttrs } from '../../utils/normalize-attrs';
12
- function isStyleProp(value) {
13
- return typeof value === 'object' && value !== null;
14
- }
15
- export const DrawerLayoutAndroid = defineComponent((_props, { slots, attrs, expose }) => {
16
- const handle = {
17
- openDrawer: () => dlog('Vue DrawerLayoutAndroid.openDrawer no-op: off Android'),
18
- closeDrawer: () => dlog('Vue DrawerLayoutAndroid.closeDrawer no-op: off Android'),
19
- };
20
- expose(handle);
21
- return () => {
22
- dlog('Vue DrawerLayoutAndroid fallback: off-Android host, rendering content only');
23
- const normalized = normalizeVueAttrs(attrs);
24
- const style = isStyleProp(normalized.style) ? normalized.style : undefined;
25
- return h(View, { style }, () => (slots.default !== undefined ? slots.default() : []));
26
- };
27
- }, {
28
- name: 'DrawerLayoutAndroid',
29
- inheritAttrs: false,
30
- emits: {
31
- drawerOpen: () => true,
32
- drawerClose: () => true,
33
- drawerSlide: (_event) => true,
34
- drawerStateChanged: (_state) => true,
35
- },
36
- });
@@ -1,18 +0,0 @@
1
- import type { IStyleProp, IViewStyle } from '@symbiote/engine';
2
- import type { IAccessibilityProps, IAriaProps, IDrawerLockMode, IDrawerPosition, IDrawerSlideEvent, IDrawerState, IKeyboardDismissMode } from '@symbiote/components';
3
- export type { IDrawerPosition, IDrawerLockMode, IKeyboardDismissMode, IDrawerState, IDrawerSlideEvent, IDrawerLayoutAndroidHandle, } from '@symbiote/components';
4
- export interface IDrawerLayoutAndroidProps extends IAccessibilityProps, IAriaProps {
5
- drawerWidth?: number;
6
- drawerPosition?: IDrawerPosition;
7
- drawerLockMode?: IDrawerLockMode;
8
- keyboardDismissMode?: IKeyboardDismissMode;
9
- drawerBackgroundColor?: string;
10
- statusBarBackgroundColor?: string;
11
- style?: IStyleProp<IViewStyle>;
12
- }
13
- export type IDrawerLayoutAndroidEmits = {
14
- drawerOpen: () => boolean;
15
- drawerClose: () => boolean;
16
- drawerSlide: (event: IDrawerSlideEvent) => boolean;
17
- drawerStateChanged: (state: IDrawerState) => boolean;
18
- };
@@ -1,6 +0,0 @@
1
- // Shared types for the Vue DrawerLayoutAndroid: the same Props/Handle contract the real Android
2
- // build (index.android.ts) and the off-Android fallback (index.ts) both implement. The
3
- // platform-agnostic enums + the imperative handle live in @symbiote/components (shared verbatim with
4
- // the React adapter, the single source of truth); this file adds only the Vue-facing prop surface.
5
- // Per ADR 0019 the filename selects the build; no Platform.OS read anywhere.
6
- export {};