@symbiote-native/angular 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.
Files changed (57) hide show
  1. package/babel-register-composed.cjs +1 -0
  2. package/build/angular/components/checkbox-props.d.ts +2 -0
  3. package/build/angular/components/checkbox-props.js +2 -0
  4. package/build/angular/components/checkbox-props.js.map +1 -0
  5. package/build/angular/components.d.ts +1 -0
  6. package/build/angular/components.js.map +1 -1
  7. package/build/angular/descriptor-to-angular/descriptor-host.d.ts +8 -0
  8. package/build/angular/descriptor-to-angular/descriptor-host.js +38 -0
  9. package/build/angular/descriptor-to-angular/descriptor-host.js.map +1 -0
  10. package/build/angular/descriptor-to-angular/index.d.ts +1 -0
  11. package/build/angular/descriptor-to-angular/index.js +4 -0
  12. package/build/angular/descriptor-to-angular/index.js.map +1 -1
  13. package/build/angular/element-base.d.ts +153 -0
  14. package/build/angular/element-base.js +272 -0
  15. package/build/angular/element-base.js.map +1 -0
  16. package/build/angular/elements-controls.d.ts +165 -0
  17. package/build/angular/elements-controls.js +382 -0
  18. package/build/angular/elements-controls.js.map +1 -0
  19. package/build/angular/elements-layout.d.ts +122 -0
  20. package/build/angular/elements-layout.js +242 -0
  21. package/build/angular/elements-layout.js.map +1 -0
  22. package/build/angular/elements-press.d.ts +96 -0
  23. package/build/angular/elements-press.js +227 -0
  24. package/build/angular/elements-press.js.map +1 -0
  25. package/build/angular/elements.d.ts +6 -516
  26. package/build/angular/elements.js +8 -1091
  27. package/build/angular/elements.js.map +1 -1
  28. package/build/angular/host-instance/index.d.ts +2 -0
  29. package/build/angular/host-instance/index.js +4 -4
  30. package/build/angular/host-instance/index.js.map +1 -1
  31. package/build/angular/index.d.ts +5 -4
  32. package/build/angular/index.js +4 -3
  33. package/build/angular/index.js.map +1 -1
  34. package/build/angular/native-view-base.d.ts +10 -2
  35. package/build/angular/native-view-base.js +23 -2
  36. package/build/angular/native-view-base.js.map +1 -1
  37. package/build/angular/services/index.d.ts +1 -0
  38. package/build/angular/services/index.js +1 -0
  39. package/build/angular/services/index.js.map +1 -1
  40. package/build/angular/services/inject-event.d.ts +6 -0
  41. package/build/angular/services/inject-event.js +24 -0
  42. package/build/angular/services/inject-event.js.map +1 -0
  43. package/package.json +6 -6
  44. package/src/components/checkbox-props.ts +12 -0
  45. package/src/components.ts +1 -0
  46. package/src/descriptor-to-angular/descriptor-host.ts +27 -0
  47. package/src/descriptor-to-angular/index.ts +5 -0
  48. package/src/element-base.ts +272 -0
  49. package/src/elements-controls.ts +282 -0
  50. package/src/elements-layout.ts +141 -0
  51. package/src/elements-press.ts +145 -0
  52. package/src/elements.ts +73 -788
  53. package/src/host-instance/index.ts +4 -4
  54. package/src/index.ts +6 -1
  55. package/src/native-view-base.ts +37 -2
  56. package/src/services/index.ts +1 -0
  57. package/src/services/inject-event.ts +52 -0
@@ -21,14 +21,14 @@ import {
21
21
 
22
22
  export type { IHostInstance } from '@symbiote-native/engine';
23
23
 
24
- function resolveHostNode(candidate: unknown): ISymbioteNode | null {
24
+ export function hostNodeOf(candidate: unknown): ISymbioteNode | null {
25
25
  if (candidate === null || candidate === undefined) return null;
26
26
  if (isSymbioteNode(candidate)) return candidate;
27
27
  if (candidate instanceof ElementRef)
28
- return resolveHostNode(candidate.nativeElement);
28
+ return hostNodeOf(candidate.nativeElement);
29
29
  const maybeHost = candidate as { nativeElement?: unknown };
30
30
  if (typeof maybeHost.nativeElement !== 'undefined') {
31
- return resolveHostNode(maybeHost.nativeElement);
31
+ return hostNodeOf(maybeHost.nativeElement);
32
32
  }
33
33
  return null;
34
34
  }
@@ -37,6 +37,6 @@ export function findNodeHandle(componentOrHandle: unknown): number | null {
37
37
  if (componentOrHandle === null || componentOrHandle === undefined)
38
38
  return null;
39
39
  if (typeof componentOrHandle === 'number') return componentOrHandle;
40
- const node = resolveHostNode(componentOrHandle);
40
+ const node = hostNodeOf(componentOrHandle);
41
41
  return node ? (getNativeTag(node) ?? null) : null;
42
42
  }
package/src/index.ts CHANGED
@@ -89,6 +89,7 @@ export type {
89
89
  IAngularTouchableOpacityProps,
90
90
  IAngularTouchableWithoutFeedbackProps,
91
91
  IButtonProps,
92
+ ICheckboxProps,
92
93
  ICellLayout,
93
94
  IFlatListHandle,
94
95
  IFlatListProps,
@@ -144,6 +145,7 @@ export {
144
145
  ActivityIndicatorElement,
145
146
  ActivityIndicatorSpinnerElement,
146
147
  ButtonElement,
148
+ CheckboxElement,
147
149
  HorizontalScrollContentElement,
148
150
  HorizontalScrollViewElement,
149
151
  ImageBackgroundElement,
@@ -187,6 +189,7 @@ export { mount, unmount } from './render';
187
189
  // @symbiote-native/slider) can render a shared @symbiote-native/components/@symbiote-native/slider Descriptor tree
188
190
  // without hand-writing its own Renderer2 walker.
189
191
  export { DescriptorOutlet } from './descriptor-to-angular';
192
+ export { DescriptorHost } from './descriptor-to-angular/descriptor-host';
190
193
  // createPortal (same-surface only — see the file header) and createTunnel (cross-surface,
191
194
  // see its file header) are the Angular twins of the React/Vue portal/tunnel primitives.
192
195
  // Angular can't synthesize components at runtime (no JIT under Metro/Hermes), so both are
@@ -206,7 +209,7 @@ export { SymbioteRenderer, SymbioteRendererFactory } from './renderer';
206
209
  // import straight from that subpath so app-screen registration never routes through this cyclic
207
210
  // barrel.
208
211
  export { registerComposedComponent } from './anchor-host-registry';
209
- export { findNodeHandle } from './host-instance';
212
+ export { findNodeHandle, hostNodeOf } from './host-instance';
210
213
  export type { IHostInstance } from './host-instance';
211
214
  export { StatusBar } from './modules/status-bar';
212
215
  export type { IStatusBarProps, IStatusBarStyle } from './modules/status-bar';
@@ -230,6 +233,8 @@ export {
230
233
  WindowDimensionsService,
231
234
  createResourceHook,
232
235
  createEventValueHook,
236
+ injectEvent,
237
+ injectEventListener,
233
238
  connectWatchedSignal,
234
239
  PermissionsServiceBase,
235
240
  } from './services';
@@ -1,18 +1,32 @@
1
- import { Directive, ElementRef, inject, Input } from '@angular/core';
1
+ import {
2
+ ChangeDetectorRef,
3
+ Directive,
4
+ ElementRef,
5
+ inject,
6
+ Input,
7
+ ViewChild,
8
+ } from '@angular/core';
9
+ import type { OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
2
10
  import { resolveAccessibilityProps } from '@symbiote-native/components';
3
11
  import type { IDescriptor } from '@symbiote-native/components';
4
12
  import type {
5
13
  IStyleProp,
6
14
  ISymbioteEvent,
15
+ ISymbioteNode,
7
16
  IViewStyle,
8
17
  } from '@symbiote-native/engine';
9
18
  import { AccessibilityInputsBase } from './accessibility-inputs';
19
+ import { DescriptorOutlet } from './descriptor-to-angular';
20
+ import { hostNodeOf } from './host-instance';
10
21
  import { anchorStyleProp } from './primitives/shared';
11
22
 
12
23
  // Shared shell of a component that paints one Expo native view: the common inputs, the anchor
13
24
  // host `class="..."` resolves onto, and a descriptor that recomputes on every OnPush check
14
25
  @Directive()
15
- export abstract class NativeViewBase extends AccessibilityInputsBase {
26
+ export abstract class NativeViewBase
27
+ extends AccessibilityInputsBase
28
+ implements OnChanges, OnDestroy
29
+ {
16
30
  @Input() style?: IStyleProp<IViewStyle>;
17
31
  @Input() testID?: string;
18
32
  @Input() nativeID?: string;
@@ -23,6 +37,27 @@ export abstract class NativeViewBase extends AccessibilityInputsBase {
23
37
 
24
38
  // This component's own `ElementRef`, the anchor a `class="..."` at the use site lands on
25
39
  private readonly elementRef = inject(ElementRef);
40
+ protected readonly changeDetector = inject(ChangeDetectorRef);
41
+
42
+ @ViewChild(DescriptorOutlet) private readonly nativeOutlet?: DescriptorOutlet;
43
+
44
+ // The committed host node of the native view, for a component that calls its view functions
45
+ protected hostNode(): ISymbioteNode | null {
46
+ return hostNodeOf(this.nativeOutlet?.rootNode);
47
+ }
48
+
49
+ // Hook for a component that holds more than its native node, runs once when it is destroyed
50
+ protected disposeView(): void {}
51
+
52
+ // A `[style]` binding reaches the `style` input without dirtying an OnPush view, a plain
53
+ // `[prop]` does. A subclass with its own `ngOnChanges` calls this one first
54
+ ngOnChanges(changes: SimpleChanges): void {
55
+ if ('style' in changes) this.changeDetector.markForCheck();
56
+ }
57
+
58
+ ngOnDestroy(): void {
59
+ this.disposeView();
60
+ }
26
61
 
27
62
  // Names of the component's own inputs that go to the native view, read off `this`
28
63
  protected abstract readonly propNames: readonly string[];
@@ -2,5 +2,6 @@ export { ColorSchemeService } from './color-scheme.service';
2
2
  export { WindowDimensionsService } from './window-dimensions.service';
3
3
  export { createResourceHook } from './create-resource-hook';
4
4
  export { createEventValueHook } from './create-event-value-hook';
5
+ export { injectEvent, injectEventListener } from './inject-event';
5
6
  export { connectWatchedSignal } from './connect-watched-signal';
6
7
  export { PermissionsServiceBase } from './permissions-service-base';
@@ -0,0 +1,52 @@
1
+ // Angular twins of `useEvent` and `useEventListener` of the `expo` package, for any emitter
2
+
3
+ import { effect, signal, untracked } from '@angular/core';
4
+ import type { Signal } from '@angular/core';
5
+ import { bindEventListener } from '@symbiote-native/engine';
6
+ import type {
7
+ IEventEmitterOf,
8
+ IEventName,
9
+ IEventPayload,
10
+ IEventsMap,
11
+ } from '@symbiote-native/engine';
12
+
13
+ /** Calls the listener for each event, the latest one, and stops with the injection context */
14
+ export function injectEventListener<
15
+ TEventsMap extends IEventsMap,
16
+ TName extends IEventName<TEventsMap>,
17
+ >(
18
+ emitter: () => IEventEmitterOf<TEventsMap>,
19
+ eventName: () => TName,
20
+ listener: () => TEventsMap[TName],
21
+ ): void {
22
+ effect(onCleanup => {
23
+ const source = emitter();
24
+ const name = eventName();
25
+ // The listener is read at each event, a new one is not a new subscription
26
+ onCleanup(bindEventListener(source, name, () => untracked(listener)));
27
+ });
28
+ }
29
+
30
+ /** The payload of the last event, `initialValue` (or `null`) until the first one */
31
+ export function injectEvent<
32
+ TEventsMap extends IEventsMap,
33
+ TName extends IEventName<TEventsMap>,
34
+ >(
35
+ emitter: () => IEventEmitterOf<TEventsMap>,
36
+ eventName: () => TName,
37
+ initialValue: IEventPayload<TEventsMap, TName> | null = null,
38
+ ): Signal<IEventPayload<TEventsMap, TName> | null> {
39
+ const event = signal<IEventPayload<TEventsMap, TName> | null>(initialValue);
40
+ effect(onCleanup => {
41
+ const source = emitter();
42
+ const name = eventName();
43
+ onCleanup(
44
+ bindEventListener(
45
+ source,
46
+ name,
47
+ () => (next: IEventPayload<TEventsMap, TName>) => event.set(next),
48
+ ),
49
+ );
50
+ });
51
+ return event.asReadonly();
52
+ }