@delacour/react-native-ui 0.1.0-alpha.20261004035233 → 0.1.0-alpha.20261004043720
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/README.md +1 -1
- package/package.json +3 -3
- package/src/components/bottom-sheet/AGENTS.md +6 -6
- package/src/components/bottom-sheet/bottom-sheet-flat-list.tsx +1 -1
- package/src/components/bottom-sheet/bottom-sheet-footer.tsx +1 -1
- package/src/components/bottom-sheet/bottom-sheet-handle.tsx +1 -1
- package/src/components/bottom-sheet/bottom-sheet-legend-list.tsx +1 -1
- package/src/components/bottom-sheet/bottom-sheet-scroll-view.tsx +2 -2
- package/src/components/bottom-sheet/bottom-sheet.tsx +6 -6
- package/src/components/bottom-sheet/bottom-sheet.variants.test.ts +2 -2
- package/src/components/bottom-sheet/bottom-sheet.variants.ts +3 -3
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ import { IconArrowRight } from "@delacour/react-native-ui/icons/central";
|
|
|
82
82
|
| --- | --- | --- |
|
|
83
83
|
| Accordion | `@delacour/react-native-ui/accordion` | Selection modes, measured panels, indicators |
|
|
84
84
|
| Badge | `@delacour/react-native-ui/badge` | Variants, colours, sizes, dismiss |
|
|
85
|
-
| BottomSheet | `@delacour/react-native-ui/bottom-sheet` |
|
|
85
|
+
| BottomSheet | `@delacour/react-native-ui/bottom-sheet` | Snap points, keyboard, sticky footer, scrollables, steps, detached, teleported portal |
|
|
86
86
|
| Button | `@delacour/react-native-ui/button` | Variants, sizes, icons, loading |
|
|
87
87
|
| Checkbox | `@delacour/react-native-ui/checkbox` | Colours, sizes, indeterminate, groups |
|
|
88
88
|
| Field | `@delacour/react-native-ui/field` | Form layout, grouping, state cascade |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delacour/react-native-ui",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.20261004043720",
|
|
4
4
|
"description": "React Native UI components — Uniwind, Reanimated, Gesture Handler",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -100,8 +100,8 @@
|
|
|
100
100
|
},
|
|
101
101
|
"peerDependencies": {
|
|
102
102
|
"@central-icons-react-native/round-outlined-radius-1-stroke-1.5": "^1.1",
|
|
103
|
-
"@delacour/react-native-bottom-sheet": "0.1.0-alpha.
|
|
104
|
-
"@delacour/react-native-charts": "0.1.0-alpha.
|
|
103
|
+
"@delacour/react-native-bottom-sheet": "0.1.0-alpha.20261004043720",
|
|
104
|
+
"@delacour/react-native-charts": "0.1.0-alpha.20261004043720",
|
|
105
105
|
"@legendapp/list": ">=3.3",
|
|
106
106
|
"expo-linear-gradient": ">=15",
|
|
107
107
|
"expo-router": ">=57",
|
|
@@ -71,7 +71,7 @@ engine's names win everywhere; this skin aliases nothing.
|
|
|
71
71
|
## What the engine owns
|
|
72
72
|
|
|
73
73
|
Everything that is not a class. The open state and the intent queue; the
|
|
74
|
-
height-space geometry and the
|
|
74
|
+
height-space geometry and the snap points; the handle and content pans and how a
|
|
75
75
|
list shares a finger with them; the keyboard — who owns it, `interactive` /
|
|
76
76
|
`extend` / `fillParent` / `none`, the lift and the restore; the sticky footer's
|
|
77
77
|
line and the safe-area band under it; the teleport portal, the hosts, the
|
|
@@ -92,7 +92,7 @@ read those before touching behaviour, because behaviour is not here.
|
|
|
92
92
|
components applies: the engine is another package.
|
|
93
93
|
- **Where the classes land.** `Content`, `Footer` and `Steps` hand `style` to
|
|
94
94
|
the engine's *measured* inner view, so a class there is height the dynamic
|
|
95
|
-
|
|
95
|
+
snap point counts. `ScrollView` puts its content classes on an inner `View`
|
|
96
96
|
rather than the content container, so the one style this file writes there —
|
|
97
97
|
the gap above a pinned footer — has a single writer. The three virtualised
|
|
98
98
|
lists have no inner box, so theirs go on `contentContainerClassName`; the
|
|
@@ -115,16 +115,16 @@ read those before touching behaviour, because behaviour is not here.
|
|
|
115
115
|
`fade` and an 8pt slop, for `Badge.CloseButton`'s and `Checkbox`'s reasons.
|
|
116
116
|
- **The root fills in three defaults.** `bottomInset` is `useSafeAreaInsets().bottom`,
|
|
117
117
|
so the engine reserves the home-indicator band and nobody pads for it by
|
|
118
|
-
hand. `
|
|
118
|
+
hand. `onSnapPointHaptic` is `Presets.System.selection` and `onCloseHaptic` is
|
|
119
119
|
`Presets.System.impactLight` — pulsar's presets are worklets, so they are
|
|
120
120
|
passed as they are; a JS function there is `undefined is not a function` on
|
|
121
121
|
the UI thread. And `useKeyboardAnimationGuard()` runs on mount, the same
|
|
122
122
|
stale-keyboard repair `Screen.Footer` runs. `topInset` stays at the engine's
|
|
123
|
-
zero, so a `%`
|
|
123
|
+
zero, so a `%` snap point is the fraction of the window it always was.
|
|
124
124
|
- **`Footer.sticky` defaults to `false`**, matching `Screen.Footer`, and this is
|
|
125
125
|
the one engine default the skin overrides. A pinned footer takes
|
|
126
126
|
`padding={BOTTOM_SHEET_FOOTER_PADDING}` through the engine's prop rather
|
|
127
|
-
than a class, so the padding is inside the box the
|
|
127
|
+
than a class, so the padding is inside the box the snap point measures; the
|
|
128
128
|
`stickyFooter` slot carries the gutter, the surface and the hairline, and
|
|
129
129
|
the test asserts it carries no vertical padding.
|
|
130
130
|
- **The overlay's opacity is 1**, because `--overlay` carries its own alpha and
|
|
@@ -178,7 +178,7 @@ problem it has:
|
|
|
178
178
|
- nesting a sheet in a sheet — the registry stacks them; `stackBehavior` and
|
|
179
179
|
`dismissAll` are for exactly that
|
|
180
180
|
- `ScrollView` needing `dynamicSizing={false}` and `snapPoints` — a list's
|
|
181
|
-
content size is the dynamic
|
|
181
|
+
content size is the dynamic snap point
|
|
182
182
|
- lifting `Overlay` and a sticky `Footer` out of the tree as render props —
|
|
183
183
|
every part renders where it is written
|
|
184
184
|
- a `Content` inside a `Container` paying the safe-area band by hand — the
|
|
@@ -37,7 +37,7 @@ export type BottomSheetFlatListProps<ItemT> = HeadlessProps<ItemT> & {
|
|
|
37
37
|
* A virtualised body.
|
|
38
38
|
*
|
|
39
39
|
* The engine's `FlatList` — the scroll lock, the drag budget and content size
|
|
40
|
-
* as the dynamic
|
|
40
|
+
* as the dynamic snap point are all its — with the library's gutter on the content
|
|
41
41
|
* container. A virtualised list has no inner box to put the classes on, so
|
|
42
42
|
* here they go on `contentContainerClassName`; the engine flattens the
|
|
43
43
|
* resulting style to one object before the list measures it.
|
|
@@ -36,7 +36,7 @@ export type BottomSheetFooterProps = HeadlessProps & {
|
|
|
36
36
|
* geometry's footer line, which the core proves holds still through a keyboard
|
|
37
37
|
* animation, so its buttons land on the keyboard's top edge without moving;
|
|
38
38
|
* the safe-area band under it is a spacer the geometry owns; and its measured
|
|
39
|
-
* height is what the body reserves and the dynamic
|
|
39
|
+
* height is what the body reserves and the dynamic snap point counts. The
|
|
40
40
|
* `padding` goes to the engine rather than a class for that reason — it has to
|
|
41
41
|
* be inside the measured box.
|
|
42
42
|
*
|
|
@@ -25,7 +25,7 @@ export type BottomSheetHandleProps = HeadlessProps & {
|
|
|
25
25
|
*
|
|
26
26
|
* The engine's handle draws nothing of its own: it owns the pan, measures
|
|
27
27
|
* itself into the sheet's height, and is the adjustable element a screen
|
|
28
|
-
* reader steps through the
|
|
28
|
+
* reader steps through the snap points with. This puts the pill inside it, and
|
|
29
29
|
* classes on both.
|
|
30
30
|
*
|
|
31
31
|
* Pass children to replace the pill; the row, the pan and the accessibility
|
|
@@ -44,7 +44,7 @@ export type BottomSheetLegendListProps<ItemT> = Omit<AnimatedLegendListProps<Ite
|
|
|
44
44
|
* Built here rather than in the engine because `@legendapp/list` is this
|
|
45
45
|
* library's optional peer, not the engine's: the engine exports the factory,
|
|
46
46
|
* and this is what the factory is for. The same scroll lock, drag budget and
|
|
47
|
-
* content-size
|
|
47
|
+
* content-size snap point as the other three bodies.
|
|
48
48
|
*
|
|
49
49
|
* @example
|
|
50
50
|
* <BottomSheet.LegendList data={rows} keyExtractor={(row) => row.id} renderItem={renderRow} />
|
|
@@ -36,13 +36,13 @@ export type BottomSheetScrollViewProps = Omit<HeadlessProps, "children"> & {
|
|
|
36
36
|
* A scrolling body.
|
|
37
37
|
*
|
|
38
38
|
* Use this rather than a plain `ScrollView`: the engine's scrollable and the
|
|
39
|
-
* sheet's pan share a finger — below the highest
|
|
39
|
+
* sheet's pan share a finger — below the highest snap point the list is held and
|
|
40
40
|
* the drag moves the sheet, at the highest the list scrolls, and a drag down
|
|
41
41
|
* from the top hands back to the sheet. A React Native `ScrollView` in here has
|
|
42
42
|
* no such arrangement.
|
|
43
43
|
*
|
|
44
44
|
* **It needs no `snapPoints` and no `dynamicSizing={false}`.** The list
|
|
45
|
-
* reports its content size, and that is the dynamic
|
|
45
|
+
* reports its content size, and that is the dynamic snap point: six rows make a
|
|
46
46
|
* short sheet, forty make one capped at `maxDynamicContentSize` that scrolls
|
|
47
47
|
* inside the cap. Explicit `snapPoints` on the root still work, for a height
|
|
48
48
|
* that is a decision rather than a measurement.
|
|
@@ -38,11 +38,11 @@ export type BottomSheetProps = HeadlessBottomSheetProps;
|
|
|
38
38
|
* Both run on the UI thread from inside the engine's pan, so they are
|
|
39
39
|
* `Presets.System.*` — themselves worklets — and nothing else: a JS function
|
|
40
40
|
* here would be `undefined is not a function` at the moment a finger crosses a
|
|
41
|
-
*
|
|
41
|
+
* snap point. `selection` on a snap point because that is what a picker's tick feels
|
|
42
42
|
* like, and `impactLight` on a close because letting go of a sheet is a
|
|
43
43
|
* heavier event than passing a stop, but not by much.
|
|
44
44
|
*/
|
|
45
|
-
const
|
|
45
|
+
const snapPointHaptic = Presets.System.selection;
|
|
46
46
|
const closeHaptic = Presets.System.impactLight;
|
|
47
47
|
|
|
48
48
|
/**
|
|
@@ -55,19 +55,19 @@ const closeHaptic = Presets.System.impactLight;
|
|
|
55
55
|
* `useSafeAreaInsets()`. The engine reserves that band under the body or the
|
|
56
56
|
* sticky footer and gives it back to the keyboard as it arrives; a caller
|
|
57
57
|
* never pads for the home indicator by hand.
|
|
58
|
-
* - **The haptics are on by default.** Pass `
|
|
58
|
+
* - **The haptics are on by default.** Pass `onSnapPointHaptic={undefined}` to
|
|
59
59
|
* turn one off, or a worklet of your own to change it.
|
|
60
60
|
* - **The stale-keyboard guard runs on mount.** A sheet mounted while
|
|
61
61
|
* `KeyboardProvider`'s shared values are pinned open by a keyboard that
|
|
62
62
|
* vanished without a `will` event would lift for a keyboard that is not
|
|
63
63
|
* there; `Screen.Footer` runs the same repair for the same reason.
|
|
64
64
|
*
|
|
65
|
-
* `topInset` is left at the engine's zero, so a `%`
|
|
65
|
+
* `topInset` is left at the engine's zero, so a `%` snap point is a fraction of the
|
|
66
66
|
* whole window — the same fraction it was before this rewrite.
|
|
67
67
|
*/
|
|
68
68
|
function BottomSheetRoot({
|
|
69
69
|
bottomInset,
|
|
70
|
-
|
|
70
|
+
onSnapPointHaptic = snapPointHaptic,
|
|
71
71
|
onCloseHaptic = closeHaptic,
|
|
72
72
|
...props
|
|
73
73
|
}: BottomSheetProps): ReactElement {
|
|
@@ -78,7 +78,7 @@ function BottomSheetRoot({
|
|
|
78
78
|
<Headless
|
|
79
79
|
bottomInset={bottomInset ?? insets.bottom}
|
|
80
80
|
onCloseHaptic={onCloseHaptic}
|
|
81
|
-
|
|
81
|
+
onSnapPointHaptic={onSnapPointHaptic}
|
|
82
82
|
{...props}
|
|
83
83
|
/>
|
|
84
84
|
);
|
|
@@ -112,7 +112,7 @@ describe("every token the slots name", () => {
|
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
describe("the backdrop indices", () => {
|
|
115
|
-
test("show the scrim from the first
|
|
115
|
+
test("show the scrim from the first snap point and hide it only when closed", () => {
|
|
116
116
|
// A modal sheet has no resting state: presented or gone.
|
|
117
117
|
expect(BOTTOM_SHEET_BACKDROP_INDICES.appearsOnIndex).toBe(0);
|
|
118
118
|
expect(BOTTOM_SHEET_BACKDROP_INDICES.disappearsOnIndex).toBe(-1);
|
|
@@ -198,7 +198,7 @@ describe("bottomSheetVariants slots", () => {
|
|
|
198
198
|
test("a pinned footer writes its vertical padding as the engine's prop, never a class", () => {
|
|
199
199
|
// The engine measures the footer's inner box into the sheet's height, and
|
|
200
200
|
// padding handed to its `padding` prop lands on that box. A class on the
|
|
201
|
-
// outer view would be height the
|
|
201
|
+
// outer view would be height the snap point never counts.
|
|
202
202
|
expect(SLOTS.stickyFooter()).not.toMatch(/\bp[tby]?-[\d.]+\b/);
|
|
203
203
|
expect(BOTTOM_SHEET_FOOTER_PADDING).toBeGreaterThan(0);
|
|
204
204
|
});
|
|
@@ -21,10 +21,10 @@ export const BOTTOM_SHEET_OVERLAY_TOKEN = "overlay";
|
|
|
21
21
|
export const BOTTOM_SHEET_OVERLAY_OPACITY = 1;
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* The
|
|
24
|
+
* The snap point indices the scrim appears and disappears on.
|
|
25
25
|
*
|
|
26
26
|
* A modal sheet has no resting state: it is either presented or gone, so the
|
|
27
|
-
* scrim belongs from the FIRST
|
|
27
|
+
* scrim belongs from the FIRST snap point (`0`) and is only absent when the sheet
|
|
28
28
|
* is closed (`-1`). These are the engine's own defaults too; they are named
|
|
29
29
|
* here so the test that pins them has something to read.
|
|
30
30
|
*/
|
|
@@ -43,7 +43,7 @@ export const BOTTOM_SHEET_CLOSE_HIT_SLOP = 8;
|
|
|
43
43
|
* The padding a pinned footer's measured box carries.
|
|
44
44
|
*
|
|
45
45
|
* A number rather than a `p-4`, because it is handed to the engine's `padding`
|
|
46
|
-
* prop: the engine measures the footer's inner box into the dynamic
|
|
46
|
+
* prop: the engine measures the footer's inner box into the dynamic snap point,
|
|
47
47
|
* and padding written there is counted where a class on the outer view would
|
|
48
48
|
* not be. The horizontal gutter still comes from the `stickyFooter` slot, which
|
|
49
49
|
* is merged after it.
|