@oxyhq/bloom 0.45.0 → 0.46.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/lib/commonjs/dialog/Dialog.js +12 -3
- package/lib/commonjs/dialog/Dialog.js.map +1 -1
- package/lib/commonjs/dialog/Dialog.web.js +23 -8
- package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
- package/lib/commonjs/index.js +29 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +73 -44
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/surfaces/SurfaceHost.js +189 -0
- package/lib/commonjs/surfaces/SurfaceHost.js.map +1 -0
- package/lib/commonjs/surfaces/api.js +83 -0
- package/lib/commonjs/surfaces/api.js.map +1 -0
- package/lib/commonjs/surfaces/index.js +82 -0
- package/lib/commonjs/surfaces/index.js.map +1 -0
- package/lib/commonjs/surfaces/index.web.js +77 -0
- package/lib/commonjs/surfaces/index.web.js.map +1 -0
- package/lib/commonjs/surfaces/prompts.js +154 -0
- package/lib/commonjs/surfaces/prompts.js.map +1 -0
- package/lib/commonjs/surfaces/surfaceStore.js +172 -0
- package/lib/commonjs/surfaces/surfaceStore.js.map +1 -0
- package/lib/commonjs/surfaces/types.js +6 -0
- package/lib/commonjs/surfaces/types.js.map +1 -0
- package/lib/commonjs/surfaces/useSurface.js +36 -0
- package/lib/commonjs/surfaces/useSurface.js.map +1 -0
- package/lib/module/dialog/Dialog.js +13 -4
- package/lib/module/dialog/Dialog.js.map +1 -1
- package/lib/module/dialog/Dialog.web.js +23 -8
- package/lib/module/dialog/Dialog.web.js.map +1 -1
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +5 -0
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/surfaces/SurfaceHost.js +184 -0
- package/lib/module/surfaces/SurfaceHost.js.map +1 -0
- package/lib/module/surfaces/api.js +48 -0
- package/lib/module/surfaces/api.js.map +1 -0
- package/lib/module/surfaces/index.js +24 -0
- package/lib/module/surfaces/index.js.map +1 -0
- package/lib/module/surfaces/index.web.js +19 -0
- package/lib/module/surfaces/index.web.js.map +1 -0
- package/lib/module/surfaces/prompts.js +148 -0
- package/lib/module/surfaces/prompts.js.map +1 -0
- package/lib/module/surfaces/surfaceStore.js +159 -0
- package/lib/module/surfaces/surfaceStore.js.map +1 -0
- package/lib/module/surfaces/types.js +4 -0
- package/lib/module/surfaces/types.js.map +1 -0
- package/lib/module/surfaces/useSurface.js +31 -0
- package/lib/module/surfaces/useSurface.js.map +1 -0
- package/lib/typescript/commonjs/dialog/Dialog.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/types.d.ts +10 -0
- package/lib/typescript/commonjs/dialog/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +2 -0
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/surfaces/SurfaceHost.d.ts +25 -0
- package/lib/typescript/commonjs/surfaces/SurfaceHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/surfaces/api.d.ts +41 -0
- package/lib/typescript/commonjs/surfaces/api.d.ts.map +1 -0
- package/lib/typescript/commonjs/surfaces/index.d.ts +25 -0
- package/lib/typescript/commonjs/surfaces/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/surfaces/index.web.d.ts +20 -0
- package/lib/typescript/commonjs/surfaces/index.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/surfaces/prompts.d.ts +13 -0
- package/lib/typescript/commonjs/surfaces/prompts.d.ts.map +1 -0
- package/lib/typescript/commonjs/surfaces/surfaceStore.d.ts +51 -0
- package/lib/typescript/commonjs/surfaces/surfaceStore.d.ts.map +1 -0
- package/lib/typescript/commonjs/surfaces/types.d.ts +145 -0
- package/lib/typescript/commonjs/surfaces/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/surfaces/useSurface.d.ts +21 -0
- package/lib/typescript/commonjs/surfaces/useSurface.d.ts.map +1 -0
- package/lib/typescript/module/dialog/Dialog.d.ts.map +1 -1
- package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/module/dialog/types.d.ts +10 -0
- package/lib/typescript/module/dialog/types.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +2 -0
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/surfaces/SurfaceHost.d.ts +25 -0
- package/lib/typescript/module/surfaces/SurfaceHost.d.ts.map +1 -0
- package/lib/typescript/module/surfaces/api.d.ts +41 -0
- package/lib/typescript/module/surfaces/api.d.ts.map +1 -0
- package/lib/typescript/module/surfaces/index.d.ts +25 -0
- package/lib/typescript/module/surfaces/index.d.ts.map +1 -0
- package/lib/typescript/module/surfaces/index.web.d.ts +20 -0
- package/lib/typescript/module/surfaces/index.web.d.ts.map +1 -0
- package/lib/typescript/module/surfaces/prompts.d.ts +13 -0
- package/lib/typescript/module/surfaces/prompts.d.ts.map +1 -0
- package/lib/typescript/module/surfaces/surfaceStore.d.ts +51 -0
- package/lib/typescript/module/surfaces/surfaceStore.d.ts.map +1 -0
- package/lib/typescript/module/surfaces/types.d.ts +145 -0
- package/lib/typescript/module/surfaces/types.d.ts.map +1 -0
- package/lib/typescript/module/surfaces/useSurface.d.ts +21 -0
- package/lib/typescript/module/surfaces/useSurface.d.ts.map +1 -0
- package/package.json +17 -1
- package/src/__tests__/surfaceStore.test.ts +219 -0
- package/src/dialog/Dialog.tsx +14 -3
- package/src/dialog/Dialog.web.tsx +25 -6
- package/src/dialog/types.ts +10 -0
- package/src/index.ts +15 -0
- package/src/index.web.ts +15 -0
- package/src/surfaces/SurfaceHost.tsx +207 -0
- package/src/surfaces/api.ts +55 -0
- package/src/surfaces/index.ts +38 -0
- package/src/surfaces/index.web.ts +33 -0
- package/src/surfaces/prompts.tsx +178 -0
- package/src/surfaces/surfaceStore.ts +168 -0
- package/src/surfaces/types.ts +156 -0
- package/src/surfaces/useSurface.ts +33 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import type { DialogInset, ResponsiveDialogPlacement } from '../dialog/types';
|
|
4
|
+
/**
|
|
5
|
+
* How a surface renders — the placement + the `Dialog` sizing/chrome options a
|
|
6
|
+
* surface needs. This is the CONTENT-AGNOSTIC presentation config: it never
|
|
7
|
+
* references a route or screen name (Bloom's stack knows nothing about routes;
|
|
8
|
+
* the SDK layers its route registry on top).
|
|
9
|
+
*
|
|
10
|
+
* The values map 1:1 onto the shared {@link ../dialog/types#DialogProps}, so a
|
|
11
|
+
* surface reads/behaves identically to a hand-mounted `<Dialog>` with the same
|
|
12
|
+
* props — only stacked/coordinated by {@link SurfaceHost}.
|
|
13
|
+
*/
|
|
14
|
+
export interface SurfacePresentation {
|
|
15
|
+
/**
|
|
16
|
+
* Surface anchor. Defaults to `{ base: 'bottom', md: 'center' }` — a bottom
|
|
17
|
+
* sheet on narrow viewports that becomes a centered modal on wide ones (the
|
|
18
|
+
* ecosystem's canonical responsive "sheet"). Pass `'center'` for a plain
|
|
19
|
+
* modal, `'left'`/`'right'` for a drawer, or any responsive placement map.
|
|
20
|
+
*/
|
|
21
|
+
placement?: ResponsiveDialogPlacement;
|
|
22
|
+
/** Side-sheet width (px) on wide screens. Forwarded to `Dialog`. */
|
|
23
|
+
width?: number;
|
|
24
|
+
/** Centered-card max width (px). Forwarded to `Dialog`. */
|
|
25
|
+
maxWidth?: number;
|
|
26
|
+
/** Bottom-sheet max height as a fraction of the viewport height. */
|
|
27
|
+
maxHeightRatio?: number;
|
|
28
|
+
/** Side-sheet inset (px) from the root overlay's edges. */
|
|
29
|
+
inset?: DialogInset;
|
|
30
|
+
/** Whether to render the drag handle in bottom-sheet mode. */
|
|
31
|
+
showHandle?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Whether tapping the backdrop / pressing Escape / Android-back dismisses the
|
|
34
|
+
* surface. Defaults to `true`. Set `false` for a blocking surface that must be
|
|
35
|
+
* dismissed programmatically (e.g. an unanswered required prompt).
|
|
36
|
+
*/
|
|
37
|
+
dismissOnBackdrop?: boolean;
|
|
38
|
+
/** Inner padding (px) of the surface content container. Forwarded to `Dialog`. */
|
|
39
|
+
contentPadding?: number;
|
|
40
|
+
/** Style overrides applied to the inner content container. */
|
|
41
|
+
style?: StyleProp<ViewStyle>;
|
|
42
|
+
/** Style overrides for the side/bottom placement panel surface. */
|
|
43
|
+
panelStyle?: StyleProp<ViewStyle>;
|
|
44
|
+
/** NativeWind classes for the side/bottom placement panel surface. */
|
|
45
|
+
panelClassName?: string;
|
|
46
|
+
/** Style for the root overlay (theme-var scope, rail offset, etc.). */
|
|
47
|
+
containerStyle?: StyleProp<ViewStyle>;
|
|
48
|
+
/** NativeWind classes for the root overlay. */
|
|
49
|
+
containerClassName?: string;
|
|
50
|
+
/** Accessibility label applied to the surface's dialog role. */
|
|
51
|
+
label?: string;
|
|
52
|
+
/** Stable testID forwarded to the underlying `Dialog`. */
|
|
53
|
+
testID?: string;
|
|
54
|
+
}
|
|
55
|
+
/** Options accepted by `present` / `SurfaceControls.present`. */
|
|
56
|
+
export type PresentOptions = SurfacePresentation;
|
|
57
|
+
/**
|
|
58
|
+
* Controls handed to a presented surface — both as the argument to its render
|
|
59
|
+
* function AND via {@link ../surfaces/useSurface#useSurface}. This is the DEPTH
|
|
60
|
+
* axis only: a surface can dismiss ITSELF (resolving its own `present()`
|
|
61
|
+
* promise) or `present` a CHILD surface stacked above it. Navigation WITHIN a
|
|
62
|
+
* surface (route history / drill-in) is deliberately NOT here — that is the
|
|
63
|
+
* SDK's route layer, which composes on top of this stack.
|
|
64
|
+
*/
|
|
65
|
+
export interface SurfaceControls {
|
|
66
|
+
/**
|
|
67
|
+
* Dismiss THIS surface, resolving the promise returned by the `present` call
|
|
68
|
+
* that created it with `result` (or `undefined`). Resolves immediately; the
|
|
69
|
+
* exit animation is cosmetic.
|
|
70
|
+
*/
|
|
71
|
+
dismiss: (result?: unknown) => void;
|
|
72
|
+
/**
|
|
73
|
+
* Stack a CHILD surface directly above this one. Resolves with the child's
|
|
74
|
+
* dismissal result. The child paints above (higher layer) and captures
|
|
75
|
+
* backdrop/Escape/back until it is dismissed.
|
|
76
|
+
*/
|
|
77
|
+
present: <Result = unknown>(render: SurfaceRenderFn, opts?: PresentOptions) => Promise<Result>;
|
|
78
|
+
}
|
|
79
|
+
/** A surface's content factory — receives its own {@link SurfaceControls}. */
|
|
80
|
+
export type SurfaceRenderFn = (surface: SurfaceControls) => ReactNode;
|
|
81
|
+
/** Lifecycle status of a stacked surface. */
|
|
82
|
+
export type SurfaceStatus = 'open' | 'closing';
|
|
83
|
+
/**
|
|
84
|
+
* One entry in the surface stack. `resolve` fulfils the `present()` promise; the
|
|
85
|
+
* `settled` guard makes resolution idempotent (resolve-once); `generation` is
|
|
86
|
+
* the per-entry stale-callback guard (carried from `BottomSheetBase`'s
|
|
87
|
+
* `closeGenerationRef`, keyed per entry id) so a completion callback from a
|
|
88
|
+
* superseded close cycle can never tear down the wrong state.
|
|
89
|
+
*/
|
|
90
|
+
export interface SurfaceEntry {
|
|
91
|
+
id: string;
|
|
92
|
+
render: SurfaceRenderFn;
|
|
93
|
+
presentation: SurfacePresentation;
|
|
94
|
+
status: SurfaceStatus;
|
|
95
|
+
resolve: (result: unknown) => void;
|
|
96
|
+
settled: boolean;
|
|
97
|
+
generation: number;
|
|
98
|
+
}
|
|
99
|
+
/** Options for the built-in {@link ../surfaces/prompts#confirm} surface. */
|
|
100
|
+
export interface SurfaceConfirmOptions {
|
|
101
|
+
/** Headline. */
|
|
102
|
+
title: string;
|
|
103
|
+
/** Supporting copy. */
|
|
104
|
+
message?: string;
|
|
105
|
+
/** Confirm button label. Defaults to `'Confirm'`. */
|
|
106
|
+
confirmLabel?: string;
|
|
107
|
+
/** Cancel button label. Defaults to `'Cancel'`. */
|
|
108
|
+
cancelLabel?: string;
|
|
109
|
+
/** Style the confirm button as destructive (negative color). */
|
|
110
|
+
destructive?: boolean;
|
|
111
|
+
/** Hide the cancel button (a single-action acknowledgement). */
|
|
112
|
+
hideCancel?: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Backdrop / Escape / back dismiss (resolving `false`). Defaults to `true`;
|
|
115
|
+
* pass `false` for a blocking confirm that must be answered by a button.
|
|
116
|
+
*/
|
|
117
|
+
dismissible?: boolean;
|
|
118
|
+
/** Placement override. Defaults to `'center'`. */
|
|
119
|
+
placement?: ResponsiveDialogPlacement;
|
|
120
|
+
}
|
|
121
|
+
/** Options for the built-in {@link ../surfaces/prompts#prompt} surface. */
|
|
122
|
+
export interface SurfacePromptOptions {
|
|
123
|
+
/** Headline. */
|
|
124
|
+
title: string;
|
|
125
|
+
/** Supporting copy. */
|
|
126
|
+
message?: string;
|
|
127
|
+
/** Input placeholder. */
|
|
128
|
+
placeholder?: string;
|
|
129
|
+
/** Initial input value. */
|
|
130
|
+
defaultValue?: string;
|
|
131
|
+
/** Accessibility label for the input. Defaults to the `title`. */
|
|
132
|
+
inputLabel?: string;
|
|
133
|
+
/** Submit button label. Defaults to `'OK'`. */
|
|
134
|
+
confirmLabel?: string;
|
|
135
|
+
/** Cancel button label. Defaults to `'Cancel'`. */
|
|
136
|
+
cancelLabel?: string;
|
|
137
|
+
/**
|
|
138
|
+
* Backdrop / Escape / back dismiss (resolving `null`). Defaults to `true`;
|
|
139
|
+
* pass `false` for a blocking prompt.
|
|
140
|
+
*/
|
|
141
|
+
dismissible?: boolean;
|
|
142
|
+
/** Placement override. Defaults to `'center'`. */
|
|
143
|
+
placement?: ResponsiveDialogPlacement;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/surfaces/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SurfaceControls } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Context carrying the CURRENT surface's controls. The host wraps each presented
|
|
4
|
+
* surface's content in a provider bound to that entry's id, so a nested surface
|
|
5
|
+
* reads its OWN controls — `dismiss` targets this surface, `present` stacks a
|
|
6
|
+
* child above it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SurfaceContext: import("react").Context<SurfaceControls | null>;
|
|
9
|
+
/**
|
|
10
|
+
* Read the current surface's {@link SurfaceControls} from inside a presented
|
|
11
|
+
* surface. Equivalent to the `surface` argument the render function receives —
|
|
12
|
+
* use whichever is convenient (props vs. context).
|
|
13
|
+
*
|
|
14
|
+
* DEPTH only: `dismiss` (resolve this surface's promise + close) and `present`
|
|
15
|
+
* (stack a child). Navigation within a surface is the SDK's route layer, not
|
|
16
|
+
* this hook.
|
|
17
|
+
*
|
|
18
|
+
* @throws if called outside a presented surface.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useSurface(): SurfaceControls;
|
|
21
|
+
//# sourceMappingURL=useSurface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSurface.d.ts","sourceRoot":"","sources":["../../../../src/surfaces/useSurface.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,cAAc,iDAA8C,CAAC;AAG1E;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,IAAI,eAAe,CAQ5C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/bloom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
4
4
|
"description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -28,6 +28,22 @@
|
|
|
28
28
|
"default": "./lib/commonjs/index.js"
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
|
+
"./surfaces": {
|
|
32
|
+
"react-native": "./src/surfaces/index.ts",
|
|
33
|
+
"browser": {
|
|
34
|
+
"types": "./lib/typescript/module/surfaces/index.web.d.ts",
|
|
35
|
+
"import": "./lib/module/surfaces/index.web.js",
|
|
36
|
+
"require": "./lib/commonjs/surfaces/index.web.js"
|
|
37
|
+
},
|
|
38
|
+
"import": {
|
|
39
|
+
"types": "./lib/typescript/module/surfaces/index.d.ts",
|
|
40
|
+
"default": "./lib/module/surfaces/index.js"
|
|
41
|
+
},
|
|
42
|
+
"require": {
|
|
43
|
+
"types": "./lib/typescript/commonjs/surfaces/index.d.ts",
|
|
44
|
+
"default": "./lib/commonjs/surfaces/index.js"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
31
47
|
"./image-resolver": {
|
|
32
48
|
"react-native": "./src/image-resolver/index.ts",
|
|
33
49
|
"import": {
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__resetSurfacesForTests,
|
|
3
|
+
dismissAll,
|
|
4
|
+
dismissToRoot,
|
|
5
|
+
dismissTop,
|
|
6
|
+
finalizeClose,
|
|
7
|
+
getSnapshot,
|
|
8
|
+
present,
|
|
9
|
+
requestDismiss,
|
|
10
|
+
resetSurfaces,
|
|
11
|
+
subscribe,
|
|
12
|
+
} from '../surfaces/surfaceStore';
|
|
13
|
+
import { confirm, prompt, surfaces } from '../surfaces/api';
|
|
14
|
+
|
|
15
|
+
const noop = () => null;
|
|
16
|
+
|
|
17
|
+
/** Id of the current top surface (guarded so strict tsc is satisfied). */
|
|
18
|
+
function topId(): string {
|
|
19
|
+
const stack = getSnapshot();
|
|
20
|
+
const top = stack[stack.length - 1];
|
|
21
|
+
if (!top) throw new Error('expected a surface on the stack');
|
|
22
|
+
return top.id;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
// Resolve any lingering promises so awaiting tests never hang, then reset the
|
|
27
|
+
// module store + id counter for isolation.
|
|
28
|
+
resetSurfaces();
|
|
29
|
+
__resetSurfacesForTests();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('surfaceStore', () => {
|
|
33
|
+
it('present pushes an entry and resolves its promise on dismiss', async () => {
|
|
34
|
+
const p = present(noop);
|
|
35
|
+
const stack = getSnapshot();
|
|
36
|
+
expect(stack).toHaveLength(1);
|
|
37
|
+
expect(stack[0]?.status).toBe('open');
|
|
38
|
+
requestDismiss(topId(), 'hello');
|
|
39
|
+
await expect(p).resolves.toBe('hello');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('stacks bottom→top; the last entry is the top', () => {
|
|
43
|
+
void present(noop);
|
|
44
|
+
void present(noop);
|
|
45
|
+
void present(noop);
|
|
46
|
+
expect(getSnapshot().map((e) => e.id)).toEqual([
|
|
47
|
+
'bloom-surface-1',
|
|
48
|
+
'bloom-surface-2',
|
|
49
|
+
'bloom-surface-3',
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('resolves ONCE — a second dismiss with a different result is ignored', async () => {
|
|
54
|
+
const p = present(noop);
|
|
55
|
+
const id = topId();
|
|
56
|
+
requestDismiss(id, 'first');
|
|
57
|
+
requestDismiss(id, 'second');
|
|
58
|
+
await expect(p).resolves.toBe('first');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('requestDismiss flips the entry to closing but does not splice it', () => {
|
|
62
|
+
void present(noop);
|
|
63
|
+
const id = topId();
|
|
64
|
+
requestDismiss(id, 1);
|
|
65
|
+
const stack = getSnapshot();
|
|
66
|
+
expect(stack).toHaveLength(1);
|
|
67
|
+
expect(stack[0]?.status).toBe('closing');
|
|
68
|
+
expect(stack[0]?.generation).toBe(1);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('finalizeClose removes a CLOSING entry; is a no-op for an OPEN entry', async () => {
|
|
72
|
+
const p = present(noop);
|
|
73
|
+
const id = topId();
|
|
74
|
+
// Open → no-op.
|
|
75
|
+
finalizeClose(id);
|
|
76
|
+
expect(getSnapshot()).toHaveLength(1);
|
|
77
|
+
// Closing → removed.
|
|
78
|
+
requestDismiss(id, 'x');
|
|
79
|
+
finalizeClose(id);
|
|
80
|
+
expect(getSnapshot()).toHaveLength(0);
|
|
81
|
+
await expect(p).resolves.toBe('x');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('dismissTop dismisses only the top surface', async () => {
|
|
85
|
+
const a = present(noop);
|
|
86
|
+
const b = present(noop);
|
|
87
|
+
dismissTop('B');
|
|
88
|
+
await expect(b).resolves.toBe('B');
|
|
89
|
+
const stack = getSnapshot();
|
|
90
|
+
expect(stack.find((e) => e.id === 'bloom-surface-1')?.status).toBe('open');
|
|
91
|
+
expect(stack.find((e) => e.id === 'bloom-surface-2')?.status).toBe('closing');
|
|
92
|
+
// Clean up the still-open root.
|
|
93
|
+
void a;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('dismissToRoot resolves everything above the root with undefined, keeps the root', async () => {
|
|
97
|
+
const root = present(noop);
|
|
98
|
+
const mid = present(noop);
|
|
99
|
+
const top = present(noop);
|
|
100
|
+
dismissToRoot();
|
|
101
|
+
await expect(mid).resolves.toBeUndefined();
|
|
102
|
+
await expect(top).resolves.toBeUndefined();
|
|
103
|
+
const stack = getSnapshot();
|
|
104
|
+
expect(stack.find((e) => e.id === 'bloom-surface-1')?.status).toBe('open');
|
|
105
|
+
void root;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('dismissToRoot is a no-op with 0 or 1 surfaces', async () => {
|
|
109
|
+
dismissToRoot();
|
|
110
|
+
expect(getSnapshot()).toHaveLength(0);
|
|
111
|
+
const only = present(noop);
|
|
112
|
+
dismissToRoot();
|
|
113
|
+
expect(getSnapshot()[0]?.status).toBe('open');
|
|
114
|
+
void only;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('dismissAll resolves ALL pending awaiters with undefined', async () => {
|
|
118
|
+
const a = present(noop);
|
|
119
|
+
const b = present(noop);
|
|
120
|
+
const c = present(noop);
|
|
121
|
+
dismissAll();
|
|
122
|
+
await expect(a).resolves.toBeUndefined();
|
|
123
|
+
await expect(b).resolves.toBeUndefined();
|
|
124
|
+
await expect(c).resolves.toBeUndefined();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('resetSurfaces resolves pending awaiters and clears the stack', async () => {
|
|
128
|
+
const a = present(noop);
|
|
129
|
+
const b = present(noop);
|
|
130
|
+
resetSurfaces();
|
|
131
|
+
await expect(a).resolves.toBeUndefined();
|
|
132
|
+
await expect(b).resolves.toBeUndefined();
|
|
133
|
+
expect(getSnapshot()).toHaveLength(0);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('requestDismiss / finalizeClose ignore an unknown id', () => {
|
|
137
|
+
expect(() => requestDismiss('does-not-exist', 1)).not.toThrow();
|
|
138
|
+
expect(() => finalizeClose('does-not-exist')).not.toThrow();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('getSnapshot returns a STABLE reference while unchanged', () => {
|
|
142
|
+
const s1 = getSnapshot();
|
|
143
|
+
expect(getSnapshot()).toBe(s1);
|
|
144
|
+
void present(noop);
|
|
145
|
+
const s2 = getSnapshot();
|
|
146
|
+
expect(s2).not.toBe(s1);
|
|
147
|
+
expect(getSnapshot()).toBe(s2);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('notifies subscribers on each stack mutation', () => {
|
|
151
|
+
let calls = 0;
|
|
152
|
+
const unsub = subscribe(() => {
|
|
153
|
+
calls += 1;
|
|
154
|
+
});
|
|
155
|
+
void present(noop); // +1
|
|
156
|
+
dismissTop(); // requestDismiss emits +1
|
|
157
|
+
expect(calls).toBe(2);
|
|
158
|
+
unsub();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('surfaces imperative API', () => {
|
|
163
|
+
it('surfaces.present + surfaces.dismiss resolve with the top result', async () => {
|
|
164
|
+
const p = surfaces.present(noop);
|
|
165
|
+
surfaces.dismiss('done');
|
|
166
|
+
await expect(p).resolves.toBe('done');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('surfaces.dismissById targets a specific surface', async () => {
|
|
170
|
+
const a = surfaces.present(noop);
|
|
171
|
+
const b = surfaces.present(noop);
|
|
172
|
+
const bottom = getSnapshot()[0];
|
|
173
|
+
if (!bottom) throw new Error('expected a surface on the stack');
|
|
174
|
+
surfaces.dismissById(bottom.id, 'A');
|
|
175
|
+
await expect(a).resolves.toBe('A');
|
|
176
|
+
void b;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('surfaces.dismissAll clears the stack and resolves pending', async () => {
|
|
180
|
+
const a = surfaces.present(noop);
|
|
181
|
+
const b = surfaces.present(noop);
|
|
182
|
+
surfaces.dismissAll();
|
|
183
|
+
await expect(a).resolves.toBeUndefined();
|
|
184
|
+
await expect(b).resolves.toBeUndefined();
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe('confirm / prompt surfaces', () => {
|
|
189
|
+
it('confirm resolves true when dismissed with true, false otherwise', async () => {
|
|
190
|
+
const yes = confirm({ title: 'Delete?' });
|
|
191
|
+
requestDismiss(topId(), true);
|
|
192
|
+
await expect(yes).resolves.toBe(true);
|
|
193
|
+
|
|
194
|
+
const no = confirm({ title: 'Delete?' });
|
|
195
|
+
requestDismiss(topId(), false);
|
|
196
|
+
await expect(no).resolves.toBe(false);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('confirm resolves false when dismissed without a result (backdrop/Escape)', async () => {
|
|
200
|
+
const c = confirm({ title: 'Proceed?' });
|
|
201
|
+
dismissTop(); // undefined → coerced to false
|
|
202
|
+
await expect(c).resolves.toBe(false);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('confirm presents a centered surface', () => {
|
|
206
|
+
void confirm({ title: 'Hi' });
|
|
207
|
+
expect(getSnapshot()[0]?.presentation.placement).toBe('center');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('prompt resolves the entered string, or null when dismissed without one', async () => {
|
|
211
|
+
const p = prompt({ title: 'Name?' });
|
|
212
|
+
requestDismiss(topId(), 'Ada');
|
|
213
|
+
await expect(p).resolves.toBe('Ada');
|
|
214
|
+
|
|
215
|
+
const cancelled = prompt({ title: 'Name?' });
|
|
216
|
+
dismissTop(); // undefined → coerced to null
|
|
217
|
+
await expect(cancelled).resolves.toBeNull();
|
|
218
|
+
});
|
|
219
|
+
});
|
package/src/dialog/Dialog.tsx
CHANGED
|
@@ -25,7 +25,7 @@ import Animated, {
|
|
|
25
25
|
} from 'react-native-reanimated';
|
|
26
26
|
|
|
27
27
|
import { BottomSheet, type BottomSheetRef } from '../bottom-sheet';
|
|
28
|
-
import { Z_INDEX } from '../styles/z-index';
|
|
28
|
+
import { Z_INDEX, Z_INDEX_LAYER_STEP } from '../styles/z-index';
|
|
29
29
|
import { useTheme } from '../theme/use-theme';
|
|
30
30
|
import { Context, useDialogControl } from './context';
|
|
31
31
|
import { DialogBody } from './DialogContent';
|
|
@@ -104,6 +104,7 @@ function CenteredOrSideDialog({
|
|
|
104
104
|
description,
|
|
105
105
|
actions,
|
|
106
106
|
placement,
|
|
107
|
+
layer,
|
|
107
108
|
width = DEFAULT_SIDE_WIDTH,
|
|
108
109
|
inset,
|
|
109
110
|
dismissOnBackdrop = true,
|
|
@@ -241,6 +242,7 @@ function CenteredOrSideDialog({
|
|
|
241
242
|
open={sideOpen}
|
|
242
243
|
onDismiss={handleDismiss}
|
|
243
244
|
side={placement}
|
|
245
|
+
layer={layer}
|
|
244
246
|
width={width}
|
|
245
247
|
inset={inset}
|
|
246
248
|
dismissOnBackdrop={dismissOnBackdrop}
|
|
@@ -311,6 +313,7 @@ function SideSheet({
|
|
|
311
313
|
open,
|
|
312
314
|
onDismiss,
|
|
313
315
|
side,
|
|
316
|
+
layer,
|
|
314
317
|
width,
|
|
315
318
|
inset,
|
|
316
319
|
dismissOnBackdrop,
|
|
@@ -331,6 +334,7 @@ function SideSheet({
|
|
|
331
334
|
open: boolean;
|
|
332
335
|
onDismiss: () => void;
|
|
333
336
|
side: 'left' | 'right';
|
|
337
|
+
layer?: number;
|
|
334
338
|
width: number;
|
|
335
339
|
inset?: DialogInset;
|
|
336
340
|
dismissOnBackdrop: boolean;
|
|
@@ -433,7 +437,13 @@ function SideSheet({
|
|
|
433
437
|
|
|
434
438
|
return (
|
|
435
439
|
<View
|
|
436
|
-
style={[
|
|
440
|
+
style={[
|
|
441
|
+
sideStyles.root,
|
|
442
|
+
// Per-layer offset so a side dialog stacked on top of another (surface
|
|
443
|
+
// stack) paints above it. Layer 0 → offset 0 → unchanged.
|
|
444
|
+
{ zIndex: Z_INDEX.fullscreen + Z_INDEX_LAYER_STEP * (layer ?? 0) },
|
|
445
|
+
containerStyle,
|
|
446
|
+
]}
|
|
437
447
|
{...(containerClassName ? ({ className: containerClassName } as Record<string, string>) : {})}
|
|
438
448
|
pointerEvents="box-none"
|
|
439
449
|
>
|
|
@@ -473,9 +483,10 @@ function SideSheet({
|
|
|
473
483
|
}
|
|
474
484
|
|
|
475
485
|
const sideStyles = StyleSheet.create({
|
|
486
|
+
// `zIndex` is applied inline (`Z_INDEX.fullscreen + layer offset`) so a side
|
|
487
|
+
// dialog stacked on top of another paints above it in the surface stack.
|
|
476
488
|
root: {
|
|
477
489
|
...StyleSheet.absoluteFill,
|
|
478
|
-
zIndex: Z_INDEX.fullscreen,
|
|
479
490
|
},
|
|
480
491
|
backdrop: {
|
|
481
492
|
...StyleSheet.absoluteFill,
|
|
@@ -44,7 +44,6 @@ import type {
|
|
|
44
44
|
} from './types';
|
|
45
45
|
|
|
46
46
|
const FADE_OUT_DURATION = CENTER_FADE_OUT_DURATION;
|
|
47
|
-
const dialogZIndex = createOverlayZIndex();
|
|
48
47
|
|
|
49
48
|
const stopPropagation = (e: { stopPropagation: () => void }) => e.stopPropagation();
|
|
50
49
|
|
|
@@ -132,6 +131,7 @@ function CenterOrSideDialog({
|
|
|
132
131
|
description,
|
|
133
132
|
actions,
|
|
134
133
|
placement,
|
|
134
|
+
layer,
|
|
135
135
|
width = DEFAULT_SIDE_WIDTH,
|
|
136
136
|
maxWidth = DEFAULT_CENTER_MAX_WIDTH,
|
|
137
137
|
inset,
|
|
@@ -150,6 +150,11 @@ function CenterOrSideDialog({
|
|
|
150
150
|
// stylesheet is present by the time the animated surface mounts).
|
|
151
151
|
useDialogCss();
|
|
152
152
|
|
|
153
|
+
// Per-layer overlay z-indices, so a dialog presented on top of another (via
|
|
154
|
+
// the surface stack) paints above it. Defaults to layer 0 — the offset is 0,
|
|
155
|
+
// so a lone dialog's z is byte-for-byte unchanged.
|
|
156
|
+
const dialogZIndex = useMemo(() => createOverlayZIndex(layer ?? 0), [layer]);
|
|
157
|
+
|
|
153
158
|
// Controlled mode is opt-in: when `open` is a boolean the host owns the
|
|
154
159
|
// visible state; otherwise the legacy imperative `control` path drives it.
|
|
155
160
|
const isControlled = controlledOpen !== undefined;
|
|
@@ -297,6 +302,7 @@ function CenterOrSideDialog({
|
|
|
297
302
|
style={style}
|
|
298
303
|
maxWidth={maxWidth}
|
|
299
304
|
contentPadding={contentPadding}
|
|
305
|
+
surfaceZIndex={dialogZIndex.surface}
|
|
300
306
|
isClosing={isClosing}
|
|
301
307
|
>
|
|
302
308
|
{children}
|
|
@@ -325,6 +331,8 @@ function CenterOrSideDialog({
|
|
|
325
331
|
inset={inset}
|
|
326
332
|
dismissOnBackdrop={dismissOnBackdrop}
|
|
327
333
|
contentPadding={contentPadding}
|
|
334
|
+
backdropZIndex={dialogZIndex.backdrop}
|
|
335
|
+
surfaceZIndex={dialogZIndex.surface}
|
|
328
336
|
onDismiss={close}
|
|
329
337
|
panelStyle={panelStyle}
|
|
330
338
|
panelClassName={panelClassName}
|
|
@@ -349,6 +357,7 @@ function DialogPanel({
|
|
|
349
357
|
style,
|
|
350
358
|
maxWidth,
|
|
351
359
|
contentPadding,
|
|
360
|
+
surfaceZIndex,
|
|
352
361
|
isClosing,
|
|
353
362
|
children,
|
|
354
363
|
}: {
|
|
@@ -360,6 +369,7 @@ function DialogPanel({
|
|
|
360
369
|
style?: DialogProps['style'];
|
|
361
370
|
maxWidth: number;
|
|
362
371
|
contentPadding: number;
|
|
372
|
+
surfaceZIndex: number;
|
|
363
373
|
isClosing: boolean;
|
|
364
374
|
children?: React.ReactNode;
|
|
365
375
|
}) {
|
|
@@ -391,7 +401,7 @@ function DialogPanel({
|
|
|
391
401
|
shadowRadius: 30,
|
|
392
402
|
shadowOffset: { width: 0, height: 4 },
|
|
393
403
|
padding: contentPadding,
|
|
394
|
-
zIndex:
|
|
404
|
+
zIndex: surfaceZIndex,
|
|
395
405
|
},
|
|
396
406
|
isClosing
|
|
397
407
|
? ({ animation: `bloomDialogZoomFadeOut ease-in ${FADE_OUT_DURATION}ms forwards` } as ViewStyle)
|
|
@@ -435,6 +445,8 @@ function SheetSurface({
|
|
|
435
445
|
inset,
|
|
436
446
|
dismissOnBackdrop,
|
|
437
447
|
contentPadding,
|
|
448
|
+
backdropZIndex,
|
|
449
|
+
surfaceZIndex,
|
|
438
450
|
onDismiss,
|
|
439
451
|
panelStyle,
|
|
440
452
|
panelClassName,
|
|
@@ -454,6 +466,8 @@ function SheetSurface({
|
|
|
454
466
|
inset?: DialogInset;
|
|
455
467
|
dismissOnBackdrop: boolean;
|
|
456
468
|
contentPadding: number;
|
|
469
|
+
backdropZIndex: number;
|
|
470
|
+
surfaceZIndex: number;
|
|
457
471
|
onDismiss: () => void;
|
|
458
472
|
panelStyle?: StyleProp<ViewStyle>;
|
|
459
473
|
panelClassName?: string;
|
|
@@ -532,7 +546,7 @@ function SheetSurface({
|
|
|
532
546
|
|
|
533
547
|
return (
|
|
534
548
|
<View
|
|
535
|
-
style={[sheetStyles.root, containerStyle]}
|
|
549
|
+
style={[sheetStyles.root, { zIndex: backdropZIndex }, containerStyle]}
|
|
536
550
|
{...(containerClassName ? ({ className: containerClassName } as Record<string, string>) : {})}
|
|
537
551
|
pointerEvents="box-none"
|
|
538
552
|
>
|
|
@@ -562,7 +576,11 @@ function SheetSurface({
|
|
|
562
576
|
{...(panelClassName ? ({ className: panelClassName } as Record<string, string>) : {})}
|
|
563
577
|
style={[
|
|
564
578
|
sheetStyles.panel,
|
|
565
|
-
{
|
|
579
|
+
{
|
|
580
|
+
backgroundColor: theme.colors.background,
|
|
581
|
+
shadowColor: theme.colors.shadow,
|
|
582
|
+
zIndex: surfaceZIndex,
|
|
583
|
+
},
|
|
566
584
|
panelGeometry,
|
|
567
585
|
panelTransition,
|
|
568
586
|
panelStyle,
|
|
@@ -659,13 +677,15 @@ export function AutoMountedDialog({
|
|
|
659
677
|
}
|
|
660
678
|
|
|
661
679
|
const sheetStyles = {
|
|
680
|
+
// `zIndex` for the root (backdrop) and panel (surface) is applied inline from
|
|
681
|
+
// the per-layer `createOverlayZIndex(layer)` in `SheetSurface`, so a side
|
|
682
|
+
// dialog stacked on top of another (surface stack) paints above it.
|
|
662
683
|
root: {
|
|
663
684
|
position: 'fixed' as 'absolute',
|
|
664
685
|
top: 0,
|
|
665
686
|
left: 0,
|
|
666
687
|
right: 0,
|
|
667
688
|
bottom: 0,
|
|
668
|
-
zIndex: dialogZIndex.backdrop,
|
|
669
689
|
} as ViewStyle,
|
|
670
690
|
backdrop: {
|
|
671
691
|
position: 'absolute',
|
|
@@ -681,7 +701,6 @@ const sheetStyles = {
|
|
|
681
701
|
shadowOpacity: 0.18,
|
|
682
702
|
shadowRadius: 24,
|
|
683
703
|
shadowOffset: { width: 0, height: 8 },
|
|
684
|
-
zIndex: dialogZIndex.surface,
|
|
685
704
|
} as ViewStyle,
|
|
686
705
|
};
|
|
687
706
|
|
package/src/dialog/types.ts
CHANGED
|
@@ -163,6 +163,16 @@ export type DialogProps = React.PropsWithChildren<{
|
|
|
163
163
|
containerClassName?: string;
|
|
164
164
|
/** Accessibility label, applied to the dialog role on web. */
|
|
165
165
|
label?: string;
|
|
166
|
+
/**
|
|
167
|
+
* Stacking layer index within a coordinated surface stack. Multiplies
|
|
168
|
+
* `Z_INDEX_LAYER_STEP` onto this dialog's overlay z-indices (backdrop +
|
|
169
|
+
* panel) so a dialog presented on top of another paints above it. Defaults to
|
|
170
|
+
* `0` — the single-dialog case, byte-for-byte unchanged. Set by the shared
|
|
171
|
+
* surface stack (`src/surfaces`); direct consumers rarely need it. No-op for
|
|
172
|
+
* the `bottom` placement, which already stacks in mount order (native RN
|
|
173
|
+
* `<Modal>` / the web portal), so `layer` is harmless there.
|
|
174
|
+
*/
|
|
175
|
+
layer?: number;
|
|
166
176
|
}>;
|
|
167
177
|
|
|
168
178
|
/**
|
package/src/index.ts
CHANGED
|
@@ -72,6 +72,21 @@ export type {
|
|
|
72
72
|
DialogProps,
|
|
73
73
|
ResponsiveDialogPlacement,
|
|
74
74
|
} from './dialog';
|
|
75
|
+
// Surface stack — the shared, coordinated overlay system. The imperative API is
|
|
76
|
+
// the `surfaces` object (its members would otherwise clash with alert-dialog's
|
|
77
|
+
// `confirm`); the individual functions are also named exports of
|
|
78
|
+
// `@oxyhq/bloom/surfaces`.
|
|
79
|
+
export { surfaces, SurfaceProvider, SurfaceHost, useSurface } from './surfaces';
|
|
80
|
+
export type {
|
|
81
|
+
SurfacePresentation,
|
|
82
|
+
PresentOptions,
|
|
83
|
+
SurfaceControls,
|
|
84
|
+
SurfaceRenderFn,
|
|
85
|
+
SurfaceStatus,
|
|
86
|
+
SurfaceEntry,
|
|
87
|
+
SurfaceConfirmOptions,
|
|
88
|
+
SurfacePromptOptions,
|
|
89
|
+
} from './surfaces';
|
|
75
90
|
export * from './button';
|
|
76
91
|
export { Fab } from './fab';
|
|
77
92
|
export type { FabProps, FabVariant, FabSize, FabPlacement } from './fab';
|
package/src/index.web.ts
CHANGED
|
@@ -77,6 +77,21 @@ export type {
|
|
|
77
77
|
DialogProps,
|
|
78
78
|
ResponsiveDialogPlacement,
|
|
79
79
|
} from './dialog/index.web';
|
|
80
|
+
// Surface stack — the shared, coordinated overlay system. The imperative API is
|
|
81
|
+
// the `surfaces` object (its members would otherwise clash with alert-dialog's
|
|
82
|
+
// `confirm`); the individual functions are also named exports of
|
|
83
|
+
// `@oxyhq/bloom/surfaces`.
|
|
84
|
+
export { surfaces, SurfaceProvider, SurfaceHost, useSurface } from './surfaces/index.web';
|
|
85
|
+
export type {
|
|
86
|
+
SurfacePresentation,
|
|
87
|
+
PresentOptions,
|
|
88
|
+
SurfaceControls,
|
|
89
|
+
SurfaceRenderFn,
|
|
90
|
+
SurfaceStatus,
|
|
91
|
+
SurfaceEntry,
|
|
92
|
+
SurfaceConfirmOptions,
|
|
93
|
+
SurfacePromptOptions,
|
|
94
|
+
} from './surfaces/index.web';
|
|
80
95
|
export * from './button/index.web';
|
|
81
96
|
export { Fab } from './fab/index.web';
|
|
82
97
|
export type { FabProps, FabVariant, FabSize, FabPlacement } from './fab/index.web';
|