@oxyhq/bloom 0.16.0 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/lib/commonjs/dialog/Dialog.js +45 -157
  2. package/lib/commonjs/dialog/Dialog.js.map +1 -1
  3. package/lib/commonjs/dialog/Dialog.web.js +64 -198
  4. package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
  5. package/lib/commonjs/dialog/DialogBottomSheet.js +216 -0
  6. package/lib/commonjs/dialog/DialogBottomSheet.js.map +1 -0
  7. package/lib/commonjs/dialog/DialogContent.js +155 -0
  8. package/lib/commonjs/dialog/DialogContent.js.map +1 -0
  9. package/lib/commonjs/dialog/placement.js +12 -1
  10. package/lib/commonjs/dialog/placement.js.map +1 -1
  11. package/lib/module/dialog/Dialog.js +48 -160
  12. package/lib/module/dialog/Dialog.js.map +1 -1
  13. package/lib/module/dialog/Dialog.web.js +66 -200
  14. package/lib/module/dialog/Dialog.web.js.map +1 -1
  15. package/lib/module/dialog/DialogBottomSheet.js +211 -0
  16. package/lib/module/dialog/DialogBottomSheet.js.map +1 -0
  17. package/lib/module/dialog/DialogContent.js +148 -0
  18. package/lib/module/dialog/DialogContent.js.map +1 -0
  19. package/lib/module/dialog/placement.js +11 -0
  20. package/lib/module/dialog/placement.js.map +1 -1
  21. package/lib/typescript/commonjs/dialog/Dialog.d.ts +1 -1
  22. package/lib/typescript/commonjs/dialog/Dialog.d.ts.map +1 -1
  23. package/lib/typescript/commonjs/dialog/Dialog.web.d.ts +11 -8
  24. package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
  25. package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts +29 -0
  26. package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts.map +1 -0
  27. package/lib/typescript/commonjs/dialog/DialogContent.d.ts +32 -0
  28. package/lib/typescript/commonjs/dialog/DialogContent.d.ts.map +1 -0
  29. package/lib/typescript/commonjs/dialog/placement.d.ts +10 -0
  30. package/lib/typescript/commonjs/dialog/placement.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/dialog/types.d.ts +12 -0
  32. package/lib/typescript/commonjs/dialog/types.d.ts.map +1 -1
  33. package/lib/typescript/module/dialog/Dialog.d.ts +1 -1
  34. package/lib/typescript/module/dialog/Dialog.d.ts.map +1 -1
  35. package/lib/typescript/module/dialog/Dialog.web.d.ts +11 -8
  36. package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
  37. package/lib/typescript/module/dialog/DialogBottomSheet.d.ts +29 -0
  38. package/lib/typescript/module/dialog/DialogBottomSheet.d.ts.map +1 -0
  39. package/lib/typescript/module/dialog/DialogContent.d.ts +32 -0
  40. package/lib/typescript/module/dialog/DialogContent.d.ts.map +1 -0
  41. package/lib/typescript/module/dialog/placement.d.ts +10 -0
  42. package/lib/typescript/module/dialog/placement.d.ts.map +1 -1
  43. package/lib/typescript/module/dialog/types.d.ts +12 -0
  44. package/lib/typescript/module/dialog/types.d.ts.map +1 -1
  45. package/package.json +1 -1
  46. package/src/__tests__/BottomSheetGesture.test.tsx +135 -0
  47. package/src/__tests__/DialogBottomSheet.test.tsx +259 -0
  48. package/src/dialog/Dialog.tsx +41 -171
  49. package/src/dialog/Dialog.web.tsx +56 -211
  50. package/src/dialog/DialogBottomSheet.tsx +250 -0
  51. package/src/dialog/DialogContent.tsx +157 -0
  52. package/src/dialog/placement.ts +11 -0
  53. package/src/dialog/types.ts +12 -0
@@ -0,0 +1,157 @@
1
+ import React, { useCallback } from 'react';
2
+ import {
3
+ Text,
4
+ TouchableOpacity,
5
+ View,
6
+ type GestureResponderEvent,
7
+ } from 'react-native';
8
+
9
+ import type { ThemeColors } from '../theme/types';
10
+ import { useTheme } from '../theme/use-theme';
11
+ import { useDialogContext } from './context';
12
+ import type { DialogAction, DialogActionColor } from './types';
13
+
14
+ /**
15
+ * Shared dialog content primitives used by every `<Dialog>` placement on both
16
+ * platforms — the centered panel, the side-sheets, and the `BottomSheet`-backed
17
+ * bottom placement. Keeping them here is the single source of truth for the
18
+ * declarative `title` / `description` / `actions` chrome, so the surfaces never
19
+ * drift in spacing, typography, or action-button behaviour.
20
+ */
21
+
22
+ /**
23
+ * Renders the dialog's body: optional declarative title + description, any
24
+ * `children`, then the action row. The `titleId`/`descriptionId` are wired by
25
+ * the caller onto the dialog role element for `aria-labelledby` /
26
+ * `aria-describedby`.
27
+ */
28
+ export function DialogBody({
29
+ titleId,
30
+ descriptionId,
31
+ title,
32
+ description,
33
+ actions,
34
+ children,
35
+ }: {
36
+ titleId: string;
37
+ descriptionId: string;
38
+ title?: string;
39
+ description?: string;
40
+ actions?: DialogAction[];
41
+ children?: React.ReactNode;
42
+ }) {
43
+ const theme = useTheme();
44
+ return (
45
+ <>
46
+ {title ? (
47
+ <Text
48
+ nativeID={titleId}
49
+ style={{
50
+ fontSize: 22,
51
+ fontWeight: '600',
52
+ color: theme.colors.text,
53
+ paddingBottom: description ? 4 : 16,
54
+ lineHeight: 30,
55
+ }}
56
+ >
57
+ {title}
58
+ </Text>
59
+ ) : null}
60
+ {description ? (
61
+ <Text
62
+ nativeID={descriptionId}
63
+ style={{
64
+ fontSize: 16,
65
+ color: theme.colors.textSecondary,
66
+ paddingBottom: 16,
67
+ lineHeight: 22,
68
+ }}
69
+ >
70
+ {description}
71
+ </Text>
72
+ ) : null}
73
+ {children}
74
+ {actions && actions.length > 0 ? <ActionRow actions={actions} /> : null}
75
+ </>
76
+ );
77
+ }
78
+
79
+ export function ActionRow({ actions }: { actions: DialogAction[] }) {
80
+ return (
81
+ <View style={{ width: '100%', gap: 8, justifyContent: 'flex-end' }}>
82
+ {actions.map((action, idx) => (
83
+ <ActionButton key={`${action.label}-${idx}`} action={action} />
84
+ ))}
85
+ </View>
86
+ );
87
+ }
88
+
89
+ function ActionButton({ action }: { action: DialogAction }) {
90
+ const { close } = useDialogContext();
91
+ const theme = useTheme();
92
+ const color: DialogActionColor = action.color ?? 'default';
93
+ const shouldCloseOnPress = action.shouldCloseOnPress ?? true;
94
+
95
+ const { background, foreground } = getActionPalette(color, theme.colors);
96
+
97
+ const handlePress = useCallback(
98
+ (e: GestureResponderEvent) => {
99
+ const onPress = action.onPress;
100
+ if (color === 'cancel') {
101
+ // Cancel always dismisses; consumer's onPress (rare) runs after.
102
+ close(onPress ? () => onPress(e) : undefined);
103
+ return;
104
+ }
105
+ if (shouldCloseOnPress) {
106
+ close(onPress ? () => onPress(e) : undefined);
107
+ } else {
108
+ onPress?.(e);
109
+ }
110
+ },
111
+ [action.onPress, close, color, shouldCloseOnPress],
112
+ );
113
+
114
+ return (
115
+ <TouchableOpacity
116
+ style={{
117
+ borderRadius: 9999,
118
+ alignItems: 'center',
119
+ justifyContent: 'center',
120
+ backgroundColor: background,
121
+ opacity: action.disabled ? 0.5 : 1,
122
+ paddingVertical: 12,
123
+ paddingHorizontal: 24,
124
+ }}
125
+ onPress={handlePress}
126
+ disabled={action.disabled}
127
+ activeOpacity={0.7}
128
+ testID={action.testID}
129
+ >
130
+ <Text style={{ fontSize: 16, fontWeight: '500', color: foreground }}>
131
+ {action.label}
132
+ </Text>
133
+ </TouchableOpacity>
134
+ );
135
+ }
136
+
137
+ export function getActionPalette(
138
+ color: DialogActionColor,
139
+ colors: ThemeColors,
140
+ ): { background: string; foreground: string } {
141
+ switch (color) {
142
+ case 'destructive':
143
+ return {
144
+ background: colors.negative,
145
+ foreground: colors.negativeForeground,
146
+ };
147
+ case 'cancel':
148
+ return { background: colors.contrast50, foreground: colors.text };
149
+ case 'default':
150
+ return { background: colors.primary, foreground: colors.primaryForeground };
151
+ /* c8 ignore next 3 -- TS exhaustiveness check guards this branch */
152
+ default: {
153
+ const _exhaustive: never = color;
154
+ return { background: colors.primary, foreground: colors.primaryForeground };
155
+ }
156
+ }
157
+ }
@@ -60,6 +60,17 @@ export const DEFAULT_MAX_HEIGHT_RATIO = 0.9;
60
60
  /** Corner radius shared by the side-sheet panel and the bottom-sheet top. */
61
61
  export const PANEL_RADIUS = 20;
62
62
 
63
+ /**
64
+ * Default inner padding (px) of the dialog content container, applied uniformly
65
+ * across every placement (centered panel, side-sheet body, bottom-sheet body)
66
+ * so the chrome reads identically regardless of which surface resolves. This is
67
+ * the legacy value — omitting the `contentPadding` prop keeps 20px everywhere,
68
+ * byte-for-byte unchanged. A host that owns its own insets (e.g. custom
69
+ * `children` that already manage padding) can override it via
70
+ * `Dialog`'s `contentPadding` prop (e.g. `contentPadding={0}`).
71
+ */
72
+ export const DEFAULT_DIALOG_CONTENT_PADDING = 20;
73
+
63
74
  /** Open/close transition duration (ms). ~280ms ease-out reads smooth. */
64
75
  export const ANIMATION_DURATION = 280;
65
76
 
@@ -134,6 +134,18 @@ export type DialogProps = React.PropsWithChildren<{
134
134
  inset?: DialogInset;
135
135
  /** Whether to render the drag handle in bottom-sheet mode. Defaults to `true`. */
136
136
  showHandle?: boolean;
137
+ /**
138
+ * Inner padding (px) of the dialog content container, applied uniformly across
139
+ * every placement (centered panel, side-sheet body, bottom-sheet body).
140
+ * Defaults to `20`.
141
+ *
142
+ * The default chrome padding is correct for the declarative
143
+ * `title`/`description`/`actions` layout, but a host passing pure custom
144
+ * `children` that already own their insets can set `contentPadding={0}` to
145
+ * render flush content and manage padding itself. Omitting the prop keeps the
146
+ * 20px default on every surface.
147
+ */
148
+ contentPadding?: number;
137
149
  /** Whether tapping the backdrop dismisses the dialog. Defaults to `true`. */
138
150
  dismissOnBackdrop?: boolean;
139
151
  /**