@symbiote-native/glass-effect 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 (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -2
  3. package/build/angular/glass/index.android.d.ts +7 -0
  4. package/build/angular/glass/index.android.js +97 -0
  5. package/build/angular/glass/index.d.ts +1 -0
  6. package/build/angular/glass/index.ios.d.ts +9 -0
  7. package/build/angular/glass/index.ios.js +116 -0
  8. package/build/angular/glass/index.js +2 -0
  9. package/build/angular/glass/shared.d.ts +16 -0
  10. package/build/angular/glass/shared.js +121 -0
  11. package/build/angular/index.d.ts +2 -0
  12. package/build/angular/index.js +3 -0
  13. package/build/core/glass-effect.d.ts +62 -0
  14. package/build/core/glass-effect.js +71 -0
  15. package/build/core/index.d.ts +1 -0
  16. package/build/core/index.js +1 -0
  17. package/build/react/glass-effect.d.ts +17 -0
  18. package/build/react/glass-effect.js +14 -0
  19. package/build/react/index.d.ts +3 -0
  20. package/build/react/index.js +3 -0
  21. package/build/solid/glass-effect.d.ts +16 -0
  22. package/build/solid/glass-effect.js +6 -0
  23. package/build/solid/index.d.ts +3 -0
  24. package/build/solid/index.js +3 -0
  25. package/build/svelte/glass-container.svelte +14 -0
  26. package/build/svelte/glass-container.svelte.d.ts +4 -0
  27. package/build/svelte/glass-props.d.ts +13 -0
  28. package/build/svelte/glass-props.js +1 -0
  29. package/build/svelte/glass-view.svelte +10 -0
  30. package/build/svelte/glass-view.svelte.d.ts +4 -0
  31. package/build/svelte/index.d.ts +4 -0
  32. package/build/svelte/index.js +4 -0
  33. package/build/vue/glass-effect.d.ts +9 -0
  34. package/build/vue/glass-effect.js +20 -0
  35. package/build/vue/index.d.ts +3 -0
  36. package/build/vue/index.js +3 -0
  37. package/build-ngc/angular/glass/index.android.d.ts +12 -0
  38. package/build-ngc/angular/glass/index.android.js +51 -0
  39. package/build-ngc/angular/glass/index.android.js.map +1 -0
  40. package/build-ngc/angular/glass/index.d.ts +1 -0
  41. package/build-ngc/angular/glass/index.ios.d.ts +14 -0
  42. package/build-ngc/angular/glass/index.ios.js +74 -0
  43. package/build-ngc/angular/glass/index.ios.js.map +1 -0
  44. package/build-ngc/angular/glass/index.js +3 -0
  45. package/build-ngc/angular/glass/index.js.map +1 -0
  46. package/build-ngc/angular/glass/shared.d.ts +21 -0
  47. package/build-ngc/angular/glass/shared.js +50 -0
  48. package/build-ngc/angular/glass/shared.js.map +1 -0
  49. package/build-ngc/angular/index.d.ts +2 -0
  50. package/build-ngc/angular/index.js +4 -0
  51. package/build-ngc/angular/index.js.map +1 -0
  52. package/build-ngc/core/glass-effect.d.ts +62 -0
  53. package/build-ngc/core/glass-effect.js +72 -0
  54. package/build-ngc/core/glass-effect.js.map +1 -0
  55. package/build-ngc/core/index.d.ts +1 -0
  56. package/build-ngc/core/index.js +2 -0
  57. package/build-ngc/core/index.js.map +1 -0
  58. package/native-link.json +5 -0
  59. package/package.json +145 -3
  60. package/src/angular/glass/index.android.ts +31 -0
  61. package/src/angular/glass/index.ios.ts +61 -0
  62. package/src/angular/glass/index.ts +2 -0
  63. package/src/angular/glass/shared.ts +38 -0
  64. package/src/angular/index.ts +15 -0
  65. package/src/core/glass-effect.ts +160 -0
  66. package/src/core/index.ts +16 -0
  67. package/src/react/glass-effect.ts +34 -0
  68. package/src/react/index.ts +17 -0
  69. package/src/solid/glass-effect.ts +27 -0
  70. package/src/solid/index.ts +17 -0
  71. package/src/svelte/glass-container.svelte +14 -0
  72. package/src/svelte/glass-props.ts +18 -0
  73. package/src/svelte/glass-view.svelte +10 -0
  74. package/src/svelte/index.ts +18 -0
  75. package/src/svelte/svelte-ambient.d.ts +6 -0
  76. package/src/svelte/svelte-compile.test-helper.ts +6 -0
  77. package/src/vue/glass-effect.ts +39 -0
  78. package/src/vue/index.ts +17 -0
@@ -0,0 +1,62 @@
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 GLASS_EFFECT_MODULE_NAME = "ExpoGlassEffect";
4
+ export type IGlassStyle = 'clear' | 'regular' | 'none';
5
+ export type IGlassEffectStyleConfig = {
6
+ /** The glass effect style to apply */
7
+ style: IGlassStyle;
8
+ /**
9
+ * Whether to animate the style change
10
+ * @default false
11
+ */
12
+ animate?: boolean;
13
+ /** Duration of the animation in seconds, the system default when omitted */
14
+ animationDuration?: number;
15
+ };
16
+ export type IGlassColorScheme = 'auto' | 'light' | 'dark';
17
+ type IViewSurface = IAccessibilityProps & IAriaProps & IResponderProps & {
18
+ style?: IStyleProp<IViewStyle>;
19
+ testID?: string;
20
+ nativeID?: string;
21
+ onLayout?: (event: ISymbioteEvent) => void;
22
+ };
23
+ export type IGlassViewProps = IViewSurface & {
24
+ /**
25
+ * A style name or a config object that controls the animation
26
+ * @default 'regular'
27
+ */
28
+ glassEffectStyle?: IGlassStyle | IGlassEffectStyleConfig;
29
+ tintColor?: IColorValue;
30
+ /**
31
+ * @default false
32
+ */
33
+ isInteractive?: boolean;
34
+ /**
35
+ * Overrides the system appearance when the app has its own theme toggle
36
+ * @default 'auto'
37
+ */
38
+ colorScheme?: IGlassColorScheme;
39
+ };
40
+ export type IGlassContainerProps = IViewSurface & {
41
+ /** The distance at which glass elements start affecting each other */
42
+ spacing?: number;
43
+ };
44
+ export declare const glassViewName: () => string;
45
+ export declare const glassContainerName: () => string;
46
+ export declare const ensureGlassViewRegistered: () => boolean;
47
+ export declare const ensureGlassContainerRegistered: () => boolean;
48
+ export declare function renderGlassView(props: object): IDescriptor;
49
+ export declare function renderGlassContainer(props: object): IDescriptor;
50
+ /**
51
+ * Whether the app renders with the Liquid Glass design, component availability only
52
+ * Accessibility settings can still limit the effect, see `AccessibilityInfo`
53
+ * @platform ios
54
+ */
55
+ export declare function isLiquidGlassAvailable(): boolean;
56
+ /**
57
+ * Whether the Liquid Glass API exists at runtime, some iOS 26 betas lack it and crash
58
+ * Check before using `GlassView` and `GlassContainer`
59
+ * @platform ios
60
+ */
61
+ export declare function isGlassEffectAPIAvailable(): boolean;
62
+ export {};
@@ -0,0 +1,72 @@
1
+ // Ядро `GlassView` и `GlassContainer`, порт expo-glass-effect, нативное только на iOS
2
+ import { Platform, requireNativeModule, requireNativeViewManager, } from 'expo-modules-core';
3
+ import { el } from '@symbiote-native/components';
4
+ import { defineExpoNativeViews } from '@symbiote-native/engine';
5
+ export const GLASS_EFFECT_MODULE_NAME = 'ExpoGlassEffect';
6
+ const GLASS_VIEW_NAME = 'GlassView';
7
+ const GLASS_CONTAINER_NAME = 'GlassContainer';
8
+ // Эти ключи имеют смысл только у нативного view, у обычного View их быть не должно
9
+ const GLASS_VIEW_KEYS = new Set([
10
+ 'glassEffectStyle',
11
+ 'tintColor',
12
+ 'isInteractive',
13
+ 'colorScheme',
14
+ ]);
15
+ const GLASS_CONTAINER_KEYS = new Set(['spacing']);
16
+ // Регистрация идёт при рендере: побочный эффект барреля теряется в release
17
+ const views = defineExpoNativeViews(requireNativeViewManager, GLASS_EFFECT_MODULE_NAME, { glass: GLASS_VIEW_NAME, container: GLASS_CONTAINER_NAME });
18
+ export const glassViewName = views.glass.name;
19
+ export const glassContainerName = views.container.name;
20
+ export const ensureGlassViewRegistered = views.glass.ensureRegistered;
21
+ export const ensureGlassContainerRegistered = views.container.ensureRegistered;
22
+ function runsOnIos() {
23
+ return Platform.select({ ios: true, default: false });
24
+ }
25
+ function withoutKeys(props, keys) {
26
+ return Object.fromEntries(Object.entries(props).filter(([key]) => !keys.has(key)));
27
+ }
28
+ // `object`, а не тип пропсов: Vue и Angular отдают нетипизированный набор атрибутов
29
+ export function renderGlassView(props) {
30
+ if (!runsOnIos())
31
+ return el('view', withoutKeys(props, GLASS_VIEW_KEYS));
32
+ if (!ensureGlassViewRegistered()) {
33
+ console.warn('GlassView is not available on this platform');
34
+ return el('view', withoutKeys(props, GLASS_VIEW_KEYS));
35
+ }
36
+ return el(glassViewName(), { ...props });
37
+ }
38
+ export function renderGlassContainer(props) {
39
+ if (!runsOnIos()) {
40
+ return el('view', withoutKeys(props, GLASS_CONTAINER_KEYS));
41
+ }
42
+ if (!ensureGlassContainerRegistered()) {
43
+ console.warn('GlassContainer is not available on this platform');
44
+ return el('view', withoutKeys(props, GLASS_CONTAINER_KEYS));
45
+ }
46
+ return el(glassContainerName(), { ...props });
47
+ }
48
+ let isLiquidGlass;
49
+ let isGlassApi;
50
+ /**
51
+ * Whether the app renders with the Liquid Glass design, component availability only
52
+ * Accessibility settings can still limit the effect, see `AccessibilityInfo`
53
+ * @platform ios
54
+ */
55
+ export function isLiquidGlassAvailable() {
56
+ if (!runsOnIos())
57
+ return false;
58
+ isLiquidGlass ??= !!Reflect.get(requireNativeModule(GLASS_EFFECT_MODULE_NAME), 'isLiquidGlassAvailable');
59
+ return isLiquidGlass;
60
+ }
61
+ /**
62
+ * Whether the Liquid Glass API exists at runtime, some iOS 26 betas lack it and crash
63
+ * Check before using `GlassView` and `GlassContainer`
64
+ * @platform ios
65
+ */
66
+ export function isGlassEffectAPIAvailable() {
67
+ if (!runsOnIos())
68
+ return false;
69
+ isGlassApi ??= !!Reflect.get(requireNativeModule(GLASS_EFFECT_MODULE_NAME), 'isGlassEffectAPIAvailable');
70
+ return isGlassApi;
71
+ }
72
+ //# sourceMappingURL=glass-effect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glass-effect.js","sourceRoot":"","sources":["../../src/core/glass-effect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export { GLASS_EFFECT_MODULE_NAME, ensureGlassContainerRegistered, ensureGlassViewRegistered, glassContainerName, glassViewName, isGlassEffectAPIAvailable, isLiquidGlassAvailable, renderGlassContainer, renderGlassView, type IGlassColorScheme, type IGlassContainerProps, type IGlassEffectStyleConfig, type IGlassStyle, type IGlassViewProps, } from './glass-effect';
@@ -0,0 +1,2 @@
1
+ export { GLASS_EFFECT_MODULE_NAME, ensureGlassContainerRegistered, ensureGlassViewRegistered, glassContainerName, glassViewName, isGlassEffectAPIAvailable, isLiquidGlassAvailable, renderGlassContainer, renderGlassView, } from './glass-effect';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,8BAA8B,EAC9B,yBAAyB,EACzB,kBAAkB,EAClB,aAAa,EACb,yBAAyB,EACzB,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,GAMhB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,5 @@
1
+ {
2
+ "ios": {
3
+ "upstreamPackage": "expo-glass-effect"
4
+ }
5
+ }
package/package.json CHANGED
@@ -1,6 +1,148 @@
1
1
  {
2
2
  "name": "@symbiote-native/glass-effect",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "expo-glass-effect wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Liquid Glass native views for iOS 26 and a plain View elsewhere.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/OneEyed1366/symbiote-native.git",
9
+ "directory": "packages/glass-effect"
10
+ },
11
+ "homepage": "https://github.com/OneEyed1366/symbiote-native/tree/master/packages/glass-effect#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/OneEyed1366/symbiote-native/issues"
14
+ },
15
+ "author": "Andrey Prokopenko <psevdoproger@gmail.com>",
16
+ "keywords": [
17
+ "react-native",
18
+ "symbiote-native",
19
+ "expo-glass-effect",
20
+ "react",
21
+ "vue",
22
+ "svelte",
23
+ "solid",
24
+ "angular",
25
+ "liquid-glass",
26
+ "glass-effect"
27
+ ],
28
+ "type": "module",
29
+ "main": "./build/core/index.js",
30
+ "module": "./build/core/index.js",
31
+ "types": "./build/core/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./build/core/index.d.ts",
35
+ "default": "./build/core/index.js"
36
+ },
37
+ "./vue": {
38
+ "types": "./build/vue/index.d.ts",
39
+ "default": "./build/vue/index.js"
40
+ },
41
+ "./react": {
42
+ "types": "./build/react/index.d.ts",
43
+ "default": "./build/react/index.js"
44
+ },
45
+ "./svelte": "./src/svelte/index.ts",
46
+ "./solid": {
47
+ "types": "./build/solid/index.d.ts",
48
+ "default": "./build/solid/index.js"
49
+ },
50
+ "./angular": {
51
+ "types": "./build-ngc/angular/index.d.ts",
52
+ "react-native": "./build-ngc/angular/index.js",
53
+ "default": "./src/angular/index.ts"
54
+ }
55
+ },
56
+ "files": [
57
+ "src",
58
+ "build",
59
+ "build-ngc",
60
+ "native-link.json",
61
+ "!src/**/*.test.*",
62
+ "!src/**/*.spec.*",
63
+ "!src/**/*.detox.*"
64
+ ],
65
+ "publishConfig": {
66
+ "access": "public"
67
+ },
68
+ "dependencies": {
69
+ "expo-glass-effect": "57.0.4",
70
+ "expo-modules-core": "57.0.20",
71
+ "@symbiote-native/components": "3.2.0"
72
+ },
73
+ "peerDependencies": {
74
+ "@angular/core": ">=20",
75
+ "@vue/runtime-core": "^3.5.13",
76
+ "react": ">=19.0.0",
77
+ "react-native": ">=0.86",
78
+ "solid-js": ">=1.9.0",
79
+ "svelte": ">=5.56.0",
80
+ "vue": ">=3.5.0",
81
+ "@symbiote-native/angular": "^3.3.0",
82
+ "@symbiote-native/engine": "^1.6.0",
83
+ "@symbiote-native/react": "^3.3.0",
84
+ "@symbiote-native/solid": "^3.2.0",
85
+ "@symbiote-native/svelte": "^3.2.0",
86
+ "@symbiote-native/vue": "^3.3.0"
87
+ },
88
+ "peerDependenciesMeta": {
89
+ "@symbiote-native/angular": {
90
+ "optional": true
91
+ },
92
+ "@symbiote-native/react": {
93
+ "optional": true
94
+ },
95
+ "@symbiote-native/solid": {
96
+ "optional": true
97
+ },
98
+ "@symbiote-native/svelte": {
99
+ "optional": true
100
+ },
101
+ "@symbiote-native/vue": {
102
+ "optional": true
103
+ },
104
+ "@angular/core": {
105
+ "optional": true
106
+ },
107
+ "@vue/runtime-core": {
108
+ "optional": true
109
+ },
110
+ "react": {
111
+ "optional": true
112
+ },
113
+ "solid-js": {
114
+ "optional": true
115
+ },
116
+ "svelte": {
117
+ "optional": true
118
+ },
119
+ "vue": {
120
+ "optional": true
121
+ }
122
+ },
123
+ "devDependencies": {
124
+ "@angular/compiler": "~22.0.8",
125
+ "@angular/compiler-cli": "~22.0.8",
126
+ "@angular/core": "~22.0.8",
127
+ "@types/node": "^26.0.0",
128
+ "@types/react": "^19.2.0",
129
+ "@vue/runtime-core": "^3.5.13",
130
+ "react": "19.2.3",
131
+ "solid-js": "^1.9.14",
132
+ "svelte": "^5.56.0",
133
+ "typescript": "~6.0.0",
134
+ "@symbiote-native/angular": "3.3.0",
135
+ "@symbiote-native/engine": "1.6.0",
136
+ "@symbiote-native/react": "3.3.0",
137
+ "@symbiote-native/solid": "3.2.0",
138
+ "@symbiote-native/svelte": "3.2.0",
139
+ "@symbiote-native/test-utils": "0.4.7",
140
+ "@symbiote-native/vue": "3.3.0"
141
+ },
142
+ "scripts": {
143
+ "typecheck": "tsc --build",
144
+ "clean": "rm -rf build-ngc",
145
+ "ng:build": "pnpm run clean && ngc -p tsconfig.angular.json",
146
+ "format": "prettier --write \"src/**/*.{ts,tsx}\""
147
+ }
6
148
  }
@@ -0,0 +1,31 @@
1
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { DescriptorHost } from '@symbiote-native/angular';
3
+ import { GlassContainerBase, GlassViewBase } from './shared';
4
+
5
+ /** Angular twin of `expo-glass-effect`'s `GlassView`, a plain View off iOS */
6
+ @Component({
7
+ selector: 'GlassView',
8
+ standalone: true,
9
+ imports: [DescriptorHost],
10
+ changeDetection: ChangeDetectionStrategy.OnPush,
11
+ template: `@if (descriptor; as node) {
12
+ <symbiote-descriptor-host [node]="node"
13
+ ><ng-content
14
+ /></symbiote-descriptor-host>
15
+ }`,
16
+ })
17
+ export class GlassView extends GlassViewBase {}
18
+
19
+ /** Angular twin of `expo-glass-effect`'s `GlassContainer`, a plain View off iOS */
20
+ @Component({
21
+ selector: 'GlassContainer',
22
+ standalone: true,
23
+ imports: [DescriptorHost],
24
+ changeDetection: ChangeDetectionStrategy.OnPush,
25
+ template: `@if (descriptor; as node) {
26
+ <symbiote-descriptor-host [node]="node"
27
+ ><ng-content
28
+ /></symbiote-descriptor-host>
29
+ }`,
30
+ })
31
+ export class GlassContainer extends GlassContainerBase {}
@@ -0,0 +1,61 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ NO_ERRORS_SCHEMA,
5
+ } from '@angular/core';
6
+ import { SymbioteHostPropsDirective } from '@symbiote-native/angular';
7
+ import { warnIfViewNameIsDynamic } from '@symbiote-native/engine';
8
+ import { GLASS_EFFECT_MODULE_NAME } from '../../core';
9
+ import { GlassContainerBase, GlassViewBase } from './shared';
10
+
11
+ const VIEW_TAG = 'ViewManagerAdapter_ExpoGlassEffect_GlassView';
12
+ const CONTAINER_TAG = 'ViewManagerAdapter_ExpoGlassEffect_GlassContainer';
13
+
14
+ /** Angular twin of `expo-glass-effect`'s `GlassView`, content goes inside the native view */
15
+ @Component({
16
+ selector: 'GlassView',
17
+ standalone: true,
18
+ imports: [SymbioteHostPropsDirective],
19
+ schemas: [NO_ERRORS_SCHEMA],
20
+ changeDetection: ChangeDetectionStrategy.OnPush,
21
+ // Имя тега это нативное имя view, его нельзя собрать динамически
22
+ template: `@if (descriptor; as node) {
23
+ <ViewManagerAdapter_ExpoGlassEffect_GlassView
24
+ [symbioteHostProps]="node.props"
25
+ >
26
+ <ng-content />
27
+ </ViewManagerAdapter_ExpoGlassEffect_GlassView>
28
+ }`,
29
+ })
30
+ export class GlassView extends GlassViewBase {
31
+ constructor() {
32
+ super();
33
+ warnIfViewNameIsDynamic(VIEW_TAG, GLASS_EFFECT_MODULE_NAME, 'GlassView');
34
+ }
35
+ }
36
+
37
+ /** Angular twin of `expo-glass-effect`'s `GlassContainer`, merges the glass views it holds */
38
+ @Component({
39
+ selector: 'GlassContainer',
40
+ standalone: true,
41
+ imports: [SymbioteHostPropsDirective],
42
+ schemas: [NO_ERRORS_SCHEMA],
43
+ changeDetection: ChangeDetectionStrategy.OnPush,
44
+ template: `@if (descriptor; as node) {
45
+ <ViewManagerAdapter_ExpoGlassEffect_GlassContainer
46
+ [symbioteHostProps]="node.props"
47
+ >
48
+ <ng-content />
49
+ </ViewManagerAdapter_ExpoGlassEffect_GlassContainer>
50
+ }`,
51
+ })
52
+ export class GlassContainer extends GlassContainerBase {
53
+ constructor() {
54
+ super();
55
+ warnIfViewNameIsDynamic(
56
+ CONTAINER_TAG,
57
+ GLASS_EFFECT_MODULE_NAME,
58
+ 'GlassContainer',
59
+ );
60
+ }
61
+ }
@@ -0,0 +1,2 @@
1
+ // Базовая точка для headless и tsc, Metro берёт `index.ios` или `index.android`
2
+ export { GlassContainer, GlassView } from './index.ios';
@@ -0,0 +1,38 @@
1
+ import { Directive, Input } from '@angular/core';
2
+ import { NativeViewBase } from '@symbiote-native/angular';
3
+ import type { IDescriptor } from '@symbiote-native/components';
4
+ import { renderGlassContainer, renderGlassView } from '../../core';
5
+ import type { IGlassContainerProps, IGlassViewProps } from '../../core';
6
+
7
+ // Inputs и сборка дескриптора общие, платформы различаются только шаблоном
8
+ @Directive()
9
+ export abstract class GlassViewBase extends NativeViewBase {
10
+ @Input() glassEffectStyle?: IGlassViewProps['glassEffectStyle'];
11
+ @Input() tintColor?: IGlassViewProps['tintColor'];
12
+ @Input() isInteractive?: IGlassViewProps['isInteractive'];
13
+ @Input() colorScheme?: IGlassViewProps['colorScheme'];
14
+
15
+ protected override readonly propNames = [
16
+ 'glassEffectStyle',
17
+ 'tintColor',
18
+ 'isInteractive',
19
+ 'colorScheme',
20
+ ] as const satisfies readonly (keyof GlassViewBase)[];
21
+
22
+ protected override renderView(props: object): IDescriptor {
23
+ return renderGlassView(props);
24
+ }
25
+ }
26
+
27
+ @Directive()
28
+ export abstract class GlassContainerBase extends NativeViewBase {
29
+ @Input() spacing?: IGlassContainerProps['spacing'];
30
+
31
+ protected override readonly propNames = [
32
+ 'spacing',
33
+ ] as const satisfies readonly (keyof GlassContainerBase)[];
34
+
35
+ protected override renderView(props: object): IDescriptor {
36
+ return renderGlassContainer(props);
37
+ }
38
+ }
@@ -0,0 +1,15 @@
1
+ // @symbiote-native/glass-effect/angular: компоненты `GlassView` и `GlassContainer` поверх общего ядра
2
+
3
+ export { GlassContainer, GlassView } from './glass';
4
+ export {
5
+ GLASS_EFFECT_MODULE_NAME,
6
+ glassContainerName,
7
+ glassViewName,
8
+ isGlassEffectAPIAvailable,
9
+ isLiquidGlassAvailable,
10
+ type IGlassColorScheme,
11
+ type IGlassContainerProps,
12
+ type IGlassEffectStyleConfig,
13
+ type IGlassStyle,
14
+ type IGlassViewProps,
15
+ } from '../core';
@@ -0,0 +1,160 @@
1
+ // Ядро `GlassView` и `GlassContainer`, порт expo-glass-effect, нативное только на iOS
2
+ import {
3
+ Platform,
4
+ requireNativeModule,
5
+ requireNativeViewManager,
6
+ } from 'expo-modules-core';
7
+ import { el } from '@symbiote-native/components';
8
+ import type {
9
+ IAccessibilityProps,
10
+ IAriaProps,
11
+ IDescriptor,
12
+ IResponderProps,
13
+ } from '@symbiote-native/components';
14
+ import { defineExpoNativeViews } from '@symbiote-native/engine';
15
+ import type {
16
+ IColorValue,
17
+ IStyleProp,
18
+ ISymbioteEvent,
19
+ IViewStyle,
20
+ } from '@symbiote-native/engine';
21
+
22
+ export const GLASS_EFFECT_MODULE_NAME = 'ExpoGlassEffect';
23
+ const GLASS_VIEW_NAME = 'GlassView';
24
+ const GLASS_CONTAINER_NAME = 'GlassContainer';
25
+
26
+ export type IGlassStyle = 'clear' | 'regular' | 'none';
27
+
28
+ export type IGlassEffectStyleConfig = {
29
+ /** The glass effect style to apply */
30
+ style: IGlassStyle;
31
+ /**
32
+ * Whether to animate the style change
33
+ * @default false
34
+ */
35
+ animate?: boolean;
36
+ /** Duration of the animation in seconds, the system default when omitted */
37
+ animationDuration?: number;
38
+ };
39
+
40
+ export type IGlassColorScheme = 'auto' | 'light' | 'dark';
41
+
42
+ type IViewSurface = IAccessibilityProps &
43
+ IAriaProps &
44
+ IResponderProps & {
45
+ style?: IStyleProp<IViewStyle>;
46
+ testID?: string;
47
+ nativeID?: string;
48
+ onLayout?: (event: ISymbioteEvent) => void;
49
+ };
50
+
51
+ export type IGlassViewProps = IViewSurface & {
52
+ /**
53
+ * A style name or a config object that controls the animation
54
+ * @default 'regular'
55
+ */
56
+ glassEffectStyle?: IGlassStyle | IGlassEffectStyleConfig;
57
+ tintColor?: IColorValue;
58
+ /**
59
+ * @default false
60
+ */
61
+ isInteractive?: boolean;
62
+ /**
63
+ * Overrides the system appearance when the app has its own theme toggle
64
+ * @default 'auto'
65
+ */
66
+ colorScheme?: IGlassColorScheme;
67
+ };
68
+
69
+ export type IGlassContainerProps = IViewSurface & {
70
+ /** The distance at which glass elements start affecting each other */
71
+ spacing?: number;
72
+ };
73
+
74
+ // Эти ключи имеют смысл только у нативного view, у обычного View их быть не должно
75
+ const GLASS_VIEW_KEYS: ReadonlySet<string> = new Set<keyof IGlassViewProps>([
76
+ 'glassEffectStyle',
77
+ 'tintColor',
78
+ 'isInteractive',
79
+ 'colorScheme',
80
+ ]);
81
+ const GLASS_CONTAINER_KEYS: ReadonlySet<string> = new Set<
82
+ keyof IGlassContainerProps
83
+ >(['spacing']);
84
+
85
+ // Регистрация идёт при рендере: побочный эффект барреля теряется в release
86
+ const views = defineExpoNativeViews(
87
+ requireNativeViewManager,
88
+ GLASS_EFFECT_MODULE_NAME,
89
+ { glass: GLASS_VIEW_NAME, container: GLASS_CONTAINER_NAME },
90
+ );
91
+
92
+ export const glassViewName = views.glass.name;
93
+ export const glassContainerName = views.container.name;
94
+ export const ensureGlassViewRegistered = views.glass.ensureRegistered;
95
+ export const ensureGlassContainerRegistered = views.container.ensureRegistered;
96
+
97
+ function runsOnIos(): boolean {
98
+ return Platform.select({ ios: true, default: false });
99
+ }
100
+
101
+ function withoutKeys(
102
+ props: object,
103
+ keys: ReadonlySet<string>,
104
+ ): Record<string, unknown> {
105
+ return Object.fromEntries(
106
+ Object.entries(props).filter(([key]) => !keys.has(key)),
107
+ );
108
+ }
109
+
110
+ // `object`, а не тип пропсов: Vue и Angular отдают нетипизированный набор атрибутов
111
+ export function renderGlassView(props: object): IDescriptor {
112
+ if (!runsOnIos()) return el('view', withoutKeys(props, GLASS_VIEW_KEYS));
113
+ if (!ensureGlassViewRegistered()) {
114
+ console.warn('GlassView is not available on this platform');
115
+ return el('view', withoutKeys(props, GLASS_VIEW_KEYS));
116
+ }
117
+ return el(glassViewName(), { ...props });
118
+ }
119
+
120
+ export function renderGlassContainer(props: object): IDescriptor {
121
+ if (!runsOnIos()) {
122
+ return el('view', withoutKeys(props, GLASS_CONTAINER_KEYS));
123
+ }
124
+ if (!ensureGlassContainerRegistered()) {
125
+ console.warn('GlassContainer is not available on this platform');
126
+ return el('view', withoutKeys(props, GLASS_CONTAINER_KEYS));
127
+ }
128
+ return el(glassContainerName(), { ...props });
129
+ }
130
+
131
+ let isLiquidGlass: boolean | undefined;
132
+ let isGlassApi: boolean | undefined;
133
+
134
+ /**
135
+ * Whether the app renders with the Liquid Glass design, component availability only
136
+ * Accessibility settings can still limit the effect, see `AccessibilityInfo`
137
+ * @platform ios
138
+ */
139
+ export function isLiquidGlassAvailable(): boolean {
140
+ if (!runsOnIos()) return false;
141
+ isLiquidGlass ??= !!Reflect.get(
142
+ requireNativeModule(GLASS_EFFECT_MODULE_NAME),
143
+ 'isLiquidGlassAvailable',
144
+ );
145
+ return isLiquidGlass;
146
+ }
147
+
148
+ /**
149
+ * Whether the Liquid Glass API exists at runtime, some iOS 26 betas lack it and crash
150
+ * Check before using `GlassView` and `GlassContainer`
151
+ * @platform ios
152
+ */
153
+ export function isGlassEffectAPIAvailable(): boolean {
154
+ if (!runsOnIos()) return false;
155
+ isGlassApi ??= !!Reflect.get(
156
+ requireNativeModule(GLASS_EFFECT_MODULE_NAME),
157
+ 'isGlassEffectAPIAvailable',
158
+ );
159
+ return isGlassApi;
160
+ }
@@ -0,0 +1,16 @@
1
+ export {
2
+ GLASS_EFFECT_MODULE_NAME,
3
+ ensureGlassContainerRegistered,
4
+ ensureGlassViewRegistered,
5
+ glassContainerName,
6
+ glassViewName,
7
+ isGlassEffectAPIAvailable,
8
+ isLiquidGlassAvailable,
9
+ renderGlassContainer,
10
+ renderGlassView,
11
+ type IGlassColorScheme,
12
+ type IGlassContainerProps,
13
+ type IGlassEffectStyleConfig,
14
+ type IGlassStyle,
15
+ type IGlassViewProps,
16
+ } from './glass-effect';
@@ -0,0 +1,34 @@
1
+ import { createElement } from 'react';
2
+ import type { ReactElement, ReactNode, RefObject } from 'react';
3
+ import type { IHostInstance, IViewProps } from '@symbiote-native/react';
4
+ import { renderGlassContainer, renderGlassView } from '../core';
5
+ import type { IGlassContainerProps, IGlassViewProps } from '../core';
6
+
7
+ // Поверхность View как у `ViewProps` в upstream, дети и `className` свои у React
8
+ export type IGlassViewReactProps = IGlassViewProps &
9
+ Omit<IViewProps, keyof IGlassViewProps | 'children' | 'ref'> & {
10
+ ref?: RefObject<IHostInstance | null>;
11
+ children?: ReactNode;
12
+ className?: string;
13
+ };
14
+
15
+ export type IGlassContainerReactProps = IGlassContainerProps &
16
+ Omit<IViewProps, keyof IGlassContainerProps | 'children' | 'ref'> & {
17
+ ref?: RefObject<IHostInstance | null>;
18
+ children?: ReactNode;
19
+ className?: string;
20
+ };
21
+
22
+ /** React twin of `expo-glass-effect`'s `GlassView`, native on iOS and a plain View elsewhere */
23
+ export function GlassView(props: IGlassViewReactProps): ReactElement {
24
+ const { children, ...rest } = props;
25
+ const descriptor = renderGlassView(rest);
26
+ return createElement(descriptor.type, descriptor.props, children);
27
+ }
28
+
29
+ /** React twin of `expo-glass-effect`'s `GlassContainer`, merges the glass views it holds */
30
+ export function GlassContainer(props: IGlassContainerReactProps): ReactElement {
31
+ const { children, ...rest } = props;
32
+ const descriptor = renderGlassContainer(rest);
33
+ return createElement(descriptor.type, descriptor.props, children);
34
+ }