reactnatively-glass 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 +20 -0
- package/dist/index.d.mts +93 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.js +336 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +321 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Hakizimana Fred
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { GlassElevation, GlassTintVariant, GlassHighlight, ResolvedColorScheme } from 'reactnatively-theme';
|
|
2
|
+
import React, { ReactNode } from 'react';
|
|
3
|
+
import { StyleProp, ViewStyle, AccessibilityRole } from 'react-native';
|
|
4
|
+
|
|
5
|
+
type GlassCapability = 'full' | 'partial' | 'none';
|
|
6
|
+
declare const GLASS_CAPABILITY: GlassCapability;
|
|
7
|
+
declare const SUPPORTS_BLUR: boolean;
|
|
8
|
+
declare const IS_FULL_GLASS: boolean;
|
|
9
|
+
declare const IS_PARTIAL_GLASS: boolean;
|
|
10
|
+
declare const IS_NO_GLASS: boolean;
|
|
11
|
+
declare function adjustBlurForCapability(intensity: number): number;
|
|
12
|
+
|
|
13
|
+
interface GlassConfig {
|
|
14
|
+
elevation?: GlassElevation;
|
|
15
|
+
variant?: GlassTintVariant;
|
|
16
|
+
highlight?: GlassHighlight | boolean;
|
|
17
|
+
border?: boolean;
|
|
18
|
+
borderWidth?: number;
|
|
19
|
+
blurOverride?: number;
|
|
20
|
+
tintOverride?: string;
|
|
21
|
+
glow?: {
|
|
22
|
+
color: string;
|
|
23
|
+
radius?: number;
|
|
24
|
+
opacity?: number;
|
|
25
|
+
} | false;
|
|
26
|
+
}
|
|
27
|
+
interface ResolvedGlassStyle {
|
|
28
|
+
blurAmount: number;
|
|
29
|
+
blurTint: 'light' | 'dark' | 'default';
|
|
30
|
+
tintColor: string;
|
|
31
|
+
highlightColor: string;
|
|
32
|
+
borderColor: string;
|
|
33
|
+
borderWidth: number;
|
|
34
|
+
shadowColor: string;
|
|
35
|
+
shadowOpacity: number;
|
|
36
|
+
shadowRadius: number;
|
|
37
|
+
shadowOffset: {
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
};
|
|
41
|
+
androidElevation: number;
|
|
42
|
+
capability: GlassCapability;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare function resolveGlass(config: GlassConfig, colorScheme: ResolvedColorScheme): ResolvedGlassStyle;
|
|
46
|
+
|
|
47
|
+
interface GlassViewProps {
|
|
48
|
+
elevation?: GlassElevation;
|
|
49
|
+
variant?: GlassTintVariant;
|
|
50
|
+
highlight?: GlassHighlight | boolean;
|
|
51
|
+
border?: boolean;
|
|
52
|
+
borderWidth?: number;
|
|
53
|
+
borderRadius?: number;
|
|
54
|
+
blurOverride?: number;
|
|
55
|
+
tintOverride?: string;
|
|
56
|
+
glow?: {
|
|
57
|
+
color: string;
|
|
58
|
+
radius?: number;
|
|
59
|
+
opacity?: number;
|
|
60
|
+
} | false;
|
|
61
|
+
style?: StyleProp<ViewStyle>;
|
|
62
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
63
|
+
children?: ReactNode;
|
|
64
|
+
testID?: string;
|
|
65
|
+
animated?: boolean;
|
|
66
|
+
accessible?: boolean;
|
|
67
|
+
accessibilityLabel?: string;
|
|
68
|
+
accessibilityRole?: AccessibilityRole;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* GlassView — the foundational rendering primitive of the Liquid Glass system.
|
|
73
|
+
*
|
|
74
|
+
* Layer stack (bottom → top):
|
|
75
|
+
* 1. Shadow shell — drop shadow / elevation (outer view, NO overflow:hidden)
|
|
76
|
+
* 2. Clip shell — clips all glass layers to border radius (overflow:hidden)
|
|
77
|
+
* 3. BlurView — native platform blur
|
|
78
|
+
* 4. Tint overlay — semi-transparent color layer
|
|
79
|
+
* 5. Highlight — top-edge refraction shimmer (LinearGradient)
|
|
80
|
+
* 6. Border ring — 1px glass edge line
|
|
81
|
+
* 7. Content — children, above all glass layers
|
|
82
|
+
*/
|
|
83
|
+
declare const GlassView: React.NamedExoticComponent<GlassViewProps>;
|
|
84
|
+
|
|
85
|
+
interface FrostPanelProps extends Omit<GlassViewProps, 'style'> {
|
|
86
|
+
style?: StyleProp<ViewStyle>;
|
|
87
|
+
edges?: ('top' | 'bottom' | 'left' | 'right')[];
|
|
88
|
+
}
|
|
89
|
+
declare const FrostPanel: React.NamedExoticComponent<FrostPanelProps>;
|
|
90
|
+
|
|
91
|
+
declare function useGlassStyle(config: GlassConfig): ResolvedGlassStyle;
|
|
92
|
+
|
|
93
|
+
export { FrostPanel, type FrostPanelProps, GLASS_CAPABILITY, type GlassCapability, type GlassConfig, GlassView, type GlassViewProps, IS_FULL_GLASS, IS_NO_GLASS, IS_PARTIAL_GLASS, type ResolvedGlassStyle, SUPPORTS_BLUR, adjustBlurForCapability, resolveGlass, useGlassStyle };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { GlassElevation, GlassTintVariant, GlassHighlight, ResolvedColorScheme } from 'reactnatively-theme';
|
|
2
|
+
import React, { ReactNode } from 'react';
|
|
3
|
+
import { StyleProp, ViewStyle, AccessibilityRole } from 'react-native';
|
|
4
|
+
|
|
5
|
+
type GlassCapability = 'full' | 'partial' | 'none';
|
|
6
|
+
declare const GLASS_CAPABILITY: GlassCapability;
|
|
7
|
+
declare const SUPPORTS_BLUR: boolean;
|
|
8
|
+
declare const IS_FULL_GLASS: boolean;
|
|
9
|
+
declare const IS_PARTIAL_GLASS: boolean;
|
|
10
|
+
declare const IS_NO_GLASS: boolean;
|
|
11
|
+
declare function adjustBlurForCapability(intensity: number): number;
|
|
12
|
+
|
|
13
|
+
interface GlassConfig {
|
|
14
|
+
elevation?: GlassElevation;
|
|
15
|
+
variant?: GlassTintVariant;
|
|
16
|
+
highlight?: GlassHighlight | boolean;
|
|
17
|
+
border?: boolean;
|
|
18
|
+
borderWidth?: number;
|
|
19
|
+
blurOverride?: number;
|
|
20
|
+
tintOverride?: string;
|
|
21
|
+
glow?: {
|
|
22
|
+
color: string;
|
|
23
|
+
radius?: number;
|
|
24
|
+
opacity?: number;
|
|
25
|
+
} | false;
|
|
26
|
+
}
|
|
27
|
+
interface ResolvedGlassStyle {
|
|
28
|
+
blurAmount: number;
|
|
29
|
+
blurTint: 'light' | 'dark' | 'default';
|
|
30
|
+
tintColor: string;
|
|
31
|
+
highlightColor: string;
|
|
32
|
+
borderColor: string;
|
|
33
|
+
borderWidth: number;
|
|
34
|
+
shadowColor: string;
|
|
35
|
+
shadowOpacity: number;
|
|
36
|
+
shadowRadius: number;
|
|
37
|
+
shadowOffset: {
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
};
|
|
41
|
+
androidElevation: number;
|
|
42
|
+
capability: GlassCapability;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare function resolveGlass(config: GlassConfig, colorScheme: ResolvedColorScheme): ResolvedGlassStyle;
|
|
46
|
+
|
|
47
|
+
interface GlassViewProps {
|
|
48
|
+
elevation?: GlassElevation;
|
|
49
|
+
variant?: GlassTintVariant;
|
|
50
|
+
highlight?: GlassHighlight | boolean;
|
|
51
|
+
border?: boolean;
|
|
52
|
+
borderWidth?: number;
|
|
53
|
+
borderRadius?: number;
|
|
54
|
+
blurOverride?: number;
|
|
55
|
+
tintOverride?: string;
|
|
56
|
+
glow?: {
|
|
57
|
+
color: string;
|
|
58
|
+
radius?: number;
|
|
59
|
+
opacity?: number;
|
|
60
|
+
} | false;
|
|
61
|
+
style?: StyleProp<ViewStyle>;
|
|
62
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
63
|
+
children?: ReactNode;
|
|
64
|
+
testID?: string;
|
|
65
|
+
animated?: boolean;
|
|
66
|
+
accessible?: boolean;
|
|
67
|
+
accessibilityLabel?: string;
|
|
68
|
+
accessibilityRole?: AccessibilityRole;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* GlassView — the foundational rendering primitive of the Liquid Glass system.
|
|
73
|
+
*
|
|
74
|
+
* Layer stack (bottom → top):
|
|
75
|
+
* 1. Shadow shell — drop shadow / elevation (outer view, NO overflow:hidden)
|
|
76
|
+
* 2. Clip shell — clips all glass layers to border radius (overflow:hidden)
|
|
77
|
+
* 3. BlurView — native platform blur
|
|
78
|
+
* 4. Tint overlay — semi-transparent color layer
|
|
79
|
+
* 5. Highlight — top-edge refraction shimmer (LinearGradient)
|
|
80
|
+
* 6. Border ring — 1px glass edge line
|
|
81
|
+
* 7. Content — children, above all glass layers
|
|
82
|
+
*/
|
|
83
|
+
declare const GlassView: React.NamedExoticComponent<GlassViewProps>;
|
|
84
|
+
|
|
85
|
+
interface FrostPanelProps extends Omit<GlassViewProps, 'style'> {
|
|
86
|
+
style?: StyleProp<ViewStyle>;
|
|
87
|
+
edges?: ('top' | 'bottom' | 'left' | 'right')[];
|
|
88
|
+
}
|
|
89
|
+
declare const FrostPanel: React.NamedExoticComponent<FrostPanelProps>;
|
|
90
|
+
|
|
91
|
+
declare function useGlassStyle(config: GlassConfig): ResolvedGlassStyle;
|
|
92
|
+
|
|
93
|
+
export { FrostPanel, type FrostPanelProps, GLASS_CAPABILITY, type GlassCapability, type GlassConfig, GlassView, type GlassViewProps, IS_FULL_GLASS, IS_NO_GLASS, IS_PARTIAL_GLASS, type ResolvedGlassStyle, SUPPORTS_BLUR, adjustBlurForCapability, resolveGlass, useGlassStyle };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var reactnativelyTheme = require('reactnatively-theme');
|
|
4
|
+
var reactnativelyUtils = require('reactnatively-utils');
|
|
5
|
+
var React = require('react');
|
|
6
|
+
var reactNative = require('react-native');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
12
|
+
|
|
13
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
14
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
15
|
+
}) : x)(function(x) {
|
|
16
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
17
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
18
|
+
});
|
|
19
|
+
function detectCapability() {
|
|
20
|
+
if (reactnativelyUtils.IS_IOS) return "full";
|
|
21
|
+
if (reactnativelyUtils.IS_ANDROID) {
|
|
22
|
+
const version = reactnativelyUtils.getAndroidVersion();
|
|
23
|
+
return version >= 31 ? "partial" : "none";
|
|
24
|
+
}
|
|
25
|
+
if (reactnativelyUtils.IS_WEB) return "partial";
|
|
26
|
+
return "none";
|
|
27
|
+
}
|
|
28
|
+
var GLASS_CAPABILITY = detectCapability();
|
|
29
|
+
var SUPPORTS_BLUR = GLASS_CAPABILITY !== "none";
|
|
30
|
+
var IS_FULL_GLASS = GLASS_CAPABILITY === "full";
|
|
31
|
+
var IS_PARTIAL_GLASS = GLASS_CAPABILITY === "partial";
|
|
32
|
+
var IS_NO_GLASS = GLASS_CAPABILITY === "none";
|
|
33
|
+
function adjustBlurForCapability(intensity) {
|
|
34
|
+
if (GLASS_CAPABILITY === "full") return intensity;
|
|
35
|
+
if (GLASS_CAPABILITY === "partial") return Math.round(intensity * 0.65);
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
var ANDROID_BLUR_METHOD = reactnativelyUtils.IS_ANDROID ? "dimezisBlurView" : void 0;
|
|
39
|
+
|
|
40
|
+
// src/engine/GlassEngine.ts
|
|
41
|
+
function resolveGlass(config, colorScheme) {
|
|
42
|
+
const {
|
|
43
|
+
elevation = 2,
|
|
44
|
+
variant = "surface",
|
|
45
|
+
highlight = true,
|
|
46
|
+
border = true,
|
|
47
|
+
borderWidth = 1,
|
|
48
|
+
blurOverride,
|
|
49
|
+
tintOverride
|
|
50
|
+
} = config;
|
|
51
|
+
const elevationConfig = reactnativelyTheme.glassTokens.elevation[elevation];
|
|
52
|
+
const rawBlur = blurOverride ?? elevationConfig.blur;
|
|
53
|
+
const blurAmount = adjustBlurForCapability(rawBlur);
|
|
54
|
+
const tintColor = tintOverride ?? reactnativelyTheme.glassTokens.tint[colorScheme][variant];
|
|
55
|
+
let highlightColor = "transparent";
|
|
56
|
+
if (highlight === true) {
|
|
57
|
+
highlightColor = reactnativelyTheme.glassTokens.highlight.medium;
|
|
58
|
+
} else if (highlight !== false && highlight !== "none") {
|
|
59
|
+
highlightColor = reactnativelyTheme.glassTokens.highlight[highlight];
|
|
60
|
+
}
|
|
61
|
+
const borderColor = border ? reactnativelyTheme.glassTokens.border[colorScheme].medium : "transparent";
|
|
62
|
+
const shadowColor = colorScheme === "dark" ? "#000" : "#1a1a2e";
|
|
63
|
+
const shadowOpacity = elevationConfig.shadowOpacity;
|
|
64
|
+
const shadowRadius = elevationConfig.shadowRadius;
|
|
65
|
+
const shadowOffset = { width: 0, height: elevationConfig.shadowY };
|
|
66
|
+
const androidElevation = Math.round(elevation * 3);
|
|
67
|
+
const blurTint = colorScheme === "dark" ? "dark" : "light";
|
|
68
|
+
return {
|
|
69
|
+
blurAmount,
|
|
70
|
+
blurTint,
|
|
71
|
+
tintColor,
|
|
72
|
+
highlightColor,
|
|
73
|
+
borderColor,
|
|
74
|
+
borderWidth,
|
|
75
|
+
shadowColor,
|
|
76
|
+
shadowOpacity,
|
|
77
|
+
shadowRadius,
|
|
78
|
+
shadowOffset,
|
|
79
|
+
androidElevation,
|
|
80
|
+
capability: GLASS_CAPABILITY
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
var BlurViewImpl = null;
|
|
84
|
+
var blurLoaded = false;
|
|
85
|
+
var LinearGradientImpl = null;
|
|
86
|
+
var gradientLoaded = false;
|
|
87
|
+
function loadBlurView() {
|
|
88
|
+
if (blurLoaded) return BlurViewImpl;
|
|
89
|
+
blurLoaded = true;
|
|
90
|
+
try {
|
|
91
|
+
BlurViewImpl = __require("expo-blur").BlurView;
|
|
92
|
+
} catch {
|
|
93
|
+
BlurViewImpl = null;
|
|
94
|
+
}
|
|
95
|
+
return BlurViewImpl;
|
|
96
|
+
}
|
|
97
|
+
function loadLinearGradient() {
|
|
98
|
+
if (gradientLoaded) return LinearGradientImpl;
|
|
99
|
+
gradientLoaded = true;
|
|
100
|
+
try {
|
|
101
|
+
const gradientModule = __require("react-native-linear-gradient");
|
|
102
|
+
LinearGradientImpl = gradientModule?.default ?? gradientModule;
|
|
103
|
+
} catch {
|
|
104
|
+
LinearGradientImpl = null;
|
|
105
|
+
}
|
|
106
|
+
return LinearGradientImpl;
|
|
107
|
+
}
|
|
108
|
+
var GlassView = React__default.default.memo(
|
|
109
|
+
({
|
|
110
|
+
elevation = 2,
|
|
111
|
+
variant = "surface",
|
|
112
|
+
highlight = true,
|
|
113
|
+
border = true,
|
|
114
|
+
borderWidth = 1,
|
|
115
|
+
borderRadius = 16,
|
|
116
|
+
blurOverride,
|
|
117
|
+
tintOverride,
|
|
118
|
+
glow,
|
|
119
|
+
style,
|
|
120
|
+
contentStyle,
|
|
121
|
+
children,
|
|
122
|
+
testID,
|
|
123
|
+
accessible,
|
|
124
|
+
accessibilityLabel,
|
|
125
|
+
accessibilityRole
|
|
126
|
+
}) => {
|
|
127
|
+
const { colorScheme } = reactnativelyTheme.useTheme();
|
|
128
|
+
const resolved = React.useMemo(
|
|
129
|
+
() => resolveGlass(
|
|
130
|
+
{ elevation, variant, highlight, border, borderWidth, blurOverride, tintOverride },
|
|
131
|
+
colorScheme
|
|
132
|
+
),
|
|
133
|
+
[elevation, variant, highlight, border, borderWidth, blurOverride, tintOverride, colorScheme]
|
|
134
|
+
);
|
|
135
|
+
const shadowStyle = React.useMemo(() => {
|
|
136
|
+
if (reactNative.Platform.OS === "android") {
|
|
137
|
+
return { elevation: resolved.androidElevation };
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
shadowColor: resolved.shadowColor,
|
|
141
|
+
shadowOffset: resolved.shadowOffset,
|
|
142
|
+
shadowOpacity: resolved.shadowOpacity,
|
|
143
|
+
shadowRadius: resolved.shadowRadius
|
|
144
|
+
};
|
|
145
|
+
}, [resolved]);
|
|
146
|
+
const glowStyle = React.useMemo(() => {
|
|
147
|
+
if (!glow || reactNative.Platform.OS !== "ios") return {};
|
|
148
|
+
return {
|
|
149
|
+
shadowColor: glow.color,
|
|
150
|
+
shadowOffset: { width: 0, height: 0 },
|
|
151
|
+
shadowOpacity: glow.opacity ?? 0.35,
|
|
152
|
+
shadowRadius: glow.radius ?? 24
|
|
153
|
+
};
|
|
154
|
+
}, [glow]);
|
|
155
|
+
const BlurViewComponent = loadBlurView();
|
|
156
|
+
const LinearGradientComponent = loadLinearGradient();
|
|
157
|
+
if (IS_NO_GLASS) {
|
|
158
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
159
|
+
reactNative.View,
|
|
160
|
+
{
|
|
161
|
+
testID,
|
|
162
|
+
accessible,
|
|
163
|
+
accessibilityLabel,
|
|
164
|
+
accessibilityRole,
|
|
165
|
+
style: [styles.shadowShell, { borderRadius }, shadowStyle, style],
|
|
166
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.clipShell, { borderRadius }], children: [
|
|
167
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
168
|
+
reactNative.View,
|
|
169
|
+
{
|
|
170
|
+
style: [
|
|
171
|
+
reactNative.StyleSheet.absoluteFill,
|
|
172
|
+
{ backgroundColor: resolved.tintColor }
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
),
|
|
176
|
+
resolved.highlightColor !== "transparent" && (LinearGradientComponent ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
177
|
+
LinearGradientComponent,
|
|
178
|
+
{
|
|
179
|
+
colors: [resolved.highlightColor, "transparent"],
|
|
180
|
+
start: { x: 0, y: 0 },
|
|
181
|
+
end: { x: 0, y: 1 },
|
|
182
|
+
style: styles.highlightGradient
|
|
183
|
+
}
|
|
184
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
185
|
+
reactNative.View,
|
|
186
|
+
{
|
|
187
|
+
pointerEvents: "none",
|
|
188
|
+
style: [
|
|
189
|
+
styles.highlightGradient,
|
|
190
|
+
{ backgroundColor: resolved.highlightColor, opacity: 0.2 }
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
)),
|
|
194
|
+
border && /* @__PURE__ */ jsxRuntime.jsx(
|
|
195
|
+
reactNative.View,
|
|
196
|
+
{
|
|
197
|
+
pointerEvents: "none",
|
|
198
|
+
style: [
|
|
199
|
+
reactNative.StyleSheet.absoluteFill,
|
|
200
|
+
{ borderWidth: resolved.borderWidth, borderColor: resolved.borderColor }
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
),
|
|
204
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.content, contentStyle], children })
|
|
205
|
+
] })
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
210
|
+
reactNative.View,
|
|
211
|
+
{
|
|
212
|
+
testID,
|
|
213
|
+
accessible,
|
|
214
|
+
accessibilityLabel,
|
|
215
|
+
accessibilityRole,
|
|
216
|
+
style: [styles.shadowShell, { borderRadius }, shadowStyle, glowStyle, style],
|
|
217
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.clipShell, { borderRadius }], children: [
|
|
218
|
+
BlurViewComponent ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
219
|
+
BlurViewComponent,
|
|
220
|
+
{
|
|
221
|
+
intensity: resolved.blurAmount,
|
|
222
|
+
tint: resolved.blurTint,
|
|
223
|
+
experimentalBlurMethod: ANDROID_BLUR_METHOD,
|
|
224
|
+
style: reactNative.StyleSheet.absoluteFill
|
|
225
|
+
}
|
|
226
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
227
|
+
reactNative.View,
|
|
228
|
+
{
|
|
229
|
+
pointerEvents: "none",
|
|
230
|
+
style: [reactNative.StyleSheet.absoluteFill, { backgroundColor: resolved.tintColor }]
|
|
231
|
+
}
|
|
232
|
+
),
|
|
233
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
234
|
+
reactNative.View,
|
|
235
|
+
{
|
|
236
|
+
pointerEvents: "none",
|
|
237
|
+
style: [reactNative.StyleSheet.absoluteFill, { backgroundColor: resolved.tintColor }]
|
|
238
|
+
}
|
|
239
|
+
),
|
|
240
|
+
resolved.highlightColor !== "transparent" && (LinearGradientComponent ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
241
|
+
LinearGradientComponent,
|
|
242
|
+
{
|
|
243
|
+
colors: [resolved.highlightColor, "transparent"],
|
|
244
|
+
start: { x: 0, y: 0 },
|
|
245
|
+
end: { x: 0, y: 1 },
|
|
246
|
+
style: styles.highlightGradient,
|
|
247
|
+
pointerEvents: "none"
|
|
248
|
+
}
|
|
249
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
250
|
+
reactNative.View,
|
|
251
|
+
{
|
|
252
|
+
pointerEvents: "none",
|
|
253
|
+
style: [
|
|
254
|
+
styles.highlightGradient,
|
|
255
|
+
{ backgroundColor: resolved.highlightColor, opacity: 0.18 }
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
)),
|
|
259
|
+
border && /* @__PURE__ */ jsxRuntime.jsx(
|
|
260
|
+
reactNative.View,
|
|
261
|
+
{
|
|
262
|
+
pointerEvents: "none",
|
|
263
|
+
style: [
|
|
264
|
+
reactNative.StyleSheet.absoluteFill,
|
|
265
|
+
{ borderWidth: resolved.borderWidth, borderColor: resolved.borderColor }
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
),
|
|
269
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.content, contentStyle], children })
|
|
270
|
+
] })
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
GlassView.displayName = "GlassView";
|
|
276
|
+
var styles = reactNative.StyleSheet.create({
|
|
277
|
+
shadowShell: {
|
|
278
|
+
// Shadow lives here — no overflow:hidden so Android elevation renders
|
|
279
|
+
},
|
|
280
|
+
clipShell: {
|
|
281
|
+
overflow: "hidden"
|
|
282
|
+
},
|
|
283
|
+
highlightGradient: {
|
|
284
|
+
position: "absolute",
|
|
285
|
+
top: 0,
|
|
286
|
+
left: 0,
|
|
287
|
+
right: 0,
|
|
288
|
+
height: "35%"
|
|
289
|
+
},
|
|
290
|
+
content: {
|
|
291
|
+
position: "relative"
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
var FrostPanel = React__default.default.memo(
|
|
295
|
+
({ style, edges, borderRadius, children, ...glassProps }) => {
|
|
296
|
+
const radiusStyle = edges ? {
|
|
297
|
+
borderTopLeftRadius: edges.includes("top") ? borderRadius ?? 20 : 0,
|
|
298
|
+
borderTopRightRadius: edges.includes("top") ? borderRadius ?? 20 : 0,
|
|
299
|
+
borderBottomLeftRadius: edges.includes("bottom") ? borderRadius ?? 20 : 0,
|
|
300
|
+
borderBottomRightRadius: edges.includes("bottom") ? borderRadius ?? 20 : 0
|
|
301
|
+
} : { borderRadius: borderRadius ?? 20 };
|
|
302
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
303
|
+
GlassView,
|
|
304
|
+
{
|
|
305
|
+
elevation: glassProps.elevation ?? 3,
|
|
306
|
+
style: [styles2.panel, style],
|
|
307
|
+
borderRadius: 0,
|
|
308
|
+
...glassProps,
|
|
309
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: radiusStyle, children })
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
);
|
|
314
|
+
FrostPanel.displayName = "FrostPanel";
|
|
315
|
+
var styles2 = reactNative.StyleSheet.create({
|
|
316
|
+
panel: {
|
|
317
|
+
width: "100%"
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
function useGlassStyle(config) {
|
|
321
|
+
const { colorScheme } = reactnativelyTheme.useTheme();
|
|
322
|
+
return React.useMemo(() => resolveGlass(config, colorScheme), [config, colorScheme]);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
exports.FrostPanel = FrostPanel;
|
|
326
|
+
exports.GLASS_CAPABILITY = GLASS_CAPABILITY;
|
|
327
|
+
exports.GlassView = GlassView;
|
|
328
|
+
exports.IS_FULL_GLASS = IS_FULL_GLASS;
|
|
329
|
+
exports.IS_NO_GLASS = IS_NO_GLASS;
|
|
330
|
+
exports.IS_PARTIAL_GLASS = IS_PARTIAL_GLASS;
|
|
331
|
+
exports.SUPPORTS_BLUR = SUPPORTS_BLUR;
|
|
332
|
+
exports.adjustBlurForCapability = adjustBlurForCapability;
|
|
333
|
+
exports.resolveGlass = resolveGlass;
|
|
334
|
+
exports.useGlassStyle = useGlassStyle;
|
|
335
|
+
//# sourceMappingURL=index.js.map
|
|
336
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/engine/CapabilityDetector.ts","../src/engine/GlassEngine.ts","../src/components/GlassView/GlassView.tsx","../src/components/FrostPanel/FrostPanel.tsx","../src/hooks/useGlassStyle.ts"],"names":["IS_IOS","IS_ANDROID","getAndroidVersion","IS_WEB","glassTokens","React","useTheme","useMemo","Platform","jsx","View","jsxs","StyleSheet","styles"],"mappings":"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","file":"index.js","sourcesContent":["import { IS_IOS, IS_ANDROID, IS_WEB, getAndroidVersion } from 'reactnatively-utils';\n\nexport type GlassCapability = 'full' | 'partial' | 'none';\n\nfunction detectCapability(): GlassCapability {\n if (IS_IOS) return 'full';\n if (IS_ANDROID) {\n const version = getAndroidVersion();\n return version >= 31 ? 'partial' : 'none';\n }\n if (IS_WEB) return 'partial';\n return 'none';\n}\n\nexport const GLASS_CAPABILITY: GlassCapability = detectCapability();\nexport const SUPPORTS_BLUR: boolean = GLASS_CAPABILITY !== 'none';\nexport const IS_FULL_GLASS: boolean = GLASS_CAPABILITY === 'full';\nexport const IS_PARTIAL_GLASS: boolean = GLASS_CAPABILITY === 'partial';\nexport const IS_NO_GLASS: boolean = GLASS_CAPABILITY === 'none';\n\nexport function adjustBlurForCapability(intensity: number): number {\n if (GLASS_CAPABILITY === 'full') return intensity;\n if (GLASS_CAPABILITY === 'partial') return Math.round(intensity * 0.65);\n return 0;\n}\n\nexport const ANDROID_BLUR_METHOD = IS_ANDROID ? 'dimezisBlurView' : undefined;\n","import { glassTokens } from 'reactnatively-theme';\nimport type { ResolvedColorScheme } from 'reactnatively-theme';\nimport type { GlassConfig, ResolvedGlassStyle } from './GlassEngine.types';\nimport { GLASS_CAPABILITY, adjustBlurForCapability } from './CapabilityDetector';\n\n// Core resolver: takes a GlassConfig + color scheme, returns a fully\n// resolved style recipe that GlassView uses to render all layers.\nexport function resolveGlass(\n config: GlassConfig,\n colorScheme: ResolvedColorScheme,\n): ResolvedGlassStyle {\n const {\n elevation = 2,\n variant = 'surface',\n highlight = true,\n border = true,\n borderWidth = 1,\n blurOverride,\n tintOverride,\n } = config;\n\n const elevationConfig = glassTokens.elevation[elevation];\n\n // Blur\n const rawBlur = blurOverride ?? elevationConfig.blur;\n const blurAmount = adjustBlurForCapability(rawBlur);\n\n // Tint\n const tintColor = tintOverride ?? glassTokens.tint[colorScheme][variant];\n\n // Highlight\n let highlightColor = 'transparent';\n if (highlight === true) {\n highlightColor = glassTokens.highlight.medium;\n } else if (highlight !== false && highlight !== 'none') {\n highlightColor = glassTokens.highlight[highlight];\n }\n\n // Border\n const borderColor = border\n ? glassTokens.border[colorScheme].medium\n : 'transparent';\n\n // Shadow — glass shadows use a deep, soft spread\n const shadowColor = colorScheme === 'dark' ? '#000' : '#1a1a2e';\n const shadowOpacity = elevationConfig.shadowOpacity;\n const shadowRadius = elevationConfig.shadowRadius;\n const shadowOffset = { width: 0, height: elevationConfig.shadowY };\n const androidElevation = Math.round(elevation * 3);\n\n // BlurView tint hint — 'light' | 'dark' | 'default'\n const blurTint = colorScheme === 'dark' ? 'dark' : 'light';\n\n return {\n blurAmount,\n blurTint,\n tintColor,\n highlightColor,\n borderColor,\n borderWidth,\n shadowColor,\n shadowOpacity,\n shadowRadius,\n shadowOffset,\n androidElevation,\n capability: GLASS_CAPABILITY,\n };\n}\n","import React, { useMemo } from 'react';\nimport {\n View,\n StyleSheet,\n Platform,\n type ViewStyle,\n} from 'react-native';\nimport { useTheme } from 'reactnatively-theme';\nimport { resolveGlass } from '../../engine/GlassEngine';\nimport { IS_NO_GLASS, ANDROID_BLUR_METHOD } from '../../engine/CapabilityDetector';\nimport type { GlassViewProps } from './GlassView.types';\n\nlet BlurViewImpl: typeof import('expo-blur').BlurView | null = null;\nlet blurLoaded = false;\n\nlet LinearGradientImpl:\n | typeof import('react-native-linear-gradient').default\n | null = null;\nlet gradientLoaded = false;\n\nfunction loadBlurView(): typeof import('expo-blur').BlurView | null {\n if (blurLoaded) return BlurViewImpl;\n blurLoaded = true;\n try {\n // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires\n BlurViewImpl = require('expo-blur').BlurView;\n } catch {\n BlurViewImpl = null;\n }\n return BlurViewImpl;\n}\n\nfunction loadLinearGradient():\n | typeof import('react-native-linear-gradient').default\n | null {\n if (gradientLoaded) return LinearGradientImpl;\n gradientLoaded = true;\n try {\n // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires\n const gradientModule = require('react-native-linear-gradient');\n LinearGradientImpl = gradientModule?.default ?? gradientModule;\n } catch {\n LinearGradientImpl = null;\n }\n return LinearGradientImpl;\n}\n\n/**\n * GlassView — the foundational rendering primitive of the Liquid Glass system.\n *\n * Layer stack (bottom → top):\n * 1. Shadow shell — drop shadow / elevation (outer view, NO overflow:hidden)\n * 2. Clip shell — clips all glass layers to border radius (overflow:hidden)\n * 3. BlurView — native platform blur\n * 4. Tint overlay — semi-transparent color layer\n * 5. Highlight — top-edge refraction shimmer (LinearGradient)\n * 6. Border ring — 1px glass edge line\n * 7. Content — children, above all glass layers\n */\nexport const GlassView = React.memo<GlassViewProps>(\n ({\n elevation = 2,\n variant = 'surface',\n highlight = true,\n border = true,\n borderWidth = 1,\n borderRadius = 16,\n blurOverride,\n tintOverride,\n glow,\n style,\n contentStyle,\n children,\n testID,\n accessible,\n accessibilityLabel,\n accessibilityRole,\n }) => {\n const { colorScheme } = useTheme();\n\n const resolved = useMemo(\n () =>\n resolveGlass(\n { elevation, variant, highlight, border, borderWidth, blurOverride, tintOverride },\n colorScheme,\n ),\n [elevation, variant, highlight, border, borderWidth, blurOverride, tintOverride, colorScheme],\n );\n\n const shadowStyle = useMemo((): ViewStyle => {\n if (Platform.OS === 'android') {\n return { elevation: resolved.androidElevation };\n }\n return {\n shadowColor: resolved.shadowColor,\n shadowOffset: resolved.shadowOffset,\n shadowOpacity: resolved.shadowOpacity,\n shadowRadius: resolved.shadowRadius,\n };\n }, [resolved]);\n\n const glowStyle = useMemo((): ViewStyle => {\n if (!glow || Platform.OS !== 'ios') return {};\n return {\n shadowColor: glow.color,\n shadowOffset: { width: 0, height: 0 },\n shadowOpacity: glow.opacity ?? 0.35,\n shadowRadius: glow.radius ?? 24,\n };\n }, [glow]);\n\n const BlurViewComponent = loadBlurView();\n const LinearGradientComponent = loadLinearGradient();\n\n // Fallback: no-blur devices — solid semi-transparent + gradient only\n if (IS_NO_GLASS) {\n return (\n <View\n testID={testID}\n accessible={accessible}\n accessibilityLabel={accessibilityLabel}\n accessibilityRole={accessibilityRole}\n style={[styles.shadowShell, { borderRadius }, shadowStyle, style]}\n >\n <View style={[styles.clipShell, { borderRadius }]}>\n <View\n style={[\n StyleSheet.absoluteFill,\n { backgroundColor: resolved.tintColor },\n ]}\n />\n {resolved.highlightColor !== 'transparent' && (\n LinearGradientComponent ? (\n <LinearGradientComponent\n colors={[resolved.highlightColor, 'transparent']}\n start={{ x: 0, y: 0 }}\n end={{ x: 0, y: 1 }}\n style={styles.highlightGradient}\n />\n ) : (\n <View\n pointerEvents=\"none\"\n style={[\n styles.highlightGradient,\n { backgroundColor: resolved.highlightColor, opacity: 0.2 },\n ]}\n />\n )\n )}\n {border && (\n <View\n pointerEvents=\"none\"\n style={[\n StyleSheet.absoluteFill,\n { borderWidth: resolved.borderWidth, borderColor: resolved.borderColor },\n ]}\n />\n )}\n <View style={[styles.content, contentStyle]}>{children}</View>\n </View>\n </View>\n );\n }\n\n return (\n <View\n testID={testID}\n accessible={accessible}\n accessibilityLabel={accessibilityLabel}\n accessibilityRole={accessibilityRole}\n style={[styles.shadowShell, { borderRadius }, shadowStyle, glowStyle, style]}\n >\n <View style={[styles.clipShell, { borderRadius }]}>\n {/* Layer 1: Native blur */}\n {BlurViewComponent ? (\n <BlurViewComponent\n intensity={resolved.blurAmount}\n tint={resolved.blurTint}\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore — experimentalBlurMethod is Android-only, conditionally applied\n experimentalBlurMethod={ANDROID_BLUR_METHOD}\n style={StyleSheet.absoluteFill}\n />\n ) : (\n <View\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: resolved.tintColor }]}\n />\n )}\n\n {/* Layer 2: Tint overlay */}\n <View\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: resolved.tintColor }]}\n />\n\n {/* Layer 3: Top-edge highlight — the \"liquid glass\" refraction shimmer */}\n {resolved.highlightColor !== 'transparent' && (\n LinearGradientComponent ? (\n <LinearGradientComponent\n colors={[resolved.highlightColor, 'transparent']}\n start={{ x: 0, y: 0 }}\n end={{ x: 0, y: 1 }}\n style={styles.highlightGradient}\n pointerEvents=\"none\"\n />\n ) : (\n <View\n pointerEvents=\"none\"\n style={[\n styles.highlightGradient,\n { backgroundColor: resolved.highlightColor, opacity: 0.18 },\n ]}\n />\n )\n )}\n\n {/* Layer 4: Glass edge border */}\n {border && (\n <View\n pointerEvents=\"none\"\n style={[\n StyleSheet.absoluteFill,\n { borderWidth: resolved.borderWidth, borderColor: resolved.borderColor },\n ]}\n />\n )}\n\n {/* Layer 5: Children */}\n <View style={[styles.content, contentStyle]}>{children}</View>\n </View>\n </View>\n );\n },\n);\n\nGlassView.displayName = 'GlassView';\n\nconst styles = StyleSheet.create({\n shadowShell: {\n // Shadow lives here — no overflow:hidden so Android elevation renders\n },\n clipShell: {\n overflow: 'hidden',\n },\n highlightGradient: {\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n height: '35%',\n },\n content: {\n position: 'relative',\n },\n});\n","import React from 'react';\nimport { View, StyleSheet, type StyleProp, type ViewStyle } from 'react-native';\nimport { GlassView } from '../GlassView';\nimport type { GlassViewProps } from '../GlassView/GlassView.types';\n\n// FrostPanel: full-width glass surface for bottom sheets, headers, sidebars.\n// Unlike GlassView (which sizes to content), FrostPanel fills its container.\nexport interface FrostPanelProps extends Omit<GlassViewProps, 'style'> {\n style?: StyleProp<ViewStyle>;\n edges?: ('top' | 'bottom' | 'left' | 'right')[];\n}\n\nexport const FrostPanel = React.memo<FrostPanelProps>(\n ({ style, edges, borderRadius, children, ...glassProps }) => {\n // For panels, we typically want sharp edges on some sides\n // e.g., bottom sheet: round top, flat bottom (or vice versa)\n const radiusStyle = edges\n ? {\n borderTopLeftRadius: edges.includes('top') ? (borderRadius ?? 20) : 0,\n borderTopRightRadius: edges.includes('top') ? (borderRadius ?? 20) : 0,\n borderBottomLeftRadius: edges.includes('bottom') ? (borderRadius ?? 20) : 0,\n borderBottomRightRadius: edges.includes('bottom') ? (borderRadius ?? 20) : 0,\n }\n : { borderRadius: borderRadius ?? 20 };\n\n return (\n <GlassView\n elevation={glassProps.elevation ?? 3}\n style={[styles.panel, style]}\n borderRadius={0} // We handle radius manually for panels\n {...glassProps}\n >\n <View style={radiusStyle}>\n {children}\n </View>\n </GlassView>\n );\n },\n);\n\nFrostPanel.displayName = 'FrostPanel';\n\nconst styles = StyleSheet.create({\n panel: {\n width: '100%',\n },\n});\n","import { useMemo } from 'react';\nimport { useTheme } from 'reactnatively-theme';\nimport { resolveGlass } from '../engine/GlassEngine';\nimport type { GlassConfig, ResolvedGlassStyle } from '../engine/GlassEngine.types';\n\n// Hook: resolves the full glass style recipe using the current theme's color scheme.\n// Use this when building custom glass components outside of GlassView.\nexport function useGlassStyle(config: GlassConfig): ResolvedGlassStyle {\n const { colorScheme } = useTheme();\n return useMemo(() => resolveGlass(config, colorScheme), [config, colorScheme]);\n}\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { useTheme, glassTokens } from 'reactnatively-theme';
|
|
2
|
+
import { IS_IOS, IS_ANDROID, getAndroidVersion, IS_WEB } from 'reactnatively-utils';
|
|
3
|
+
import React, { useMemo } from 'react';
|
|
4
|
+
import { Platform, View, StyleSheet } from 'react-native';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
+
});
|
|
13
|
+
function detectCapability() {
|
|
14
|
+
if (IS_IOS) return "full";
|
|
15
|
+
if (IS_ANDROID) {
|
|
16
|
+
const version = getAndroidVersion();
|
|
17
|
+
return version >= 31 ? "partial" : "none";
|
|
18
|
+
}
|
|
19
|
+
if (IS_WEB) return "partial";
|
|
20
|
+
return "none";
|
|
21
|
+
}
|
|
22
|
+
var GLASS_CAPABILITY = detectCapability();
|
|
23
|
+
var SUPPORTS_BLUR = GLASS_CAPABILITY !== "none";
|
|
24
|
+
var IS_FULL_GLASS = GLASS_CAPABILITY === "full";
|
|
25
|
+
var IS_PARTIAL_GLASS = GLASS_CAPABILITY === "partial";
|
|
26
|
+
var IS_NO_GLASS = GLASS_CAPABILITY === "none";
|
|
27
|
+
function adjustBlurForCapability(intensity) {
|
|
28
|
+
if (GLASS_CAPABILITY === "full") return intensity;
|
|
29
|
+
if (GLASS_CAPABILITY === "partial") return Math.round(intensity * 0.65);
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
var ANDROID_BLUR_METHOD = IS_ANDROID ? "dimezisBlurView" : void 0;
|
|
33
|
+
|
|
34
|
+
// src/engine/GlassEngine.ts
|
|
35
|
+
function resolveGlass(config, colorScheme) {
|
|
36
|
+
const {
|
|
37
|
+
elevation = 2,
|
|
38
|
+
variant = "surface",
|
|
39
|
+
highlight = true,
|
|
40
|
+
border = true,
|
|
41
|
+
borderWidth = 1,
|
|
42
|
+
blurOverride,
|
|
43
|
+
tintOverride
|
|
44
|
+
} = config;
|
|
45
|
+
const elevationConfig = glassTokens.elevation[elevation];
|
|
46
|
+
const rawBlur = blurOverride ?? elevationConfig.blur;
|
|
47
|
+
const blurAmount = adjustBlurForCapability(rawBlur);
|
|
48
|
+
const tintColor = tintOverride ?? glassTokens.tint[colorScheme][variant];
|
|
49
|
+
let highlightColor = "transparent";
|
|
50
|
+
if (highlight === true) {
|
|
51
|
+
highlightColor = glassTokens.highlight.medium;
|
|
52
|
+
} else if (highlight !== false && highlight !== "none") {
|
|
53
|
+
highlightColor = glassTokens.highlight[highlight];
|
|
54
|
+
}
|
|
55
|
+
const borderColor = border ? glassTokens.border[colorScheme].medium : "transparent";
|
|
56
|
+
const shadowColor = colorScheme === "dark" ? "#000" : "#1a1a2e";
|
|
57
|
+
const shadowOpacity = elevationConfig.shadowOpacity;
|
|
58
|
+
const shadowRadius = elevationConfig.shadowRadius;
|
|
59
|
+
const shadowOffset = { width: 0, height: elevationConfig.shadowY };
|
|
60
|
+
const androidElevation = Math.round(elevation * 3);
|
|
61
|
+
const blurTint = colorScheme === "dark" ? "dark" : "light";
|
|
62
|
+
return {
|
|
63
|
+
blurAmount,
|
|
64
|
+
blurTint,
|
|
65
|
+
tintColor,
|
|
66
|
+
highlightColor,
|
|
67
|
+
borderColor,
|
|
68
|
+
borderWidth,
|
|
69
|
+
shadowColor,
|
|
70
|
+
shadowOpacity,
|
|
71
|
+
shadowRadius,
|
|
72
|
+
shadowOffset,
|
|
73
|
+
androidElevation,
|
|
74
|
+
capability: GLASS_CAPABILITY
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
var BlurViewImpl = null;
|
|
78
|
+
var blurLoaded = false;
|
|
79
|
+
var LinearGradientImpl = null;
|
|
80
|
+
var gradientLoaded = false;
|
|
81
|
+
function loadBlurView() {
|
|
82
|
+
if (blurLoaded) return BlurViewImpl;
|
|
83
|
+
blurLoaded = true;
|
|
84
|
+
try {
|
|
85
|
+
BlurViewImpl = __require("expo-blur").BlurView;
|
|
86
|
+
} catch {
|
|
87
|
+
BlurViewImpl = null;
|
|
88
|
+
}
|
|
89
|
+
return BlurViewImpl;
|
|
90
|
+
}
|
|
91
|
+
function loadLinearGradient() {
|
|
92
|
+
if (gradientLoaded) return LinearGradientImpl;
|
|
93
|
+
gradientLoaded = true;
|
|
94
|
+
try {
|
|
95
|
+
const gradientModule = __require("react-native-linear-gradient");
|
|
96
|
+
LinearGradientImpl = gradientModule?.default ?? gradientModule;
|
|
97
|
+
} catch {
|
|
98
|
+
LinearGradientImpl = null;
|
|
99
|
+
}
|
|
100
|
+
return LinearGradientImpl;
|
|
101
|
+
}
|
|
102
|
+
var GlassView = React.memo(
|
|
103
|
+
({
|
|
104
|
+
elevation = 2,
|
|
105
|
+
variant = "surface",
|
|
106
|
+
highlight = true,
|
|
107
|
+
border = true,
|
|
108
|
+
borderWidth = 1,
|
|
109
|
+
borderRadius = 16,
|
|
110
|
+
blurOverride,
|
|
111
|
+
tintOverride,
|
|
112
|
+
glow,
|
|
113
|
+
style,
|
|
114
|
+
contentStyle,
|
|
115
|
+
children,
|
|
116
|
+
testID,
|
|
117
|
+
accessible,
|
|
118
|
+
accessibilityLabel,
|
|
119
|
+
accessibilityRole
|
|
120
|
+
}) => {
|
|
121
|
+
const { colorScheme } = useTheme();
|
|
122
|
+
const resolved = useMemo(
|
|
123
|
+
() => resolveGlass(
|
|
124
|
+
{ elevation, variant, highlight, border, borderWidth, blurOverride, tintOverride },
|
|
125
|
+
colorScheme
|
|
126
|
+
),
|
|
127
|
+
[elevation, variant, highlight, border, borderWidth, blurOverride, tintOverride, colorScheme]
|
|
128
|
+
);
|
|
129
|
+
const shadowStyle = useMemo(() => {
|
|
130
|
+
if (Platform.OS === "android") {
|
|
131
|
+
return { elevation: resolved.androidElevation };
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
shadowColor: resolved.shadowColor,
|
|
135
|
+
shadowOffset: resolved.shadowOffset,
|
|
136
|
+
shadowOpacity: resolved.shadowOpacity,
|
|
137
|
+
shadowRadius: resolved.shadowRadius
|
|
138
|
+
};
|
|
139
|
+
}, [resolved]);
|
|
140
|
+
const glowStyle = useMemo(() => {
|
|
141
|
+
if (!glow || Platform.OS !== "ios") return {};
|
|
142
|
+
return {
|
|
143
|
+
shadowColor: glow.color,
|
|
144
|
+
shadowOffset: { width: 0, height: 0 },
|
|
145
|
+
shadowOpacity: glow.opacity ?? 0.35,
|
|
146
|
+
shadowRadius: glow.radius ?? 24
|
|
147
|
+
};
|
|
148
|
+
}, [glow]);
|
|
149
|
+
const BlurViewComponent = loadBlurView();
|
|
150
|
+
const LinearGradientComponent = loadLinearGradient();
|
|
151
|
+
if (IS_NO_GLASS) {
|
|
152
|
+
return /* @__PURE__ */ jsx(
|
|
153
|
+
View,
|
|
154
|
+
{
|
|
155
|
+
testID,
|
|
156
|
+
accessible,
|
|
157
|
+
accessibilityLabel,
|
|
158
|
+
accessibilityRole,
|
|
159
|
+
style: [styles.shadowShell, { borderRadius }, shadowStyle, style],
|
|
160
|
+
children: /* @__PURE__ */ jsxs(View, { style: [styles.clipShell, { borderRadius }], children: [
|
|
161
|
+
/* @__PURE__ */ jsx(
|
|
162
|
+
View,
|
|
163
|
+
{
|
|
164
|
+
style: [
|
|
165
|
+
StyleSheet.absoluteFill,
|
|
166
|
+
{ backgroundColor: resolved.tintColor }
|
|
167
|
+
]
|
|
168
|
+
}
|
|
169
|
+
),
|
|
170
|
+
resolved.highlightColor !== "transparent" && (LinearGradientComponent ? /* @__PURE__ */ jsx(
|
|
171
|
+
LinearGradientComponent,
|
|
172
|
+
{
|
|
173
|
+
colors: [resolved.highlightColor, "transparent"],
|
|
174
|
+
start: { x: 0, y: 0 },
|
|
175
|
+
end: { x: 0, y: 1 },
|
|
176
|
+
style: styles.highlightGradient
|
|
177
|
+
}
|
|
178
|
+
) : /* @__PURE__ */ jsx(
|
|
179
|
+
View,
|
|
180
|
+
{
|
|
181
|
+
pointerEvents: "none",
|
|
182
|
+
style: [
|
|
183
|
+
styles.highlightGradient,
|
|
184
|
+
{ backgroundColor: resolved.highlightColor, opacity: 0.2 }
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
)),
|
|
188
|
+
border && /* @__PURE__ */ jsx(
|
|
189
|
+
View,
|
|
190
|
+
{
|
|
191
|
+
pointerEvents: "none",
|
|
192
|
+
style: [
|
|
193
|
+
StyleSheet.absoluteFill,
|
|
194
|
+
{ borderWidth: resolved.borderWidth, borderColor: resolved.borderColor }
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
),
|
|
198
|
+
/* @__PURE__ */ jsx(View, { style: [styles.content, contentStyle], children })
|
|
199
|
+
] })
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
return /* @__PURE__ */ jsx(
|
|
204
|
+
View,
|
|
205
|
+
{
|
|
206
|
+
testID,
|
|
207
|
+
accessible,
|
|
208
|
+
accessibilityLabel,
|
|
209
|
+
accessibilityRole,
|
|
210
|
+
style: [styles.shadowShell, { borderRadius }, shadowStyle, glowStyle, style],
|
|
211
|
+
children: /* @__PURE__ */ jsxs(View, { style: [styles.clipShell, { borderRadius }], children: [
|
|
212
|
+
BlurViewComponent ? /* @__PURE__ */ jsx(
|
|
213
|
+
BlurViewComponent,
|
|
214
|
+
{
|
|
215
|
+
intensity: resolved.blurAmount,
|
|
216
|
+
tint: resolved.blurTint,
|
|
217
|
+
experimentalBlurMethod: ANDROID_BLUR_METHOD,
|
|
218
|
+
style: StyleSheet.absoluteFill
|
|
219
|
+
}
|
|
220
|
+
) : /* @__PURE__ */ jsx(
|
|
221
|
+
View,
|
|
222
|
+
{
|
|
223
|
+
pointerEvents: "none",
|
|
224
|
+
style: [StyleSheet.absoluteFill, { backgroundColor: resolved.tintColor }]
|
|
225
|
+
}
|
|
226
|
+
),
|
|
227
|
+
/* @__PURE__ */ jsx(
|
|
228
|
+
View,
|
|
229
|
+
{
|
|
230
|
+
pointerEvents: "none",
|
|
231
|
+
style: [StyleSheet.absoluteFill, { backgroundColor: resolved.tintColor }]
|
|
232
|
+
}
|
|
233
|
+
),
|
|
234
|
+
resolved.highlightColor !== "transparent" && (LinearGradientComponent ? /* @__PURE__ */ jsx(
|
|
235
|
+
LinearGradientComponent,
|
|
236
|
+
{
|
|
237
|
+
colors: [resolved.highlightColor, "transparent"],
|
|
238
|
+
start: { x: 0, y: 0 },
|
|
239
|
+
end: { x: 0, y: 1 },
|
|
240
|
+
style: styles.highlightGradient,
|
|
241
|
+
pointerEvents: "none"
|
|
242
|
+
}
|
|
243
|
+
) : /* @__PURE__ */ jsx(
|
|
244
|
+
View,
|
|
245
|
+
{
|
|
246
|
+
pointerEvents: "none",
|
|
247
|
+
style: [
|
|
248
|
+
styles.highlightGradient,
|
|
249
|
+
{ backgroundColor: resolved.highlightColor, opacity: 0.18 }
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
)),
|
|
253
|
+
border && /* @__PURE__ */ jsx(
|
|
254
|
+
View,
|
|
255
|
+
{
|
|
256
|
+
pointerEvents: "none",
|
|
257
|
+
style: [
|
|
258
|
+
StyleSheet.absoluteFill,
|
|
259
|
+
{ borderWidth: resolved.borderWidth, borderColor: resolved.borderColor }
|
|
260
|
+
]
|
|
261
|
+
}
|
|
262
|
+
),
|
|
263
|
+
/* @__PURE__ */ jsx(View, { style: [styles.content, contentStyle], children })
|
|
264
|
+
] })
|
|
265
|
+
}
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
GlassView.displayName = "GlassView";
|
|
270
|
+
var styles = StyleSheet.create({
|
|
271
|
+
shadowShell: {
|
|
272
|
+
// Shadow lives here — no overflow:hidden so Android elevation renders
|
|
273
|
+
},
|
|
274
|
+
clipShell: {
|
|
275
|
+
overflow: "hidden"
|
|
276
|
+
},
|
|
277
|
+
highlightGradient: {
|
|
278
|
+
position: "absolute",
|
|
279
|
+
top: 0,
|
|
280
|
+
left: 0,
|
|
281
|
+
right: 0,
|
|
282
|
+
height: "35%"
|
|
283
|
+
},
|
|
284
|
+
content: {
|
|
285
|
+
position: "relative"
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
var FrostPanel = React.memo(
|
|
289
|
+
({ style, edges, borderRadius, children, ...glassProps }) => {
|
|
290
|
+
const radiusStyle = edges ? {
|
|
291
|
+
borderTopLeftRadius: edges.includes("top") ? borderRadius ?? 20 : 0,
|
|
292
|
+
borderTopRightRadius: edges.includes("top") ? borderRadius ?? 20 : 0,
|
|
293
|
+
borderBottomLeftRadius: edges.includes("bottom") ? borderRadius ?? 20 : 0,
|
|
294
|
+
borderBottomRightRadius: edges.includes("bottom") ? borderRadius ?? 20 : 0
|
|
295
|
+
} : { borderRadius: borderRadius ?? 20 };
|
|
296
|
+
return /* @__PURE__ */ jsx(
|
|
297
|
+
GlassView,
|
|
298
|
+
{
|
|
299
|
+
elevation: glassProps.elevation ?? 3,
|
|
300
|
+
style: [styles2.panel, style],
|
|
301
|
+
borderRadius: 0,
|
|
302
|
+
...glassProps,
|
|
303
|
+
children: /* @__PURE__ */ jsx(View, { style: radiusStyle, children })
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
FrostPanel.displayName = "FrostPanel";
|
|
309
|
+
var styles2 = StyleSheet.create({
|
|
310
|
+
panel: {
|
|
311
|
+
width: "100%"
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
function useGlassStyle(config) {
|
|
315
|
+
const { colorScheme } = useTheme();
|
|
316
|
+
return useMemo(() => resolveGlass(config, colorScheme), [config, colorScheme]);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export { FrostPanel, GLASS_CAPABILITY, GlassView, IS_FULL_GLASS, IS_NO_GLASS, IS_PARTIAL_GLASS, SUPPORTS_BLUR, adjustBlurForCapability, resolveGlass, useGlassStyle };
|
|
320
|
+
//# sourceMappingURL=index.mjs.map
|
|
321
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/engine/CapabilityDetector.ts","../src/engine/GlassEngine.ts","../src/components/GlassView/GlassView.tsx","../src/components/FrostPanel/FrostPanel.tsx","../src/hooks/useGlassStyle.ts"],"names":["React","jsx","styles","View","StyleSheet","useTheme","useMemo"],"mappings":"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","file":"index.mjs","sourcesContent":["import { IS_IOS, IS_ANDROID, IS_WEB, getAndroidVersion } from 'reactnatively-utils';\n\nexport type GlassCapability = 'full' | 'partial' | 'none';\n\nfunction detectCapability(): GlassCapability {\n if (IS_IOS) return 'full';\n if (IS_ANDROID) {\n const version = getAndroidVersion();\n return version >= 31 ? 'partial' : 'none';\n }\n if (IS_WEB) return 'partial';\n return 'none';\n}\n\nexport const GLASS_CAPABILITY: GlassCapability = detectCapability();\nexport const SUPPORTS_BLUR: boolean = GLASS_CAPABILITY !== 'none';\nexport const IS_FULL_GLASS: boolean = GLASS_CAPABILITY === 'full';\nexport const IS_PARTIAL_GLASS: boolean = GLASS_CAPABILITY === 'partial';\nexport const IS_NO_GLASS: boolean = GLASS_CAPABILITY === 'none';\n\nexport function adjustBlurForCapability(intensity: number): number {\n if (GLASS_CAPABILITY === 'full') return intensity;\n if (GLASS_CAPABILITY === 'partial') return Math.round(intensity * 0.65);\n return 0;\n}\n\nexport const ANDROID_BLUR_METHOD = IS_ANDROID ? 'dimezisBlurView' : undefined;\n","import { glassTokens } from 'reactnatively-theme';\nimport type { ResolvedColorScheme } from 'reactnatively-theme';\nimport type { GlassConfig, ResolvedGlassStyle } from './GlassEngine.types';\nimport { GLASS_CAPABILITY, adjustBlurForCapability } from './CapabilityDetector';\n\n// Core resolver: takes a GlassConfig + color scheme, returns a fully\n// resolved style recipe that GlassView uses to render all layers.\nexport function resolveGlass(\n config: GlassConfig,\n colorScheme: ResolvedColorScheme,\n): ResolvedGlassStyle {\n const {\n elevation = 2,\n variant = 'surface',\n highlight = true,\n border = true,\n borderWidth = 1,\n blurOverride,\n tintOverride,\n } = config;\n\n const elevationConfig = glassTokens.elevation[elevation];\n\n // Blur\n const rawBlur = blurOverride ?? elevationConfig.blur;\n const blurAmount = adjustBlurForCapability(rawBlur);\n\n // Tint\n const tintColor = tintOverride ?? glassTokens.tint[colorScheme][variant];\n\n // Highlight\n let highlightColor = 'transparent';\n if (highlight === true) {\n highlightColor = glassTokens.highlight.medium;\n } else if (highlight !== false && highlight !== 'none') {\n highlightColor = glassTokens.highlight[highlight];\n }\n\n // Border\n const borderColor = border\n ? glassTokens.border[colorScheme].medium\n : 'transparent';\n\n // Shadow — glass shadows use a deep, soft spread\n const shadowColor = colorScheme === 'dark' ? '#000' : '#1a1a2e';\n const shadowOpacity = elevationConfig.shadowOpacity;\n const shadowRadius = elevationConfig.shadowRadius;\n const shadowOffset = { width: 0, height: elevationConfig.shadowY };\n const androidElevation = Math.round(elevation * 3);\n\n // BlurView tint hint — 'light' | 'dark' | 'default'\n const blurTint = colorScheme === 'dark' ? 'dark' : 'light';\n\n return {\n blurAmount,\n blurTint,\n tintColor,\n highlightColor,\n borderColor,\n borderWidth,\n shadowColor,\n shadowOpacity,\n shadowRadius,\n shadowOffset,\n androidElevation,\n capability: GLASS_CAPABILITY,\n };\n}\n","import React, { useMemo } from 'react';\nimport {\n View,\n StyleSheet,\n Platform,\n type ViewStyle,\n} from 'react-native';\nimport { useTheme } from 'reactnatively-theme';\nimport { resolveGlass } from '../../engine/GlassEngine';\nimport { IS_NO_GLASS, ANDROID_BLUR_METHOD } from '../../engine/CapabilityDetector';\nimport type { GlassViewProps } from './GlassView.types';\n\nlet BlurViewImpl: typeof import('expo-blur').BlurView | null = null;\nlet blurLoaded = false;\n\nlet LinearGradientImpl:\n | typeof import('react-native-linear-gradient').default\n | null = null;\nlet gradientLoaded = false;\n\nfunction loadBlurView(): typeof import('expo-blur').BlurView | null {\n if (blurLoaded) return BlurViewImpl;\n blurLoaded = true;\n try {\n // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires\n BlurViewImpl = require('expo-blur').BlurView;\n } catch {\n BlurViewImpl = null;\n }\n return BlurViewImpl;\n}\n\nfunction loadLinearGradient():\n | typeof import('react-native-linear-gradient').default\n | null {\n if (gradientLoaded) return LinearGradientImpl;\n gradientLoaded = true;\n try {\n // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires\n const gradientModule = require('react-native-linear-gradient');\n LinearGradientImpl = gradientModule?.default ?? gradientModule;\n } catch {\n LinearGradientImpl = null;\n }\n return LinearGradientImpl;\n}\n\n/**\n * GlassView — the foundational rendering primitive of the Liquid Glass system.\n *\n * Layer stack (bottom → top):\n * 1. Shadow shell — drop shadow / elevation (outer view, NO overflow:hidden)\n * 2. Clip shell — clips all glass layers to border radius (overflow:hidden)\n * 3. BlurView — native platform blur\n * 4. Tint overlay — semi-transparent color layer\n * 5. Highlight — top-edge refraction shimmer (LinearGradient)\n * 6. Border ring — 1px glass edge line\n * 7. Content — children, above all glass layers\n */\nexport const GlassView = React.memo<GlassViewProps>(\n ({\n elevation = 2,\n variant = 'surface',\n highlight = true,\n border = true,\n borderWidth = 1,\n borderRadius = 16,\n blurOverride,\n tintOverride,\n glow,\n style,\n contentStyle,\n children,\n testID,\n accessible,\n accessibilityLabel,\n accessibilityRole,\n }) => {\n const { colorScheme } = useTheme();\n\n const resolved = useMemo(\n () =>\n resolveGlass(\n { elevation, variant, highlight, border, borderWidth, blurOverride, tintOverride },\n colorScheme,\n ),\n [elevation, variant, highlight, border, borderWidth, blurOverride, tintOverride, colorScheme],\n );\n\n const shadowStyle = useMemo((): ViewStyle => {\n if (Platform.OS === 'android') {\n return { elevation: resolved.androidElevation };\n }\n return {\n shadowColor: resolved.shadowColor,\n shadowOffset: resolved.shadowOffset,\n shadowOpacity: resolved.shadowOpacity,\n shadowRadius: resolved.shadowRadius,\n };\n }, [resolved]);\n\n const glowStyle = useMemo((): ViewStyle => {\n if (!glow || Platform.OS !== 'ios') return {};\n return {\n shadowColor: glow.color,\n shadowOffset: { width: 0, height: 0 },\n shadowOpacity: glow.opacity ?? 0.35,\n shadowRadius: glow.radius ?? 24,\n };\n }, [glow]);\n\n const BlurViewComponent = loadBlurView();\n const LinearGradientComponent = loadLinearGradient();\n\n // Fallback: no-blur devices — solid semi-transparent + gradient only\n if (IS_NO_GLASS) {\n return (\n <View\n testID={testID}\n accessible={accessible}\n accessibilityLabel={accessibilityLabel}\n accessibilityRole={accessibilityRole}\n style={[styles.shadowShell, { borderRadius }, shadowStyle, style]}\n >\n <View style={[styles.clipShell, { borderRadius }]}>\n <View\n style={[\n StyleSheet.absoluteFill,\n { backgroundColor: resolved.tintColor },\n ]}\n />\n {resolved.highlightColor !== 'transparent' && (\n LinearGradientComponent ? (\n <LinearGradientComponent\n colors={[resolved.highlightColor, 'transparent']}\n start={{ x: 0, y: 0 }}\n end={{ x: 0, y: 1 }}\n style={styles.highlightGradient}\n />\n ) : (\n <View\n pointerEvents=\"none\"\n style={[\n styles.highlightGradient,\n { backgroundColor: resolved.highlightColor, opacity: 0.2 },\n ]}\n />\n )\n )}\n {border && (\n <View\n pointerEvents=\"none\"\n style={[\n StyleSheet.absoluteFill,\n { borderWidth: resolved.borderWidth, borderColor: resolved.borderColor },\n ]}\n />\n )}\n <View style={[styles.content, contentStyle]}>{children}</View>\n </View>\n </View>\n );\n }\n\n return (\n <View\n testID={testID}\n accessible={accessible}\n accessibilityLabel={accessibilityLabel}\n accessibilityRole={accessibilityRole}\n style={[styles.shadowShell, { borderRadius }, shadowStyle, glowStyle, style]}\n >\n <View style={[styles.clipShell, { borderRadius }]}>\n {/* Layer 1: Native blur */}\n {BlurViewComponent ? (\n <BlurViewComponent\n intensity={resolved.blurAmount}\n tint={resolved.blurTint}\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore — experimentalBlurMethod is Android-only, conditionally applied\n experimentalBlurMethod={ANDROID_BLUR_METHOD}\n style={StyleSheet.absoluteFill}\n />\n ) : (\n <View\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: resolved.tintColor }]}\n />\n )}\n\n {/* Layer 2: Tint overlay */}\n <View\n pointerEvents=\"none\"\n style={[StyleSheet.absoluteFill, { backgroundColor: resolved.tintColor }]}\n />\n\n {/* Layer 3: Top-edge highlight — the \"liquid glass\" refraction shimmer */}\n {resolved.highlightColor !== 'transparent' && (\n LinearGradientComponent ? (\n <LinearGradientComponent\n colors={[resolved.highlightColor, 'transparent']}\n start={{ x: 0, y: 0 }}\n end={{ x: 0, y: 1 }}\n style={styles.highlightGradient}\n pointerEvents=\"none\"\n />\n ) : (\n <View\n pointerEvents=\"none\"\n style={[\n styles.highlightGradient,\n { backgroundColor: resolved.highlightColor, opacity: 0.18 },\n ]}\n />\n )\n )}\n\n {/* Layer 4: Glass edge border */}\n {border && (\n <View\n pointerEvents=\"none\"\n style={[\n StyleSheet.absoluteFill,\n { borderWidth: resolved.borderWidth, borderColor: resolved.borderColor },\n ]}\n />\n )}\n\n {/* Layer 5: Children */}\n <View style={[styles.content, contentStyle]}>{children}</View>\n </View>\n </View>\n );\n },\n);\n\nGlassView.displayName = 'GlassView';\n\nconst styles = StyleSheet.create({\n shadowShell: {\n // Shadow lives here — no overflow:hidden so Android elevation renders\n },\n clipShell: {\n overflow: 'hidden',\n },\n highlightGradient: {\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n height: '35%',\n },\n content: {\n position: 'relative',\n },\n});\n","import React from 'react';\nimport { View, StyleSheet, type StyleProp, type ViewStyle } from 'react-native';\nimport { GlassView } from '../GlassView';\nimport type { GlassViewProps } from '../GlassView/GlassView.types';\n\n// FrostPanel: full-width glass surface for bottom sheets, headers, sidebars.\n// Unlike GlassView (which sizes to content), FrostPanel fills its container.\nexport interface FrostPanelProps extends Omit<GlassViewProps, 'style'> {\n style?: StyleProp<ViewStyle>;\n edges?: ('top' | 'bottom' | 'left' | 'right')[];\n}\n\nexport const FrostPanel = React.memo<FrostPanelProps>(\n ({ style, edges, borderRadius, children, ...glassProps }) => {\n // For panels, we typically want sharp edges on some sides\n // e.g., bottom sheet: round top, flat bottom (or vice versa)\n const radiusStyle = edges\n ? {\n borderTopLeftRadius: edges.includes('top') ? (borderRadius ?? 20) : 0,\n borderTopRightRadius: edges.includes('top') ? (borderRadius ?? 20) : 0,\n borderBottomLeftRadius: edges.includes('bottom') ? (borderRadius ?? 20) : 0,\n borderBottomRightRadius: edges.includes('bottom') ? (borderRadius ?? 20) : 0,\n }\n : { borderRadius: borderRadius ?? 20 };\n\n return (\n <GlassView\n elevation={glassProps.elevation ?? 3}\n style={[styles.panel, style]}\n borderRadius={0} // We handle radius manually for panels\n {...glassProps}\n >\n <View style={radiusStyle}>\n {children}\n </View>\n </GlassView>\n );\n },\n);\n\nFrostPanel.displayName = 'FrostPanel';\n\nconst styles = StyleSheet.create({\n panel: {\n width: '100%',\n },\n});\n","import { useMemo } from 'react';\nimport { useTheme } from 'reactnatively-theme';\nimport { resolveGlass } from '../engine/GlassEngine';\nimport type { GlassConfig, ResolvedGlassStyle } from '../engine/GlassEngine.types';\n\n// Hook: resolves the full glass style recipe using the current theme's color scheme.\n// Use this when building custom glass components outside of GlassView.\nexport function useGlassStyle(config: GlassConfig): ResolvedGlassStyle {\n const { colorScheme } = useTheme();\n return useMemo(() => resolveGlass(config, colorScheme), [config, colorScheme]);\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "reactnatively-glass",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Liquid Glass rendering engine for Reactnatively — blur, vibrancy, depth",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"reactnatively-animations": "0.1.0",
|
|
21
|
+
"reactnatively-theme": "0.1.0",
|
|
22
|
+
"reactnatively-utils": "0.1.0"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"expo-blur": ">=13.0.0",
|
|
26
|
+
"react": ">=18.0.0",
|
|
27
|
+
"react-native": ">=0.73.0",
|
|
28
|
+
"react-native-linear-gradient": ">=2.8.0",
|
|
29
|
+
"react-native-reanimated": ">=3.6.0"
|
|
30
|
+
},
|
|
31
|
+
"peerDependenciesMeta": {
|
|
32
|
+
"expo-blur": {
|
|
33
|
+
"optional": true
|
|
34
|
+
},
|
|
35
|
+
"react-native-linear-gradient": {
|
|
36
|
+
"optional": true
|
|
37
|
+
},
|
|
38
|
+
"react-native-reanimated": {
|
|
39
|
+
"optional": false
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"tsup": "^8.2.4",
|
|
44
|
+
"typescript": "^5.5.3",
|
|
45
|
+
"@reactnatively/tsconfig": "0.0.0"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"react-native",
|
|
49
|
+
"expo",
|
|
50
|
+
"glass",
|
|
51
|
+
"liquid-glass",
|
|
52
|
+
"blur",
|
|
53
|
+
"vibrancy",
|
|
54
|
+
"glassmorphism",
|
|
55
|
+
"ui",
|
|
56
|
+
"design-system",
|
|
57
|
+
"typescript"
|
|
58
|
+
],
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "https://github.com/hakizimana-fred/reactnatively.git",
|
|
62
|
+
"directory": "packages/glass"
|
|
63
|
+
},
|
|
64
|
+
"license": "MIT",
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "tsup",
|
|
70
|
+
"dev": "tsup --watch",
|
|
71
|
+
"typecheck": "tsc --noEmit",
|
|
72
|
+
"clean": "rm -rf dist"
|
|
73
|
+
}
|
|
74
|
+
}
|