@mrmeg/expo-ui 0.12.1 → 0.13.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/CHANGELOG.md +14 -0
- package/LLM_USAGE.md +15 -0
- package/README.md +15 -1
- package/dist/components/Drawer.d.ts +9 -2
- package/dist/components/Drawer.js +28 -2
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
All notable changes to `@mrmeg/expo-ui` are documented here. This project
|
|
4
4
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
5
|
|
|
6
|
+
## [0.13.0]
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **`Drawer.Header` now supports a compact app-brand layout.** Use the new
|
|
11
|
+
`icon`, `title`, and `action` slots for a leading brand icon and title with a
|
|
12
|
+
trailing close or `Drawer.ToggleCollapse` control. Existing child-based
|
|
13
|
+
headers remain supported.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Expanded peer compatibility through Expo 57, React Native 0.86, and their
|
|
18
|
+
matching `@expo/ui` and gesture-handler releases.
|
|
19
|
+
|
|
6
20
|
## [0.12.1]
|
|
7
21
|
|
|
8
22
|
### Fixed
|
package/LLM_USAGE.md
CHANGED
|
@@ -212,6 +212,7 @@ Use this table before creating a new app-local primitive.
|
|
|
212
212
|
- Use `EmptyState` for no-data or recoverable error regions.
|
|
213
213
|
- Use `Skeleton` for loading content with stable layout.
|
|
214
214
|
- Use `Progress` for real progress or indeterminate long-running work.
|
|
215
|
+
- Use `Drawer.Header` with `icon`, `title`, and `action` for a compact app-brand row; place `Drawer.ToggleCollapse` in `action` for a trailing rail control.
|
|
215
216
|
|
|
216
217
|
## Minimal Examples
|
|
217
218
|
|
|
@@ -249,6 +250,20 @@ import { Button, Switch, TextInput } from "@mrmeg/expo-ui/components";
|
|
|
249
250
|
</Button>
|
|
250
251
|
```
|
|
251
252
|
|
|
253
|
+
```tsx
|
|
254
|
+
import { Drawer, Icon } from "@mrmeg/expo-ui/components";
|
|
255
|
+
|
|
256
|
+
<Drawer.Header
|
|
257
|
+
icon={<Icon name="hexagon" color="accent" />}
|
|
258
|
+
title="Acme"
|
|
259
|
+
action={
|
|
260
|
+
<Drawer.ToggleCollapse>
|
|
261
|
+
<Icon name="sidebar" decorative />
|
|
262
|
+
</Drawer.ToggleCollapse>
|
|
263
|
+
}
|
|
264
|
+
/>
|
|
265
|
+
```
|
|
266
|
+
|
|
252
267
|
```tsx
|
|
253
268
|
import { EmptyState, Progress, SkeletonCard } from "@mrmeg/expo-ui/components";
|
|
254
269
|
import { notify } from "@mrmeg/expo-ui/state";
|
package/README.md
CHANGED
|
@@ -254,7 +254,7 @@ Most compound components support both direct named imports and dot notation on t
|
|
|
254
254
|
| `Card` | `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter` |
|
|
255
255
|
| `Collapsible` | `CollapsibleTrigger`, `CollapsibleContent` |
|
|
256
256
|
| `Dialog` | `DialogTrigger`, `DialogContent`, `DialogHeader`, `DialogFooter`, `DialogTitle`, `DialogDescription`, `DialogClose` |
|
|
257
|
-
| `Drawer` | `DrawerTrigger`, `DrawerContent`, `DrawerHeader`, `DrawerBody`, `DrawerFooter`, `DrawerClose` |
|
|
257
|
+
| `Drawer` | `DrawerTrigger`, `DrawerContent`, `DrawerHeader`, `DrawerBody`, `DrawerFooter`, `DrawerClose`, `DrawerToggleCollapse` |
|
|
258
258
|
| `DropdownMenu` | `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuGroup`, `DropdownMenuItem`, `DropdownMenuCheckboxItem`, `DropdownMenuRadioGroup`, `DropdownMenuRadioItem`, `DropdownMenuLabel`, `DropdownMenuSeparator`, `DropdownMenuShortcut`, `DropdownMenuPortal`, `DropdownMenuSub`, `DropdownMenuSubTrigger`, `DropdownMenuSubContent` |
|
|
259
259
|
| `Popover` | `PopoverTrigger`, `PopoverContent`, `PopoverHeader`, `PopoverBody`, `PopoverFooter` |
|
|
260
260
|
| `RadioGroup` | `RadioGroupItem` |
|
|
@@ -273,6 +273,20 @@ Use `Button.preset`, not `variant`. `default` is the neutral primary action, `se
|
|
|
273
273
|
|
|
274
274
|
Use `StyledText` or its aliases instead of raw `Text` whenever the text is part of app UI. Use `TextInput` for labeled fields because it already owns label, helper text, error text, clear buttons, password visibility, numeric filtering, and left/right elements.
|
|
275
275
|
|
|
276
|
+
`Drawer.Header` accepts `icon`, `title`, and `action` slots for a compact app-brand row. Pass a string title to use the package typography, and place `Drawer.ToggleCollapse` in `action` for a trailing rail control:
|
|
277
|
+
|
|
278
|
+
```tsx
|
|
279
|
+
<Drawer.Header
|
|
280
|
+
icon={<Icon name="hexagon" color="accent" />}
|
|
281
|
+
title="Acme"
|
|
282
|
+
action={
|
|
283
|
+
<Drawer.ToggleCollapse>
|
|
284
|
+
<Icon name="sidebar" decorative />
|
|
285
|
+
</Drawer.ToggleCollapse>
|
|
286
|
+
}
|
|
287
|
+
/>
|
|
288
|
+
```
|
|
289
|
+
|
|
276
290
|
Mount `UIProvider` once near the root before using `Dialog`, `AlertDialog`, `BottomSheet`, `Drawer`, `DropdownMenu`, `Popover`, `SelectContent`, `Tooltip`, or package notifications. On native, `UIProvider` also wraps app content in the package keyboard-avoiding root by default, so ordinary screens and fixed footers stay above the soft keyboard without repeated app-local wrappers; pass `keyboardAvoiding={false}` to opt out, or use `KeyboardAvoidingView` directly for a subtree with custom behavior. Web skips the root keyboard wrapper unless `keyboardAvoiding` is explicitly enabled. `BottomSheet.Content` listens to React Native keyboard events when `avoidKeyboard` is enabled; it defaults to `true` and can be disabled per sheet. Trigger transient feedback with `notify`.
|
|
277
291
|
|
|
278
292
|
Use `Skeleton` components for loading content with stable dimensions, `EmptyState` for no-data/recoverable errors, `Alert` for blocking confirm/alert dialogs, and `Notification` for transient global feedback.
|
|
@@ -65,7 +65,14 @@ interface DrawerContentProps extends ViewProps {
|
|
|
65
65
|
children: React.ReactNode;
|
|
66
66
|
}
|
|
67
67
|
interface DrawerHeaderProps extends ViewProps {
|
|
68
|
-
|
|
68
|
+
/** Custom header content. Used when the structured slots are omitted. */
|
|
69
|
+
children?: React.ReactNode;
|
|
70
|
+
/** Brand or app icon shown before the title. */
|
|
71
|
+
icon?: React.ReactNode;
|
|
72
|
+
/** Header title. Strings receive the package's default drawer title style. */
|
|
73
|
+
title?: React.ReactNode;
|
|
74
|
+
/** Trailing control, such as a close or rail-collapse button. */
|
|
75
|
+
action?: React.ReactNode;
|
|
69
76
|
}
|
|
70
77
|
interface DrawerBodyProps extends ViewProps {
|
|
71
78
|
children: React.ReactNode;
|
|
@@ -95,7 +102,7 @@ declare function DrawerToggleCollapse({ asChild, children, style: styleOverride
|
|
|
95
102
|
* so their hooks never run conditionally.
|
|
96
103
|
*/
|
|
97
104
|
declare function DrawerContent(props: DrawerContentProps): React.JSX.Element;
|
|
98
|
-
declare function DrawerHeader({ children, style, ...props }: DrawerHeaderProps): React.JSX.Element;
|
|
105
|
+
declare function DrawerHeader({ children, icon, title, action, style, ...props }: DrawerHeaderProps): React.JSX.Element;
|
|
99
106
|
declare function DrawerBody({ children, style, ...props }: DrawerBodyProps): React.JSX.Element;
|
|
100
107
|
declare function DrawerFooter({ children, style, ...props }: DrawerFooterProps): React.JSX.Element;
|
|
101
108
|
declare function useDrawerClose(): () => void;
|
|
@@ -10,6 +10,7 @@ import { shouldUseNativeDriver } from "../lib/animations.js";
|
|
|
10
10
|
import { spacing } from "../constants/spacing.js";
|
|
11
11
|
import { TextColorContext, TextClassContext } from "./StyledText.context";
|
|
12
12
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
13
|
+
import { SansSerifBoldText } from "./StyledText.js";
|
|
13
14
|
/**
|
|
14
15
|
* Drawer Component with Sub-components
|
|
15
16
|
*
|
|
@@ -536,8 +537,11 @@ swipeEnabled: _swipeEnabled, swipeThreshold: _swipeThreshold, velocityThreshold:
|
|
|
536
537
|
// ============================================================================
|
|
537
538
|
// Drawer Header Component
|
|
538
539
|
// ============================================================================
|
|
539
|
-
function DrawerHeader({ children, style, ...props }) {
|
|
540
|
+
function DrawerHeader({ children, icon, title, action, style, ...props }) {
|
|
540
541
|
const { theme } = useTheme();
|
|
542
|
+
const { variant, expandedWidth } = useDrawerContext();
|
|
543
|
+
const usesStructuredLayout = icon !== undefined || title !== undefined || action !== undefined;
|
|
544
|
+
const structuredRowWidth = variant === "rail" ? Math.max(0, expandedWidth - spacing.md * 2) : undefined;
|
|
541
545
|
return (_jsx(View, { style: [
|
|
542
546
|
{
|
|
543
547
|
paddingHorizontal: spacing.md,
|
|
@@ -545,8 +549,30 @@ function DrawerHeader({ children, style, ...props }) {
|
|
|
545
549
|
borderBottomWidth: 1,
|
|
546
550
|
borderBottomColor: theme.colors.border,
|
|
547
551
|
},
|
|
552
|
+
usesStructuredLayout && {
|
|
553
|
+
minHeight: 56,
|
|
554
|
+
paddingVertical: spacing.sm,
|
|
555
|
+
flexDirection: "row",
|
|
556
|
+
alignItems: "center",
|
|
557
|
+
gap: spacing.md,
|
|
558
|
+
},
|
|
559
|
+
usesStructuredLayout && variant === "rail" && {
|
|
560
|
+
overflow: "hidden",
|
|
561
|
+
},
|
|
548
562
|
style,
|
|
549
|
-
], ...props, children:
|
|
563
|
+
], ...props, children: usesStructuredLayout ? (_jsxs(View, { style: {
|
|
564
|
+
flex: variant === "rail" ? undefined : 1,
|
|
565
|
+
flexShrink: variant === "rail" ? 0 : 1,
|
|
566
|
+
width: structuredRowWidth,
|
|
567
|
+
minWidth: 0,
|
|
568
|
+
flexDirection: "row",
|
|
569
|
+
alignItems: "center",
|
|
570
|
+
gap: spacing.md,
|
|
571
|
+
}, children: [icon === undefined ? null : (_jsx(View, { style: { flexShrink: 0 }, children: icon })), title === undefined ? null : (_jsx(View, { style: { flex: 1, minWidth: 0 }, children: typeof title === "string" || typeof title === "number" ? (_jsx(SansSerifBoldText, { numberOfLines: 1, selectable: false, style: { fontSize: 16, lineHeight: 20 }, children: title })) : (title) })), action === undefined ? null : (_jsx(View, { style: {
|
|
572
|
+
flexShrink: 0,
|
|
573
|
+
alignItems: "center",
|
|
574
|
+
justifyContent: "center",
|
|
575
|
+
}, children: action }))] })) : (children) }));
|
|
550
576
|
}
|
|
551
577
|
// ============================================================================
|
|
552
578
|
// Drawer Body Component
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrmeg/expo-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Reusable Expo and React Native UI primitives for MrMeg projects.",
|
|
6
6
|
"keywords": [
|
|
@@ -109,14 +109,14 @@
|
|
|
109
109
|
"@rn-primitives/tooltip": "~1.4.0"
|
|
110
110
|
},
|
|
111
111
|
"peerDependencies": {
|
|
112
|
-
"@expo/ui": ">=56.0.0 <
|
|
112
|
+
"@expo/ui": ">=56.0.0 <58.0.0",
|
|
113
113
|
"@react-native-async-storage/async-storage": ">=2.2.0 <2.3.0",
|
|
114
|
-
"expo": ">=55.0.0 <
|
|
115
|
-
"expo-font": ">=55.0.0 <
|
|
116
|
-
"expo-haptics": ">=55.0.0 <
|
|
114
|
+
"expo": ">=55.0.0 <58.0.0",
|
|
115
|
+
"expo-font": ">=55.0.0 <58.0.0",
|
|
116
|
+
"expo-haptics": ">=55.0.0 <58.0.0",
|
|
117
117
|
"react": ">=19.2.0 <20.0.0",
|
|
118
|
-
"react-native": ">=0.83.0 <0.
|
|
119
|
-
"react-native-gesture-handler": ">=2.30.0 <2.
|
|
118
|
+
"react-native": ">=0.83.0 <0.87.0",
|
|
119
|
+
"react-native-gesture-handler": ">=2.30.0 <2.33.0",
|
|
120
120
|
"react-native-keyboard-controller": ">=1.21.0 <2.0.0",
|
|
121
121
|
"react-native-safe-area-context": ">=5.6.0 <6.0.0",
|
|
122
122
|
"react-native-screens": ">=4.23.0 <5.0.0",
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
},
|
|
126
126
|
"devDependencies": {
|
|
127
127
|
"@types/react": "~19.2.17",
|
|
128
|
-
"react-native-keyboard-controller": "1.21.
|
|
128
|
+
"react-native-keyboard-controller": "1.21.9",
|
|
129
129
|
"typescript": "~6.0.3"
|
|
130
130
|
}
|
|
131
131
|
}
|