@symbiote-native/vue 3.2.0 → 3.3.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.
@@ -0,0 +1,3 @@
1
+ import type { ICheckboxProps as ICoreCheckboxProps } from '@symbiote-native/components';
2
+ import type { IViewProps } from './view-props';
3
+ export type ICheckboxProps = ICoreCheckboxProps & Omit<IViewProps, keyof ICoreCheckboxProps>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import { type VNode } from '@vue/runtime-core';
2
+ import type { ICreateNativeViewController } from '@symbiote-native/components';
3
+ export declare function defineNativeViewComponent<THandle extends object>(name: string, createController: ICreateNativeViewController<THandle>): import("vue").DefineComponent<{}, () => VNode | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,23 @@
1
+ import { defineComponent, onUnmounted } from '@vue/runtime-core';
2
+ import { createHostNodeHolder } from '@symbiote-native/components';
3
+ import { descriptorToVue } from '../descriptor-to-vue.js';
4
+ import { normalizeVueAttrs } from '../utils/normalize-attrs.js';
5
+ // A Vue component over a package's native view: every attr is a prop of the native view, the
6
+ // handle of its controller is exposed to a template ref
7
+ export function defineNativeViewComponent(name, createController) {
8
+ return defineComponent({
9
+ name,
10
+ // Nothing may leak onto the wrapper, the attrs are the props of the native view
11
+ inheritAttrs: false,
12
+ setup(_props, { attrs, expose }) {
13
+ const host = createHostNodeHolder();
14
+ const controller = createController(host.getNode);
15
+ expose(controller.handle);
16
+ onUnmounted(() => controller.dispose?.());
17
+ return () => {
18
+ const descriptor = controller.render(normalizeVueAttrs(attrs));
19
+ return descriptor ? descriptorToVue(host.capture(descriptor)) : null;
20
+ };
21
+ },
22
+ });
23
+ }
@@ -0,0 +1,6 @@
1
+ import type { MaybeRef, MaybeRefOrGetter, ShallowRef } from '@vue/runtime-core';
2
+ import type { IEventEmitterOf, IEventName, IEventPayload, IEventsMap } from '@symbiote-native/engine';
3
+ /** Calls the listener for each event, the latest one, and stops when the owner unmounts */
4
+ export declare function useEventListener<TEventsMap extends IEventsMap, TName extends IEventName<TEventsMap>>(emitter: MaybeRefOrGetter<IEventEmitterOf<TEventsMap>>, eventName: MaybeRefOrGetter<TName>, listener: MaybeRef<TEventsMap[TName]>): void;
5
+ /** The payload of the last event, `initialValue` (or `null`) until the first one */
6
+ export declare function useEvent<TEventsMap extends IEventsMap, TName extends IEventName<TEventsMap>>(emitter: MaybeRefOrGetter<IEventEmitterOf<TEventsMap>>, eventName: MaybeRefOrGetter<TName>, initialValue?: IEventPayload<TEventsMap, TName> | null): Readonly<ShallowRef<IEventPayload<TEventsMap, TName> | null>>;
@@ -0,0 +1,22 @@
1
+ // Vue twins of `useEvent` and `useEventListener` of the `expo` package, for any emitter
2
+ import { shallowRef, toValue, unref, watch } from '@vue/runtime-core';
3
+ import { bindEventListener } from '@symbiote-native/engine';
4
+ /** Calls the listener for each event, the latest one, and stops when the owner unmounts */
5
+ export function useEventListener(emitter, eventName,
6
+ // A ref, not a getter: a function here is the listener itself
7
+ listener) {
8
+ watch([() => toValue(emitter), () => toValue(eventName)], ([source, name], _previous, onCleanup) => {
9
+ // The listener is read at each event, a new one is not a new subscription
10
+ onCleanup(bindEventListener(source, name, () => unref(listener)));
11
+ }, { immediate: true });
12
+ }
13
+ /** The payload of the last event, `initialValue` (or `null`) until the first one */
14
+ export function useEvent(emitter, eventName, initialValue = null) {
15
+ const event = shallowRef(initialValue);
16
+ watch([() => toValue(emitter), () => toValue(eventName)], ([source, name], _previous, onCleanup) => {
17
+ onCleanup(bindEventListener(source, name, () => (next) => {
18
+ event.value = next;
19
+ }));
20
+ }, { immediate: true });
21
+ return event;
22
+ }
@@ -1,3 +1,3 @@
1
1
  import { type VNode } from '@vue/runtime-core';
2
2
  import type { IDescriptor } from '@symbiote-native/components';
3
- export declare function descriptorToVue(node: IDescriptor): VNode;
3
+ export declare function descriptorToVue(node: IDescriptor, extraChildren?: readonly VNode[]): VNode;
@@ -4,10 +4,14 @@
4
4
  // renderer → engine → Fabric, exactly like a hand-written h('view'). The React
5
5
  // twin is `adapters/react/src/descriptor-to-react.ts`.
6
6
  import { h } from '@vue/runtime-core';
7
- export function descriptorToVue(node) {
7
+ // `extraChildren` are caller vnodes (a component's slot) that go after the descriptor's own
8
+ export function descriptorToVue(node, extraChildren = []) {
8
9
  // String type → host element (the Vue renderer's createElement → descriptorFor maps it to
9
10
  // 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
+ return h(node.type, { ...node.props, key: node.key }, [
12
+ ...node.children.map(toChild),
13
+ ...extraChildren,
14
+ ]);
11
15
  }
12
16
  function toChild(child) {
13
17
  return typeof child === 'string' ? child : descriptorToVue(child);
package/build/index.d.ts CHANGED
@@ -25,6 +25,7 @@ export { TouchableNativeFeedback } from '@symbiote-native/components';
25
25
  export type { INativeFeedbackBackground, IThemeAttrBackground, IRippleBackground, } from '@symbiote-native/components';
26
26
  export type { ITouchableNativeFeedbackProps } from './components/touchable-native-feedback-props';
27
27
  export type { IButtonProps } from './components/button-props';
28
+ export type { ICheckboxProps } from './components/checkbox-props';
28
29
  export type { ITextInputProps } from './components/text-input/text-input-props';
29
30
  export type { ITextInputHandle, ITextInputChangeEvent, } from '@symbiote-native/components';
30
31
  export { VirtualizedList } from './components/virtualized-list';
@@ -56,7 +57,9 @@ export { useColorScheme } from './composables/use-color-scheme';
56
57
  export { useWindowDimensions } from './composables/use-window-dimensions';
57
58
  export { createPermissionHook } from './composables/create-permission-hook';
58
59
  export { createResourceHook } from './composables/create-resource-hook';
60
+ export { defineNativeViewComponent } from './composables/define-native-view-component';
59
61
  export { createEventValueHook } from './composables/create-event-value-hook';
62
+ export { useEvent, useEventListener } from './composables/use-event';
60
63
  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';
61
64
  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';
62
65
  export { Platform, StyleSheet, processColor, setColorProcessor, dlog, isDebug, } from '@symbiote-native/engine';
package/build/index.js CHANGED
@@ -75,7 +75,9 @@ export { useColorScheme } from './composables/use-color-scheme.js';
75
75
  export { useWindowDimensions } from './composables/use-window-dimensions.js';
76
76
  export { createPermissionHook } from './composables/create-permission-hook.js';
77
77
  export { createResourceHook } from './composables/create-resource-hook.js';
78
+ export { defineNativeViewComponent } from './composables/define-native-view-component.js';
78
79
  export { createEventValueHook } from './composables/create-event-value-hook.js';
80
+ export { useEvent, useEventListener } from './composables/use-event.js';
79
81
  // Imperative runtime modules: the SAME module both adapters share, re-exported from @symbiote-native/engine.
80
82
  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';
81
83
  // Re-export the framework-agnostic engine surface (pure utilities + diagnostics).
@@ -4,6 +4,7 @@ import type { IViewProps } from './components/view-props';
4
4
  import type { ITextProps } from './components/text-props';
5
5
  import type { IPressableProps } from './components/pressable-props';
6
6
  import type { IButtonProps } from './components/button-props';
7
+ import type { ICheckboxProps } from './components/checkbox-props';
7
8
  import type { IImageProps } from './components/image-props';
8
9
  import type { IImageBackgroundProps } from './components/image-background-props';
9
10
  import type { IInputAccessoryViewProps } from './components/input-accessory-view-props';
@@ -23,6 +24,7 @@ export interface ICrossedPrimitiveProps {
23
24
  text: ITextProps;
24
25
  pressable: IPressableProps;
25
26
  button: IButtonProps;
27
+ checkbox: ICheckboxProps;
26
28
  image: IImageProps;
27
29
  'image-background': IImageBackgroundProps;
28
30
  'input-accessory-view': IInputAccessoryViewProps;
@@ -9,14 +9,20 @@
9
9
  // and `data-*`. Event keys are already `onXxx` (Vue folds `@value-change` → onValueChange, no
10
10
  // dash) so they pass through untouched.
11
11
  const KEBAB_SEGMENT = /-([a-z])/g;
12
+ // The template compiler keys a native tag's `@valueChange` as `on:valueChange`, to keep its case
13
+ const NATIVE_EVENT_PREFIX = 'on:';
14
+ function foldNativeEventKey(key) {
15
+ const name = key.slice(NATIVE_EVENT_PREFIX.length);
16
+ return `on${name.charAt(0).toUpperCase()}${name.slice(1)}`;
17
+ }
12
18
  function toCamel(key) {
13
19
  return key.replace(KEBAB_SEGMENT, (_match, char) => char.toUpperCase());
14
20
  }
15
- // The per-KEY half, for the path that never sees a whole attrs bag: an intrinsic tag reaches the
16
- // renderer one patchProp call at a time, with no component in between to fold the bag. Returns the
17
- // key unchanged (same string identity)
18
- // whenever there is nothing to convert, which is every key on the hot path.
21
+ // The per-KEY half, for an intrinsic tag: `patchProp` hands over one key with no bag to fold
22
+ // Returns the same string when nothing converts, which is every key on the hot path
19
23
  export function normalizeVueAttrKey(key) {
24
+ if (key.startsWith(NATIVE_EVENT_PREFIX))
25
+ return foldNativeEventKey(key);
20
26
  if (!key.includes('-'))
21
27
  return key;
22
28
  if (key.startsWith('aria-') || key.startsWith('data-'))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/vue",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
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
  "license": "MIT",
6
6
  "repository": {
@@ -70,18 +70,18 @@
70
70
  "@vue/compiler-sfc": "^3.5.13",
71
71
  "@vue/runtime-core": "^3.5.13",
72
72
  "typescript": "~6.0.0",
73
- "@symbiote-native/components": "3.1.4",
74
- "@symbiote-native/css-parser": "0.5.3"
73
+ "@symbiote-native/components": "3.2.0",
74
+ "@symbiote-native/css-parser": "0.5.4"
75
75
  },
76
76
  "peerDependencies": {
77
77
  "vue": ">=3.5.0",
78
78
  "react-native": ">=0.86",
79
- "@symbiote-native/engine": "^1.5.0"
79
+ "@symbiote-native/engine": "^1.6.0"
80
80
  },
81
81
  "devDependencies": {
82
82
  "@babel/core": "^7.25.2",
83
- "@symbiote-native/engine": "1.5.0",
84
- "@symbiote-native/test-utils": "0.4.6"
83
+ "@symbiote-native/engine": "1.6.0",
84
+ "@symbiote-native/test-utils": "0.4.7"
85
85
  },
86
86
  "scripts": {
87
87
  "typecheck": "tsc --build",