@proulr/sdui-components 0.1.9 → 0.1.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proulr/sdui-components",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "main": "./src/renderer/index.ts",
5
5
  "types": "./src/renderer/index.ts",
6
6
  "exports": {
@@ -9,7 +9,7 @@
9
9
  "./renderer": "./src/renderer/index.ts",
10
10
  "./ui": "./src/renderer/ui/index.ts",
11
11
  "./primitives": "./src/renderer/ui/primitives.ts",
12
- "./theme": "./src/renderer/theme/proulr-theme.ts"
12
+ "./theme": "./src/renderer/theme/index.ts"
13
13
  },
14
14
  "files": [
15
15
  "src"
@@ -32,6 +32,7 @@
32
32
  "@proulr/sdui-contract": "^0.1.0"
33
33
  },
34
34
  "peerDependencies": {
35
+ "expo-blur": ">=15",
35
36
  "expo-linear-gradient": ">=56",
36
37
  "react": ">=19",
37
38
  "react-native": ">=0.85",
@@ -48,6 +49,9 @@
48
49
  "expo-linear-gradient": {
49
50
  "optional": true
50
51
  },
52
+ "expo-blur": {
53
+ "optional": true
54
+ },
51
55
  "react-native-google-mobile-ads": {
52
56
  "optional": true
53
57
  },
@@ -60,6 +64,7 @@
60
64
  "@types/react": "~19.2.2",
61
65
  "eslint": "^9.0.0",
62
66
  "eslint-config-expo": "~56.0.4",
67
+ "expo-blur": "~56.0.3",
63
68
  "expo-linear-gradient": "~56.0.4",
64
69
  "globals": "^16.0.0",
65
70
  "husky": "^9.1.7",
@@ -27,6 +27,9 @@ export type {
27
27
  SduiNativeBlockRenderProps,
28
28
  } from './sdui-hybrid-renderer-context';
29
29
  export * from './theme/proulr-theme';
30
+ export * from './theme/chrome-glass';
31
+ export * from './theme/chrome-layout';
32
+ export * from './theme/chrome-pill-styles';
30
33
  export { resolveViewStyleProps, resolveTextStyleProps } from './theme/resolve-style-props';
31
34
  export {
32
35
  formatDistanceLabel,
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Chrome glass (BlurView) — fonte única Night Stalker.
3
+ * Overlay de cor fica em `colors.chromeGlassOverlay` (tema).
4
+ */
5
+
6
+ /** Intensidade do frost — mais blur, menos “placa” opaca. */
7
+ export const CHROME_GLASS_BLUR_INTENSITY = 50;
8
+
9
+ /**
10
+ * Tint limpo (sem material cinza do system material).
11
+ * Tipado como string literal para não exigir peer `expo-blur` na lib.
12
+ */
13
+ export const CHROME_GLASS_BLUR_TINT = 'dark' as const;
14
+
15
+ export type ChromeGlassBlurTint = typeof CHROME_GLASS_BLUR_TINT;
@@ -0,0 +1,42 @@
1
+ import { m3Shape, spacing } from './proulr-theme';
2
+
3
+ export const CHROME_HORIZONTAL_INSET = spacing.md;
4
+ export const CHROME_VERTICAL_GAP = spacing.sm;
5
+ export const CHROME_PILL_RADIUS = m3Shape.cornerFull;
6
+ export const HEADER_PILL_ROW_HEIGHT = 56;
7
+ export const HEADER_SLOT_GAP = spacing.md;
8
+ export const HEADER_SLOT_ROW_HEIGHT = 52;
9
+ export const FLOATING_TAB_BAR_HEIGHT = 60;
10
+ export const CHROME_CONTENT_TOP_GAP = spacing.sm;
11
+ export const CHROME_SCROLL_BOTTOM_EXTRA = spacing.sm;
12
+ export const CHROME_SCROLL_FADE_EXTRA = 32;
13
+ export const CHROME_SCROLL_FADE_Z_BEHIND_CHROME = 1;
14
+ export const CHROME_SCROLL_FADE_Z_ABOVE_CONTENT = 5;
15
+ export const CHROME_PILL_Z_INDEX = 10;
16
+ export const CHROME_DRAWER_OVERLAY_Z_INDEX = 30;
17
+ export const CHROME_DRAWER_Z_INDEX = 31;
18
+
19
+ type HeaderChromeOptions = {
20
+ hasSlot?: boolean;
21
+ searchOpen?: boolean;
22
+ };
23
+
24
+ export function estimateHeaderChromeHeight(
25
+ topInset: number,
26
+ options: boolean | HeaderChromeOptions = false,
27
+ ): number {
28
+ const { hasSlot = false, searchOpen = false } =
29
+ typeof options === 'boolean' ? { hasSlot: options } : options;
30
+ const showsSlot = hasSlot && !searchOpen;
31
+ const slotRow = showsSlot ? HEADER_SLOT_GAP + HEADER_SLOT_ROW_HEIGHT : 0;
32
+ return topInset + CHROME_VERTICAL_GAP + HEADER_PILL_ROW_HEIGHT + slotRow;
33
+ }
34
+
35
+ export function estimateTabBarChromeHeight(bottomInset: number): number {
36
+ return bottomInset + CHROME_VERTICAL_GAP + FLOATING_TAB_BAR_HEIGHT;
37
+ }
38
+
39
+ /** Mesma área reservada que a tab bar — footer flutuante com CTA primário. */
40
+ export function estimateChromeFooterActionHeight(bottomInset: number): number {
41
+ return estimateTabBarChromeHeight(bottomInset);
42
+ }
@@ -0,0 +1,13 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { colors } from './proulr-theme';
3
+ import { CHROME_PILL_RADIUS } from './chrome-layout';
4
+
5
+ export const chromePillStyles = StyleSheet.create({
6
+ shell: {
7
+ backgroundColor: colors.chromePillBackground,
8
+ borderWidth: 1,
9
+ borderColor: colors.chromePillBorder,
10
+ borderRadius: CHROME_PILL_RADIUS,
11
+ overflow: 'hidden',
12
+ },
13
+ });
@@ -0,0 +1,4 @@
1
+ export * from './proulr-theme';
2
+ export * from './chrome-glass';
3
+ export * from './chrome-layout';
4
+ export * from './chrome-pill-styles';
@@ -1,6 +1,6 @@
1
- import { Platform, ViewStyle } from 'react-native';
1
+ import { Platform, type ViewStyle } from 'react-native';
2
2
 
3
- /** Proulr UI theme — Night Stalker OLED. */
3
+ /** Proulr UI theme — Night Stalker OLED (fonte única para app + SDUI). */
4
4
 
5
5
  export const m3Colors = {
6
6
  primary: '#D946EF',
@@ -30,9 +30,8 @@ export const m3Colors = {
30
30
  inverseSurface: '#FFFFFF',
31
31
  inverseOnSurface: '#000000',
32
32
  scrim: 'rgba(0,0,0,0.78)',
33
+ storyInteractionScrim: 'rgba(0,0,0,0.48)',
33
34
  online: '#34FF88',
34
- chromePillBackground: 'rgba(28, 28, 30, 0.82)',
35
- chromePillBorder: 'rgba(255, 255, 255, 0.06)',
36
35
  } as const;
37
36
 
38
37
  export const m3Shape = {
@@ -79,35 +78,58 @@ export const colors = {
79
78
  dangerSoft: 'rgba(255,68,68,0.22)',
80
79
  overlay: 'rgba(0,0,0,0.78)',
81
80
  surfaceGlass: 'rgba(255,255,255,0.06)',
82
- chromePillBackground: 'rgba(28, 28, 30, 0.82)',
83
- chromePillBorder: 'rgba(255, 255, 255, 0.06)',
84
- primary: m3Colors.primary,
85
- onSurface: m3Colors.onSurface,
86
- onSurfaceVariant: m3Colors.onSurfaceVariant,
87
- };
81
+ /** Pílula / chrome sem BlurView — preto OLED. */
82
+ chromePillBackground: 'rgba(0, 0, 0, 0.82)',
83
+ /** Overlay sobre BlurView — transparente = frost limpo (sem placa acinzentada). */
84
+ chromeGlassOverlay: 'rgba(0, 0, 0, 0)',
85
+ chromePillBorder: 'rgba(255, 255, 255, 0.08)',
86
+ videoControlBackground: 'rgba(0,0,0,0.45)',
87
+ videoTrackBackground: 'rgba(255,255,255,0.25)',
88
+ scrollbarThumb: 'rgba(255, 255, 255, 0.28)',
89
+ scrollbarThumbHover: 'rgba(255, 255, 255, 0.45)',
90
+ primary: '#D946EF',
91
+ onSurface: '#FFFFFF',
92
+ onSurfaceVariant: '#A3A3A3',
93
+ } as const;
88
94
 
89
- export const radius = {
95
+ export const spacing = {
96
+ xs: 4,
90
97
  sm: 8,
91
98
  md: 12,
92
99
  lg: 16,
93
100
  xl: 24,
94
- pill: 999,
101
+ xxl: 32,
95
102
  };
96
103
 
97
- export const spacing = {
98
- xs: 4,
104
+ export const radius = {
99
105
  sm: 8,
100
106
  md: 12,
101
107
  lg: 16,
102
108
  xl: 24,
103
- xxl: 32,
104
- } as const;
109
+ pill: 999,
110
+ };
105
111
 
106
112
  export const gradients = {
107
113
  brand: ['#7A0D8A', '#D946EF'] as const,
108
114
  header: ['#2D0A35', '#D946EF'] as const,
109
115
  headerGlow: ['rgba(217,70,239,0.20)', 'rgba(217,70,239,0)'] as const,
116
+ authHero: ['rgba(217,70,239,0.14)', 'rgba(0,229,255,0.06)', 'transparent'] as const,
110
117
  neonBorder: [colors.headerEnd, colors.accent] as const,
118
+ videoControlsShade: ['transparent', 'rgba(0,0,0,0.72)'] as const,
119
+ chromeScrollFadeBottom: [
120
+ 'transparent',
121
+ 'rgba(0,0,0,0.22)',
122
+ 'rgba(0,0,0,0.65)',
123
+ '#000000',
124
+ ] as const,
125
+ chromeScrollFadeBottomLocations: [0, 0.45, 0.82, 1] as const,
126
+ chromeScrollFadeTop: [
127
+ '#000000',
128
+ 'rgba(0,0,0,0.65)',
129
+ 'rgba(0,0,0,0.22)',
130
+ 'transparent',
131
+ ] as const,
132
+ chromeScrollFadeTopLocations: [0, 0.18, 0.55, 1] as const,
111
133
  };
112
134
 
113
135
  export const fonts = {
@@ -116,7 +138,7 @@ export const fonts = {
116
138
  semibold: 'Montserrat_600SemiBold',
117
139
  bold: 'Montserrat_700Bold',
118
140
  extrabold: 'Montserrat_800ExtraBold',
119
- };
141
+ } as const;
120
142
 
121
143
  export const shadows = {
122
144
  card: Platform.select<ViewStyle>({
@@ -0,0 +1,78 @@
1
+ import React, { type ReactNode } from 'react';
2
+ import { BlurView } from 'expo-blur';
3
+ import {
4
+ StyleSheet,
5
+ View,
6
+ type StyleProp,
7
+ type ViewProps,
8
+ type ViewStyle,
9
+ } from 'react-native';
10
+ import { colors } from '../theme/proulr-theme';
11
+ import {
12
+ CHROME_GLASS_BLUR_INTENSITY,
13
+ CHROME_GLASS_BLUR_TINT,
14
+ } from '../theme/chrome-glass';
15
+ import { chromePillStyles } from '../theme/chrome-pill-styles';
16
+
17
+ const absoluteFill: ViewStyle = {
18
+ position: 'absolute',
19
+ top: 0,
20
+ right: 0,
21
+ bottom: 0,
22
+ left: 0,
23
+ };
24
+
25
+ type ChromeGlassLayerProps = {
26
+ style?: StyleProp<ViewStyle>;
27
+ pointerEvents?: ViewProps['pointerEvents'];
28
+ };
29
+
30
+ /**
31
+ * Camada de frost única do sistema (igual AppSideDrawer topo/base).
32
+ * BlurView + `colors.chromeGlassOverlay` — sem cast cinza.
33
+ */
34
+ export function ChromeGlassLayer({
35
+ style,
36
+ pointerEvents = 'none',
37
+ }: ChromeGlassLayerProps) {
38
+ return (
39
+ <View style={[absoluteFill, style]} pointerEvents={pointerEvents}>
40
+ <BlurView
41
+ intensity={CHROME_GLASS_BLUR_INTENSITY}
42
+ tint={CHROME_GLASS_BLUR_TINT}
43
+ style={StyleSheet.absoluteFill}
44
+ />
45
+ <View style={[absoluteFill, styles.tint]} />
46
+ </View>
47
+ );
48
+ }
49
+
50
+ type ChromeGlassShellProps = {
51
+ children: ReactNode;
52
+ style?: StyleProp<ViewStyle>;
53
+ contentStyle?: StyleProp<ViewStyle>;
54
+ };
55
+
56
+ /**
57
+ * Pílula chrome com o mesmo glass do drawer (shell + blur + overlay).
58
+ */
59
+ export function ChromeGlassShell({ children, style, contentStyle }: ChromeGlassShellProps) {
60
+ return (
61
+ <View style={[chromePillStyles.shell, styles.shellTransparent, style]}>
62
+ <ChromeGlassLayer />
63
+ <View style={[styles.content, contentStyle]}>{children}</View>
64
+ </View>
65
+ );
66
+ }
67
+
68
+ const styles = StyleSheet.create({
69
+ tint: {
70
+ backgroundColor: colors.chromeGlassOverlay,
71
+ },
72
+ shellTransparent: {
73
+ backgroundColor: 'transparent',
74
+ },
75
+ content: {
76
+ zIndex: 1,
77
+ },
78
+ });
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { TextInput, TextInputProps, ViewStyle } from 'react-native';
2
+ import { TextInput, TextInputProps, type StyleProp, type ViewStyle } from 'react-native';
3
3
  import { maskDateBR } from '../lib/brazilian-date-input';
4
4
  import { TextField } from './TextField';
5
5
 
@@ -15,7 +15,7 @@ export function DateField({
15
15
  value: string;
16
16
  onChange: (masked: string) => void;
17
17
  placeholder?: string;
18
- containerStyle?: ViewStyle;
18
+ containerStyle?: StyleProp<ViewStyle>;
19
19
  inputRef?: React.Ref<TextInput>;
20
20
  returnKeyType?: TextInputProps['returnKeyType'];
21
21
  onSubmitEditing?: TextInputProps['onSubmitEditing'];
@@ -11,9 +11,11 @@ import {
11
11
  View,
12
12
  type StyleProp,
13
13
  type TextInputProps,
14
+ type TextStyle,
14
15
  type ViewStyle,
15
16
  } from 'react-native';
16
17
  import { AppIcon } from './app-icon';
18
+ import { ChromeGlassLayer } from './ChromeGlass';
17
19
  import { colors, fonts, m3Colors, m3Shape, spacing } from '../theme/proulr-theme';
18
20
 
19
21
  const ACTION_BUTTON_SIZE = 48;
@@ -138,7 +140,7 @@ export type ComposePillTextFieldProps = Omit<TextInputProps, 'multiline' | 'styl
138
140
  maxLines?: number;
139
141
  inputRef?: ComposePillTextInputRef;
140
142
  layout?: ComposePillLineLayoutState;
141
- style?: StyleProp<ViewStyle>;
143
+ style?: StyleProp<TextStyle>;
142
144
  };
143
145
 
144
146
  export function ComposePillTextField({
@@ -240,9 +242,11 @@ function composePillVariantColors(variant: ComposePillVariant) {
240
242
  return {
241
243
  text: colors.white,
242
244
  placeholder: 'rgba(255,255,255,0.55)',
243
- pillBackground: 'rgba(28,28,30,0.72)',
244
- pillBorder: 'rgba(255,255,255,0.08)',
245
- replyBackground: 'rgba(28,28,30,0.72)',
245
+ /** Transparent — frost via ChromeGlassLayer (mesmo glass do drawer). */
246
+ pillBackground: 'transparent',
247
+ pillBorder: colors.chromePillBorder,
248
+ replyBackground: 'transparent',
249
+ useChromeGlass: true,
246
250
  };
247
251
  }
248
252
 
@@ -250,9 +254,10 @@ function composePillVariantColors(variant: ComposePillVariant) {
250
254
  return {
251
255
  text: m3Colors.onSurface,
252
256
  placeholder: m3Colors.onSurfaceVariant,
253
- pillBackground: colors.chromePillBackground,
257
+ pillBackground: 'transparent',
254
258
  pillBorder: colors.chromePillBorder,
255
- replyBackground: colors.chromePillBackground,
259
+ replyBackground: 'transparent',
260
+ useChromeGlass: true,
256
261
  };
257
262
  }
258
263
 
@@ -262,6 +267,7 @@ function composePillVariantColors(variant: ComposePillVariant) {
262
267
  pillBackground: m3Colors.surfaceContainerHigh,
263
268
  pillBorder: 'transparent',
264
269
  replyBackground: m3Colors.surfaceContainerHigh,
270
+ useChromeGlass: false,
265
271
  };
266
272
  }
267
273
 
@@ -275,7 +281,14 @@ export function ComposePillReplyBar({
275
281
  const variantColors = composePillVariantColors(variant);
276
282
 
277
283
  return (
278
- <View style={[styles.replyBar, { backgroundColor: variantColors.replyBackground }]}>
284
+ <View
285
+ style={[
286
+ styles.replyBar,
287
+ { backgroundColor: variantColors.replyBackground },
288
+ variantColors.useChromeGlass && styles.replyBarGlass,
289
+ ]}
290
+ >
291
+ {variantColors.useChromeGlass ? <ChromeGlassLayer /> : null}
279
292
  <View style={styles.replyBarAccent} />
280
293
  <View style={styles.replyBarContent}>
281
294
  <Text style={styles.replyBarName} numberOfLines={1}>
@@ -426,8 +439,10 @@ export function ComposePillInput({
426
439
  borderColor: variantColors.pillBorder,
427
440
  borderWidth: variant === 'overlay' || variant === 'chrome' ? 1 : 0,
428
441
  },
442
+ variantColors.useChromeGlass && styles.inputPillGlass,
429
443
  ]}
430
444
  >
445
+ {variantColors.useChromeGlass ? <ChromeGlassLayer /> : null}
431
446
  {leading}
432
447
  {textField}
433
448
  {trailing ? (
@@ -465,15 +480,20 @@ const styles = StyleSheet.create({
465
480
  borderRadius: m3Shape.cornerMedium,
466
481
  gap: spacing.sm,
467
482
  },
483
+ replyBarGlass: {
484
+ overflow: 'hidden',
485
+ },
468
486
  replyBarAccent: {
469
487
  width: 4,
470
488
  alignSelf: 'stretch',
471
489
  borderRadius: m3Shape.cornerExtraSmall,
472
490
  backgroundColor: m3Colors.primary,
491
+ zIndex: 1,
473
492
  },
474
493
  replyBarContent: {
475
494
  flex: 1,
476
495
  gap: 2,
496
+ zIndex: 1,
477
497
  },
478
498
  replyBarName: {
479
499
  color: m3Colors.primary,
@@ -490,6 +510,7 @@ const styles = StyleSheet.create({
490
510
  height: 32,
491
511
  alignItems: 'center',
492
512
  justifyContent: 'center',
513
+ zIndex: 1,
493
514
  },
494
515
  composerRow: {
495
516
  flexDirection: 'row',
@@ -505,6 +526,7 @@ const styles = StyleSheet.create({
505
526
  paddingRight: spacing.xs,
506
527
  overflow: 'hidden',
507
528
  },
529
+ inputPillGlass: {},
508
530
  inputPillSingle: {
509
531
  borderRadius: m3Shape.cornerFull,
510
532
  },
@@ -516,6 +538,7 @@ const styles = StyleSheet.create({
516
538
  height: 40,
517
539
  alignItems: 'center',
518
540
  justifyContent: 'center',
541
+ zIndex: 1,
519
542
  },
520
543
  iconButtonDisabled: {
521
544
  opacity: 0.4,
@@ -523,6 +546,7 @@ const styles = StyleSheet.create({
523
546
  trailingIcons: {
524
547
  flexDirection: 'row',
525
548
  alignItems: 'center',
549
+ zIndex: 1,
526
550
  },
527
551
  trailingIconsExpanded: {
528
552
  paddingBottom: spacing.xs,
@@ -535,6 +559,7 @@ const styles = StyleSheet.create({
535
559
  paddingHorizontal: spacing.xs,
536
560
  paddingTop: VERTICAL_PAD,
537
561
  paddingBottom: VERTICAL_PAD,
562
+ zIndex: 1,
538
563
  },
539
564
  bareInput: {
540
565
  minHeight: 40,
@@ -4,6 +4,7 @@ export { AppLoadingSpinner } from './AppLoadingSpinner';
4
4
  export { AuthStepShell } from './AuthStepShell';
5
5
  export { Card } from './Card';
6
6
  export { Chip } from './Chip';
7
+ export { ChromeGlassLayer, ChromeGlassShell } from './ChromeGlass';
7
8
  export { ComposeAudiencePill } from './compose-audience-pill';
8
9
  export {
9
10
  ComposeCameraGlyph,