@symbiote-native/linear-gradient 0.0.0-stage → 0.1.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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +196 -2
  3. package/build/angular/index.d.ts +2 -0
  4. package/build/angular/index.js +3 -0
  5. package/build/angular/linear-gradient/index.android.d.ts +4 -0
  6. package/build/angular/linear-gradient/index.android.js +67 -0
  7. package/build/angular/linear-gradient/index.d.ts +1 -0
  8. package/build/angular/linear-gradient/index.ios.d.ts +5 -0
  9. package/build/angular/linear-gradient/index.ios.js +76 -0
  10. package/build/angular/linear-gradient/index.js +2 -0
  11. package/build/angular/linear-gradient/shared.d.ts +12 -0
  12. package/build/angular/linear-gradient/shared.js +97 -0
  13. package/build/core/index.d.ts +1 -0
  14. package/build/core/index.js +1 -0
  15. package/build/core/linear-gradient.d.ts +33 -0
  16. package/build/core/linear-gradient.js +105 -0
  17. package/build/react/index.d.ts +3 -0
  18. package/build/react/index.js +3 -0
  19. package/build/react/linear-gradient.d.ts +9 -0
  20. package/build/react/linear-gradient.js +7 -0
  21. package/build/solid/index.d.ts +3 -0
  22. package/build/solid/index.js +3 -0
  23. package/build/solid/linear-gradient.d.ts +10 -0
  24. package/build/solid/linear-gradient.js +4 -0
  25. package/build/svelte/index.d.ts +3 -0
  26. package/build/svelte/index.js +3 -0
  27. package/build/svelte/linear-gradient-props.d.ts +6 -0
  28. package/build/svelte/linear-gradient-props.js +1 -0
  29. package/build/svelte/linear-gradient.svelte +10 -0
  30. package/build/svelte/linear-gradient.svelte.d.ts +4 -0
  31. package/build/vue/index.d.ts +3 -0
  32. package/build/vue/index.js +3 -0
  33. package/build/vue/linear-gradient.d.ts +6 -0
  34. package/build/vue/linear-gradient.js +16 -0
  35. package/build-ngc/angular/index.d.ts +2 -0
  36. package/build-ngc/angular/index.js +4 -0
  37. package/build-ngc/angular/index.js.map +1 -0
  38. package/build-ngc/angular/linear-gradient/index.android.d.ts +7 -0
  39. package/build-ngc/angular/linear-gradient/index.android.js +28 -0
  40. package/build-ngc/angular/linear-gradient/index.android.js.map +1 -0
  41. package/build-ngc/angular/linear-gradient/index.d.ts +1 -0
  42. package/build-ngc/angular/linear-gradient/index.ios.d.ts +8 -0
  43. package/build-ngc/angular/linear-gradient/index.ios.js +37 -0
  44. package/build-ngc/angular/linear-gradient/index.ios.js.map +1 -0
  45. package/build-ngc/angular/linear-gradient/index.js +3 -0
  46. package/build-ngc/angular/linear-gradient/index.js.map +1 -0
  47. package/build-ngc/angular/linear-gradient/shared.d.ts +15 -0
  48. package/build-ngc/angular/linear-gradient/shared.js +39 -0
  49. package/build-ngc/angular/linear-gradient/shared.js.map +1 -0
  50. package/build-ngc/core/index.d.ts +1 -0
  51. package/build-ngc/core/index.js +2 -0
  52. package/build-ngc/core/index.js.map +1 -0
  53. package/build-ngc/core/linear-gradient.d.ts +33 -0
  54. package/build-ngc/core/linear-gradient.js +106 -0
  55. package/build-ngc/core/linear-gradient.js.map +1 -0
  56. package/native-link.json +12 -0
  57. package/package.json +145 -3
  58. package/src/angular/index.ts +10 -0
  59. package/src/angular/linear-gradient/index.android.ts +17 -0
  60. package/src/angular/linear-gradient/index.ios.ts +32 -0
  61. package/src/angular/linear-gradient/index.ts +2 -0
  62. package/src/angular/linear-gradient/shared.ts +27 -0
  63. package/src/core/index.ts +10 -0
  64. package/src/core/linear-gradient.ts +183 -0
  65. package/src/react/index.ts +10 -0
  66. package/src/react/linear-gradient.ts +18 -0
  67. package/src/solid/index.ts +10 -0
  68. package/src/solid/linear-gradient.ts +17 -0
  69. package/src/svelte/index.ts +10 -0
  70. package/src/svelte/linear-gradient-props.ts +9 -0
  71. package/src/svelte/linear-gradient.svelte +10 -0
  72. package/src/svelte/svelte-ambient.d.ts +6 -0
  73. package/src/svelte/svelte-compile.test-helper.ts +6 -0
  74. package/src/vue/index.ts +10 -0
  75. package/src/vue/linear-gradient.ts +23 -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,197 @@
1
- # Temporary Holding Version
1
+ # @symbiote-native/linear-gradient
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-linear-gradient`](https://docs.expo.dev/versions/latest/sdk/linear-gradient/) for **every**
4
+ [SymbioteNative](../../README.md) adapter: React, Vue, Svelte, Solid and Angular. A native view that
5
+ paints a multi-color gradient behind its children.
6
+
7
+ Built the same way as [`@symbiote-native/clipboard`](../clipboard)'s `ClipboardPasteButton`, an
8
+ `expo-modules-core` native view reached through `requireNativeViewManager` (see the
9
+ `symbiote-expo-native-module` project skill: why `expo-modules-core` is depended on directly and never
10
+ the `expo` meta-package, why the upstream JS is hand-ported into `core/`, and how autolinking finds the
11
+ native module). One render function in `core/` returns a descriptor and every adapter only turns it into
12
+ its own element.
13
+
14
+ ## Install
15
+
16
+ **New app:**
17
+
18
+ ```bash
19
+ npx @symbiote-native/cli new my-app --linear-gradient
20
+ ```
21
+
22
+ **Existing SymbioteNative app:**
23
+
24
+ ```bash
25
+ npx @symbiote-native/cli add --linear-gradient
26
+ ```
27
+
28
+ Either way: installs `@symbiote-native/linear-gradient` and wires the native autolinking automatically,
29
+ see [`@symbiote-native/cli`](../cli).
30
+
31
+ <details>
32
+ <summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
33
+
34
+ ```bash
35
+ npm install @symbiote-native/linear-gradient
36
+ ```
37
+
38
+ Depends on `expo-linear-gradient` and `expo-modules-core` directly (regular dependencies, pinned to exact
39
+ versions, since this package's `core/` is hand-ported against one specific native API shape).
40
+ Never install `expo-linear-gradient` yourself, and never add the `expo` package to this project: it bundles
41
+ its own Metro/Babel pipeline that conflicts with this project's own.
42
+
43
+ ### Required one-time step: native autolinking wiring
44
+
45
+ `expo-linear-gradient`'s native code is discovered by `expo-modules-autolinking`, not by RN's own
46
+ `react-native.config.cjs` mechanism. Wire it into the native host app **once**, it covers this package
47
+ and every other `expo-modules-core` package with zero further changes. The steps and the
48
+ mechanics behind them are in the `symbiote-expo-native-module` skill and in
49
+ [`@symbiote-native/network`](../network)'s README; the reference app is `examples/expo-react`.
50
+
51
+ </details>
52
+
53
+ ## Shape
54
+
55
+ ```
56
+ src/core/ linear-gradient.ts - props, point normalization, the locations check and
57
+ renderLinearGradient (iOS: the native view is the root, Android: a View that
58
+ carries style and children over an absolute-fill native leaf).
59
+ src/react/ @symbiote-native/linear-gradient/react - LinearGradient
60
+ src/vue/ @symbiote-native/linear-gradient/vue - LinearGradient
61
+ src/svelte/ @symbiote-native/linear-gradient/svelte - LinearGradient (.svelte)
62
+ src/solid/ @symbiote-native/linear-gradient/solid - LinearGradient
63
+ src/angular/linear-gradient/ @symbiote-native/linear-gradient/angular - LinearGradient
64
+ ```
65
+
66
+ Angular cannot spell a dynamic tag name or project into a descriptor-built node, so it keeps one
67
+ component per platform (`index.ios.ts` / `index.android.ts`) with the same inputs.
68
+
69
+ ## Use it
70
+
71
+ ### React
72
+
73
+ ```tsx
74
+ import { LinearGradient } from '@symbiote-native/linear-gradient/react';
75
+
76
+ <LinearGradient colors={['#4c669f', '#192f6a']} start={{ x: 0, y: 0 }} end={[1, 1]}>
77
+ <Text>Over the gradient</Text>
78
+ </LinearGradient>;
79
+ ```
80
+
81
+ ### Vue
82
+
83
+ ```vue
84
+ <script setup lang="ts">
85
+ import { LinearGradient } from '@symbiote-native/linear-gradient/vue';
86
+ </script>
87
+
88
+ <template>
89
+ <LinearGradient :colors="['#4c669f', '#192f6a']" :end="[1, 1]">
90
+ <text>Over the gradient</text>
91
+ </LinearGradient>
92
+ </template>
93
+ ```
94
+
95
+ ### Angular
96
+
97
+ ```ts
98
+ import { LinearGradient } from '@symbiote-native/linear-gradient/angular';
99
+
100
+ @Component({
101
+ imports: [LinearGradient],
102
+ template: `<LinearGradient [colors]="colors" [end]="[1, 1]"><text>Over the gradient</text></LinearGradient>`,
103
+ })
104
+ export class Card {
105
+ readonly colors = ['#4c669f', '#192f6a'];
106
+ }
107
+ ```
108
+
109
+ ### Svelte
110
+
111
+ ```svelte
112
+ <script lang="ts">
113
+ import { LinearGradient } from '@symbiote-native/linear-gradient/svelte';
114
+ </script>
115
+
116
+ <LinearGradient colors={['#4c669f', '#192f6a']} end={[1, 1]}>
117
+ <text>Over the gradient</text>
118
+ </LinearGradient>
119
+ ```
120
+
121
+ ### Solid
122
+
123
+ ```tsx
124
+ import { LinearGradient } from '@symbiote-native/linear-gradient/solid';
125
+
126
+ <LinearGradient colors={['#4c669f', '#192f6a']} end={[1, 1]}>
127
+ <text>Over the gradient</text>
128
+ </LinearGradient>;
129
+ ```
130
+
131
+ ## API
132
+
133
+ ```ts
134
+ <LinearGradient
135
+ colors={[ColorValue, ColorValue, ...ColorValue[]]} // two or more, one color is style.backgroundColor
136
+ locations?={number[]} // 0..1 ascending, same length as colors
137
+ start?={{ x, y } | [x, y]} // default { x: 0.5, y: 0 }
138
+ end?={{ x, y } | [x, y]} // default { x: 0.5, y: 1 }
139
+ dither?={boolean} // Android only, default true
140
+ style? testID? nativeID? onLayout? ...View accessibility and responder props
141
+ />
142
+ ```
143
+
144
+ Plus `ILinearGradientProps` and `ILinearGradientPoint`, hand-ported from upstream's `LinearGradient.tsx`.
145
+ Children come through the framework's own mechanism (`children`, default slot, snippet,
146
+ `<ng-content>`).
147
+
148
+ ## Notes
149
+
150
+ - **Children go inside the gradient.** It paints behind them. On iOS the native gradient is the root
151
+ view. On Android the root is a plain `View` that carries your `style`, with the native gradient
152
+ filling it as a child, so `children`, `onLayout` and touches behave as on any `View`.
153
+ - **Rounded corners go in `style`.** `borderRadius` and the four per-corner radii are read from
154
+ `style` and handed to the native gradient, because React Native does not clip a native view by a
155
+ radius on its own.
156
+ - **A bad value warns instead of throwing.** A `start` or `end` that is not `[x, y]` or `{ x, y }` is
157
+ ignored, and a `locations` array of the wrong length is cut to the number of `colors`. Both log a
158
+ warning and the gradient still draws.
159
+ - **`colors` takes anything `style` colors take.** Names, hex, `rgb()`/`rgba()` and `hsl()` all go
160
+ through the same color processing as every other view.
161
+ - **Where the native view is missing, you get a plain `View`.** The package logs
162
+ `LinearGradient is not available on this platform` and renders your children without a gradient.
163
+ - **Not ported.** The web variant (`LinearGradient.web.tsx`) and `NativeLinearGradient` re-export: native only.
164
+
165
+ ## Common questions
166
+
167
+ - **`transparent` to a color looks gray on iOS.** Apple platforms fade `transparent` as transparent
168
+ black. Fade to the same color with alpha 0 instead: `['rgba(255, 255, 255, 0)', '#ffffff']`.
169
+ - **Different on iOS and Android.** Do not give `start` and `end` the same point, that case is
170
+ reported to render differently. Use distinct points, such as `[0, 0]` and `[1, 1]`.
171
+ - **Rounded corners.** Put `borderRadius` (or the four per-corner radii) in `style`. Android passes
172
+ the radii to the native gradient by hand.
173
+ - **Gradient text.** Needs `@react-native-masked-view/masked-view`, a third-party React component,
174
+ so React adapter only. No masked view ships for Vue, Svelte, Solid or Angular.
175
+ - **Animating colors.** `colors` is a plain prop, so drive it from state, a signal or a rune. For a
176
+ smooth fade, stack two gradients and animate the `opacity` of the top one.
177
+ - **Slow in a long list.** Draw one gradient behind the list instead of one per row, and try
178
+ `dither={false}` on Android.
179
+ - **One color.** `colors` needs two entries, use `style.backgroundColor` for one.
180
+
181
+ Sources: [Expo docs: LinearGradient](https://docs.expo.dev/versions/latest/sdk/linear-gradient/),
182
+ [expo/expo#7418](https://github.com/expo/expo/issues/7418),
183
+ [expo/expo#25595](https://github.com/expo/expo/issues/25595),
184
+ [expo/expo#32934](https://github.com/expo/expo/issues/32934),
185
+ [expo/expo#29408](https://github.com/expo/expo/issues/29408),
186
+ [expo/expo#23861](https://github.com/expo/expo/issues/23861).
187
+
188
+ ## Test it
189
+
190
+ No device is needed for the logic. The core tests (`src/core/*.test.ts`) replace `expo-modules-core`
191
+ (`requireNativeViewManager`, `Platform`) and assert the descriptor a render function returns. The
192
+ adapter tests (`src/{react,vue,solid,angular}/**/*.test.*`, `vitest`) render the real component over the
193
+ recording Fabric with an injected view config and assert the committed payload, and Svelte's
194
+ `*.smoke.test.ts` compile the `.svelte` files and mount them. Painting itself is verified on a device in
195
+ the six `examples/expo-*` canary apps (`examples/expo-react`, `examples/expo-vue-sfc`,
196
+ `examples/expo-vue-tsx`, `examples/expo-svelte`, `examples/expo-solid`, `examples/expo-angular`), see the
197
+ parent [README](../../README.md).
@@ -0,0 +1,2 @@
1
+ export { LinearGradient } from './linear-gradient';
2
+ export { LINEAR_GRADIENT_MODULE_NAME, linearGradientViewName, type ILinearGradientProps, type ILinearGradientPoint, type INativeLinearGradientPoint, } from '../core';
@@ -0,0 +1,3 @@
1
+ // @symbiote-native/linear-gradient/angular: компонент `LinearGradient` поверх общего ядра
2
+ export { LinearGradient } from './linear-gradient';
3
+ export { LINEAR_GRADIENT_MODULE_NAME, linearGradientViewName, } from '../core/index.js';
@@ -0,0 +1,4 @@
1
+ import { LinearGradientBase } from './shared';
2
+ /** Angular twin of `expo-linear-gradient`'s `LinearGradient`, a View wraps the gradient leaf */
3
+ export declare class LinearGradient extends LinearGradientBase {
4
+ }
@@ -0,0 +1,67 @@
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 } from '@symbiote-native/angular';
37
+ import { LinearGradientBase } from './shared.js';
38
+ /** Angular twin of `expo-linear-gradient`'s `LinearGradient`, a View wraps the gradient leaf */
39
+ let LinearGradient = (() => {
40
+ let _classDecorators = [Component({
41
+ selector: 'LinearGradient',
42
+ standalone: true,
43
+ imports: [DescriptorHost],
44
+ changeDetection: ChangeDetectionStrategy.OnPush,
45
+ template: `@if (descriptor; as node) {
46
+ <symbiote-descriptor-host [node]="node"
47
+ ><ng-content
48
+ /></symbiote-descriptor-host>
49
+ }`,
50
+ })];
51
+ let _classDescriptor;
52
+ let _classExtraInitializers = [];
53
+ let _classThis;
54
+ let _classSuper = LinearGradientBase;
55
+ var LinearGradient = class extends _classSuper {
56
+ static { _classThis = this; }
57
+ static {
58
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
59
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
60
+ LinearGradient = _classThis = _classDescriptor.value;
61
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
62
+ __runInitializers(_classThis, _classExtraInitializers);
63
+ }
64
+ };
65
+ return LinearGradient = _classThis;
66
+ })();
67
+ export { LinearGradient };
@@ -0,0 +1 @@
1
+ export { LinearGradient } from './index.ios';
@@ -0,0 +1,5 @@
1
+ import { LinearGradientBase } from './shared';
2
+ /** Angular twin of `expo-linear-gradient`'s `LinearGradient`, children go inside the native view */
3
+ export declare class LinearGradient extends LinearGradientBase {
4
+ constructor();
5
+ }
@@ -0,0 +1,76 @@
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, NO_ERRORS_SCHEMA, } from '@angular/core';
36
+ import { SymbioteHostPropsDirective } from '@symbiote-native/angular';
37
+ import { warnIfViewNameIsDynamic } from '@symbiote-native/engine';
38
+ import { LINEAR_GRADIENT_MODULE_NAME } from '../../core/index.js';
39
+ import { LinearGradientBase } from './shared.js';
40
+ const TEMPLATE_TAG = 'ViewManagerAdapter_ExpoLinearGradient';
41
+ /** Angular twin of `expo-linear-gradient`'s `LinearGradient`, children go inside the native view */
42
+ let LinearGradient = (() => {
43
+ let _classDecorators = [Component({
44
+ selector: 'LinearGradient',
45
+ standalone: true,
46
+ imports: [SymbioteHostPropsDirective],
47
+ schemas: [NO_ERRORS_SCHEMA],
48
+ changeDetection: ChangeDetectionStrategy.OnPush,
49
+ // Имя тега это нативное имя view, его нельзя собрать динамически
50
+ template: `@if (descriptor; as node) {
51
+ <ViewManagerAdapter_ExpoLinearGradient [symbioteHostProps]="node.props">
52
+ <ng-content />
53
+ </ViewManagerAdapter_ExpoLinearGradient>
54
+ }`,
55
+ })];
56
+ let _classDescriptor;
57
+ let _classExtraInitializers = [];
58
+ let _classThis;
59
+ let _classSuper = LinearGradientBase;
60
+ var LinearGradient = class extends _classSuper {
61
+ static { _classThis = this; }
62
+ static {
63
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
64
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
65
+ LinearGradient = _classThis = _classDescriptor.value;
66
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
67
+ __runInitializers(_classThis, _classExtraInitializers);
68
+ }
69
+ constructor() {
70
+ super();
71
+ warnIfViewNameIsDynamic(TEMPLATE_TAG, LINEAR_GRADIENT_MODULE_NAME);
72
+ }
73
+ };
74
+ return LinearGradient = _classThis;
75
+ })();
76
+ export { LinearGradient };
@@ -0,0 +1,2 @@
1
+ // Базовая точка для headless и tsc, Metro берёт `index.ios` или `index.android`
2
+ export { LinearGradient } from './index.ios.js';
@@ -0,0 +1,12 @@
1
+ import { NativeViewBase } from '@symbiote-native/angular';
2
+ import type { IDescriptor } from '@symbiote-native/components';
3
+ import type { ILinearGradientProps } from '../../core/linear-gradient';
4
+ export declare abstract class LinearGradientBase extends NativeViewBase {
5
+ colors: ILinearGradientProps['colors'];
6
+ locations?: ILinearGradientProps['locations'];
7
+ start?: ILinearGradientProps['start'];
8
+ end?: ILinearGradientProps['end'];
9
+ dither?: ILinearGradientProps['dither'];
10
+ protected readonly propNames: readonly ["colors", "locations", "start", "end", "dither"];
11
+ protected renderView(props: object): IDescriptor;
12
+ }
@@ -0,0 +1,97 @@
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 { Directive, Input } from '@angular/core';
36
+ import { NativeViewBase } from '@symbiote-native/angular';
37
+ import { renderLinearGradient } from '../../core/linear-gradient.js';
38
+ // Inputs и сборка дескриптора общие, платформы различаются только шаблоном
39
+ let LinearGradientBase = (() => {
40
+ let _classDecorators = [Directive()];
41
+ let _classDescriptor;
42
+ let _classExtraInitializers = [];
43
+ let _classThis;
44
+ let _classSuper = NativeViewBase;
45
+ let _colors_decorators;
46
+ let _colors_initializers = [];
47
+ let _colors_extraInitializers = [];
48
+ let _locations_decorators;
49
+ let _locations_initializers = [];
50
+ let _locations_extraInitializers = [];
51
+ let _start_decorators;
52
+ let _start_initializers = [];
53
+ let _start_extraInitializers = [];
54
+ let _end_decorators;
55
+ let _end_initializers = [];
56
+ let _end_extraInitializers = [];
57
+ let _dither_decorators;
58
+ let _dither_initializers = [];
59
+ let _dither_extraInitializers = [];
60
+ var LinearGradientBase = class extends _classSuper {
61
+ static { _classThis = this; }
62
+ static {
63
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
64
+ _colors_decorators = [Input({ required: true })];
65
+ _locations_decorators = [Input()];
66
+ _start_decorators = [Input()];
67
+ _end_decorators = [Input()];
68
+ _dither_decorators = [Input()];
69
+ __esDecorate(null, null, _colors_decorators, { kind: "field", name: "colors", static: false, private: false, access: { has: obj => "colors" in obj, get: obj => obj.colors, set: (obj, value) => { obj.colors = value; } }, metadata: _metadata }, _colors_initializers, _colors_extraInitializers);
70
+ __esDecorate(null, null, _locations_decorators, { kind: "field", name: "locations", static: false, private: false, access: { has: obj => "locations" in obj, get: obj => obj.locations, set: (obj, value) => { obj.locations = value; } }, metadata: _metadata }, _locations_initializers, _locations_extraInitializers);
71
+ __esDecorate(null, null, _start_decorators, { kind: "field", name: "start", static: false, private: false, access: { has: obj => "start" in obj, get: obj => obj.start, set: (obj, value) => { obj.start = value; } }, metadata: _metadata }, _start_initializers, _start_extraInitializers);
72
+ __esDecorate(null, null, _end_decorators, { kind: "field", name: "end", static: false, private: false, access: { has: obj => "end" in obj, get: obj => obj.end, set: (obj, value) => { obj.end = value; } }, metadata: _metadata }, _end_initializers, _end_extraInitializers);
73
+ __esDecorate(null, null, _dither_decorators, { kind: "field", name: "dither", static: false, private: false, access: { has: obj => "dither" in obj, get: obj => obj.dither, set: (obj, value) => { obj.dither = value; } }, metadata: _metadata }, _dither_initializers, _dither_extraInitializers);
74
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
75
+ LinearGradientBase = _classThis = _classDescriptor.value;
76
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
77
+ __runInitializers(_classThis, _classExtraInitializers);
78
+ }
79
+ colors = __runInitializers(this, _colors_initializers, void 0);
80
+ locations = (__runInitializers(this, _colors_extraInitializers), __runInitializers(this, _locations_initializers, void 0));
81
+ start = (__runInitializers(this, _locations_extraInitializers), __runInitializers(this, _start_initializers, void 0));
82
+ end = (__runInitializers(this, _start_extraInitializers), __runInitializers(this, _end_initializers, void 0));
83
+ dither = (__runInitializers(this, _end_extraInitializers), __runInitializers(this, _dither_initializers, void 0));
84
+ propNames = (__runInitializers(this, _dither_extraInitializers), [
85
+ 'colors',
86
+ 'locations',
87
+ 'start',
88
+ 'end',
89
+ 'dither',
90
+ ]);
91
+ renderView(props) {
92
+ return renderLinearGradient(props);
93
+ }
94
+ };
95
+ return LinearGradientBase = _classThis;
96
+ })();
97
+ export { LinearGradientBase };
@@ -0,0 +1 @@
1
+ export { LINEAR_GRADIENT_MODULE_NAME, ensureLinearGradientRegistered, linearGradientViewName, normalizePoint, renderLinearGradient, type ILinearGradientPoint, type ILinearGradientProps, type INativeLinearGradientPoint, } from './linear-gradient';
@@ -0,0 +1 @@
1
+ export { LINEAR_GRADIENT_MODULE_NAME, ensureLinearGradientRegistered, linearGradientViewName, normalizePoint, renderLinearGradient, } from './linear-gradient.js';
@@ -0,0 +1,33 @@
1
+ import type { IAccessibilityProps, IAriaProps, IDescriptor, IResponderProps } from '@symbiote-native/components';
2
+ import type { IColorValue, IStyleProp, ISymbioteEvent, IViewStyle } from '@symbiote-native/engine';
3
+ export declare const LINEAR_GRADIENT_MODULE_NAME = "ExpoLinearGradient";
4
+ export type INativeLinearGradientPoint = [x: number, y: number];
5
+ /** Доли размера градиента от 0 до 1, объект `{ x, y }` или пара `[x, y]` */
6
+ export type ILinearGradientPoint = {
7
+ x: number;
8
+ y: number;
9
+ } | INativeLinearGradientPoint;
10
+ export type ILinearGradientProps = IAccessibilityProps & IAriaProps & IResponderProps & {
11
+ /** Минимум два цвета, для одного используйте `style.backgroundColor` у View */
12
+ colors: readonly [IColorValue, IColorValue, ...IColorValue[]];
13
+ /** Позиции остановок от 0 до 1 по возрастанию, той же длины, что и `colors` */
14
+ locations?: readonly [number, number, ...number[]] | null;
15
+ /** @default { x: 0.5, y: 0.0 } */
16
+ start?: ILinearGradientPoint | null;
17
+ /** @default { x: 0.5, y: 1.0 } */
18
+ end?: ILinearGradientPoint | null;
19
+ /**
20
+ * Дизеринг убирает полосы в градиенте, false может ускорить отрисовку
21
+ * @default true
22
+ * @platform android
23
+ */
24
+ dither?: boolean;
25
+ style?: IStyleProp<IViewStyle>;
26
+ testID?: string;
27
+ nativeID?: string;
28
+ onLayout?: (event: ISymbioteEvent) => void;
29
+ };
30
+ export declare function linearGradientViewName(): string;
31
+ export declare function ensureLinearGradientRegistered(): boolean;
32
+ export declare function normalizePoint(point: unknown): INativeLinearGradientPoint | undefined;
33
+ export declare function renderLinearGradient(props: object): IDescriptor;
@@ -0,0 +1,105 @@
1
+ // Ядро `LinearGradient`, порт `LinearGradient.tsx` из expo-linear-gradient
2
+ import { Platform, requireNativeViewManager } from 'expo-modules-core';
3
+ import { el } from '@symbiote-native/components';
4
+ import { expoViewManagerName, flattenStyle, processColor, styleOfProps, tryRegisterNativeView, } from '@symbiote-native/engine';
5
+ export const LINEAR_GRADIENT_MODULE_NAME = 'ExpoLinearGradient';
6
+ // Эти ключи разбираются здесь, остальное уходит на хост как есть
7
+ const GRADIENT_KEYS = new Set([
8
+ 'colors',
9
+ 'locations',
10
+ 'start',
11
+ 'end',
12
+ 'dither',
13
+ ]);
14
+ const ABSOLUTE_FILL = {
15
+ position: 'absolute',
16
+ left: 0,
17
+ right: 0,
18
+ top: 0,
19
+ bottom: 0,
20
+ };
21
+ export function linearGradientViewName() {
22
+ return expoViewManagerName(LINEAR_GRADIENT_MODULE_NAME);
23
+ }
24
+ // Регистрирует view config в реестре RN, из него движок берёт события и процессоры пропсов
25
+ // Зовётся при рендере, а не при импорте: регистрация в побочном эффекте барреля теряется в release
26
+ export function ensureLinearGradientRegistered() {
27
+ return tryRegisterNativeView(() => requireNativeViewManager(LINEAR_GRADIENT_MODULE_NAME));
28
+ }
29
+ function isNumber(value) {
30
+ return typeof value === 'number';
31
+ }
32
+ export function normalizePoint(point) {
33
+ if (!point)
34
+ return undefined;
35
+ if (Array.isArray(point)) {
36
+ const [x, y] = point;
37
+ if (point.length === 2 && isNumber(x) && isNumber(y))
38
+ return [x, y];
39
+ console.warn('start and end props for LinearGradient must be of the format [x,y] or {x, y}');
40
+ return undefined;
41
+ }
42
+ const x = Reflect.get(Object(point), 'x');
43
+ const y = Reflect.get(Object(point), 'y');
44
+ return isNumber(x) && isNumber(y) ? [x, y] : undefined;
45
+ }
46
+ function resolveLocations(colors, locations) {
47
+ if (!Array.isArray(locations))
48
+ return undefined;
49
+ if (locations.length === colors.length)
50
+ return locations;
51
+ console.warn('LinearGradient colors and locations props should be arrays of the same length');
52
+ return locations.slice(0, colors.length);
53
+ }
54
+ function radiusOr(value, fallback) {
55
+ return isNumber(value) ? value : fallback;
56
+ }
57
+ // Формат из `Path.addRoundRect`: по два значения на угол, начиная с верхнего левого
58
+ function borderRadiiOf(style) {
59
+ const flat = flattenStyle(style) ?? {};
60
+ const base = radiusOr(flat.borderRadius, 0);
61
+ const topLeft = radiusOr(flat.borderTopLeftRadius, base);
62
+ const topRight = radiusOr(flat.borderTopRightRadius, base);
63
+ const bottomRight = radiusOr(flat.borderBottomRightRadius, base);
64
+ const bottomLeft = radiusOr(flat.borderBottomLeftRadius, base);
65
+ return [
66
+ topLeft,
67
+ topLeft,
68
+ topRight,
69
+ topRight,
70
+ bottomRight,
71
+ bottomRight,
72
+ bottomLeft,
73
+ bottomLeft,
74
+ ];
75
+ }
76
+ function withoutUndefined(props) {
77
+ return Object.fromEntries(Object.entries(props).filter(([, value]) => value !== undefined));
78
+ }
79
+ // `object`, а не тип пропсов: Vue и Angular отдают нетипизированный набор атрибутов
80
+ export function renderLinearGradient(props) {
81
+ const hostProps = Object.fromEntries(Object.entries(props).filter(([key]) => !GRADIENT_KEYS.has(key)));
82
+ if (!ensureLinearGradientRegistered()) {
83
+ console.warn('LinearGradient is not available on this platform');
84
+ return el('view', hostProps);
85
+ }
86
+ const colors = Reflect.get(props, 'colors');
87
+ const colorList = Array.isArray(colors) ? colors : [];
88
+ const nativeProps = withoutUndefined({
89
+ colors: colorList.map(color => processColor(color)),
90
+ locations: resolveLocations(colorList, Reflect.get(props, 'locations')),
91
+ startPoint: normalizePoint(Reflect.get(props, 'start')),
92
+ endPoint: normalizePoint(Reflect.get(props, 'end')),
93
+ });
94
+ if (!Platform.select({ android: true, default: false })) {
95
+ return el(linearGradientViewName(), { ...hostProps, ...nativeProps });
96
+ }
97
+ // Нативный градиент заполняет View и сам скругляет углы, т.к. RN не обрезает по радиусу
98
+ const native = el(linearGradientViewName(), withoutUndefined({
99
+ ...nativeProps,
100
+ style: ABSOLUTE_FILL,
101
+ borderRadii: borderRadiiOf(styleOfProps(props)),
102
+ dither: Reflect.get(props, 'dither'),
103
+ }));
104
+ return el('view', hostProps, [native]);
105
+ }