@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.
- package/LICENSE +21 -0
- package/README.md +205 -2
- package/build/angular/glass/index.android.d.ts +7 -0
- package/build/angular/glass/index.android.js +97 -0
- package/build/angular/glass/index.d.ts +1 -0
- package/build/angular/glass/index.ios.d.ts +9 -0
- package/build/angular/glass/index.ios.js +116 -0
- package/build/angular/glass/index.js +2 -0
- package/build/angular/glass/shared.d.ts +16 -0
- package/build/angular/glass/shared.js +121 -0
- package/build/angular/index.d.ts +2 -0
- package/build/angular/index.js +3 -0
- package/build/core/glass-effect.d.ts +62 -0
- package/build/core/glass-effect.js +71 -0
- package/build/core/index.d.ts +1 -0
- package/build/core/index.js +1 -0
- package/build/react/glass-effect.d.ts +17 -0
- package/build/react/glass-effect.js +14 -0
- package/build/react/index.d.ts +3 -0
- package/build/react/index.js +3 -0
- package/build/solid/glass-effect.d.ts +16 -0
- package/build/solid/glass-effect.js +6 -0
- package/build/solid/index.d.ts +3 -0
- package/build/solid/index.js +3 -0
- package/build/svelte/glass-container.svelte +14 -0
- package/build/svelte/glass-container.svelte.d.ts +4 -0
- package/build/svelte/glass-props.d.ts +13 -0
- package/build/svelte/glass-props.js +1 -0
- package/build/svelte/glass-view.svelte +10 -0
- package/build/svelte/glass-view.svelte.d.ts +4 -0
- package/build/svelte/index.d.ts +4 -0
- package/build/svelte/index.js +4 -0
- package/build/vue/glass-effect.d.ts +9 -0
- package/build/vue/glass-effect.js +20 -0
- package/build/vue/index.d.ts +3 -0
- package/build/vue/index.js +3 -0
- package/build-ngc/angular/glass/index.android.d.ts +12 -0
- package/build-ngc/angular/glass/index.android.js +51 -0
- package/build-ngc/angular/glass/index.android.js.map +1 -0
- package/build-ngc/angular/glass/index.d.ts +1 -0
- package/build-ngc/angular/glass/index.ios.d.ts +14 -0
- package/build-ngc/angular/glass/index.ios.js +74 -0
- package/build-ngc/angular/glass/index.ios.js.map +1 -0
- package/build-ngc/angular/glass/index.js +3 -0
- package/build-ngc/angular/glass/index.js.map +1 -0
- package/build-ngc/angular/glass/shared.d.ts +21 -0
- package/build-ngc/angular/glass/shared.js +50 -0
- package/build-ngc/angular/glass/shared.js.map +1 -0
- package/build-ngc/angular/index.d.ts +2 -0
- package/build-ngc/angular/index.js +4 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/core/glass-effect.d.ts +62 -0
- package/build-ngc/core/glass-effect.js +72 -0
- package/build-ngc/core/glass-effect.js.map +1 -0
- package/build-ngc/core/index.d.ts +1 -0
- package/build-ngc/core/index.js +2 -0
- package/build-ngc/core/index.js.map +1 -0
- package/native-link.json +5 -0
- package/package.json +145 -3
- package/src/angular/glass/index.android.ts +31 -0
- package/src/angular/glass/index.ios.ts +61 -0
- package/src/angular/glass/index.ts +2 -0
- package/src/angular/glass/shared.ts +38 -0
- package/src/angular/index.ts +15 -0
- package/src/core/glass-effect.ts +160 -0
- package/src/core/index.ts +16 -0
- package/src/react/glass-effect.ts +34 -0
- package/src/react/index.ts +17 -0
- package/src/solid/glass-effect.ts +27 -0
- package/src/solid/index.ts +17 -0
- package/src/svelte/glass-container.svelte +14 -0
- package/src/svelte/glass-props.ts +18 -0
- package/src/svelte/glass-view.svelte +10 -0
- package/src/svelte/index.ts +18 -0
- package/src/svelte/svelte-ambient.d.ts +6 -0
- package/src/svelte/svelte-compile.test-helper.ts +6 -0
- package/src/vue/glass-effect.ts +39 -0
- 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"}
|
package/native-link.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,148 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/glass-effect",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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,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
|
+
}
|