@bacon-pos/ui 0.10.0 → 0.15.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/components/Alert/Alert.js +137 -0
- package/lib/commonjs/components/Alert/Alert.js.map +1 -0
- package/lib/commonjs/components/Alert/Alert.stories.js +104 -0
- package/lib/commonjs/components/Alert/Alert.stories.js.map +1 -0
- package/lib/commonjs/components/Alert/Alert.test.js +62 -0
- package/lib/commonjs/components/Alert/Alert.test.js.map +1 -0
- package/lib/commonjs/components/Alert/index.js +13 -0
- package/lib/commonjs/components/Alert/index.js.map +1 -0
- package/lib/commonjs/components/CodeInput/CodeInput.js +28 -9
- package/lib/commonjs/components/CodeInput/CodeInput.js.map +1 -1
- package/lib/commonjs/components/CodeInput/CodeInput.stories.js +28 -1
- package/lib/commonjs/components/CodeInput/CodeInput.stories.js.map +1 -1
- package/lib/commonjs/components/CodeInput/CodeInput.test.js +16 -0
- package/lib/commonjs/components/CodeInput/CodeInput.test.js.map +1 -1
- package/lib/commonjs/components/Dialog/Dialog.js +116 -71
- package/lib/commonjs/components/Dialog/Dialog.js.map +1 -1
- package/lib/commonjs/components/Dialog/StepDialog.js +111 -0
- package/lib/commonjs/components/Dialog/StepDialog.js.map +1 -0
- package/lib/commonjs/components/Dialog/StepDialog.stories.js +157 -0
- package/lib/commonjs/components/Dialog/StepDialog.stories.js.map +1 -0
- package/lib/commonjs/components/Dialog/StepDialog.test.js +118 -0
- package/lib/commonjs/components/Dialog/StepDialog.test.js.map +1 -0
- package/lib/commonjs/components/Dialog/index.js +7 -0
- package/lib/commonjs/components/Dialog/index.js.map +1 -1
- package/lib/commonjs/components/Icon/icons.generated.js +9 -1
- package/lib/commonjs/components/Icon/icons.generated.js.map +1 -1
- package/lib/commonjs/components/List/List.js +45 -0
- package/lib/commonjs/components/List/List.js.map +1 -0
- package/lib/commonjs/components/List/List.stories.js +215 -0
- package/lib/commonjs/components/List/List.stories.js.map +1 -0
- package/lib/commonjs/components/List/List.test.js +94 -0
- package/lib/commonjs/components/List/List.test.js.map +1 -0
- package/lib/commonjs/components/List/ListItem.js +210 -0
- package/lib/commonjs/components/List/ListItem.js.map +1 -0
- package/lib/commonjs/components/List/index.js +20 -0
- package/lib/commonjs/components/List/index.js.map +1 -0
- package/lib/commonjs/components/Menu/Menu.js +11 -3
- package/lib/commonjs/components/Menu/Menu.js.map +1 -1
- package/lib/commonjs/components/ProgressBar/ProgressBar.js +4 -3
- package/lib/commonjs/components/ProgressBar/ProgressBar.js.map +1 -1
- package/lib/commonjs/components/Sidebar/Sidebar.js +20 -3
- package/lib/commonjs/components/Sidebar/Sidebar.js.map +1 -1
- package/lib/commonjs/components/Sidebar/Sidebar.stories.js +17 -1
- package/lib/commonjs/components/Sidebar/Sidebar.stories.js.map +1 -1
- package/lib/commonjs/components/StatTile/StatTile.js +94 -0
- package/lib/commonjs/components/StatTile/StatTile.js.map +1 -0
- package/lib/commonjs/components/StatTile/StatTile.stories.js +100 -0
- package/lib/commonjs/components/StatTile/StatTile.stories.js.map +1 -0
- package/lib/commonjs/components/StatTile/StatTile.test.js +49 -0
- package/lib/commonjs/components/StatTile/StatTile.test.js.map +1 -0
- package/lib/commonjs/components/StatTile/index.js +13 -0
- package/lib/commonjs/components/StatTile/index.js.map +1 -0
- package/lib/commonjs/components/Table/Table.js +69 -6
- package/lib/commonjs/components/Table/Table.js.map +1 -1
- package/lib/commonjs/components/Table/Table.stories.js +118 -1
- package/lib/commonjs/components/Table/Table.stories.js.map +1 -1
- package/lib/commonjs/components/Table/Table.test.js +58 -0
- package/lib/commonjs/components/Table/Table.test.js.map +1 -1
- package/lib/commonjs/components/_internal/ModalHostContext.js +21 -0
- package/lib/commonjs/components/_internal/ModalHostContext.js.map +1 -0
- package/lib/commonjs/components/index.js +64 -31
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/module/components/Alert/Alert.js +133 -0
- package/lib/module/components/Alert/Alert.js.map +1 -0
- package/lib/module/components/Alert/Alert.stories.js +100 -0
- package/lib/module/components/Alert/Alert.stories.js.map +1 -0
- package/lib/module/components/Alert/Alert.test.js +62 -0
- package/lib/module/components/Alert/Alert.test.js.map +1 -0
- package/lib/module/components/Alert/index.js +4 -0
- package/lib/module/components/Alert/index.js.map +1 -0
- package/lib/module/components/CodeInput/CodeInput.js +28 -9
- package/lib/module/components/CodeInput/CodeInput.js.map +1 -1
- package/lib/module/components/CodeInput/CodeInput.stories.js +27 -0
- package/lib/module/components/CodeInput/CodeInput.stories.js.map +1 -1
- package/lib/module/components/CodeInput/CodeInput.test.js +16 -0
- package/lib/module/components/CodeInput/CodeInput.test.js.map +1 -1
- package/lib/module/components/Dialog/Dialog.js +116 -71
- package/lib/module/components/Dialog/Dialog.js.map +1 -1
- package/lib/module/components/Dialog/StepDialog.js +107 -0
- package/lib/module/components/Dialog/StepDialog.js.map +1 -0
- package/lib/module/components/Dialog/StepDialog.stories.js +153 -0
- package/lib/module/components/Dialog/StepDialog.stories.js.map +1 -0
- package/lib/module/components/Dialog/StepDialog.test.js +117 -0
- package/lib/module/components/Dialog/StepDialog.test.js.map +1 -0
- package/lib/module/components/Dialog/index.js +1 -0
- package/lib/module/components/Dialog/index.js.map +1 -1
- package/lib/module/components/Icon/icons.generated.js +9 -1
- package/lib/module/components/Icon/icons.generated.js.map +1 -1
- package/lib/module/components/List/List.js +41 -0
- package/lib/module/components/List/List.js.map +1 -0
- package/lib/module/components/List/List.stories.js +211 -0
- package/lib/module/components/List/List.stories.js.map +1 -0
- package/lib/module/components/List/List.test.js +94 -0
- package/lib/module/components/List/List.test.js.map +1 -0
- package/lib/module/components/List/ListItem.js +206 -0
- package/lib/module/components/List/ListItem.js.map +1 -0
- package/lib/module/components/List/index.js +5 -0
- package/lib/module/components/List/index.js.map +1 -0
- package/lib/module/components/Menu/Menu.js +12 -4
- package/lib/module/components/Menu/Menu.js.map +1 -1
- package/lib/module/components/ProgressBar/ProgressBar.js +4 -3
- package/lib/module/components/ProgressBar/ProgressBar.js.map +1 -1
- package/lib/module/components/Sidebar/Sidebar.js +20 -3
- package/lib/module/components/Sidebar/Sidebar.js.map +1 -1
- package/lib/module/components/Sidebar/Sidebar.stories.js +16 -0
- package/lib/module/components/Sidebar/Sidebar.stories.js.map +1 -1
- package/lib/module/components/StatTile/StatTile.js +90 -0
- package/lib/module/components/StatTile/StatTile.js.map +1 -0
- package/lib/module/components/StatTile/StatTile.stories.js +96 -0
- package/lib/module/components/StatTile/StatTile.stories.js.map +1 -0
- package/lib/module/components/StatTile/StatTile.test.js +49 -0
- package/lib/module/components/StatTile/StatTile.test.js.map +1 -0
- package/lib/module/components/StatTile/index.js +4 -0
- package/lib/module/components/StatTile/index.js.map +1 -0
- package/lib/module/components/Table/Table.js +70 -7
- package/lib/module/components/Table/Table.js.map +1 -1
- package/lib/module/components/Table/Table.stories.js +117 -0
- package/lib/module/components/Table/Table.stories.js.map +1 -1
- package/lib/module/components/Table/Table.test.js +58 -0
- package/lib/module/components/Table/Table.test.js.map +1 -1
- package/lib/module/components/_internal/ModalHostContext.js +17 -0
- package/lib/module/components/_internal/ModalHostContext.js.map +1 -0
- package/lib/module/components/index.js +3 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/typescript/commonjs/components/Alert/Alert.d.ts +41 -0
- package/lib/typescript/commonjs/components/Alert/Alert.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/Alert/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/Alert/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/CodeInput/CodeInput.d.ts +5 -1
- package/lib/typescript/commonjs/components/CodeInput/CodeInput.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts +13 -1
- package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/Dialog/StepDialog.d.ts +63 -0
- package/lib/typescript/commonjs/components/Dialog/StepDialog.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/Dialog/index.d.ts +1 -0
- package/lib/typescript/commonjs/components/Dialog/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/Icon/icons.generated.d.ts +8 -0
- package/lib/typescript/commonjs/components/Icon/icons.generated.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/List/List.d.ts +22 -0
- package/lib/typescript/commonjs/components/List/List.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/List/ListItem.d.ts +61 -0
- package/lib/typescript/commonjs/components/List/ListItem.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/List/index.d.ts +5 -0
- package/lib/typescript/commonjs/components/List/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/Menu/Menu.d.ts +4 -2
- package/lib/typescript/commonjs/components/Menu/Menu.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/ProgressBar/ProgressBar.d.ts +3 -1
- package/lib/typescript/commonjs/components/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/Sidebar/Sidebar.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/StatTile/StatTile.d.ts +31 -0
- package/lib/typescript/commonjs/components/StatTile/StatTile.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/StatTile/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/StatTile/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/Table/Table.d.ts +25 -3
- package/lib/typescript/commonjs/components/Table/Table.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/_internal/ModalHostContext.d.ts +11 -0
- package/lib/typescript/commonjs/components/_internal/ModalHostContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/index.d.ts.map +1 -1
- package/lib/typescript/module/components/Alert/Alert.d.ts +41 -0
- package/lib/typescript/module/components/Alert/Alert.d.ts.map +1 -0
- package/lib/typescript/module/components/Alert/index.d.ts +3 -0
- package/lib/typescript/module/components/Alert/index.d.ts.map +1 -0
- package/lib/typescript/module/components/CodeInput/CodeInput.d.ts +5 -1
- package/lib/typescript/module/components/CodeInput/CodeInput.d.ts.map +1 -1
- package/lib/typescript/module/components/Dialog/Dialog.d.ts +13 -1
- package/lib/typescript/module/components/Dialog/Dialog.d.ts.map +1 -1
- package/lib/typescript/module/components/Dialog/StepDialog.d.ts +63 -0
- package/lib/typescript/module/components/Dialog/StepDialog.d.ts.map +1 -0
- package/lib/typescript/module/components/Dialog/index.d.ts +1 -0
- package/lib/typescript/module/components/Dialog/index.d.ts.map +1 -1
- package/lib/typescript/module/components/Icon/icons.generated.d.ts +8 -0
- package/lib/typescript/module/components/Icon/icons.generated.d.ts.map +1 -1
- package/lib/typescript/module/components/List/List.d.ts +22 -0
- package/lib/typescript/module/components/List/List.d.ts.map +1 -0
- package/lib/typescript/module/components/List/ListItem.d.ts +61 -0
- package/lib/typescript/module/components/List/ListItem.d.ts.map +1 -0
- package/lib/typescript/module/components/List/index.d.ts +5 -0
- package/lib/typescript/module/components/List/index.d.ts.map +1 -0
- package/lib/typescript/module/components/Menu/Menu.d.ts +4 -2
- package/lib/typescript/module/components/Menu/Menu.d.ts.map +1 -1
- package/lib/typescript/module/components/ProgressBar/ProgressBar.d.ts +3 -1
- package/lib/typescript/module/components/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/lib/typescript/module/components/Sidebar/Sidebar.d.ts.map +1 -1
- package/lib/typescript/module/components/StatTile/StatTile.d.ts +31 -0
- package/lib/typescript/module/components/StatTile/StatTile.d.ts.map +1 -0
- package/lib/typescript/module/components/StatTile/index.d.ts +3 -0
- package/lib/typescript/module/components/StatTile/index.d.ts.map +1 -0
- package/lib/typescript/module/components/Table/Table.d.ts +25 -3
- package/lib/typescript/module/components/Table/Table.d.ts.map +1 -1
- package/lib/typescript/module/components/_internal/ModalHostContext.d.ts +11 -0
- package/lib/typescript/module/components/_internal/ModalHostContext.d.ts.map +1 -0
- package/lib/typescript/module/components/index.d.ts +3 -0
- package/lib/typescript/module/components/index.d.ts.map +1 -1
- package/package.json +16 -14
- package/src/components/Alert/Alert.tsx +132 -0
- package/src/components/Alert/index.ts +2 -0
- package/src/components/CodeInput/CodeInput.tsx +46 -14
- package/src/components/Dialog/Dialog.tsx +92 -31
- package/src/components/Dialog/StepDialog.tsx +173 -0
- package/src/components/Dialog/index.ts +1 -0
- package/src/components/Icon/icons.generated.ts +9 -1
- package/src/components/List/List.tsx +45 -0
- package/src/components/List/ListItem.tsx +214 -0
- package/src/components/List/index.ts +4 -0
- package/src/components/Menu/Menu.tsx +15 -6
- package/src/components/ProgressBar/ProgressBar.tsx +6 -3
- package/src/components/Sidebar/Sidebar.tsx +18 -10
- package/src/components/StatTile/StatTile.tsx +98 -0
- package/src/components/StatTile/index.ts +2 -0
- package/src/components/Table/Table.tsx +93 -8
- package/src/components/_internal/ModalHostContext.tsx +15 -0
- package/src/components/index.ts +3 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useRef, useState, type ReactNode } from 'react';
|
|
1
|
+
import { useContext, useEffect, useRef, useState, type ReactNode } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Animated,
|
|
4
4
|
Dimensions,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type StyleProp,
|
|
13
13
|
type ViewStyle,
|
|
14
14
|
} from 'react-native';
|
|
15
|
+
import { ModalHostContext } from '../_internal/ModalHostContext';
|
|
15
16
|
import { computeMenuPlacement } from './placement';
|
|
16
17
|
|
|
17
18
|
export type OriginVertical = 'top' | 'center' | 'bottom';
|
|
@@ -57,8 +58,10 @@ export interface MenuProps {
|
|
|
57
58
|
insets?: EdgeInsets;
|
|
58
59
|
/**
|
|
59
60
|
* Offset between window coordinates (where the trigger is measured) and the
|
|
60
|
-
* full-screen Modal the menu paints into.
|
|
61
|
-
*
|
|
61
|
+
* full-screen Modal the menu paints into. When omitted it defaults to the
|
|
62
|
+
* Android status bar height, or 0 when rendered inside a full-screen Modal host
|
|
63
|
+
* (e.g. `Dialog`) or on iOS / web. Pass an explicit value to override (e.g. 0
|
|
64
|
+
* when the host runs edge-to-edge / immersive).
|
|
62
65
|
*/
|
|
63
66
|
statusBarHeight?: number;
|
|
64
67
|
/** Controlled open state. Omit for uncontrolled. */
|
|
@@ -105,13 +108,18 @@ export function Menu({
|
|
|
105
108
|
offset = 8,
|
|
106
109
|
autoFit = true,
|
|
107
110
|
insets = ZERO_INSETS,
|
|
108
|
-
statusBarHeight
|
|
111
|
+
statusBarHeight,
|
|
109
112
|
open: openProp,
|
|
110
113
|
onOpenChange,
|
|
111
114
|
screenPadding = 8,
|
|
112
115
|
triggerStyle,
|
|
113
116
|
}: MenuProps) {
|
|
114
117
|
const triggerRef = useRef<View>(null);
|
|
118
|
+
// Inside a full-screen Modal host (e.g. Dialog) the trigger is already measured
|
|
119
|
+
// in full-screen coordinates, so no status-bar compensation is needed there.
|
|
120
|
+
const insideModalHost = useContext(ModalHostContext);
|
|
121
|
+
const effectiveStatusBarHeight =
|
|
122
|
+
statusBarHeight ?? (insideModalHost ? 0 : DEFAULT_STATUS_BAR_HEIGHT);
|
|
115
123
|
const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
|
|
116
124
|
const isControlled = openProp !== undefined;
|
|
117
125
|
const open = isControlled ? openProp : uncontrolledOpen;
|
|
@@ -182,7 +190,7 @@ export function Menu({
|
|
|
182
190
|
insets,
|
|
183
191
|
screenWidth: screen.width,
|
|
184
192
|
screenHeight: screen.height,
|
|
185
|
-
statusBarHeight,
|
|
193
|
+
statusBarHeight: effectiveStatusBarHeight,
|
|
186
194
|
autoFit,
|
|
187
195
|
})
|
|
188
196
|
: null;
|
|
@@ -231,7 +239,8 @@ export function Menu({
|
|
|
231
239
|
transform: [{ scale: placed ? anim.interpolate({ inputRange: [0, 1], outputRange: [0.96, 1] }) : 1 }],
|
|
232
240
|
}}
|
|
233
241
|
>
|
|
234
|
-
{
|
|
242
|
+
{/* The overlay is itself a full-screen Modal host for nested menus. */}
|
|
243
|
+
<ModalHostContext.Provider value={true}>{content}</ModalHostContext.Provider>
|
|
235
244
|
</Animated.View>
|
|
236
245
|
</Pressable>
|
|
237
246
|
</Modal>
|
|
@@ -24,6 +24,8 @@ export interface ProgressBarProps {
|
|
|
24
24
|
trackColor?: string;
|
|
25
25
|
/** Track border color. Defaults to `border.default`. */
|
|
26
26
|
borderColor?: string;
|
|
27
|
+
/** Draw the track border (and inner inset). Set false for a flat, thin bar. Defaults to true. */
|
|
28
|
+
bordered?: boolean;
|
|
27
29
|
/** Animate width changes. Defaults to true. */
|
|
28
30
|
animated?: boolean;
|
|
29
31
|
/** Override the container style. */
|
|
@@ -51,6 +53,7 @@ export function ProgressBar({
|
|
|
51
53
|
color,
|
|
52
54
|
trackColor,
|
|
53
55
|
borderColor,
|
|
56
|
+
bordered = true,
|
|
54
57
|
animated = true,
|
|
55
58
|
style,
|
|
56
59
|
accessibilityLabel,
|
|
@@ -79,7 +82,7 @@ export function ProgressBar({
|
|
|
79
82
|
extrapolate: 'clamp',
|
|
80
83
|
});
|
|
81
84
|
|
|
82
|
-
const inset = 2.5;
|
|
85
|
+
const inset = bordered ? 2.5 : 0;
|
|
83
86
|
|
|
84
87
|
return (
|
|
85
88
|
<View
|
|
@@ -92,8 +95,8 @@ export function ProgressBar({
|
|
|
92
95
|
style={{
|
|
93
96
|
height,
|
|
94
97
|
borderRadius: height / 2,
|
|
95
|
-
borderWidth: 1.5,
|
|
96
|
-
borderColor: borderColor ?? theme.colors.border.default,
|
|
98
|
+
borderWidth: bordered ? 1.5 : 0,
|
|
99
|
+
borderColor: bordered ? borderColor ?? theme.colors.border.default : 'transparent',
|
|
97
100
|
backgroundColor: trackColor ?? theme.colors.background.subtle,
|
|
98
101
|
padding: inset,
|
|
99
102
|
justifyContent: 'center',
|
|
@@ -234,6 +234,15 @@ export function Sidebar({
|
|
|
234
234
|
extrapolate: 'clamp',
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
+
// The logo glides to center the same way (measured so any custom logo works),
|
|
238
|
+
// at a constant size — no discrete size/alignment swap, so no flicker.
|
|
239
|
+
const [logoWidth, setLogoWidth] = useState(0);
|
|
240
|
+
const logoTranslateX = width.interpolate({
|
|
241
|
+
inputRange: [collapsedWidth, expandedWidth],
|
|
242
|
+
outputRange: [Math.max(0, (collapsedWidth - 2 * PADDING - logoWidth) / 2), 0],
|
|
243
|
+
extrapolate: 'clamp',
|
|
244
|
+
});
|
|
245
|
+
|
|
237
246
|
const selectItem = (item: SidebarItem) => {
|
|
238
247
|
if (!activeControlled) setActiveInternal(item.key);
|
|
239
248
|
item.onPress?.();
|
|
@@ -246,7 +255,7 @@ export function Sidebar({
|
|
|
246
255
|
onCollapsedChange?.(next);
|
|
247
256
|
};
|
|
248
257
|
|
|
249
|
-
const logoNode = logo ?? <BaconLogo height={
|
|
258
|
+
const logoNode = logo ?? <BaconLogo height={40} />;
|
|
250
259
|
const toggle = showToggle ? (
|
|
251
260
|
<Animated.View style={{ transform: [{ translateX: toggleTranslateX }] }}>
|
|
252
261
|
<Animated.View style={{ transform: [{ rotate: arrowRotate }] }}>
|
|
@@ -276,15 +285,14 @@ export function Sidebar({
|
|
|
276
285
|
style,
|
|
277
286
|
]}
|
|
278
287
|
>
|
|
279
|
-
{/* Logo */}
|
|
280
|
-
<View
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
{logoNode}
|
|
288
|
+
{/* Logo — constant size, glides to center on collapse (no flicker) */}
|
|
289
|
+
<View style={{ minHeight: 56, alignItems: 'flex-start', justifyContent: 'center' }}>
|
|
290
|
+
<Animated.View
|
|
291
|
+
onLayout={(e) => setLogoWidth(e.nativeEvent.layout.width)}
|
|
292
|
+
style={{ transform: [{ translateX: logoTranslateX }] }}
|
|
293
|
+
>
|
|
294
|
+
{logoNode}
|
|
295
|
+
</Animated.View>
|
|
288
296
|
</View>
|
|
289
297
|
|
|
290
298
|
{/* Tabs (scrolls when overflowing; footer stays pinned below) */}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { Pressable, View, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
import { useTheme } from '../../theme';
|
|
4
|
+
import { Icon } from '../Icon/Icon';
|
|
5
|
+
import type { IconName } from '../Icon/icons.generated';
|
|
6
|
+
import { Text } from '../Text/Text';
|
|
7
|
+
|
|
8
|
+
export interface StatTileProps {
|
|
9
|
+
/** Small label above the value. */
|
|
10
|
+
label: string;
|
|
11
|
+
/** The value — a string/number, or a node for custom formatting/color. */
|
|
12
|
+
value: ReactNode;
|
|
13
|
+
/** Optional leading icon. */
|
|
14
|
+
icon?: IconName;
|
|
15
|
+
/** Fixed width. Omit to fill the parent (works in a flex/grid row of tiles). */
|
|
16
|
+
width?: number;
|
|
17
|
+
/** Make the tile pressable. */
|
|
18
|
+
onPress?: () => void;
|
|
19
|
+
/** Tile background. Defaults to `background.subtle`. */
|
|
20
|
+
backgroundColor?: string;
|
|
21
|
+
/** Label color. Defaults to `secondary`. */
|
|
22
|
+
labelColor?: string;
|
|
23
|
+
/** Value color (when `value` is text). Defaults to `primary`. */
|
|
24
|
+
valueColor?: string;
|
|
25
|
+
/** Override the container style. */
|
|
26
|
+
style?: StyleProp<ViewStyle>;
|
|
27
|
+
accessibilityLabel?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* StatTile — a compact KPI card: a small label over a prominent value (Figma
|
|
32
|
+
* "info card"). Fills its parent by default (so a row/grid of tiles sizes evenly)
|
|
33
|
+
* or takes a fixed `width`. `value` accepts a node for custom color/formatting.
|
|
34
|
+
*/
|
|
35
|
+
export function StatTile({
|
|
36
|
+
label,
|
|
37
|
+
value,
|
|
38
|
+
icon,
|
|
39
|
+
width,
|
|
40
|
+
onPress,
|
|
41
|
+
backgroundColor,
|
|
42
|
+
labelColor,
|
|
43
|
+
valueColor,
|
|
44
|
+
style,
|
|
45
|
+
accessibilityLabel,
|
|
46
|
+
}: StatTileProps) {
|
|
47
|
+
const theme = useTheme();
|
|
48
|
+
|
|
49
|
+
const content = (
|
|
50
|
+
<>
|
|
51
|
+
{icon ? (
|
|
52
|
+
<View style={{ width: 24, height: 24, flexShrink: 0 }}>
|
|
53
|
+
<Icon name={icon} size={24} color="secondary" />
|
|
54
|
+
</View>
|
|
55
|
+
) : null}
|
|
56
|
+
<View style={{ flex: 1, minWidth: 0, gap: 4 }}>
|
|
57
|
+
<Text variant="bodySmall" color={labelColor ?? 'secondary'} numberOfLines={1}>
|
|
58
|
+
{label}
|
|
59
|
+
</Text>
|
|
60
|
+
{typeof value === 'string' || typeof value === 'number' ? (
|
|
61
|
+
<Text variant="bodyLarge" weight="semibold" color={valueColor ?? 'primary'} numberOfLines={1}>
|
|
62
|
+
{String(value)}
|
|
63
|
+
</Text>
|
|
64
|
+
) : (
|
|
65
|
+
value
|
|
66
|
+
)}
|
|
67
|
+
</View>
|
|
68
|
+
</>
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const containerStyle: ViewStyle = {
|
|
72
|
+
flexDirection: 'row',
|
|
73
|
+
alignItems: 'center',
|
|
74
|
+
gap: 12,
|
|
75
|
+
padding: 12,
|
|
76
|
+
borderRadius: theme.radii.lg,
|
|
77
|
+
backgroundColor: backgroundColor ?? theme.colors.background.subtle,
|
|
78
|
+
...(width != null ? { width } : { alignSelf: 'stretch' }),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
if (!onPress) {
|
|
82
|
+
return (
|
|
83
|
+
<View accessibilityLabel={accessibilityLabel} style={[containerStyle, style]}>
|
|
84
|
+
{content}
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return (
|
|
89
|
+
<Pressable
|
|
90
|
+
onPress={onPress}
|
|
91
|
+
accessibilityRole="button"
|
|
92
|
+
accessibilityLabel={accessibilityLabel ?? label}
|
|
93
|
+
style={({ pressed }) => [containerStyle, { opacity: pressed ? 0.96 : 1 }, style]}
|
|
94
|
+
>
|
|
95
|
+
{content}
|
|
96
|
+
</Pressable>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
@@ -11,13 +11,30 @@ import {
|
|
|
11
11
|
type ViewStyle,
|
|
12
12
|
} from 'react-native';
|
|
13
13
|
import { useTheme } from '../../theme';
|
|
14
|
+
import type { DropdownEntry } from '../Dropdown/Dropdown';
|
|
14
15
|
import { Checkbox } from '../Checkbox/Checkbox';
|
|
15
16
|
import { Icon } from '../Icon/Icon';
|
|
17
|
+
import type { IconName } from '../Icon/icons.generated';
|
|
18
|
+
import { MenuButton } from '../Menu/MenuButton';
|
|
16
19
|
import { Spinner } from '../Spinner/Spinner';
|
|
17
20
|
import { Text } from '../Text/Text';
|
|
18
21
|
import { ScrollArea } from '../_internal/ScrollArea';
|
|
19
22
|
|
|
20
23
|
export type ColumnAlign = 'left' | 'center' | 'right';
|
|
24
|
+
|
|
25
|
+
/** A structured row action (inline icon, or a row in the overflow "More" menu). */
|
|
26
|
+
export interface TableAction {
|
|
27
|
+
key: string;
|
|
28
|
+
label: string;
|
|
29
|
+
icon: IconName;
|
|
30
|
+
onPress?: () => void;
|
|
31
|
+
/** Render in the error color (red icon inline, red row in the menu). */
|
|
32
|
+
destructive?: boolean;
|
|
33
|
+
/** Dim and block the action. */
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
/** Icon color override (ignored when `destructive`). */
|
|
36
|
+
color?: string;
|
|
37
|
+
}
|
|
21
38
|
export type SortDirection = 'asc' | 'desc';
|
|
22
39
|
export interface TableSort {
|
|
23
40
|
key: string;
|
|
@@ -63,9 +80,17 @@ export interface TableProps<T> {
|
|
|
63
80
|
defaultExpandedKeys?: string[];
|
|
64
81
|
onExpandedChange?: (keys: string[]) => void;
|
|
65
82
|
|
|
66
|
-
/** Trailing per-row actions (e.g.
|
|
83
|
+
/** Trailing per-row actions, free-form (e.g. custom buttons) — mutually exclusive with `expandable`. */
|
|
67
84
|
renderActions?: (row: T, index: number) => ReactNode;
|
|
68
|
-
/**
|
|
85
|
+
/** Structured per-row actions: the first `maxInlineActions` render as inline icons, the rest collapse into a "More" menu. */
|
|
86
|
+
actions?: (row: T, index: number) => TableAction[];
|
|
87
|
+
/** How many actions render inline before overflowing into the More menu. Defaults to 2. */
|
|
88
|
+
maxInlineActions?: number;
|
|
89
|
+
/** Icon for the overflow menu trigger. Defaults to `more`. */
|
|
90
|
+
moreIcon?: IconName;
|
|
91
|
+
/** Width of the overflow More menu. */
|
|
92
|
+
actionsMenuWidth?: number;
|
|
93
|
+
/** Width reserved for the actions column. Defaults to fit the actions. */
|
|
69
94
|
actionsWidth?: number;
|
|
70
95
|
|
|
71
96
|
/** Row selection via checkboxes. */
|
|
@@ -135,7 +160,11 @@ export function Table<T>({
|
|
|
135
160
|
defaultExpandedKeys,
|
|
136
161
|
onExpandedChange,
|
|
137
162
|
renderActions,
|
|
138
|
-
|
|
163
|
+
actions,
|
|
164
|
+
maxInlineActions = 2,
|
|
165
|
+
moreIcon = 'more',
|
|
166
|
+
actionsMenuWidth,
|
|
167
|
+
actionsWidth,
|
|
139
168
|
selectable = false,
|
|
140
169
|
selectedKeys,
|
|
141
170
|
defaultSelectedKeys,
|
|
@@ -159,8 +188,11 @@ export function Table<T>({
|
|
|
159
188
|
const isMobile = screenWidth < mobileBreakpoint;
|
|
160
189
|
|
|
161
190
|
// Expandable and actions are mutually exclusive; expandable wins if both set.
|
|
162
|
-
const hasActions = !expandable && !!renderActions;
|
|
163
|
-
const
|
|
191
|
+
const hasActions = !expandable && (!!renderActions || !!actions);
|
|
192
|
+
const ACTION_SLOT = 44; // sm icon-button footprint (40 + gap)
|
|
193
|
+
const actionsColWidth =
|
|
194
|
+
actionsWidth ?? (renderActions ? 96 : (maxInlineActions + 1) * ACTION_SLOT);
|
|
195
|
+
const trailingWidth = expandable ? CHEVRON_WIDTH : hasActions ? actionsColWidth : 0;
|
|
164
196
|
|
|
165
197
|
// ---- sort ----
|
|
166
198
|
const sortControlled = sort !== undefined;
|
|
@@ -245,6 +277,57 @@ export function Table<T>({
|
|
|
245
277
|
return content as ReactNode;
|
|
246
278
|
};
|
|
247
279
|
|
|
280
|
+
// Actions cell: free-form `renderActions`, or structured `actions` with the
|
|
281
|
+
// first N inline and the rest in a "More" menu (all in the menu on mobile).
|
|
282
|
+
const renderActionsCell = (row: T, index: number): ReactNode => {
|
|
283
|
+
if (renderActions) return renderActions(row, index);
|
|
284
|
+
if (!actions) return null;
|
|
285
|
+
const list = actions(row, index);
|
|
286
|
+
const inlineCount = isMobile ? 0 : maxInlineActions;
|
|
287
|
+
const inline = list.slice(0, inlineCount);
|
|
288
|
+
const overflow = list.slice(inlineCount);
|
|
289
|
+
return (
|
|
290
|
+
<>
|
|
291
|
+
{inline.map((a) => (
|
|
292
|
+
<Pressable
|
|
293
|
+
key={a.key}
|
|
294
|
+
onPress={a.disabled ? undefined : a.onPress}
|
|
295
|
+
disabled={a.disabled}
|
|
296
|
+
hitSlop={8}
|
|
297
|
+
accessibilityRole="button"
|
|
298
|
+
accessibilityLabel={a.label}
|
|
299
|
+
style={{ width: 40, height: 40, alignItems: 'center', justifyContent: 'center', opacity: a.disabled ? 0.5 : 1 }}
|
|
300
|
+
>
|
|
301
|
+
<Icon
|
|
302
|
+
name={a.icon}
|
|
303
|
+
size={24}
|
|
304
|
+
color={a.destructive ? theme.colors.feedback.error : a.color ?? theme.colors.text.secondary}
|
|
305
|
+
/>
|
|
306
|
+
</Pressable>
|
|
307
|
+
))}
|
|
308
|
+
{overflow.length > 0 ? (
|
|
309
|
+
<MenuButton
|
|
310
|
+
icon={moreIcon}
|
|
311
|
+
accessibilityLabel="More actions"
|
|
312
|
+
variant="ghost"
|
|
313
|
+
size="sm"
|
|
314
|
+
menuWidth={actionsMenuWidth}
|
|
315
|
+
options={overflow.map(
|
|
316
|
+
(a): DropdownEntry => ({
|
|
317
|
+
key: a.key,
|
|
318
|
+
label: a.label,
|
|
319
|
+
disabled: a.disabled,
|
|
320
|
+
destructive: a.destructive,
|
|
321
|
+
leading: { kind: 'icon', name: a.icon, ...(a.destructive || !a.color ? {} : { color: a.color }) },
|
|
322
|
+
onPress: a.onPress,
|
|
323
|
+
}),
|
|
324
|
+
)}
|
|
325
|
+
/>
|
|
326
|
+
) : null}
|
|
327
|
+
</>
|
|
328
|
+
);
|
|
329
|
+
};
|
|
330
|
+
|
|
248
331
|
// ---------- Desktop ----------
|
|
249
332
|
const header = (
|
|
250
333
|
<View
|
|
@@ -347,8 +430,8 @@ export function Table<T>({
|
|
|
347
430
|
<Icon name="chevron-down" size={24} color="secondary" />
|
|
348
431
|
</View>
|
|
349
432
|
) : hasActions ? (
|
|
350
|
-
<View style={{ width:
|
|
351
|
-
{
|
|
433
|
+
<View style={{ width: actionsColWidth, flexShrink: 0, flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end', gap: 4 }}>
|
|
434
|
+
{renderActionsCell(row, index)}
|
|
352
435
|
</View>
|
|
353
436
|
) : null}
|
|
354
437
|
</Pressable>
|
|
@@ -418,7 +501,9 @@ export function Table<T>({
|
|
|
418
501
|
) : null}
|
|
419
502
|
</View>
|
|
420
503
|
{hasActions ? (
|
|
421
|
-
<View style={{ flexDirection: 'row', justifyContent: 'flex-end', gap: 4 }}>
|
|
504
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end', gap: 4 }}>
|
|
505
|
+
{renderActionsCell(row, index)}
|
|
506
|
+
</View>
|
|
422
507
|
) : null}
|
|
423
508
|
</Pressable>
|
|
424
509
|
{expandable && isOpen && renderExpanded ? (
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* True when the subtree is rendered inside a full-screen (statusBarTranslucent)
|
|
5
|
+
* Modal host — e.g. `Dialog`, or a `Menu` overlay. Anchored menus use this to
|
|
6
|
+
* skip the status-bar coordinate compensation: inside such a host, a trigger
|
|
7
|
+
* measured with `measureInWindow` is already in full-screen coordinates that
|
|
8
|
+
* match the menu's own Modal, so adding the status bar height would push the
|
|
9
|
+
* menu down (a visible gap). Outside a host, the compensation is still applied.
|
|
10
|
+
*/
|
|
11
|
+
export const ModalHostContext = createContext(false);
|
|
12
|
+
|
|
13
|
+
export function useInsideModalHost(): boolean {
|
|
14
|
+
return useContext(ModalHostContext);
|
|
15
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from './Alert';
|
|
1
2
|
export * from './Avatar';
|
|
2
3
|
export * from './Badge';
|
|
3
4
|
export * from './brand';
|
|
@@ -15,6 +16,7 @@ export * from './IconButton';
|
|
|
15
16
|
export * from './Input';
|
|
16
17
|
export * from './Keypad';
|
|
17
18
|
export * from './LinkButton';
|
|
19
|
+
export * from './List';
|
|
18
20
|
export * from './Menu';
|
|
19
21
|
export * from './ProductCard';
|
|
20
22
|
export * from './ProgressBar';
|
|
@@ -23,6 +25,7 @@ export * from './Radio';
|
|
|
23
25
|
export * from './Select';
|
|
24
26
|
export * from './Sidebar';
|
|
25
27
|
export * from './Spinner';
|
|
28
|
+
export * from './StatTile';
|
|
26
29
|
export * from './Surface';
|
|
27
30
|
export * from './Switch';
|
|
28
31
|
export * from './Table';
|