@mrmeg/expo-ui 0.22.0 → 0.23.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 +31 -0
- package/LLM_USAGE.md +8 -1
- package/README.md +18 -0
- package/dist/components/Accordion.js +1 -1
- package/dist/components/Card.js +7 -7
- package/dist/components/Checkbox.js +1 -1
- package/dist/components/Dialog.js +24 -5
- package/dist/components/EmptyState.js +2 -2
- package/dist/components/Item.d.ts +3 -2
- package/dist/components/Item.js +8 -6
- package/dist/components/RadioGroup.js +1 -1
- package/dist/components/StatCard.js +1 -1
- package/dist/constants/spacing.d.ts +16 -6
- package/dist/constants/spacing.js +17 -6
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,37 @@
|
|
|
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.23.0]
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **Density tokens.** `spacing` gains `smd` (12) and `mdl` (20) scale steps plus
|
|
11
|
+
semantic layout tokens: `screenPadding`, `sectionSpacing` (now 24),
|
|
12
|
+
`cardPadding`, `dialogPadding`, `rowPaddingY`/`rowPaddingX`, `rowGap`,
|
|
13
|
+
`rowMinHeight`, and `formRowMinHeight`. Components read these so a single
|
|
14
|
+
retune shifts the whole library's density.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **Compact surfaces and rows.** `Card` header/content/footer padding drops
|
|
19
|
+
from 24 to 16 with a 16px title. `Item` rows use 10/16 padding and a 12px
|
|
20
|
+
gap, with a 40px visual min height on web while native keeps the 44pt touch
|
|
21
|
+
target. `Checkbox` and `RadioGroup` rows are 32px on web (native unchanged).
|
|
22
|
+
`Dialog`/`AlertDialog` padding is 20, `StatCard` padding is 16,
|
|
23
|
+
`EmptyState` vertical padding is 32, and `Accordion` triggers use 12px
|
|
24
|
+
vertical padding.
|
|
25
|
+
- **Dialog spacing is symmetric.** `DialogContent` and `AlertDialogContent`
|
|
26
|
+
space their children with a 16px gap instead of fixed header/footer
|
|
27
|
+
margins, so a header-only dialog no longer reads bottom-heavy.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **Dialog could not be dismissed on web.** The web overlay primitive wrapped
|
|
32
|
+
the backdrop in a react-native-web `Pressable` whose click handler stopped
|
|
33
|
+
propagation, so outside presses (mouse and touch) and Escape never reached
|
|
34
|
+
Radix. `DialogContent` now renders the overlay `asChild` on web; native is
|
|
35
|
+
unchanged.
|
|
36
|
+
|
|
6
37
|
## [0.22.0]
|
|
7
38
|
|
|
8
39
|
### Added
|
package/LLM_USAGE.md
CHANGED
|
@@ -157,11 +157,18 @@ semantic colors without destructuring `withAlpha` outside the factory:
|
|
|
157
157
|
const { styles } = useStyles(({ theme, spacing, withAlpha }) => ({
|
|
158
158
|
card: {
|
|
159
159
|
backgroundColor: withAlpha(theme.colors.primary, 0.08),
|
|
160
|
-
padding: spacing.
|
|
160
|
+
padding: spacing.cardPadding,
|
|
161
161
|
},
|
|
162
162
|
}));
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
+
Layout spacing uses semantic density tokens rather than raw scale steps:
|
|
166
|
+
`spacing.screenPadding` (16) for screen and block gutters, `spacing.cardPadding`
|
|
167
|
+
(16) for bordered panels, `spacing.sectionSpacing` (24) between grouped lists,
|
|
168
|
+
`spacing.dialogPadding` (20) for dialogs, and `spacing.rowPaddingY`/`rowPaddingX`
|
|
169
|
+
(10/16) with `spacing.rowGap` (12) for list rows. `Item` already applies the row
|
|
170
|
+
tokens and keeps a 44px hit area on native while rendering 40px on web.
|
|
171
|
+
|
|
165
172
|
When the saved theme preference is `system`, the package theme store owns the
|
|
166
173
|
OS color-scheme subscription, including web `prefers-color-scheme`. Do not add
|
|
167
174
|
app-local Appearance or `matchMedia` listeners for package components; import
|
package/README.md
CHANGED
|
@@ -124,6 +124,24 @@ const { styles } = useStyles(({ theme, spacing, withAlpha }) => ({
|
|
|
124
124
|
}));
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
### Spacing and density
|
|
128
|
+
|
|
129
|
+
`spacing` exposes an 8px base scale (`xxs` 2, `xs` 4, `sm` 8, `smd` 12, `md` 16, `mdl` 20, `lg` 24, `xl` 32, `xxl` 48, `xxxl` 64) plus semantic density tokens that package components read from. Reach for the semantic names in app code so a future density tune lands everywhere at once:
|
|
130
|
+
|
|
131
|
+
| Token | Value | Use |
|
|
132
|
+
|-------|-------|-----|
|
|
133
|
+
| `screenPadding` | 16 | Horizontal gutter for screens, scroll content, and landing blocks |
|
|
134
|
+
| `sectionSpacing` | 24 | Gap between grouped lists, cards, or settings groups |
|
|
135
|
+
| `cardPadding` | 16 | `Card`, `StatCard`, and any bordered panel |
|
|
136
|
+
| `dialogPadding` | 20 | `Dialog` and `AlertDialog` content |
|
|
137
|
+
| `rowPaddingY` / `rowPaddingX` | 10 / 16 | `Item` rows and hand-rolled list rows |
|
|
138
|
+
| `rowGap` | 12 | Gap between row media, content, and actions |
|
|
139
|
+
| `rowMinHeight` | 40 | Visual row height on web; native rows keep `touchTarget` |
|
|
140
|
+
| `formRowMinHeight` | 32 | Checkbox and radio rows on web; native keeps `touchTarget` |
|
|
141
|
+
| `touchTarget` | 44 | Minimum native hit area; pair a smaller visual height with `hitSlop` |
|
|
142
|
+
|
|
143
|
+
Controls (`Button`, `TextInput`, `Select`, `Tabs`, `Toggle`) size themselves from their own `size` prop (28/32/36/40) and do not read these tokens.
|
|
144
|
+
|
|
127
145
|
### Color overrides
|
|
128
146
|
|
|
129
147
|
The package ships a neutral default palette. `useTheme()` resolves colors in
|
|
@@ -132,7 +132,7 @@ function AccordionTrigger({ children, style: styleOverride, disabled, ...props }
|
|
|
132
132
|
justifyContent: "space-between",
|
|
133
133
|
gap: spacing.md,
|
|
134
134
|
borderRadius: spacing.radiusMd,
|
|
135
|
-
paddingVertical: spacing.
|
|
135
|
+
paddingVertical: spacing.smd,
|
|
136
136
|
...(Platform.OS === "web" && {
|
|
137
137
|
cursor: "pointer",
|
|
138
138
|
userSelect: "none",
|
package/dist/components/Card.js
CHANGED
|
@@ -98,24 +98,24 @@ const createCardStyles = (theme) => StyleSheet.create({
|
|
|
98
98
|
backgroundColor: "transparent",
|
|
99
99
|
},
|
|
100
100
|
header: {
|
|
101
|
-
padding: spacing.
|
|
101
|
+
padding: spacing.cardPadding,
|
|
102
102
|
paddingBottom: spacing.xs,
|
|
103
103
|
gap: spacing.xs,
|
|
104
104
|
},
|
|
105
105
|
content: {
|
|
106
|
-
paddingHorizontal: spacing.
|
|
107
|
-
paddingBottom: spacing.
|
|
106
|
+
paddingHorizontal: spacing.cardPadding,
|
|
107
|
+
paddingBottom: spacing.cardPadding,
|
|
108
108
|
},
|
|
109
109
|
footer: {
|
|
110
110
|
flexDirection: "row",
|
|
111
111
|
alignItems: "center",
|
|
112
|
-
paddingHorizontal: spacing.
|
|
113
|
-
paddingBottom: spacing.
|
|
112
|
+
paddingHorizontal: spacing.cardPadding,
|
|
113
|
+
paddingBottom: spacing.cardPadding,
|
|
114
114
|
paddingTop: 0,
|
|
115
115
|
},
|
|
116
116
|
title: {
|
|
117
|
-
fontSize:
|
|
118
|
-
lineHeight:
|
|
117
|
+
fontSize: 16,
|
|
118
|
+
lineHeight: 22,
|
|
119
119
|
letterSpacing: 0,
|
|
120
120
|
},
|
|
121
121
|
description: {
|
|
@@ -117,7 +117,7 @@ const styles = StyleSheet.create({
|
|
|
117
117
|
flexDirection: "row",
|
|
118
118
|
alignItems: "center",
|
|
119
119
|
gap: spacing.sm,
|
|
120
|
-
minHeight: spacing.touchTarget,
|
|
120
|
+
minHeight: Platform.select({ web: spacing.formRowMinHeight, default: spacing.touchTarget }),
|
|
121
121
|
},
|
|
122
122
|
labelContainer: {
|
|
123
123
|
flex: 1,
|
|
@@ -22,7 +22,25 @@ function DialogRoot({ children, ...props }) {
|
|
|
22
22
|
function DialogContent({ portalHost, style, children, ...props }) {
|
|
23
23
|
const { theme, getShadowStyle, getContrastingColor } = useTheme();
|
|
24
24
|
const textColor = getContrastingColor(theme.colors.popover, palette.white, palette.black);
|
|
25
|
-
return (_jsx(DialogPrimitive.Portal, { hostName: portalHost, children: _jsx(FullWindowOverlay, { children: _jsx(DialogPrimitive.Overlay
|
|
25
|
+
return (_jsx(DialogPrimitive.Portal, { hostName: portalHost, children: _jsx(FullWindowOverlay, { children: _jsx(DialogPrimitive.Overlay
|
|
26
|
+
// On web the primitive wraps the overlay in a react-native-web
|
|
27
|
+
// Pressable whose click handler stops propagation unconditionally.
|
|
28
|
+
// With that Pressable between the overlay and the Radix content,
|
|
29
|
+
// outside presses (mouse and touch) and Escape never dismissed the
|
|
30
|
+
// dialog, so a dialog without a Close button was stuck. `asChild`
|
|
31
|
+
// makes the primitive clone our fade wrapper instead, removing the
|
|
32
|
+
// Pressable; Radix dismissal then works. Native keeps the Pressable,
|
|
33
|
+
// which is what implements closeOnPress there.
|
|
34
|
+
, {
|
|
35
|
+
// On web the primitive wraps the overlay in a react-native-web
|
|
36
|
+
// Pressable whose click handler stops propagation unconditionally.
|
|
37
|
+
// With that Pressable between the overlay and the Radix content,
|
|
38
|
+
// outside presses (mouse and touch) and Escape never dismissed the
|
|
39
|
+
// dialog, so a dialog without a Close button was stuck. `asChild`
|
|
40
|
+
// makes the primitive clone our fade wrapper instead, removing the
|
|
41
|
+
// Pressable; Radix dismissal then works. Native keeps the Pressable,
|
|
42
|
+
// which is what implements closeOnPress there.
|
|
43
|
+
asChild: Platform.OS === "web", style: StyleSheet.flatten([
|
|
26
44
|
StyleSheet.absoluteFill,
|
|
27
45
|
{ backgroundColor: theme.colors.overlay },
|
|
28
46
|
Platform.OS === "web" && { zIndex: 50 },
|
|
@@ -32,7 +50,8 @@ function DialogContent({ portalHost, style, children, ...props }) {
|
|
|
32
50
|
borderColor: theme.colors.border,
|
|
33
51
|
borderWidth: 1,
|
|
34
52
|
borderRadius: spacing.radiusLg,
|
|
35
|
-
padding: spacing.
|
|
53
|
+
padding: spacing.dialogPadding,
|
|
54
|
+
gap: spacing.md,
|
|
36
55
|
width: "100%",
|
|
37
56
|
...getShadowStyle("soft"),
|
|
38
57
|
},
|
|
@@ -40,7 +59,7 @@ function DialogContent({ portalHost, style, children, ...props }) {
|
|
|
40
59
|
]), ...props, children: children }) }) }) }) }) }) }) }) }));
|
|
41
60
|
}
|
|
42
61
|
function DialogHeader({ children, style, ...props }) {
|
|
43
|
-
return (_jsx(View, { style: StyleSheet.flatten([{ gap: spacing.xs
|
|
62
|
+
return (_jsx(View, { style: StyleSheet.flatten([{ gap: spacing.xs }, style]), ...props, children: children }));
|
|
44
63
|
}
|
|
45
64
|
function DialogFooter({ children, style, ...props }) {
|
|
46
65
|
return (_jsx(View, { style: StyleSheet.flatten([
|
|
@@ -48,7 +67,6 @@ function DialogFooter({ children, style, ...props }) {
|
|
|
48
67
|
flexDirection: "row",
|
|
49
68
|
justifyContent: "flex-end",
|
|
50
69
|
gap: spacing.sm,
|
|
51
|
-
marginTop: spacing.lg,
|
|
52
70
|
},
|
|
53
71
|
style,
|
|
54
72
|
]), ...props, children: children }));
|
|
@@ -107,7 +125,8 @@ function AlertDialogContent({ portalHost, style, children, ...props }) {
|
|
|
107
125
|
borderColor: theme.colors.border,
|
|
108
126
|
borderWidth: 1,
|
|
109
127
|
borderRadius: spacing.radiusLg,
|
|
110
|
-
padding: spacing.
|
|
128
|
+
padding: spacing.dialogPadding,
|
|
129
|
+
gap: spacing.md,
|
|
111
130
|
width: "100%",
|
|
112
131
|
...getShadowStyle("soft"),
|
|
113
132
|
},
|
|
@@ -33,7 +33,7 @@ const createStyles = (theme) => StyleSheet.create({
|
|
|
33
33
|
container: {
|
|
34
34
|
alignItems: "center",
|
|
35
35
|
justifyContent: "center",
|
|
36
|
-
paddingVertical: spacing.
|
|
36
|
+
paddingVertical: spacing.xl,
|
|
37
37
|
paddingHorizontal: spacing.lg,
|
|
38
38
|
},
|
|
39
39
|
bordered: {
|
|
@@ -41,7 +41,7 @@ const createStyles = (theme) => StyleSheet.create({
|
|
|
41
41
|
borderStyle: "dashed",
|
|
42
42
|
borderColor: theme.colors.border,
|
|
43
43
|
borderRadius: spacing.radiusLg,
|
|
44
|
-
padding: spacing.
|
|
44
|
+
padding: spacing.lg,
|
|
45
45
|
},
|
|
46
46
|
iconWrapper: {
|
|
47
47
|
width: MEDIA_SIZE,
|
|
@@ -16,8 +16,9 @@ export interface ItemProps {
|
|
|
16
16
|
/**
|
|
17
17
|
* Item
|
|
18
18
|
*
|
|
19
|
-
* Grouped-list row container: `spacing.
|
|
20
|
-
* 44pt min height
|
|
19
|
+
* Grouped-list row container: compact row padding (`spacing.rowPaddingY` /
|
|
20
|
+
* `spacing.rowPaddingX`) and gap, a 44pt min height on native (40 on web),
|
|
21
|
+
* and an optional pressable scale interaction. Compose
|
|
21
22
|
* with `ItemMedia`, `ItemContent` (+ `ItemTitle`/`ItemDescription`), and
|
|
22
23
|
* `ItemActions`.
|
|
23
24
|
*
|
package/dist/components/Item.js
CHANGED
|
@@ -8,12 +8,13 @@ import { spacing } from "../constants/spacing.js";
|
|
|
8
8
|
// Default ItemMedia footprint (see ItemMedia's `size` prop) — used to inset
|
|
9
9
|
// the optional separator past the media slot without needing a context.
|
|
10
10
|
const DEFAULT_MEDIA_SIZE = 40;
|
|
11
|
-
const SEPARATOR_INSET = spacing.
|
|
11
|
+
const SEPARATOR_INSET = spacing.rowPaddingX + DEFAULT_MEDIA_SIZE + spacing.rowGap;
|
|
12
12
|
/**
|
|
13
13
|
* Item
|
|
14
14
|
*
|
|
15
|
-
* Grouped-list row container: `spacing.
|
|
16
|
-
* 44pt min height
|
|
15
|
+
* Grouped-list row container: compact row padding (`spacing.rowPaddingY` /
|
|
16
|
+
* `spacing.rowPaddingX`) and gap, a 44pt min height on native (40 on web),
|
|
17
|
+
* and an optional pressable scale interaction. Compose
|
|
17
18
|
* with `ItemMedia`, `ItemContent` (+ `ItemTitle`/`ItemDescription`), and
|
|
18
19
|
* `ItemActions`.
|
|
19
20
|
*
|
|
@@ -104,9 +105,10 @@ const styles = StyleSheet.create({
|
|
|
104
105
|
row: {
|
|
105
106
|
flexDirection: "row",
|
|
106
107
|
alignItems: "center",
|
|
107
|
-
minHeight: spacing.touchTarget,
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
minHeight: Platform.select({ web: spacing.rowMinHeight, default: spacing.touchTarget }),
|
|
109
|
+
paddingVertical: spacing.rowPaddingY,
|
|
110
|
+
paddingHorizontal: spacing.rowPaddingX,
|
|
111
|
+
gap: spacing.rowGap,
|
|
110
112
|
},
|
|
111
113
|
content: {
|
|
112
114
|
flex: 1,
|
|
@@ -151,7 +151,7 @@ const styles = StyleSheet.create({
|
|
|
151
151
|
flexDirection: "row",
|
|
152
152
|
alignItems: "center",
|
|
153
153
|
gap: spacing.sm,
|
|
154
|
-
minHeight: spacing.touchTarget,
|
|
154
|
+
minHeight: Platform.select({ web: spacing.formRowMinHeight, default: spacing.touchTarget }),
|
|
155
155
|
},
|
|
156
156
|
labelContainer: {
|
|
157
157
|
flex: 1,
|
|
@@ -37,5 +37,5 @@ export function StatCard({ label, value, unit, change, icon, onPress, style }) {
|
|
|
37
37
|
: change.direction === "down"
|
|
38
38
|
? theme.colors.destructive
|
|
39
39
|
: theme.colors.textDim;
|
|
40
|
-
return (_jsx(Card, { onPress: onPress, style: style, children: _jsxs(View, { style: { padding: spacing.
|
|
40
|
+
return (_jsx(Card, { onPress: onPress, style: style, children: _jsxs(View, { style: { padding: spacing.cardPadding, gap: spacing.xs }, children: [_jsxs(View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, children: [_jsx(CaptionText, { selectable: false, style: { color: theme.colors.textDim, textTransform: "uppercase", letterSpacing: 0.96 }, children: label }), !!icon && _jsx(Icon, { name: icon, size: spacing.iconSm, color: theme.colors.mutedForeground, decorative: true })] }), _jsxs(View, { style: { flexDirection: "row", alignItems: "flex-end" }, children: [_jsx(StyledText, { selectable: false, size: "xxl", fontWeight: "bold", style: { color: theme.colors.foreground, fontVariant: ["tabular-nums"] }, children: value }), !!unit && (_jsx(StyledText, { selectable: false, size: "base", style: { color: theme.colors.textDim, marginLeft: spacing.xxs, marginBottom: 2 }, children: unit }))] }), !!change && (_jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: spacing.xxs }, children: [change.direction !== "neutral" && (_jsx(Icon, { name: DIRECTION_ICON[change.direction], size: spacing.iconXs, color: changeColor, decorative: true })), _jsx(StyledText, { selectable: false, size: "sm", style: { color: changeColor }, children: change.value })] }))] }) }));
|
|
41
41
|
}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Spacing Constants
|
|
3
3
|
*
|
|
4
|
-
* A consistent spacing scale based on an 8px base unit
|
|
5
|
-
*
|
|
4
|
+
* A consistent spacing scale based on an 8px base unit, plus a small set of
|
|
5
|
+
* density tokens that components reach for when composing rows, surfaces, and
|
|
6
|
+
* screens. Tune the density tokens to shift the whole library's feel without
|
|
7
|
+
* touching the base scale.
|
|
6
8
|
*/
|
|
7
9
|
export declare const spacing: {
|
|
8
10
|
readonly base: 8;
|
|
9
11
|
readonly xxs: 2;
|
|
10
12
|
readonly xs: 4;
|
|
11
13
|
readonly sm: 8;
|
|
14
|
+
readonly smd: 12;
|
|
12
15
|
readonly md: 16;
|
|
16
|
+
readonly mdl: 20;
|
|
13
17
|
readonly lg: 24;
|
|
14
18
|
readonly xl: 32;
|
|
15
19
|
readonly xxl: 48;
|
|
@@ -17,12 +21,18 @@ export declare const spacing: {
|
|
|
17
21
|
readonly gutter: 16;
|
|
18
22
|
readonly gutterVertical: 24;
|
|
19
23
|
readonly screenPadding: 16;
|
|
24
|
+
readonly sectionSpacing: 24;
|
|
25
|
+
readonly rowPaddingY: 10;
|
|
26
|
+
readonly rowPaddingX: 16;
|
|
27
|
+
readonly rowGap: 12;
|
|
28
|
+
readonly rowMinHeight: 40;
|
|
29
|
+
readonly formRowMinHeight: 32;
|
|
30
|
+
readonly cardPadding: 16;
|
|
31
|
+
readonly dialogPadding: 20;
|
|
32
|
+
readonly touchTarget: 44;
|
|
20
33
|
readonly buttonPadding: 10;
|
|
21
34
|
readonly inputPadding: 10;
|
|
22
|
-
readonly cardPadding: 16;
|
|
23
|
-
readonly sectionSpacing: 32;
|
|
24
35
|
readonly listItemSpacing: 8;
|
|
25
|
-
readonly touchTarget: 44;
|
|
26
36
|
readonly radiusNone: 0;
|
|
27
37
|
readonly radiusXs: 4;
|
|
28
38
|
readonly radiusSm: 8;
|
|
@@ -37,5 +47,5 @@ export declare const spacing: {
|
|
|
37
47
|
readonly iconLg: 32;
|
|
38
48
|
readonly iconXl: 48;
|
|
39
49
|
};
|
|
40
|
-
export declare const base: 8, xxs: 2, xs: 4, sm: 8, md: 16, lg: 24, xl: 32, xxl: 48, xxxl: 64, gutter: 16, gutterVertical: 24, screenPadding: 16,
|
|
50
|
+
export declare const base: 8, xxs: 2, xs: 4, sm: 8, smd: 12, md: 16, mdl: 20, lg: 24, xl: 32, xxl: 48, xxxl: 64, gutter: 16, gutterVertical: 24, screenPadding: 16, sectionSpacing: 24, rowPaddingY: 10, rowPaddingX: 16, rowGap: 12, rowMinHeight: 40, formRowMinHeight: 32, cardPadding: 16, dialogPadding: 20, touchTarget: 44, buttonPadding: 10, inputPadding: 10, listItemSpacing: 8, radiusNone: 0, radiusXs: 4, radiusSm: 8, radiusMd: 12, radiusLg: 14, radiusXl: 18, radius2xl: 24, radiusFull: 9999, iconXs: 12, iconSm: 16, iconMd: 24, iconLg: 32, iconXl: 48;
|
|
41
51
|
export declare const space: (multiplier: number) => number;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Spacing Constants
|
|
3
3
|
*
|
|
4
|
-
* A consistent spacing scale based on an 8px base unit
|
|
5
|
-
*
|
|
4
|
+
* A consistent spacing scale based on an 8px base unit, plus a small set of
|
|
5
|
+
* density tokens that components reach for when composing rows, surfaces, and
|
|
6
|
+
* screens. Tune the density tokens to shift the whole library's feel without
|
|
7
|
+
* touching the base scale.
|
|
6
8
|
*/
|
|
7
9
|
export const spacing = {
|
|
8
10
|
// Base unit: 8px
|
|
@@ -11,7 +13,9 @@ export const spacing = {
|
|
|
11
13
|
xxs: 2, // 2px - Minimal spacing
|
|
12
14
|
xs: 4, // 4px - Extra small
|
|
13
15
|
sm: 8, // 8px - Small (1 unit)
|
|
16
|
+
smd: 12, // 12px - Between small and medium (1.5 units); compact gaps and row padding
|
|
14
17
|
md: 16, // 16px - Medium (2 units)
|
|
18
|
+
mdl: 20, // 20px - Between medium and large (2.5 units); dialog and sheet padding
|
|
15
19
|
lg: 24, // 24px - Large (3 units)
|
|
16
20
|
xl: 32, // 32px - Extra large (4 units)
|
|
17
21
|
xxl: 48, // 48px - 2x extra large (6 units)
|
|
@@ -20,13 +24,20 @@ export const spacing = {
|
|
|
20
24
|
gutter: 16, // Standard horizontal gutter
|
|
21
25
|
gutterVertical: 24, // Standard vertical spacing
|
|
22
26
|
screenPadding: 16, // Default screen edge padding
|
|
27
|
+
sectionSpacing: 24, // Space between major sections / grouped lists
|
|
28
|
+
// Density tokens — rows, surfaces, overlays
|
|
29
|
+
rowPaddingY: 10, // Vertical padding for list / settings rows
|
|
30
|
+
rowPaddingX: 16, // Horizontal padding for list / settings rows
|
|
31
|
+
rowGap: 12, // Gap between media, content, and actions in a row
|
|
32
|
+
rowMinHeight: 40, // Visual min height for rows on pointer surfaces (web)
|
|
33
|
+
formRowMinHeight: 32, // Visual min height for checkbox / radio rows on web
|
|
34
|
+
cardPadding: 16, // Default card / stat card padding
|
|
35
|
+
dialogPadding: 20, // Default dialog / alert dialog padding
|
|
36
|
+
touchTarget: 44, // Minimum comfortable native touch target
|
|
23
37
|
// Component-specific
|
|
24
38
|
buttonPadding: 10, // Default button padding
|
|
25
39
|
inputPadding: 10, // Default input padding
|
|
26
|
-
cardPadding: 16, // Default card padding
|
|
27
|
-
sectionSpacing: 32, // Space between major sections
|
|
28
40
|
listItemSpacing: 8, // Space between list items
|
|
29
|
-
touchTarget: 44, // Minimum comfortable native touch target
|
|
30
41
|
// Border radius — shadcn-inspired scale (radiusMd = 12px default)
|
|
31
42
|
radiusNone: 0,
|
|
32
43
|
radiusXs: 4,
|
|
@@ -44,6 +55,6 @@ export const spacing = {
|
|
|
44
55
|
iconXl: 48,
|
|
45
56
|
};
|
|
46
57
|
// Export individual constants for convenience
|
|
47
|
-
export const { base, xxs, xs, sm, md, lg, xl, xxl, xxxl, gutter, gutterVertical, screenPadding,
|
|
58
|
+
export const { base, xxs, xs, sm, smd, md, mdl, lg, xl, xxl, xxxl, gutter, gutterVertical, screenPadding, sectionSpacing, rowPaddingY, rowPaddingX, rowGap, rowMinHeight, formRowMinHeight, cardPadding, dialogPadding, touchTarget, buttonPadding, inputPadding, listItemSpacing, radiusNone, radiusXs, radiusSm, radiusMd, radiusLg, radiusXl, radius2xl, radiusFull, iconXs, iconSm, iconMd, iconLg, iconXl, } = spacing;
|
|
48
59
|
// Helper function to multiply base spacing
|
|
49
60
|
export const space = (multiplier) => spacing.base * multiplier;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrmeg/expo-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Reusable Expo and React Native UI primitives for MrMeg projects.",
|
|
6
6
|
"keywords": [
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"scripts": {
|
|
84
84
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
85
85
|
"test": "jest --config ../../jest.config.js packages/ui/src --runInBand --watchman=false && bun run check:forbidden-imports",
|
|
86
|
-
"build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/fix-
|
|
86
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/fix-package-esm.mjs ui && bun run check:forbidden-imports",
|
|
87
87
|
"check:forbidden-imports": "node ../../scripts/check-ui-forbidden-imports.mjs",
|
|
88
88
|
"publish:dry-run": "bun pm pack --dry-run"
|
|
89
89
|
},
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"@rn-primitives/dropdown-menu": "~1.5.2",
|
|
99
99
|
"@rn-primitives/label": "~1.5.2",
|
|
100
100
|
"@rn-primitives/popover": "~1.5.2",
|
|
101
|
-
"@rn-primitives/portal": "~1.5.
|
|
101
|
+
"@rn-primitives/portal": "~1.5.3",
|
|
102
102
|
"@rn-primitives/radio-group": "~1.5.2",
|
|
103
103
|
"@rn-primitives/select": "~1.5.2",
|
|
104
104
|
"@rn-primitives/separator": "~1.5.2",
|