@symbiote-native/angular 0.0.0-canary-20260724081123
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/LICENSE +21 -0
- package/README.md +157 -0
- package/babel-linker.cjs +7 -0
- package/babel-register-composed.cjs +127 -0
- package/bin/symbiote-angular-dev.cjs +93 -0
- package/build/angular/anchor-host-registry.d.ts +2 -0
- package/build/angular/anchor-host-registry.js +84 -0
- package/build/angular/anchor-host-registry.js.map +1 -0
- package/build/angular/bootstrap.d.ts +7 -0
- package/build/angular/bootstrap.js +15 -0
- package/build/angular/bootstrap.js.map +1 -0
- package/build/angular/components/activity-indicator/index.android.d.ts +12 -0
- package/build/angular/components/activity-indicator/index.android.js +21 -0
- package/build/angular/components/activity-indicator/index.android.js.map +1 -0
- package/build/angular/components/activity-indicator/index.d.ts +1 -0
- package/build/angular/components/activity-indicator/index.ios.d.ts +8 -0
- package/build/angular/components/activity-indicator/index.ios.js +21 -0
- package/build/angular/components/activity-indicator/index.ios.js.map +1 -0
- package/build/angular/components/activity-indicator/index.js +4 -0
- package/build/angular/components/activity-indicator/index.js.map +1 -0
- package/build/angular/components/activity-indicator/shared.d.ts +67 -0
- package/build/angular/components/activity-indicator/shared.js +260 -0
- package/build/angular/components/activity-indicator/shared.js.map +1 -0
- package/build/angular/components/button.d.ts +63 -0
- package/build/angular/components/button.js +332 -0
- package/build/angular/components/button.js.map +1 -0
- package/build/angular/components/flat-list/index.d.ts +122 -0
- package/build/angular/components/flat-list/index.js +688 -0
- package/build/angular/components/flat-list/index.js.map +1 -0
- package/build/angular/components/image/index.android.d.ts +10 -0
- package/build/angular/components/image/index.android.js +59 -0
- package/build/angular/components/image/index.android.js.map +1 -0
- package/build/angular/components/image/index.d.ts +1 -0
- package/build/angular/components/image/index.ios.d.ts +10 -0
- package/build/angular/components/image/index.ios.js +59 -0
- package/build/angular/components/image/index.ios.js.map +1 -0
- package/build/angular/components/image/index.js +3 -0
- package/build/angular/components/image/index.js.map +1 -0
- package/build/angular/components/image/shared.d.ts +101 -0
- package/build/angular/components/image/shared.js +406 -0
- package/build/angular/components/image/shared.js.map +1 -0
- package/build/angular/components/image-background.d.ts +81 -0
- package/build/angular/components/image-background.js +418 -0
- package/build/angular/components/image-background.js.map +1 -0
- package/build/angular/components/input-accessory-view/index.d.ts +60 -0
- package/build/angular/components/input-accessory-view/index.js +251 -0
- package/build/angular/components/input-accessory-view/index.js.map +1 -0
- package/build/angular/components/keyboard-avoiding-view/index.d.ts +82 -0
- package/build/angular/components/keyboard-avoiding-view/index.js +358 -0
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -0
- package/build/angular/components/modal/index.d.ts +92 -0
- package/build/angular/components/modal/index.js +360 -0
- package/build/angular/components/modal/index.js.map +1 -0
- package/build/angular/components/pressable/index.d.ts +123 -0
- package/build/angular/components/pressable/index.js +420 -0
- package/build/angular/components/pressable/index.js.map +1 -0
- package/build/angular/components/refresh-control/index.d.ts +86 -0
- package/build/angular/components/refresh-control/index.js +342 -0
- package/build/angular/components/refresh-control/index.js.map +1 -0
- package/build/angular/components/safe-area-view/index.d.ts +57 -0
- package/build/angular/components/safe-area-view/index.js +248 -0
- package/build/angular/components/safe-area-view/index.js.map +1 -0
- package/build/angular/components/scroll-view/index.android.d.ts +21 -0
- package/build/angular/components/scroll-view/index.android.js +235 -0
- package/build/angular/components/scroll-view/index.android.js.map +1 -0
- package/build/angular/components/scroll-view/index.d.ts +3 -0
- package/build/angular/components/scroll-view/index.ios.d.ts +9 -0
- package/build/angular/components/scroll-view/index.ios.js +86 -0
- package/build/angular/components/scroll-view/index.ios.js.map +1 -0
- package/build/angular/components/scroll-view/index.js +6 -0
- package/build/angular/components/scroll-view/index.js.map +1 -0
- package/build/angular/components/scroll-view/projection.d.ts +32 -0
- package/build/angular/components/scroll-view/projection.js +334 -0
- package/build/angular/components/scroll-view/projection.js.map +1 -0
- package/build/angular/components/scroll-view/shared.d.ts +230 -0
- package/build/angular/components/scroll-view/shared.js +682 -0
- package/build/angular/components/scroll-view/shared.js.map +1 -0
- package/build/angular/components/scroll-view/sticky-header.d.ts +40 -0
- package/build/angular/components/scroll-view/sticky-header.js +225 -0
- package/build/angular/components/scroll-view/sticky-header.js.map +1 -0
- package/build/angular/components/section-list/index.d.ts +116 -0
- package/build/angular/components/section-list/index.js +641 -0
- package/build/angular/components/section-list/index.js.map +1 -0
- package/build/angular/components/switch/index.android.d.ts +20 -0
- package/build/angular/components/switch/index.android.js +70 -0
- package/build/angular/components/switch/index.android.js.map +1 -0
- package/build/angular/components/switch/index.d.ts +1 -0
- package/build/angular/components/switch/index.ios.d.ts +19 -0
- package/build/angular/components/switch/index.ios.js +69 -0
- package/build/angular/components/switch/index.ios.js.map +1 -0
- package/build/angular/components/switch/index.js +4 -0
- package/build/angular/components/switch/index.js.map +1 -0
- package/build/angular/components/switch/shared.d.ts +40 -0
- package/build/angular/components/switch/shared.js +127 -0
- package/build/angular/components/switch/shared.js.map +1 -0
- package/build/angular/components/text-input.d.ts +153 -0
- package/build/angular/components/text-input.js +599 -0
- package/build/angular/components/text-input.js.map +1 -0
- package/build/angular/components/touchable/index.d.ts +245 -0
- package/build/angular/components/touchable/index.js +1186 -0
- package/build/angular/components/touchable/index.js.map +1 -0
- package/build/angular/components/touchable-native-feedback/index.d.ts +83 -0
- package/build/angular/components/touchable-native-feedback/index.js +371 -0
- package/build/angular/components/touchable-native-feedback/index.js.map +1 -0
- package/build/angular/components/virtualized-list/directives.d.ts +54 -0
- package/build/angular/components/virtualized-list/directives.js +153 -0
- package/build/angular/components/virtualized-list/directives.js.map +1 -0
- package/build/angular/components/virtualized-list/index.d.ts +253 -0
- package/build/angular/components/virtualized-list/index.js +1031 -0
- package/build/angular/components/virtualized-list/index.js.map +1 -0
- package/build/angular/components/virtualized-section-list/directives.d.ts +37 -0
- package/build/angular/components/virtualized-section-list/directives.js +76 -0
- package/build/angular/components/virtualized-section-list/directives.js.map +1 -0
- package/build/angular/components/virtualized-section-list/index.d.ts +178 -0
- package/build/angular/components/virtualized-section-list/index.js +720 -0
- package/build/angular/components/virtualized-section-list/index.js.map +1 -0
- package/build/angular/components.d.ts +39 -0
- package/build/angular/components.js +38 -0
- package/build/angular/components.js.map +1 -0
- package/build/angular/create-portal/index.d.ts +22 -0
- package/build/angular/create-portal/index.js +74 -0
- package/build/angular/create-portal/index.js.map +1 -0
- package/build/angular/create-tunnel/index.d.ts +34 -0
- package/build/angular/create-tunnel/index.js +126 -0
- package/build/angular/create-tunnel/index.js.map +1 -0
- package/build/angular/descriptor-to-angular/index.d.ts +22 -0
- package/build/angular/descriptor-to-angular/index.js +142 -0
- package/build/angular/descriptor-to-angular/index.js.map +1 -0
- package/build/angular/host-instance/index.d.ts +2 -0
- package/build/angular/host-instance/index.js +37 -0
- package/build/angular/host-instance/index.js.map +1 -0
- package/build/angular/index.d.ts +22 -0
- package/build/angular/index.js +46 -0
- package/build/angular/index.js.map +1 -0
- package/build/angular/modules/animated/animated-leaf-binder/index.d.ts +14 -0
- package/build/angular/modules/animated/animated-leaf-binder/index.js +91 -0
- package/build/angular/modules/animated/animated-leaf-binder/index.js.map +1 -0
- package/build/angular/modules/animated/animated-leaf-binder.d.ts +14 -0
- package/build/angular/modules/animated/animated-leaf-binder.js +91 -0
- package/build/angular/modules/animated/animated-leaf-binder.js.map +1 -0
- package/build/angular/modules/animated/create-animated-component.d.ts +59 -0
- package/build/angular/modules/animated/create-animated-component.js +364 -0
- package/build/angular/modules/animated/create-animated-component.js.map +1 -0
- package/build/angular/modules/animated/index.d.ts +51 -0
- package/build/angular/modules/animated/index.js +59 -0
- package/build/angular/modules/animated/index.js.map +1 -0
- package/build/angular/modules/app-registry/index.d.ts +7 -0
- package/build/angular/modules/app-registry/index.js +23 -0
- package/build/angular/modules/app-registry/index.js.map +1 -0
- package/build/angular/modules/status-bar/index.d.ts +20 -0
- package/build/angular/modules/status-bar/index.js +59 -0
- package/build/angular/modules/status-bar/index.js.map +1 -0
- package/build/angular/primitives/index.d.ts +63 -0
- package/build/angular/primitives/index.js +189 -0
- package/build/angular/primitives/index.js.map +1 -0
- package/build/angular/primitives/shared.d.ts +88 -0
- package/build/angular/primitives/shared.js +171 -0
- package/build/angular/primitives/shared.js.map +1 -0
- package/build/angular/render/index.d.ts +11 -0
- package/build/angular/render/index.js +171 -0
- package/build/angular/render/index.js.map +1 -0
- package/build/angular/renderer/index.d.ts +37 -0
- package/build/angular/renderer/index.js +301 -0
- package/build/angular/renderer/index.js.map +1 -0
- package/build/angular/services/color-scheme.service.d.ts +16 -0
- package/build/angular/services/color-scheme.service.js +31 -0
- package/build/angular/services/color-scheme.service.js.map +1 -0
- package/build/angular/services/index.d.ts +2 -0
- package/build/angular/services/index.js +3 -0
- package/build/angular/services/index.js.map +1 -0
- package/build/angular/services/window-dimensions.service.d.ts +16 -0
- package/build/angular/services/window-dimensions.service.js +42 -0
- package/build/angular/services/window-dimensions.service.js.map +1 -0
- package/metro-config.cjs +48 -0
- package/metro-css-parser.cjs +13 -0
- package/package.json +80 -0
- package/src/__tests__/responder-change-detection.test.ts +102 -0
- package/src/__tests__/responder-nested-cd.test.ts +113 -0
- package/src/anchor-host-registry.ts +88 -0
- package/src/angular-gaps.test.ts +142 -0
- package/src/angular-imports.test.ts +100 -0
- package/src/bootstrap.ts +26 -0
- package/src/components/activity-indicator/activity-indicator-class-style.test.ts +44 -0
- package/src/components/activity-indicator/activity-indicator.test.ts +131 -0
- package/src/components/activity-indicator/index.android.ts +16 -0
- package/src/components/activity-indicator/index.ios.ts +17 -0
- package/src/components/activity-indicator/index.ts +4 -0
- package/src/components/activity-indicator/shared.ts +206 -0
- package/src/components/button.ts +193 -0
- package/src/components/flat-list/column-wrapper-style-class.test.ts +116 -0
- package/src/components/flat-list/flat-list-array-style.test.ts +131 -0
- package/src/components/flat-list/flat-list-class-style.test.ts +74 -0
- package/src/components/flat-list/flat-list-scroll-containment.test.ts +107 -0
- package/src/components/flat-list/flat-list.test.ts +99 -0
- package/src/components/flat-list/index.ts +574 -0
- package/src/components/image/image-class-style.test.ts +46 -0
- package/src/components/image/index.android.ts +48 -0
- package/src/components/image/index.ios.ts +48 -0
- package/src/components/image/index.ts +3 -0
- package/src/components/image/shared.ts +479 -0
- package/src/components/image-background-class-style.test.ts +48 -0
- package/src/components/image-background-style-class.test.ts +79 -0
- package/src/components/image-background.ts +270 -0
- package/src/components/input-accessory-view/index.ts +185 -0
- package/src/components/input-accessory-view/input-accessory-view.test.ts +43 -0
- package/src/components/keyboard-avoiding-view/index.ts +303 -0
- package/src/components/keyboard-avoiding-view/keyboard-avoiding-view.test.ts +54 -0
- package/src/components/modal/index.ts +299 -0
- package/src/components/modal/modal.test.ts +43 -0
- package/src/components/pressable/index.ts +373 -0
- package/src/components/pressable/pressable.test.ts +85 -0
- package/src/components/refresh-control/index.ts +284 -0
- package/src/components/refresh-control/refresh-control.test.ts +41 -0
- package/src/components/safe-area-view/index.ts +177 -0
- package/src/components/safe-area-view/safe-area-view.test.ts +43 -0
- package/src/components/scroll-view/android-scroll-view-axis-projection.test.ts +123 -0
- package/src/components/scroll-view/content-container-style-class.test.ts +75 -0
- package/src/components/scroll-view/index.android.ts +190 -0
- package/src/components/scroll-view/index.ios.ts +79 -0
- package/src/components/scroll-view/index.ts +7 -0
- package/src/components/scroll-view/projection.ts +414 -0
- package/src/components/scroll-view/scroll-view-class-style.test.ts +134 -0
- package/src/components/scroll-view/scroll-view-projection.test.ts +194 -0
- package/src/components/scroll-view/shared.ts +844 -0
- package/src/components/scroll-view/sticky-header.ts +255 -0
- package/src/components/section-list/index.ts +445 -0
- package/src/components/switch/android-switch-class.test.ts +42 -0
- package/src/components/switch/index.android.ts +61 -0
- package/src/components/switch/index.ios.ts +60 -0
- package/src/components/switch/index.ts +4 -0
- package/src/components/switch/shared.ts +197 -0
- package/src/components/switch/switch.test.ts +92 -0
- package/src/components/text-input-class-style.test.ts +54 -0
- package/src/components/text-input.ts +531 -0
- package/src/components/touchable/index.ts +629 -0
- package/src/components/touchable/touchable.test.ts +102 -0
- package/src/components/touchable-native-feedback/index.ts +254 -0
- package/src/components/touchable-native-feedback/touchable-native-feedback.test.ts +49 -0
- package/src/components/virtualized-list/directives.ts +168 -0
- package/src/components/virtualized-list/index.ts +992 -0
- package/src/components/virtualized-list/virtualized-list-class-style.test.ts +76 -0
- package/src/components/virtualized-section-list/directives.ts +90 -0
- package/src/components/virtualized-section-list/index.ts +601 -0
- package/src/components/virtualized-section-list/virtualized-section-list-class-style.test.ts +60 -0
- package/src/components.ts +149 -0
- package/src/create-portal/create-portal.test.ts +90 -0
- package/src/create-portal/index.ts +74 -0
- package/src/create-tunnel/create-tunnel.test.ts +120 -0
- package/src/create-tunnel/index.ts +136 -0
- package/src/descriptor-to-angular/descriptor-outlet.test.ts +94 -0
- package/src/descriptor-to-angular/index.ts +190 -0
- package/src/host-instance/host-instance.test.ts +49 -0
- package/src/host-instance/index.ts +36 -0
- package/src/index.ts +261 -0
- package/src/modules/animated/animated-leaf-binder/animated-leaf-binder.test.ts +116 -0
- package/src/modules/animated/animated-leaf-binder/index.ts +101 -0
- package/src/modules/animated/animated-leaf-binder.test.ts +116 -0
- package/src/modules/animated/animated-leaf-binder.ts +101 -0
- package/src/modules/animated/animated-scroll-view.test.ts +102 -0
- package/src/modules/animated/create-animated-component.ts +365 -0
- package/src/modules/animated/index.ts +105 -0
- package/src/modules/app-registry/app-registry.test.ts +106 -0
- package/src/modules/app-registry/index.ts +60 -0
- package/src/modules/status-bar/index.ts +57 -0
- package/src/modules/status-bar/status-bar.test.ts +50 -0
- package/src/primitives/index.ts +119 -0
- package/src/primitives/shared.ts +190 -0
- package/src/render/index.ts +235 -0
- package/src/render/render.test.ts +147 -0
- package/src/renderer/index.ts +347 -0
- package/src/renderer/renderer.test.ts +203 -0
- package/src/services/color-scheme.service.ts +27 -0
- package/src/services/index.ts +2 -0
- package/src/services/services.test.ts +163 -0
- package/src/services/window-dimensions.service.ts +41 -0
- package/tsconfig.angular.base.json +30 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Angular source-import harness regression. Vitest imports adapter source (`src/*.ts`) directly,
|
|
2
|
+
// not the partial-Ivy `ngc` output Metro consumes. The adapter tsconfig must therefore enable
|
|
3
|
+
// TypeScript's legacy decorator lowering so Node never sees raw `@Component` / `@Directive`
|
|
4
|
+
// syntax. Importing `@angular/compiler` enables JIT metadata creation for these source-only tests;
|
|
5
|
+
// production still proves AOT/partial-Ivy through `pnpm --filter @symbiote-native/angular ng:build`.
|
|
6
|
+
|
|
7
|
+
import '@angular/compiler';
|
|
8
|
+
import { ElementRef, Injector, runInInjectionContext } from '@angular/core';
|
|
9
|
+
import { describe, expect, it } from 'vitest';
|
|
10
|
+
import { Image, ScrollView, Text, View } from './components';
|
|
11
|
+
import { VirtualizedList, VListItemDirective } from './components/virtualized-list';
|
|
12
|
+
import { SymbioteHostPropsDirective } from './primitives';
|
|
13
|
+
import {
|
|
14
|
+
Animated,
|
|
15
|
+
AnimatedImage,
|
|
16
|
+
AnimatedScrollView,
|
|
17
|
+
AnimatedText,
|
|
18
|
+
AnimatedView,
|
|
19
|
+
createAnimatedComponent,
|
|
20
|
+
} from './modules/animated';
|
|
21
|
+
|
|
22
|
+
interface IAngularCompiledComponent {
|
|
23
|
+
ɵcmp?: { selectors?: unknown };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface IAngularCompiledDirective {
|
|
27
|
+
ɵdir?: { selectors?: unknown };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('Angular source imports under Vitest', () => {
|
|
31
|
+
it('imports decorated adapter components and directives directly from src', () => {
|
|
32
|
+
expect(VirtualizedList).toBeTypeOf('function');
|
|
33
|
+
expect(VListItemDirective).toBeTypeOf('function');
|
|
34
|
+
expect(AnimatedView).toBeTypeOf('function');
|
|
35
|
+
expect(SymbioteHostPropsDirective).toBeTypeOf('function');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('keeps Angular decorator metadata available to the JIT test runtime', () => {
|
|
39
|
+
expect((VirtualizedList as IAngularCompiledComponent).ɵcmp?.selectors).toEqual([
|
|
40
|
+
['VirtualizedList'],
|
|
41
|
+
]);
|
|
42
|
+
expect((AnimatedView as IAngularCompiledComponent).ɵcmp?.selectors).toEqual([
|
|
43
|
+
['AnimatedView'],
|
|
44
|
+
['symbiote-animated-view'],
|
|
45
|
+
]);
|
|
46
|
+
expect((VListItemDirective as IAngularCompiledDirective).ɵdir?.selectors).toEqual([
|
|
47
|
+
['', 'vListItem', ''],
|
|
48
|
+
]);
|
|
49
|
+
expect((SymbioteHostPropsDirective as IAngularCompiledDirective).ɵdir?.selectors).toEqual([
|
|
50
|
+
['', 'symbioteHostProps', ''],
|
|
51
|
+
]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('imports the Animated namespace without losing component identity', () => {
|
|
55
|
+
expect(Animated.View).toBe(AnimatedView);
|
|
56
|
+
expect(Animated.Text).toBe(AnimatedText);
|
|
57
|
+
expect(Animated.Image).toBe(AnimatedImage);
|
|
58
|
+
expect(Animated.ScrollView).toBe(AnimatedScrollView);
|
|
59
|
+
expect(Animated.FlatList).toBeTypeOf('function');
|
|
60
|
+
expect(Animated.SectionList).toBeTypeOf('function');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('keeps createAnimatedComponent limited to pre-authored AOT-safe Angular wrappers', () => {
|
|
64
|
+
class CustomComponent {}
|
|
65
|
+
|
|
66
|
+
expect(createAnimatedComponent(View)).toBe(AnimatedView);
|
|
67
|
+
expect(createAnimatedComponent(Text)).toBe(AnimatedText);
|
|
68
|
+
expect(createAnimatedComponent(Image)).toBe(AnimatedImage);
|
|
69
|
+
expect(createAnimatedComponent(ScrollView)).toBe(AnimatedScrollView);
|
|
70
|
+
expect(() => createAnimatedComponent(CustomComponent)).toThrow(
|
|
71
|
+
/Angular cannot synthesize a component at runtime \(no JIT compiler under AOT\/Metro\)/,
|
|
72
|
+
);
|
|
73
|
+
expect(() => createAnimatedComponent(CustomComponent)).toThrow(
|
|
74
|
+
/Author an explicit standalone @Component extending AnimatedComponentBase instead/,
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('resolves AnimatedImage props through the composed Image path', () => {
|
|
79
|
+
// AnimatedImage is an ANCHOR_HOST_COMPONENT: its field initializer injects its own
|
|
80
|
+
// ElementRef (anchorHostStyle merge, see create-animated-component.ts), so constructing it
|
|
81
|
+
// outside Angular's component machinery needs an explicit injection context.
|
|
82
|
+
const injector = Injector.create({
|
|
83
|
+
providers: [{ provide: ElementRef, useValue: new ElementRef({}) }],
|
|
84
|
+
});
|
|
85
|
+
const image = runInInjectionContext(injector, () => new AnimatedImage());
|
|
86
|
+
image.src = 'https://example.invalid/image.png';
|
|
87
|
+
image.width = 32;
|
|
88
|
+
image.height = 24;
|
|
89
|
+
image.alt = 'Preview';
|
|
90
|
+
image.animatedProps = { style: { opacity: 0.5 }, testID: 'animated-image' };
|
|
91
|
+
|
|
92
|
+
expect(image.animatedImageProps).toMatchObject({
|
|
93
|
+
testID: 'animated-image',
|
|
94
|
+
accessible: true,
|
|
95
|
+
accessibilityLabel: 'Preview',
|
|
96
|
+
source: [{ uri: 'https://example.invalid/image.png' }],
|
|
97
|
+
style: [undefined, [{ width: 32, height: 24 }, { opacity: 0.5 }]],
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
package/src/bootstrap.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Zero-config app entry, the Angular twin of adapters/react/src/bootstrap.ts. Lives OUTSIDE the
|
|
2
|
+
// package's main barrel — see that file's header for why anything importing react-native must
|
|
3
|
+
// stay there. No @Component/@Directive decorators here, so unlike the package's main "." entry
|
|
4
|
+
// this needs no ngc/AOT build step and can stay a plain source reference.
|
|
5
|
+
|
|
6
|
+
import { AppRegistry as RNAppRegistry } from 'react-native';
|
|
7
|
+
import type { Type } from '@angular/core';
|
|
8
|
+
import { bootstrapHost, type IBootstrapHostOptions } from '@symbiote-native/components/bootstrap';
|
|
9
|
+
import { AppRegistry, setHostRegistrar } from './modules/app-registry';
|
|
10
|
+
|
|
11
|
+
export type { IBootstrapHostOptions } from '@symbiote-native/components/bootstrap';
|
|
12
|
+
|
|
13
|
+
export type IBootstrapApplicationOptions = IBootstrapHostOptions & {
|
|
14
|
+
appName: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Mirrors real Angular's bootstrapApplication(RootComponent, config) idiom.
|
|
18
|
+
export function bootstrapApplication(
|
|
19
|
+
AppComponent: Type<unknown>,
|
|
20
|
+
options: IBootstrapApplicationOptions,
|
|
21
|
+
): void {
|
|
22
|
+
const { appName, ...bootstrapOptions } = options;
|
|
23
|
+
bootstrapHost(bootstrapOptions);
|
|
24
|
+
setHostRegistrar(RNAppRegistry);
|
|
25
|
+
AppRegistry.registerComponent(appName, () => AppComponent);
|
|
26
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Regression test for the anchor/class bug: ActivityIndicator is its own
|
|
2
|
+
// ANCHOR_HOST_COMPONENTS entry AND renders through DescriptorOutlet (`symbiote-descriptor-outlet`,
|
|
3
|
+
// itself also an anchor entry) — a class= on <ActivityIndicator> resolves onto ActivityIndicator's
|
|
4
|
+
// OWN anchor, two levels up from the real committed wrapper View the descriptor builds, and needs
|
|
5
|
+
// its OWN anchorHostStyle merge (see shared.ts's `descriptor` getter). Mirrors pressable.test.ts's
|
|
6
|
+
// "resolves a class=" case.
|
|
7
|
+
import '@angular/compiler';
|
|
8
|
+
import { Component } from '@angular/core';
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
10
|
+
import { clearGlobalStyles, registerStyles } from '@symbiote-native/engine';
|
|
11
|
+
import { installFabric } from '@symbiote-native/test-utils';
|
|
12
|
+
|
|
13
|
+
import { mount, unmount } from '../../render';
|
|
14
|
+
import { ActivityIndicator } from './index';
|
|
15
|
+
|
|
16
|
+
const ROOT_TAG = 909;
|
|
17
|
+
const fabric = installFabric();
|
|
18
|
+
|
|
19
|
+
@Component({
|
|
20
|
+
selector: 'symbiote-activity-indicator-class-host',
|
|
21
|
+
standalone: true,
|
|
22
|
+
imports: [ActivityIndicator],
|
|
23
|
+
template: `<ActivityIndicator class="card" />`,
|
|
24
|
+
})
|
|
25
|
+
class ActivityIndicatorClassHost {}
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
fabric.reset();
|
|
29
|
+
registerStyles({ card: { backgroundColor: 'red' } });
|
|
30
|
+
});
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
unmount(ROOT_TAG);
|
|
33
|
+
clearGlobalStyles();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('ActivityIndicator anchor class= resolution', () => {
|
|
37
|
+
it('resolves a class= on the ActivityIndicator use site onto the real committed wrapper View', async () => {
|
|
38
|
+
mount(ROOT_TAG, ActivityIndicatorClassHost);
|
|
39
|
+
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
|
40
|
+
|
|
41
|
+
const node = fabric.find(n => n.props.backgroundColor === 'red');
|
|
42
|
+
expect(node, 'a real Fabric node carries the class-derived style').toBeDefined();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import '@angular/compiler';
|
|
2
|
+
import { Component, signal } from '@angular/core';
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
|
|
5
|
+
|
|
6
|
+
import { mount, unmount } from '../../render';
|
|
7
|
+
import { ActivityIndicator } from './index';
|
|
8
|
+
|
|
9
|
+
const ROOT_TAG = 905;
|
|
10
|
+
const fabric = installFabric();
|
|
11
|
+
|
|
12
|
+
let capturedHost: ActivityIndicatorHost | undefined;
|
|
13
|
+
|
|
14
|
+
function findCommitted(predicate: (node: IFakeNode) => boolean): IFakeNode | undefined {
|
|
15
|
+
const visit = (node: IFakeNode): IFakeNode | undefined => {
|
|
16
|
+
if (predicate(node)) return node;
|
|
17
|
+
for (const child of node.children) {
|
|
18
|
+
const found = visit(child);
|
|
19
|
+
if (found) return found;
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
};
|
|
23
|
+
for (const node of fabric.committed) {
|
|
24
|
+
const found = visit(node);
|
|
25
|
+
if (found) return found;
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function findSpinner(): IFakeNode {
|
|
31
|
+
const node = findCommitted(n => n.viewName === 'ActivityIndicatorView');
|
|
32
|
+
if (!node) throw new Error('no ActivityIndicatorView was created');
|
|
33
|
+
return node;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function findWrapper(): IFakeNode {
|
|
37
|
+
const node = findCommitted(n => n.viewName === 'RCTView' && n.props.pointerEvents !== 'box-none');
|
|
38
|
+
if (!node) throw new Error('no ActivityIndicator wrapper was created');
|
|
39
|
+
return node;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Component({
|
|
43
|
+
selector: 'symbiote-activity-indicator-host',
|
|
44
|
+
standalone: true,
|
|
45
|
+
imports: [ActivityIndicator],
|
|
46
|
+
template: `
|
|
47
|
+
<ActivityIndicator
|
|
48
|
+
[size]="size()"
|
|
49
|
+
[color]="color()"
|
|
50
|
+
[animating]="animating()"
|
|
51
|
+
[testID]="testID()"
|
|
52
|
+
[accessible]="true"
|
|
53
|
+
[accessibilityLabel]="'loading'"
|
|
54
|
+
(layout)="onLayout($event)"
|
|
55
|
+
/>
|
|
56
|
+
`,
|
|
57
|
+
})
|
|
58
|
+
class ActivityIndicatorHost {
|
|
59
|
+
readonly size = signal<'small' | 'large' | number>('large');
|
|
60
|
+
readonly color = signal('#0000ff');
|
|
61
|
+
readonly animating = signal(false);
|
|
62
|
+
readonly testID = signal('spinner-wrapper');
|
|
63
|
+
onLayout = vi.fn();
|
|
64
|
+
|
|
65
|
+
constructor() {
|
|
66
|
+
// Captures the live component instance so the test can drive its signals after mount.
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
68
|
+
capturedHost = this;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
capturedHost = undefined;
|
|
74
|
+
fabric.reset();
|
|
75
|
+
});
|
|
76
|
+
afterEach(() => unmount(ROOT_TAG));
|
|
77
|
+
|
|
78
|
+
describe('ActivityIndicator', () => {
|
|
79
|
+
it('renders through the shared descriptor shape and forwards wrapper props', async () => {
|
|
80
|
+
mount(ROOT_TAG, ActivityIndicatorHost);
|
|
81
|
+
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
|
82
|
+
|
|
83
|
+
expect(fabric.serialize(fabric.appRoot().children)).toBe('RCTView(ActivityIndicatorView)');
|
|
84
|
+
|
|
85
|
+
const spinner = findSpinner();
|
|
86
|
+
expect(spinner.props.animating).toBe(false);
|
|
87
|
+
expect(spinner.props.color).toBe('#0000ff');
|
|
88
|
+
expect(spinner.props.hidesWhenStopped).toBe(true);
|
|
89
|
+
expect(spinner.props.size).toBe('large');
|
|
90
|
+
expect(spinner.props.width).toBe(36);
|
|
91
|
+
expect(spinner.props.height).toBe(36);
|
|
92
|
+
|
|
93
|
+
const wrapper = findWrapper();
|
|
94
|
+
expect(wrapper.props.testID).toBe('spinner-wrapper');
|
|
95
|
+
expect(wrapper.props.accessible).toBe(true);
|
|
96
|
+
expect(wrapper.props.accessibilityLabel).toBe('loading');
|
|
97
|
+
expect(wrapper.props.alignItems).toBe('center');
|
|
98
|
+
expect(wrapper.props.justifyContent).toBe('center');
|
|
99
|
+
|
|
100
|
+
fabric.fireEvent(wrapper.instanceHandle, 'topLayout', {});
|
|
101
|
+
expect(capturedHost?.onLayout).toHaveBeenCalledOnce();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('patches the descriptor without remounting the native wrapper', async () => {
|
|
105
|
+
mount(ROOT_TAG, ActivityIndicatorHost);
|
|
106
|
+
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
|
107
|
+
|
|
108
|
+
const before = findWrapper();
|
|
109
|
+
const createdBefore = fabric.counts.createNode;
|
|
110
|
+
|
|
111
|
+
if (!capturedHost) throw new Error('host was not captured');
|
|
112
|
+
capturedHost.size.set(48);
|
|
113
|
+
capturedHost.color.set('#ff0000');
|
|
114
|
+
capturedHost.animating.set(true);
|
|
115
|
+
capturedHost.testID.set('spinner-wrapper-updated');
|
|
116
|
+
await Promise.resolve();
|
|
117
|
+
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
|
118
|
+
|
|
119
|
+
const after = findWrapper();
|
|
120
|
+
expect(after.instanceHandle).toBe(before.instanceHandle);
|
|
121
|
+
expect(fabric.counts.createNode).toBe(createdBefore);
|
|
122
|
+
expect(after.props.testID).toBe('spinner-wrapper-updated');
|
|
123
|
+
const spinner = findSpinner();
|
|
124
|
+
expect(spinner.props.color).toBe('#ff0000');
|
|
125
|
+
expect(spinner.props.animating).toBe(true);
|
|
126
|
+
expect(spinner.props.width).toBe(48);
|
|
127
|
+
expect(spinner.props.height).toBe(48);
|
|
128
|
+
// Updating from string size to numeric size sends null to reset the previous native enum.
|
|
129
|
+
expect(spinner.props.size).toBeNull();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
2
|
+
import { DescriptorOutlet } from '../../descriptor-to-angular';
|
|
3
|
+
import { ActivityIndicatorBase } from './shared';
|
|
4
|
+
export type { IActivityIndicatorProps } from './shared';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'ActivityIndicator',
|
|
8
|
+
standalone: true,
|
|
9
|
+
imports: [DescriptorOutlet],
|
|
10
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
11
|
+
template: `<symbiote-descriptor-outlet [node]="descriptor" />`,
|
|
12
|
+
})
|
|
13
|
+
export class ActivityIndicator extends ActivityIndicatorBase {
|
|
14
|
+
protected readonly defaultColor = null;
|
|
15
|
+
protected override readonly nativeExtras = { styleAttr: 'Normal', indeterminate: true };
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
2
|
+
import { DescriptorOutlet } from '../../descriptor-to-angular';
|
|
3
|
+
import { ActivityIndicatorBase } from './shared';
|
|
4
|
+
export type { IActivityIndicatorProps } from './shared';
|
|
5
|
+
|
|
6
|
+
const IOS_DEFAULT_COLOR = '#999999';
|
|
7
|
+
|
|
8
|
+
@Component({
|
|
9
|
+
selector: 'ActivityIndicator',
|
|
10
|
+
standalone: true,
|
|
11
|
+
imports: [DescriptorOutlet],
|
|
12
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
13
|
+
template: `<symbiote-descriptor-outlet [node]="descriptor" />`,
|
|
14
|
+
})
|
|
15
|
+
export class ActivityIndicator extends ActivityIndicatorBase {
|
|
16
|
+
protected readonly defaultColor = IOS_DEFAULT_COLOR;
|
|
17
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { Directive, ElementRef, EventEmitter, inject, Input, Output } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
renderActivityIndicator,
|
|
4
|
+
resolveAccessibilityProps,
|
|
5
|
+
type IAccessibilityProps,
|
|
6
|
+
type IAccessibilityStateValue,
|
|
7
|
+
type IActivityIndicatorPlatform,
|
|
8
|
+
type IActivityIndicatorProps,
|
|
9
|
+
type IActivityIndicatorSize,
|
|
10
|
+
type IAriaProps,
|
|
11
|
+
} from '@symbiote-native/components';
|
|
12
|
+
import type { ISymbioteEvent } from '@symbiote-native/engine';
|
|
13
|
+
import type { IDescriptor } from '@symbiote-native/components';
|
|
14
|
+
import { anchorHostStyle } from '../../primitives';
|
|
15
|
+
|
|
16
|
+
export type { IActivityIndicatorProps, IActivityIndicatorSize } from '@symbiote-native/components';
|
|
17
|
+
|
|
18
|
+
// Narrows anchorHostStyle's `unknown` (it reads an opaque engine prop bag) to the shape
|
|
19
|
+
// IActivityIndicatorProps['style'] actually declares, the same runtime guard image/shared.ts's
|
|
20
|
+
// asStyle() uses for the identical reason — a style value is structurally opaque at the type
|
|
21
|
+
// level, so this only rules out non-objects, never validates individual style keys.
|
|
22
|
+
function asStyle(value: unknown): IActivityIndicatorProps['style'] {
|
|
23
|
+
return typeof value === 'object' && value !== null ? value : undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// What the component itself takes as plain @Input()s: the full shared, cross-adapter
|
|
27
|
+
// IActivityIndicatorProps surface minus onLayout and the accessibility callbacks, which it
|
|
28
|
+
// exposes as real @Output() EventEmitters instead (see the class below), mirroring Pressable's
|
|
29
|
+
// IAngularPressableInputs split.
|
|
30
|
+
export type IActivityIndicatorInputs = Omit<
|
|
31
|
+
IActivityIndicatorProps,
|
|
32
|
+
| 'onLayout'
|
|
33
|
+
| 'onAccessibilityAction'
|
|
34
|
+
| 'onAccessibilityTap'
|
|
35
|
+
| 'onMagicTap'
|
|
36
|
+
| 'onAccessibilityEscape'
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
@Directive()
|
|
40
|
+
export abstract class ActivityIndicatorBase implements IActivityIndicatorInputs {
|
|
41
|
+
@Input() animating?: boolean;
|
|
42
|
+
@Input() color?: string;
|
|
43
|
+
@Input() hidesWhenStopped?: boolean;
|
|
44
|
+
@Input() nativeID?: string;
|
|
45
|
+
@Input() size?: IActivityIndicatorSize;
|
|
46
|
+
@Input() style?: IActivityIndicatorProps['style'];
|
|
47
|
+
@Input() testID?: string;
|
|
48
|
+
// Real @Output()s. This component renders through DescriptorOutlet (`symbiote-descriptor-
|
|
49
|
+
// outlet`), not a hand-written template: emitterHandler() below wraps each EventEmitter into a
|
|
50
|
+
// plain callback under the SAME onX key the shared, cross-adapter IActivityIndicatorProps
|
|
51
|
+
// expects, so the Descriptor's props bag still carries a function the engine's routeProp
|
|
52
|
+
// recognizes and routes to setEventListener — the outlet's renderer.setProperty call never
|
|
53
|
+
// sees an EventEmitter, only the wrapper.
|
|
54
|
+
@Output() readonly layout = new EventEmitter<ISymbioteEvent>();
|
|
55
|
+
|
|
56
|
+
@Input() accessible?: boolean;
|
|
57
|
+
@Input() accessibilityLabel?: string;
|
|
58
|
+
@Input() accessibilityHint?: string;
|
|
59
|
+
@Input() accessibilityRole?: IAccessibilityProps['accessibilityRole'];
|
|
60
|
+
@Input() accessibilityState?: IAccessibilityStateValue;
|
|
61
|
+
@Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
|
|
62
|
+
@Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
|
|
63
|
+
@Input() accessibilityLabelledBy?: string | string[];
|
|
64
|
+
@Input() importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
|
|
65
|
+
@Input() accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
|
|
66
|
+
@Input() screenReaderFocusable?: boolean;
|
|
67
|
+
@Input() accessibilityViewIsModal?: boolean;
|
|
68
|
+
@Input() accessibilityElementsHidden?: boolean;
|
|
69
|
+
@Input() accessibilityIgnoresInvertColors?: boolean;
|
|
70
|
+
@Input() accessibilityLanguage?: string;
|
|
71
|
+
@Input() accessibilityRespondsToUserInteraction?: boolean;
|
|
72
|
+
@Input() accessibilityShowsLargeContentViewer?: boolean;
|
|
73
|
+
@Input() accessibilityLargeContentTitle?: string;
|
|
74
|
+
@Output() readonly accessibilityAction = new EventEmitter<ISymbioteEvent>();
|
|
75
|
+
@Output() readonly accessibilityTap = new EventEmitter<ISymbioteEvent>();
|
|
76
|
+
@Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
|
|
77
|
+
@Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
|
|
78
|
+
|
|
79
|
+
@Input() role?: IAriaProps['role'];
|
|
80
|
+
@Input('aria-label') ariaLabel?: string;
|
|
81
|
+
@Input('aria-labelledby') ariaLabelledBy?: string;
|
|
82
|
+
@Input('aria-live') ariaLive?: IAriaProps['aria-live'];
|
|
83
|
+
@Input('aria-hidden') ariaHidden?: boolean;
|
|
84
|
+
@Input('aria-busy') ariaBusy?: boolean;
|
|
85
|
+
@Input('aria-checked') ariaChecked?: boolean | 'mixed';
|
|
86
|
+
@Input('aria-disabled') ariaDisabled?: boolean;
|
|
87
|
+
@Input('aria-expanded') ariaExpanded?: boolean;
|
|
88
|
+
@Input('aria-selected') ariaSelected?: boolean;
|
|
89
|
+
@Input('aria-modal') ariaModal?: boolean;
|
|
90
|
+
@Input('aria-valuemax') ariaValueMax?: number;
|
|
91
|
+
@Input('aria-valuemin') ariaValueMin?: number;
|
|
92
|
+
@Input('aria-valuenow') ariaValueNow?: number;
|
|
93
|
+
@Input('aria-valuetext') ariaValueText?: string;
|
|
94
|
+
|
|
95
|
+
protected abstract readonly defaultColor: string | null;
|
|
96
|
+
protected readonly nativeExtras: Readonly<Record<string, unknown>> = {};
|
|
97
|
+
|
|
98
|
+
// This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
|
|
99
|
+
// onto (see anchorHostStyle's doc comment) — NOT the descriptor-outlet-rendered wrapper the
|
|
100
|
+
// `descriptor` getter below builds. Merged FIRST so an explicit `style` @Input still wins
|
|
101
|
+
// (flattenStyle's later-wins collapse), mirroring every other composed component's anchor merge.
|
|
102
|
+
private readonly elementRef = inject(ElementRef);
|
|
103
|
+
|
|
104
|
+
// Stable bound wrappers (created ONCE, not per `descriptor` access) fed to emitterHandler below.
|
|
105
|
+
// jsonEqual (core/engine/src/commit.ts) falls through to Object.is for a function leaf, so a
|
|
106
|
+
// fresh closure built inside the `descriptor` getter would never equal the previous frame's
|
|
107
|
+
// closure even when nothing changed — every CD pass would read as "props changed" and force a
|
|
108
|
+
// real Fabric re-clone cascading up every ancestor to the root (the same hazard
|
|
109
|
+
// AnimatedComponentBase.reconcile() and ScrollViewStickyHeader's onHostLayout guard against).
|
|
110
|
+
private readonly onLayoutHandler = (event: ISymbioteEvent): void => this.layout.emit(event);
|
|
111
|
+
private readonly onAccessibilityActionHandler = (event: ISymbioteEvent): void =>
|
|
112
|
+
this.accessibilityAction.emit(event);
|
|
113
|
+
private readonly onAccessibilityTapHandler = (event: ISymbioteEvent): void =>
|
|
114
|
+
this.accessibilityTap.emit(event);
|
|
115
|
+
private readonly onMagicTapHandler = (event: ISymbioteEvent): void => this.magicTap.emit(event);
|
|
116
|
+
private readonly onAccessibilityEscapeHandler = (event: ISymbioteEvent): void =>
|
|
117
|
+
this.accessibilityEscape.emit(event);
|
|
118
|
+
|
|
119
|
+
get descriptor(): IDescriptor {
|
|
120
|
+
const props = resolveAccessibilityProps<IActivityIndicatorProps>({
|
|
121
|
+
animating: this.animating,
|
|
122
|
+
color: this.color,
|
|
123
|
+
hidesWhenStopped: this.hidesWhenStopped,
|
|
124
|
+
nativeID: this.nativeID,
|
|
125
|
+
size: this.size,
|
|
126
|
+
style: [asStyle(anchorHostStyle(this.elementRef)), this.style],
|
|
127
|
+
testID: this.testID,
|
|
128
|
+
onLayout: this.emitterHandler(this.layout, this.onLayoutHandler),
|
|
129
|
+
accessible: this.accessible,
|
|
130
|
+
accessibilityLabel: this.accessibilityLabel,
|
|
131
|
+
accessibilityHint: this.accessibilityHint,
|
|
132
|
+
accessibilityRole: this.accessibilityRole,
|
|
133
|
+
accessibilityState: this.accessibilityState,
|
|
134
|
+
accessibilityValue: this.accessibilityValue,
|
|
135
|
+
accessibilityActions: this.accessibilityActions,
|
|
136
|
+
accessibilityLabelledBy: this.accessibilityLabelledBy,
|
|
137
|
+
importantForAccessibility: this.importantForAccessibility,
|
|
138
|
+
accessibilityLiveRegion: this.accessibilityLiveRegion,
|
|
139
|
+
screenReaderFocusable: this.screenReaderFocusable,
|
|
140
|
+
accessibilityViewIsModal: this.accessibilityViewIsModal,
|
|
141
|
+
accessibilityElementsHidden: this.accessibilityElementsHidden,
|
|
142
|
+
accessibilityIgnoresInvertColors: this.accessibilityIgnoresInvertColors,
|
|
143
|
+
accessibilityLanguage: this.accessibilityLanguage,
|
|
144
|
+
accessibilityRespondsToUserInteraction: this.accessibilityRespondsToUserInteraction,
|
|
145
|
+
accessibilityShowsLargeContentViewer: this.accessibilityShowsLargeContentViewer,
|
|
146
|
+
accessibilityLargeContentTitle: this.accessibilityLargeContentTitle,
|
|
147
|
+
onAccessibilityAction: this.emitterHandler(
|
|
148
|
+
this.accessibilityAction,
|
|
149
|
+
this.onAccessibilityActionHandler,
|
|
150
|
+
),
|
|
151
|
+
onAccessibilityTap: this.emitterHandler(
|
|
152
|
+
this.accessibilityTap,
|
|
153
|
+
this.onAccessibilityTapHandler,
|
|
154
|
+
),
|
|
155
|
+
onMagicTap: this.emitterHandler(this.magicTap, this.onMagicTapHandler),
|
|
156
|
+
onAccessibilityEscape: this.emitterHandler(
|
|
157
|
+
this.accessibilityEscape,
|
|
158
|
+
this.onAccessibilityEscapeHandler,
|
|
159
|
+
),
|
|
160
|
+
role: this.role,
|
|
161
|
+
'aria-label': this.ariaLabel,
|
|
162
|
+
'aria-labelledby': this.ariaLabelledBy,
|
|
163
|
+
'aria-live': this.ariaLive,
|
|
164
|
+
'aria-hidden': this.ariaHidden,
|
|
165
|
+
'aria-busy': this.ariaBusy,
|
|
166
|
+
'aria-checked': this.ariaChecked,
|
|
167
|
+
'aria-disabled': this.ariaDisabled,
|
|
168
|
+
'aria-expanded': this.ariaExpanded,
|
|
169
|
+
'aria-selected': this.ariaSelected,
|
|
170
|
+
'aria-modal': this.ariaModal,
|
|
171
|
+
'aria-valuemax': this.ariaValueMax,
|
|
172
|
+
'aria-valuemin': this.ariaValueMin,
|
|
173
|
+
'aria-valuenow': this.ariaValueNow,
|
|
174
|
+
'aria-valuetext': this.ariaValueText,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const { animating, color, hidesWhenStopped, size, style, ...passthrough } = props;
|
|
178
|
+
return renderActivityIndicator(
|
|
179
|
+
{
|
|
180
|
+
animating: animating !== false,
|
|
181
|
+
color,
|
|
182
|
+
hidesWhenStopped: hidesWhenStopped !== false,
|
|
183
|
+
size: size ?? 'small',
|
|
184
|
+
style,
|
|
185
|
+
passthrough,
|
|
186
|
+
},
|
|
187
|
+
this.platform,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private get platform(): IActivityIndicatorPlatform {
|
|
192
|
+
return { defaultColor: this.defaultColor, nativeExtras: this.nativeExtras };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Gates a STABLE bound handler behind `observed`, so an unbound event still resolves to
|
|
196
|
+
// `undefined` in the descriptor's props bag — the same "undefined means nobody cares" contract
|
|
197
|
+
// the old @Input() callback had, letting resolveAccessibilityProps drop the key entirely instead
|
|
198
|
+
// of handing the engine a no-op function. `handler` must be one of the readonly bound fields
|
|
199
|
+
// above, never a closure built here — see their doc comment for why.
|
|
200
|
+
private emitterHandler(
|
|
201
|
+
emitter: EventEmitter<ISymbioteEvent>,
|
|
202
|
+
handler: (event: ISymbioteEvent) => void,
|
|
203
|
+
): ((event: ISymbioteEvent) => void) | undefined {
|
|
204
|
+
return emitter.observed ? handler : undefined;
|
|
205
|
+
}
|
|
206
|
+
}
|