@oxyhq/bloom 0.16.0 → 0.16.1
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/lib/commonjs/dialog/Dialog.js +38 -150
- package/lib/commonjs/dialog/Dialog.js.map +1 -1
- package/lib/commonjs/dialog/Dialog.web.js +55 -193
- package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
- package/lib/commonjs/dialog/DialogBottomSheet.js +205 -0
- package/lib/commonjs/dialog/DialogBottomSheet.js.map +1 -0
- package/lib/commonjs/dialog/DialogContent.js +155 -0
- package/lib/commonjs/dialog/DialogContent.js.map +1 -0
- package/lib/module/dialog/Dialog.js +41 -153
- package/lib/module/dialog/Dialog.js.map +1 -1
- package/lib/module/dialog/Dialog.web.js +57 -195
- package/lib/module/dialog/Dialog.web.js.map +1 -1
- package/lib/module/dialog/DialogBottomSheet.js +200 -0
- package/lib/module/dialog/DialogBottomSheet.js.map +1 -0
- package/lib/module/dialog/DialogContent.js +148 -0
- package/lib/module/dialog/DialogContent.js.map +1 -0
- package/lib/typescript/commonjs/dialog/Dialog.d.ts +1 -1
- package/lib/typescript/commonjs/dialog/Dialog.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/Dialog.web.d.ts +11 -8
- package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts +29 -0
- package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts.map +1 -0
- package/lib/typescript/commonjs/dialog/DialogContent.d.ts +32 -0
- package/lib/typescript/commonjs/dialog/DialogContent.d.ts.map +1 -0
- package/lib/typescript/module/dialog/Dialog.d.ts +1 -1
- package/lib/typescript/module/dialog/Dialog.d.ts.map +1 -1
- package/lib/typescript/module/dialog/Dialog.web.d.ts +11 -8
- package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/module/dialog/DialogBottomSheet.d.ts +29 -0
- package/lib/typescript/module/dialog/DialogBottomSheet.d.ts.map +1 -0
- package/lib/typescript/module/dialog/DialogContent.d.ts +32 -0
- package/lib/typescript/module/dialog/DialogContent.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/BottomSheetGesture.test.tsx +135 -0
- package/src/__tests__/DialogBottomSheet.test.tsx +130 -0
- package/src/dialog/Dialog.tsx +34 -166
- package/src/dialog/Dialog.web.tsx +46 -206
- package/src/dialog/DialogBottomSheet.tsx +235 -0
- package/src/dialog/DialogContent.tsx +157 -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
|
+
}
|