@symbiote-native/image 0.0.0-stage → 0.0.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 (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -2
  3. package/build/angular/image/index.d.ts +20 -0
  4. package/build/angular/image/index.js +113 -0
  5. package/build/angular/image-background/index.d.ts +14 -0
  6. package/build/angular/image-background/index.js +107 -0
  7. package/build/angular/index.d.ts +4 -0
  8. package/build/angular/index.js +6 -0
  9. package/build/angular/inject-image.d.ts +8 -0
  10. package/build/angular/inject-image.js +20 -0
  11. package/build/core/constants.d.ts +3 -0
  12. package/build/core/constants.js +4 -0
  13. package/build/core/hash-strings.d.ts +5 -0
  14. package/build/core/hash-strings.js +20 -0
  15. package/build/core/image-api.d.ts +18 -0
  16. package/build/core/image-api.js +30 -0
  17. package/build/core/image-background.d.ts +11 -0
  18. package/build/core/image-background.js +26 -0
  19. package/build/core/image-loader.d.ts +20 -0
  20. package/build/core/image-loader.js +42 -0
  21. package/build/core/image-ref-loader.d.ts +4 -0
  22. package/build/core/image-ref-loader.js +10 -0
  23. package/build/core/image-view-events.d.ts +9 -0
  24. package/build/core/image-view-events.js +32 -0
  25. package/build/core/image-view.d.ts +14 -0
  26. package/build/core/image-view.js +111 -0
  27. package/build/core/index.d.ts +10 -0
  28. package/build/core/index.js +7 -0
  29. package/build/core/native-module.d.ts +27 -0
  30. package/build/core/native-module.js +3 -0
  31. package/build/core/native-style.d.ts +19 -0
  32. package/build/core/native-style.js +62 -0
  33. package/build/core/props.d.ts +11 -0
  34. package/build/core/props.js +87 -0
  35. package/build/core/sources.d.ts +8 -0
  36. package/build/core/sources.js +59 -0
  37. package/build/core/types.d.ts +186 -0
  38. package/build/core/types.js +1 -0
  39. package/build/react/image-background.d.ts +7 -0
  40. package/build/react/image-background.js +7 -0
  41. package/build/react/image.d.ts +8 -0
  42. package/build/react/image.js +6 -0
  43. package/build/react/index.d.ts +6 -0
  44. package/build/react/index.js +5 -0
  45. package/build/react/use-image.d.ts +8 -0
  46. package/build/react/use-image.js +20 -0
  47. package/build/solid/image-background.d.ts +7 -0
  48. package/build/solid/image-background.js +4 -0
  49. package/build/solid/image.d.ts +45 -0
  50. package/build/solid/image.js +4 -0
  51. package/build/solid/index.d.ts +5 -0
  52. package/build/solid/index.js +5 -0
  53. package/build/solid/use-image.d.ts +8 -0
  54. package/build/solid/use-image.js +16 -0
  55. package/build/svelte/image-background-props.d.ts +5 -0
  56. package/build/svelte/image-background-props.js +1 -0
  57. package/build/svelte/image-background.svelte +10 -0
  58. package/build/svelte/image-background.svelte.d.ts +4 -0
  59. package/build/svelte/image.svelte +26 -0
  60. package/build/svelte/image.svelte.d.ts +10 -0
  61. package/build/svelte/index.d.ts +6 -0
  62. package/build/svelte/index.js +5 -0
  63. package/build/svelte/use-image.svelte.d.ts +10 -0
  64. package/build/svelte/use-image.svelte.js +26 -0
  65. package/build/vue/image-background.d.ts +3 -0
  66. package/build/vue/image-background.js +12 -0
  67. package/build/vue/image.d.ts +2 -0
  68. package/build/vue/image.js +4 -0
  69. package/build/vue/index.d.ts +4 -0
  70. package/build/vue/index.js +5 -0
  71. package/build/vue/use-image.d.ts +8 -0
  72. package/build/vue/use-image.js +18 -0
  73. package/build-ngc/angular/image/index.d.ts +23 -0
  74. package/build-ngc/angular/image/index.js +72 -0
  75. package/build-ngc/angular/image/index.js.map +1 -0
  76. package/build-ngc/angular/image-background/index.d.ts +17 -0
  77. package/build-ngc/angular/image-background/index.js +66 -0
  78. package/build-ngc/angular/image-background/index.js.map +1 -0
  79. package/build-ngc/angular/index.d.ts +4 -0
  80. package/build-ngc/angular/index.js +7 -0
  81. package/build-ngc/angular/index.js.map +1 -0
  82. package/build-ngc/angular/inject-image.d.ts +8 -0
  83. package/build-ngc/angular/inject-image.js +23 -0
  84. package/build-ngc/angular/inject-image.js.map +1 -0
  85. package/build-ngc/core/constants.d.ts +3 -0
  86. package/build-ngc/core/constants.js +5 -0
  87. package/build-ngc/core/constants.js.map +1 -0
  88. package/build-ngc/core/hash-strings.d.ts +5 -0
  89. package/build-ngc/core/hash-strings.js +21 -0
  90. package/build-ngc/core/hash-strings.js.map +1 -0
  91. package/build-ngc/core/image-api.d.ts +18 -0
  92. package/build-ngc/core/image-api.js +31 -0
  93. package/build-ngc/core/image-api.js.map +1 -0
  94. package/build-ngc/core/image-background.d.ts +11 -0
  95. package/build-ngc/core/image-background.js +27 -0
  96. package/build-ngc/core/image-background.js.map +1 -0
  97. package/build-ngc/core/image-loader.d.ts +20 -0
  98. package/build-ngc/core/image-loader.js +43 -0
  99. package/build-ngc/core/image-loader.js.map +1 -0
  100. package/build-ngc/core/image-ref-loader.d.ts +4 -0
  101. package/build-ngc/core/image-ref-loader.js +11 -0
  102. package/build-ngc/core/image-ref-loader.js.map +1 -0
  103. package/build-ngc/core/image-view-events.d.ts +9 -0
  104. package/build-ngc/core/image-view-events.js +33 -0
  105. package/build-ngc/core/image-view-events.js.map +1 -0
  106. package/build-ngc/core/image-view.d.ts +14 -0
  107. package/build-ngc/core/image-view.js +112 -0
  108. package/build-ngc/core/image-view.js.map +1 -0
  109. package/build-ngc/core/index.d.ts +10 -0
  110. package/build-ngc/core/index.js +8 -0
  111. package/build-ngc/core/index.js.map +1 -0
  112. package/build-ngc/core/native-module.d.ts +27 -0
  113. package/build-ngc/core/native-module.js +4 -0
  114. package/build-ngc/core/native-module.js.map +1 -0
  115. package/build-ngc/core/native-style.d.ts +19 -0
  116. package/build-ngc/core/native-style.js +63 -0
  117. package/build-ngc/core/native-style.js.map +1 -0
  118. package/build-ngc/core/props.d.ts +11 -0
  119. package/build-ngc/core/props.js +88 -0
  120. package/build-ngc/core/props.js.map +1 -0
  121. package/build-ngc/core/sources.d.ts +8 -0
  122. package/build-ngc/core/sources.js +60 -0
  123. package/build-ngc/core/sources.js.map +1 -0
  124. package/build-ngc/core/types.d.ts +186 -0
  125. package/build-ngc/core/types.js +2 -0
  126. package/build-ngc/core/types.js.map +1 -0
  127. package/native-link.json +13 -0
  128. package/package.json +145 -3
  129. package/src/angular/image/index.ts +78 -0
  130. package/src/angular/image-background/index.ts +69 -0
  131. package/src/angular/index.ts +7 -0
  132. package/src/angular/inject-image.ts +29 -0
  133. package/src/core/constants.ts +6 -0
  134. package/src/core/hash-strings.ts +27 -0
  135. package/src/core/image-api.ts +67 -0
  136. package/src/core/image-background.ts +42 -0
  137. package/src/core/image-loader.ts +65 -0
  138. package/src/core/image-ref-loader.ts +16 -0
  139. package/src/core/image-view-events.ts +41 -0
  140. package/src/core/image-view.ts +156 -0
  141. package/src/core/index.ts +50 -0
  142. package/src/core/native-module.ts +45 -0
  143. package/src/core/native-style.ts +89 -0
  144. package/src/core/props.ts +118 -0
  145. package/src/core/sources.ts +70 -0
  146. package/src/core/types.ts +278 -0
  147. package/src/react/image-background.ts +14 -0
  148. package/src/react/image.ts +14 -0
  149. package/src/react/index.ts +8 -0
  150. package/src/react/use-image.ts +29 -0
  151. package/src/solid/image-background.ts +13 -0
  152. package/src/solid/image.ts +8 -0
  153. package/src/solid/index.ts +7 -0
  154. package/src/solid/use-image.ts +30 -0
  155. package/src/svelte/image-background-props.ts +6 -0
  156. package/src/svelte/image-background.svelte +10 -0
  157. package/src/svelte/image.svelte +26 -0
  158. package/src/svelte/index.ts +8 -0
  159. package/src/svelte/svelte-ambient.d.ts +3 -0
  160. package/src/svelte/svelte-compile.test-helper.ts +6 -0
  161. package/src/svelte/use-image.svelte.ts +38 -0
  162. package/src/vue/image-background.ts +17 -0
  163. package/src/vue/image.ts +5 -0
  164. package/src/vue/index.ts +6 -0
  165. package/src/vue/use-image.ts +34 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 A. Prokopenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,46 @@
1
- # Temporary Holding Version
1
+ # @symbiote-native/image
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [`expo-image`](https://docs.expo.dev/versions/latest/sdk/image/) for **every**
4
+ [SymbioteNative](../../README.md) adapter: React, Vue, Svelte, Solid and Angular. An image view
5
+ with caching, placeholders, transitions and SF Symbols, plus the cache and hash functions.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npx @symbiote-native/cli new my-app --image # new app
11
+ npx @symbiote-native/cli add --image # existing app
12
+ ```
13
+
14
+ Manual: `npm install @symbiote-native/image`, then wire `expo-modules-autolinking` once per app.
15
+ Never install `expo-image` or the `expo` meta-package yourself.
16
+
17
+ ## Usage
18
+
19
+ ```tsx
20
+ import { Image, useImage } from '@symbiote-native/image/react';
21
+
22
+ <Image
23
+ source="https://picsum.photos/400"
24
+ placeholder="blurhash:/L6PZfSi_.AyE_3t7t7R**0o#DgR4"
25
+ contentFit="cover"
26
+ transition={200}
27
+ style={{ width: 200, height: 200 }}
28
+ />;
29
+ ```
30
+
31
+ The view takes the props of upstream's `Image`. The handle has `startAnimating`, `stopAnimating`,
32
+ `lockResourceAsync`, `unlockResourceAsync` and `reloadAsync`, reached by a `ref` in React and Solid
33
+ (`ref={fn}`), a template ref in Vue, `bind:this` in Svelte and a `@ViewChild` in Angular.
34
+
35
+ `ImageBackground` is a view with an image filling it. `useImage` (`injectImage` in Angular) loads
36
+ a native `ImageRef` and releases it with its owner. The static functions of upstream's `Image` are
37
+ plain functions: `prefetchImages`, `clearMemoryCache`, `clearDiskCache`, `getCachePathAsync`,
38
+ `writeToCacheAsync`, `readFromCacheAsync`, `configureCache`, `generateBlurhashAsync`,
39
+ `generateThumbhashAsync`, `loadImageAsync`.
40
+
41
+ Angular selectors are `ExpoImage` and `ExpoImageBackground`, `Image` and `ImageBackground` are
42
+ tags of the adapter.
43
+
44
+ ## Not ported
45
+
46
+ The web build, upstream's RSC snapshot tests and the `expo-observe` integration.
@@ -0,0 +1,20 @@
1
+ import { NativeViewBase } from '@symbiote-native/angular';
2
+ import type { IDescriptor } from '@symbiote-native/components';
3
+ import type { IStyleProp } from '@symbiote-native/engine';
4
+ import type { IImageStyle, IImageViewProps } from '../../core';
5
+ declare const IMAGE_INPUTS: readonly ["source", "placeholder", "contentFit", "placeholderContentFit", "contentPosition", "transition", "blurRadius", "tintColor", "priority", "loading", "cachePolicy", "responsivePolicy", "recyclingKey", "autoplay", "sfEffect", "onLoadStart", "onLoad", "onProgress", "onError", "onLoadEnd", "onDisplay", "defaultSource", "loadingIndicatorSource", "resizeMode", "fadeDuration", "alt", "enableLiveTextInteraction", "allowDownscaling", "decodeFormat", "useAppleWebpCodec", "enforceEarlyResizing", "preferHighDynamicRange", "draggable"];
6
+ export interface ExpoImage extends Partial<Pick<IImageViewProps, (typeof IMAGE_INPUTS)[number]>> {
7
+ }
8
+ /** Angular twin of `expo-image`'s `Image`, `Image` itself is a primitive tag of the adapter */
9
+ export declare class ExpoImage extends NativeViewBase {
10
+ style?: IStyleProp<IImageStyle>;
11
+ private readonly view;
12
+ readonly startAnimating: () => Promise<void>;
13
+ readonly stopAnimating: () => Promise<void>;
14
+ readonly lockResourceAsync: () => Promise<void>;
15
+ readonly unlockResourceAsync: () => Promise<void>;
16
+ readonly reloadAsync: () => Promise<void>;
17
+ protected readonly propNames: readonly ["source", "placeholder", "contentFit", "placeholderContentFit", "contentPosition", "transition", "blurRadius", "tintColor", "priority", "loading", "cachePolicy", "responsivePolicy", "recyclingKey", "autoplay", "sfEffect", "onLoadStart", "onLoad", "onProgress", "onError", "onLoadEnd", "onDisplay", "defaultSource", "loadingIndicatorSource", "resizeMode", "fadeDuration", "alt", "enableLiveTextInteraction", "allowDownscaling", "decodeFormat", "useAppleWebpCodec", "enforceEarlyResizing", "preferHighDynamicRange", "draggable"];
18
+ protected renderView(props: object): IDescriptor | null;
19
+ }
20
+ export {};
@@ -0,0 +1,113 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
36
+ import { DescriptorOutlet, NativeViewBase } from '@symbiote-native/angular';
37
+ import { createImageView } from '../../core/index.js';
38
+ // One list is the inputs of the component and the props it hands to the native view
39
+ const IMAGE_INPUTS = [
40
+ 'source',
41
+ 'placeholder',
42
+ 'contentFit',
43
+ 'placeholderContentFit',
44
+ 'contentPosition',
45
+ 'transition',
46
+ 'blurRadius',
47
+ 'tintColor',
48
+ 'priority',
49
+ 'loading',
50
+ 'cachePolicy',
51
+ 'responsivePolicy',
52
+ 'recyclingKey',
53
+ 'autoplay',
54
+ 'sfEffect',
55
+ 'onLoadStart',
56
+ 'onLoad',
57
+ 'onProgress',
58
+ 'onError',
59
+ 'onLoadEnd',
60
+ 'onDisplay',
61
+ 'defaultSource',
62
+ 'loadingIndicatorSource',
63
+ 'resizeMode',
64
+ 'fadeDuration',
65
+ 'alt',
66
+ 'enableLiveTextInteraction',
67
+ 'allowDownscaling',
68
+ 'decodeFormat',
69
+ 'useAppleWebpCodec',
70
+ 'enforceEarlyResizing',
71
+ 'preferHighDynamicRange',
72
+ 'draggable',
73
+ ];
74
+ /** Angular twin of `expo-image`'s `Image`, `Image` itself is a primitive tag of the adapter */
75
+ let ExpoImage = (() => {
76
+ let _classDecorators = [Component({
77
+ selector: 'ExpoImage',
78
+ standalone: true,
79
+ imports: [DescriptorOutlet],
80
+ inputs: [...IMAGE_INPUTS],
81
+ changeDetection: ChangeDetectionStrategy.OnPush,
82
+ template: `@if (descriptor; as node) {
83
+ <symbiote-descriptor-outlet [node]="node" />
84
+ }`,
85
+ })];
86
+ let _classDescriptor;
87
+ let _classExtraInitializers = [];
88
+ let _classThis;
89
+ let _classSuper = NativeViewBase;
90
+ var ExpoImage = class extends _classSuper {
91
+ static { _classThis = this; }
92
+ static {
93
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
94
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
95
+ ExpoImage = _classThis = _classDescriptor.value;
96
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
97
+ __runInitializers(_classThis, _classExtraInitializers);
98
+ }
99
+ view = createImageView(() => this.hostNode());
100
+ // The functions of the view are members of the component, a `@ViewChild` reaches them
101
+ startAnimating = this.view.handle.startAnimating;
102
+ stopAnimating = this.view.handle.stopAnimating;
103
+ lockResourceAsync = this.view.handle.lockResourceAsync;
104
+ unlockResourceAsync = this.view.handle.unlockResourceAsync;
105
+ reloadAsync = this.view.handle.reloadAsync;
106
+ propNames = IMAGE_INPUTS;
107
+ renderView(props) {
108
+ return this.view.render(props);
109
+ }
110
+ };
111
+ return ExpoImage = _classThis;
112
+ })();
113
+ export { ExpoImage };
@@ -0,0 +1,14 @@
1
+ import { NativeViewBase } from '@symbiote-native/angular';
2
+ import type { IDescriptor } from '@symbiote-native/components';
3
+ import type { IStyleProp } from '@symbiote-native/engine';
4
+ import type { IImageBackgroundProps, IImageStyle } from '../../core';
5
+ declare const IMAGE_BACKGROUND_INPUTS: readonly ["source", "placeholder", "contentFit", "placeholderContentFit", "contentPosition", "transition", "blurRadius", "tintColor", "priority", "loading", "cachePolicy", "responsivePolicy", "recyclingKey", "autoplay", "sfEffect", "onLoadStart", "onLoad", "onProgress", "onError", "onLoadEnd", "onDisplay", "defaultSource", "loadingIndicatorSource", "resizeMode", "fadeDuration", "alt", "enableLiveTextInteraction", "allowDownscaling", "decodeFormat", "useAppleWebpCodec", "enforceEarlyResizing", "preferHighDynamicRange", "draggable", "imageStyle"];
6
+ export interface ExpoImageBackground extends Partial<Pick<IImageBackgroundProps, (typeof IMAGE_BACKGROUND_INPUTS)[number]>> {
7
+ }
8
+ /** Angular twin of `expo-image`'s `ImageBackground`, the content paints over the image */
9
+ export declare class ExpoImageBackground extends NativeViewBase {
10
+ style?: IStyleProp<IImageStyle>;
11
+ protected readonly propNames: readonly ["source", "placeholder", "contentFit", "placeholderContentFit", "contentPosition", "transition", "blurRadius", "tintColor", "priority", "loading", "cachePolicy", "responsivePolicy", "recyclingKey", "autoplay", "sfEffect", "onLoadStart", "onLoad", "onProgress", "onError", "onLoadEnd", "onDisplay", "defaultSource", "loadingIndicatorSource", "resizeMode", "fadeDuration", "alt", "enableLiveTextInteraction", "allowDownscaling", "decodeFormat", "useAppleWebpCodec", "enforceEarlyResizing", "preferHighDynamicRange", "draggable", "imageStyle"];
12
+ protected renderView(props: object): IDescriptor;
13
+ }
14
+ export {};
@@ -0,0 +1,107 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
36
+ import { DescriptorHost, NativeViewBase } from '@symbiote-native/angular';
37
+ import { renderImageBackground } from '../../core/index.js';
38
+ // One list is the inputs of the component and the props it hands to the image
39
+ const IMAGE_BACKGROUND_INPUTS = [
40
+ 'source',
41
+ 'placeholder',
42
+ 'contentFit',
43
+ 'placeholderContentFit',
44
+ 'contentPosition',
45
+ 'transition',
46
+ 'blurRadius',
47
+ 'tintColor',
48
+ 'priority',
49
+ 'loading',
50
+ 'cachePolicy',
51
+ 'responsivePolicy',
52
+ 'recyclingKey',
53
+ 'autoplay',
54
+ 'sfEffect',
55
+ 'onLoadStart',
56
+ 'onLoad',
57
+ 'onProgress',
58
+ 'onError',
59
+ 'onLoadEnd',
60
+ 'onDisplay',
61
+ 'defaultSource',
62
+ 'loadingIndicatorSource',
63
+ 'resizeMode',
64
+ 'fadeDuration',
65
+ 'alt',
66
+ 'enableLiveTextInteraction',
67
+ 'allowDownscaling',
68
+ 'decodeFormat',
69
+ 'useAppleWebpCodec',
70
+ 'enforceEarlyResizing',
71
+ 'preferHighDynamicRange',
72
+ 'draggable',
73
+ 'imageStyle',
74
+ ];
75
+ /** Angular twin of `expo-image`'s `ImageBackground`, the content paints over the image */
76
+ let ExpoImageBackground = (() => {
77
+ let _classDecorators = [Component({
78
+ selector: 'ExpoImageBackground',
79
+ standalone: true,
80
+ imports: [DescriptorHost],
81
+ inputs: [...IMAGE_BACKGROUND_INPUTS],
82
+ changeDetection: ChangeDetectionStrategy.OnPush,
83
+ template: `@if (descriptor; as node) {
84
+ <symbiote-descriptor-host [node]="node"><ng-content /></symbiote-descriptor-host>
85
+ }`,
86
+ })];
87
+ let _classDescriptor;
88
+ let _classExtraInitializers = [];
89
+ let _classThis;
90
+ let _classSuper = NativeViewBase;
91
+ var ExpoImageBackground = class extends _classSuper {
92
+ static { _classThis = this; }
93
+ static {
94
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
95
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
96
+ ExpoImageBackground = _classThis = _classDescriptor.value;
97
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
98
+ __runInitializers(_classThis, _classExtraInitializers);
99
+ }
100
+ propNames = IMAGE_BACKGROUND_INPUTS;
101
+ renderView(props) {
102
+ return renderImageBackground(props);
103
+ }
104
+ };
105
+ return ExpoImageBackground = _classThis;
106
+ })();
107
+ export { ExpoImageBackground };
@@ -0,0 +1,4 @@
1
+ export { ExpoImage } from './image';
2
+ export { ExpoImageBackground } from './image-background';
3
+ export { injectImage } from './inject-image';
4
+ export * from '../core';
@@ -0,0 +1,6 @@
1
+ // @symbiote-native/image/angular: the image view, its cache and hash functions on a shared core
2
+ // The selectors are `ExpoImage*`, `Image` and `ImageBackground` are tags of the adapter
3
+ export { ExpoImage } from './image/index.js';
4
+ export { ExpoImageBackground } from './image-background/index.js';
5
+ export { injectImage } from './inject-image.js';
6
+ export * from '../core/index.js';
@@ -0,0 +1,8 @@
1
+ import type { Signal } from '@angular/core';
2
+ import type { IImageLoadOptions, IImageSource, ImageRef } from '../core';
3
+ /**
4
+ * Angular twin of `useImage`, `null` until the first image has loaded
5
+ *
6
+ * It loads again when the uri of the source or a signal read by `dependencies` changes
7
+ */
8
+ export declare function injectImage(source: () => IImageSource | string | number, options?: () => IImageLoadOptions, dependencies?: () => unknown[]): Signal<ImageRef | null>;
@@ -0,0 +1,20 @@
1
+ import { computed, effect, signal, untracked } from '@angular/core';
2
+ import { createImageRefLoader, resolveSource } from '../core/index.js';
3
+ /**
4
+ * Angular twin of `useImage`, `null` until the first image has loaded
5
+ *
6
+ * It loads again when the uri of the source or a signal read by `dependencies` changes
7
+ */
8
+ export function injectImage(source, options = () => ({}), dependencies = () => []) {
9
+ const image = signal(null);
10
+ const key = computed(() => resolveSource(source())?.uri);
11
+ effect(onCleanup => {
12
+ // Read so the effect tracks them, the source itself is not a trigger
13
+ key();
14
+ dependencies();
15
+ const loader = createImageRefLoader(loaded => image.set(loaded), options);
16
+ untracked(() => loader.load(source()));
17
+ onCleanup(loader.cancel);
18
+ });
19
+ return image.asReadonly();
20
+ }
@@ -0,0 +1,3 @@
1
+ export declare const IMAGE_MODULE_NAME = "ExpoImage";
2
+ /** `sf:name` in a source string, the native side reads `sf:/name` */
3
+ export declare const SF_SYMBOL_PREFIX = "sf:";
@@ -0,0 +1,4 @@
1
+ // Apart from `native-module`, which resolves the module on import: the view needs the name too
2
+ export const IMAGE_MODULE_NAME = 'ExpoImage';
3
+ /** `sf:name` in a source string, the native side reads `sf:/name` */
4
+ export const SF_SYMBOL_PREFIX = 'sf:';
@@ -0,0 +1,5 @@
1
+ import type { IImageSource } from './types';
2
+ /** `blurhash:/<hash>/<width>/<height>` or `<hash>/<width>/<height>` */
3
+ export declare function resolveBlurhashString(str: string): IImageSource;
4
+ /** `thumbhash:/<hash>` or `<hash>` */
5
+ export declare function resolveThumbhashString(str: string): IImageSource;
@@ -0,0 +1,20 @@
1
+ const DEFAULT_HASH_SIZE = 16;
2
+ function hashToUri(type, hash) {
3
+ const encoded = encodeURI(hash).replace(/#/g, '%23').replace(/\?/g, '%3F');
4
+ return `${type}:/${encoded}`;
5
+ }
6
+ /** `blurhash:/<hash>/<width>/<height>` or `<hash>/<width>/<height>` */
7
+ export function resolveBlurhashString(str) {
8
+ const [hash = '', width = '', height = ''] = str.replace(/^blurhash:\//, '').split('/');
9
+ return {
10
+ uri: hashToUri('blurhash', hash),
11
+ width: parseInt(width, 10) || DEFAULT_HASH_SIZE,
12
+ height: parseInt(height, 10) || DEFAULT_HASH_SIZE,
13
+ };
14
+ }
15
+ /** `thumbhash:/<hash>` or `<hash>` */
16
+ export function resolveThumbhashString(str) {
17
+ // A slash at the start of the hash would break the uri path
18
+ const hash = str.replace(/^thumbhash:\//, '').replace(/\//g, '\\');
19
+ return { uri: hashToUri('thumbhash', hash) };
20
+ }
@@ -0,0 +1,18 @@
1
+ import type { IImageCacheConfig, IImageLoadOptions, IImagePrefetchOptions, IImageSource, ImageRef } from './types';
2
+ type IPrefetchOptions = IImagePrefetchOptions['cachePolicy'] | IImagePrefetchOptions;
3
+ /** The caching policy as a string, or with request headers as an object */
4
+ export declare function prefetchImages(urls: string | string[], options?: IPrefetchOptions): Promise<boolean>;
5
+ export declare const clearMemoryCache: () => Promise<boolean>;
6
+ export declare const clearDiskCache: () => Promise<boolean>;
7
+ export declare const getCachePathAsync: (cacheKey: string) => Promise<string | null>;
8
+ export declare const writeToCacheAsync: (source: string | ImageRef, cacheKey: string) => Promise<void>;
9
+ export declare const readFromCacheAsync: (cacheKey: string) => Promise<ImageRef | null>;
10
+ export declare const configureCache: (config: IImageCacheConfig) => void;
11
+ export declare const generateBlurhashAsync: (source: string | ImageRef, numberOfComponents: [number, number] | {
12
+ width: number;
13
+ height: number;
14
+ }) => Promise<string | null>;
15
+ export declare const generateThumbhashAsync: (source: string | ImageRef) => Promise<string>;
16
+ /** Loads the image into a native `ImageRef`, the caller releases it */
17
+ export declare function loadImageAsync(source: IImageSource | string | number, options?: IImageLoadOptions): Promise<ImageRef>;
18
+ export {};
@@ -0,0 +1,30 @@
1
+ import { toNativeColor } from './native-style.js';
2
+ import { expoImage } from './native-module.js';
3
+ import { resolveSource } from './sources.js';
4
+ const DEFAULT_CACHE_POLICY = 'memory-disk';
5
+ /** The caching policy as a string, or with request headers as an object */
6
+ export function prefetchImages(urls, options) {
7
+ const { cachePolicy = DEFAULT_CACHE_POLICY, headers } = typeof options === 'string' ? { cachePolicy: options } : (options ?? {});
8
+ return expoImage.prefetch(Array.isArray(urls) ? urls : [urls], cachePolicy, headers);
9
+ }
10
+ export const clearMemoryCache = () => expoImage.clearMemoryCache();
11
+ export const clearDiskCache = () => expoImage.clearDiskCache();
12
+ export const getCachePathAsync = (cacheKey) => expoImage.getCachePathAsync(cacheKey);
13
+ export const writeToCacheAsync = (source, cacheKey) => expoImage.writeToCacheAsync(source, cacheKey);
14
+ export const readFromCacheAsync = (cacheKey) => expoImage.readFromCacheAsync(cacheKey);
15
+ export const configureCache = (config) => expoImage.configureCache(config);
16
+ export const generateBlurhashAsync = (source, numberOfComponents) => expoImage.generateBlurhashAsync(source, numberOfComponents);
17
+ export const generateThumbhashAsync = (source) => expoImage.generateThumbhashAsync(source);
18
+ /** Loads the image into a native `ImageRef`, the caller releases it */
19
+ export async function loadImageAsync(source, options = {}) {
20
+ const resolved = resolveSource(source);
21
+ if (!resolved) {
22
+ throw new Error('[expo-image]: the image source could not be resolved');
23
+ }
24
+ const { maxWidth, maxHeight, tintColor } = options;
25
+ return expoImage.loadAsync(resolved, {
26
+ maxWidth,
27
+ maxHeight,
28
+ tintColor: toNativeColor(tintColor),
29
+ });
30
+ }
@@ -0,0 +1,11 @@
1
+ import type { IDescriptor } from '@symbiote-native/components';
2
+ import type { IStyleProp } from '@symbiote-native/engine';
3
+ import type { IImageStyle, IImageViewProps } from './types';
4
+ export type IImageBackgroundProps = IImageViewProps & {
5
+ /** Style of the image itself, `style` goes to the wrapping view */
6
+ imageStyle?: IStyleProp<IImageStyle>;
7
+ /** Goes to the wrapping view, as `style` does */
8
+ className?: string;
9
+ };
10
+ /** A view with an image filling it, the children of the app go after this descriptor */
11
+ export declare function renderImageBackground(props: IImageBackgroundProps): IDescriptor;
@@ -0,0 +1,26 @@
1
+ import { el } from '@symbiote-native/components';
2
+ import { createImageView } from './image-view.js';
3
+ const CLASS_KEYS = new Set(['className', 'class']);
4
+ function isClassEntry([key]) {
5
+ return CLASS_KEYS.has(key);
6
+ }
7
+ const ABSOLUTE_FILL = {
8
+ position: 'absolute',
9
+ left: 0,
10
+ right: 0,
11
+ top: 0,
12
+ bottom: 0,
13
+ };
14
+ // The background image has no handle, so nobody asks it for a host node
15
+ const backgroundImage = createImageView(() => null);
16
+ /** A view with an image filling it, the children of the app go after this descriptor */
17
+ export function renderImageBackground(props) {
18
+ const { style, imageStyle, ...rest } = props;
19
+ const entries = Object.entries(rest);
20
+ const image = backgroundImage.render({
21
+ ...Object.fromEntries(entries.filter(entry => !isClassEntry(entry))),
22
+ style: [ABSOLUTE_FILL, imageStyle],
23
+ });
24
+ const viewClass = Object.fromEntries(entries.filter(isClassEntry));
25
+ return el('view', { style, ...viewClass }, image ? [image] : []);
26
+ }
@@ -0,0 +1,20 @@
1
+ import type { IImageLoadOptions, IImageSource } from './types';
2
+ export type IImageLoaderConfig<TImage extends {
3
+ release(): void;
4
+ }> = {
5
+ load(source: IImageSource | string | number, options: IImageLoadOptions): Promise<TImage>;
6
+ /** Called with each image that finished loading */
7
+ onImage(image: TImage): void;
8
+ /** Read at the time of a failure, so the latest callback wins */
9
+ getOptions(): IImageLoadOptions;
10
+ };
11
+ export type IImageLoader = {
12
+ /** Starts a load and drops the result of the one before */
13
+ load(source: IImageSource | string | number): void;
14
+ /** Drops a load in flight and releases the image loaded last */
15
+ cancel(): void;
16
+ };
17
+ /** What a `useImage` hook drives with its own lifecycle */
18
+ export declare function createImageLoader<TImage extends {
19
+ release(): void;
20
+ }>(config: IImageLoaderConfig<TImage>): IImageLoader;
@@ -0,0 +1,42 @@
1
+ /** What a `useImage` hook drives with its own lifecycle */
2
+ export function createImageLoader(config) {
3
+ let token = 0;
4
+ let loaded = null;
5
+ function release() {
6
+ loaded?.release();
7
+ loaded = null;
8
+ }
9
+ function fail(source, error, retry) {
10
+ const { onError } = config.getOptions();
11
+ if (onError) {
12
+ onError(error instanceof Error ? error : new Error(String(error)), retry);
13
+ return;
14
+ }
15
+ console.error(`Loading an image from '${JSON.stringify(source)}' failed, use 'onError' to handle errors`);
16
+ console.error(error);
17
+ }
18
+ function load(source) {
19
+ release();
20
+ token += 1;
21
+ const mine = token;
22
+ config
23
+ .load(source, config.getOptions())
24
+ .then(image => {
25
+ if (mine !== token)
26
+ return;
27
+ loaded = image;
28
+ config.onImage(image);
29
+ })
30
+ .catch((error) => {
31
+ if (mine === token)
32
+ fail(source, error, () => load(source));
33
+ });
34
+ }
35
+ return {
36
+ load,
37
+ cancel: () => {
38
+ token += 1;
39
+ release();
40
+ },
41
+ };
42
+ }
@@ -0,0 +1,4 @@
1
+ import type { IImageLoader } from './image-loader';
2
+ import type { IImageLoadOptions, ImageRef } from './types';
3
+ /** The loader every `useImage` hook drives, `getOptions` is read at each load and failure */
4
+ export declare function createImageRefLoader(onImage: (image: ImageRef) => void, getOptions: () => IImageLoadOptions): IImageLoader;
@@ -0,0 +1,10 @@
1
+ import { loadImageAsync } from './image-api.js';
2
+ import { createImageLoader } from './image-loader.js';
3
+ /** The loader every `useImage` hook drives, `getOptions` is read at each load and failure */
4
+ export function createImageRefLoader(onImage, getOptions) {
5
+ return createImageLoader({
6
+ load: loadImageAsync,
7
+ onImage,
8
+ getOptions,
9
+ });
10
+ }
@@ -0,0 +1,9 @@
1
+ import type { ISymbioteEvent } from '@symbiote-native/engine';
2
+ import type { IImageViewProps } from './types';
3
+ /** The native payload sits in `nativeEvent`, callbacks get it as the event itself */
4
+ export declare function toImageEvents(props: IImageViewProps): {
5
+ onLoadStart: () => void | undefined;
6
+ onLoad: (event: ISymbioteEvent) => void;
7
+ onProgress: (event: ISymbioteEvent) => void;
8
+ onError: (event: ISymbioteEvent) => void;
9
+ };
@@ -0,0 +1,32 @@
1
+ function field(value, key) {
2
+ return Reflect.get(Object(value), key);
3
+ }
4
+ function isLoad(value) {
5
+ return typeof field(value, 'cacheType') === 'string' && typeof field(value, 'source') === 'object';
6
+ }
7
+ function isProgress(value) {
8
+ return typeof field(value, 'loaded') === 'number' && typeof field(value, 'total') === 'number';
9
+ }
10
+ function isError(value) {
11
+ return typeof field(value, 'error') === 'string';
12
+ }
13
+ /** The native payload sits in `nativeEvent`, callbacks get it as the event itself */
14
+ export function toImageEvents(props) {
15
+ return {
16
+ onLoadStart: () => props.onLoadStart?.(),
17
+ onLoad: (event) => {
18
+ if (isLoad(event.nativeEvent))
19
+ props.onLoad?.(event.nativeEvent);
20
+ props.onLoadEnd?.();
21
+ },
22
+ onProgress: (event) => {
23
+ if (isProgress(event.nativeEvent))
24
+ props.onProgress?.(event.nativeEvent);
25
+ },
26
+ onError: (event) => {
27
+ if (isError(event.nativeEvent))
28
+ props.onError?.(event.nativeEvent);
29
+ props.onLoadEnd?.();
30
+ },
31
+ };
32
+ }
@@ -0,0 +1,14 @@
1
+ import type { IDescriptor } from '@symbiote-native/components';
2
+ import type { ISymbioteNode } from '@symbiote-native/engine';
3
+ import type { IImageViewHandle, IImageViewProps } from './types';
4
+ export declare const imageViewName: () => string;
5
+ export declare const ensureImageViewRegistered: () => boolean;
6
+ export type IImageView = {
7
+ handle: IImageViewHandle;
8
+ /** `null` means the view cannot register and the caller renders nothing */
9
+ render(props: IImageViewProps): IDescriptor | null;
10
+ };
11
+ type IGetNode = () => ISymbioteNode | null | undefined;
12
+ /** One per mounted view, `getNode` answers the host node once it exists */
13
+ export declare function createImageView(getNode: IGetNode): IImageView;
14
+ export {};