@oxyhq/bloom 0.8.2 → 0.8.4
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/avatar/Avatar.js +2 -1
- package/lib/commonjs/avatar/Avatar.js.map +1 -1
- package/lib/commonjs/badge/Badge.js +2 -1
- package/lib/commonjs/badge/Badge.js.map +1 -1
- package/lib/commonjs/bottom-sheet/index.js +3 -2
- package/lib/commonjs/bottom-sheet/index.js.map +1 -1
- package/lib/commonjs/context-menu/index.web.js +5 -2
- package/lib/commonjs/context-menu/index.web.js.map +1 -1
- package/lib/commonjs/dialog/CenteredDialog.web.js +4 -2
- package/lib/commonjs/dialog/CenteredDialog.web.js.map +1 -1
- package/lib/commonjs/dialog/Dialog.web.js +4 -2
- package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
- package/lib/commonjs/dialog/SheetShell.js +2 -1
- package/lib/commonjs/dialog/SheetShell.js.map +1 -1
- package/lib/commonjs/index.js +14 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +14 -0
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/menu/index.web.js +95 -29
- package/lib/commonjs/menu/index.web.js.map +1 -1
- package/lib/commonjs/popover/index.web.js +5 -2
- package/lib/commonjs/popover/index.web.js.map +1 -1
- package/lib/commonjs/portal/index.js +2 -1
- package/lib/commonjs/portal/index.js.map +1 -1
- package/lib/commonjs/portal/index.web.js +2 -1
- package/lib/commonjs/portal/index.web.js.map +1 -1
- package/lib/commonjs/prompt-input/PromptInput.js +4 -3
- package/lib/commonjs/prompt-input/PromptInput.js.map +1 -1
- package/lib/commonjs/select/index.web.js +5 -2
- package/lib/commonjs/select/index.web.js.map +1 -1
- package/lib/commonjs/styles/index.js +13 -0
- package/lib/commonjs/styles/index.js.map +1 -1
- package/lib/commonjs/styles/z-index.js +52 -0
- package/lib/commonjs/styles/z-index.js.map +1 -0
- package/lib/commonjs/tooltip/index.web.js +2 -1
- package/lib/commonjs/tooltip/index.web.js.map +1 -1
- package/lib/module/avatar/Avatar.js +2 -1
- package/lib/module/avatar/Avatar.js.map +1 -1
- package/lib/module/badge/Badge.js +2 -1
- package/lib/module/badge/Badge.js.map +1 -1
- package/lib/module/bottom-sheet/index.js +3 -2
- package/lib/module/bottom-sheet/index.js.map +1 -1
- package/lib/module/context-menu/index.web.js +6 -3
- package/lib/module/context-menu/index.web.js.map +1 -1
- package/lib/module/dialog/CenteredDialog.web.js +4 -2
- package/lib/module/dialog/CenteredDialog.web.js.map +1 -1
- package/lib/module/dialog/Dialog.web.js +4 -2
- package/lib/module/dialog/Dialog.web.js.map +1 -1
- package/lib/module/dialog/SheetShell.js +2 -1
- package/lib/module/dialog/SheetShell.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +1 -1
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/menu/index.web.js +97 -31
- package/lib/module/menu/index.web.js.map +1 -1
- package/lib/module/popover/index.web.js +4 -2
- package/lib/module/popover/index.web.js.map +1 -1
- package/lib/module/portal/index.js +2 -1
- package/lib/module/portal/index.js.map +1 -1
- package/lib/module/portal/index.web.js +2 -1
- package/lib/module/portal/index.web.js.map +1 -1
- package/lib/module/prompt-input/PromptInput.js +4 -3
- package/lib/module/prompt-input/PromptInput.js.map +1 -1
- package/lib/module/select/index.web.js +4 -2
- package/lib/module/select/index.web.js.map +1 -1
- package/lib/module/styles/index.js +1 -0
- package/lib/module/styles/index.js.map +1 -1
- package/lib/module/styles/z-index.js +45 -0
- package/lib/module/styles/z-index.js.map +1 -0
- package/lib/module/tooltip/index.web.js +2 -1
- package/lib/module/tooltip/index.web.js.map +1 -1
- package/lib/typescript/commonjs/avatar/Avatar.d.ts.map +1 -1
- package/lib/typescript/commonjs/badge/Badge.d.ts.map +1 -1
- package/lib/typescript/commonjs/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/context-menu/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/CenteredDialog.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/SheetShell.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +1 -1
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/menu/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/menu/types.d.ts +11 -0
- package/lib/typescript/commonjs/menu/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/popover/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/portal/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/portal/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/prompt-input/PromptInput.d.ts.map +1 -1
- package/lib/typescript/commonjs/select/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/styles/index.d.ts +1 -0
- package/lib/typescript/commonjs/styles/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/styles/z-index.d.ts +49 -0
- package/lib/typescript/commonjs/styles/z-index.d.ts.map +1 -0
- package/lib/typescript/commonjs/tooltip/index.web.d.ts.map +1 -1
- package/lib/typescript/module/avatar/Avatar.d.ts.map +1 -1
- package/lib/typescript/module/badge/Badge.d.ts.map +1 -1
- package/lib/typescript/module/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/module/context-menu/index.web.d.ts.map +1 -1
- package/lib/typescript/module/dialog/CenteredDialog.web.d.ts.map +1 -1
- package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/module/dialog/SheetShell.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +1 -1
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/menu/index.web.d.ts.map +1 -1
- package/lib/typescript/module/menu/types.d.ts +11 -0
- package/lib/typescript/module/menu/types.d.ts.map +1 -1
- package/lib/typescript/module/popover/index.web.d.ts.map +1 -1
- package/lib/typescript/module/portal/index.d.ts.map +1 -1
- package/lib/typescript/module/portal/index.web.d.ts.map +1 -1
- package/lib/typescript/module/prompt-input/PromptInput.d.ts.map +1 -1
- package/lib/typescript/module/select/index.web.d.ts.map +1 -1
- package/lib/typescript/module/styles/index.d.ts +1 -0
- package/lib/typescript/module/styles/index.d.ts.map +1 -1
- package/lib/typescript/module/styles/z-index.d.ts +49 -0
- package/lib/typescript/module/styles/z-index.d.ts.map +1 -0
- package/lib/typescript/module/tooltip/index.web.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/avatar/Avatar.tsx +2 -1
- package/src/badge/Badge.tsx +2 -1
- package/src/bottom-sheet/index.tsx +3 -2
- package/src/context-menu/index.web.tsx +5 -2
- package/src/dialog/CenteredDialog.web.tsx +4 -2
- package/src/dialog/Dialog.web.tsx +4 -2
- package/src/dialog/SheetShell.tsx +2 -1
- package/src/index.ts +1 -1
- package/src/index.web.ts +1 -1
- package/src/menu/index.web.tsx +127 -40
- package/src/menu/types.ts +12 -0
- package/src/popover/index.web.tsx +5 -2
- package/src/portal/index.tsx +2 -1
- package/src/portal/index.web.tsx +2 -1
- package/src/prompt-input/PromptInput.tsx +4 -3
- package/src/select/index.web.tsx +5 -2
- package/src/styles/index.ts +1 -0
- package/src/styles/z-index.ts +47 -0
- package/src/tooltip/index.web.tsx +2 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared stacking scale for Bloom primitives.
|
|
3
|
+
*
|
|
4
|
+
* Keep component layers on this scale instead of introducing local magic
|
|
5
|
+
* numbers. App-specific surfaces can still sit above Bloom's inline layers,
|
|
6
|
+
* while portaled overlays use `portalRoot` as the document-level host.
|
|
7
|
+
*/
|
|
8
|
+
export declare const Z_INDEX: {
|
|
9
|
+
readonly base: 0;
|
|
10
|
+
readonly raised: 1;
|
|
11
|
+
readonly floating: 10;
|
|
12
|
+
readonly sticky: 20;
|
|
13
|
+
readonly dropdown: 40;
|
|
14
|
+
readonly dropdownSurface: 41;
|
|
15
|
+
readonly overlayBackdrop: 50;
|
|
16
|
+
readonly overlaySurface: 60;
|
|
17
|
+
readonly tooltip: 70;
|
|
18
|
+
readonly toast: 80;
|
|
19
|
+
readonly fullscreen: 90;
|
|
20
|
+
readonly fullscreenControl: 91;
|
|
21
|
+
readonly sheetHandle: 100;
|
|
22
|
+
readonly portalRoot: 999999;
|
|
23
|
+
};
|
|
24
|
+
export declare const Z_INDEX_LAYER_STEP = 10;
|
|
25
|
+
export declare function createDropdownZIndex(layer?: number): {
|
|
26
|
+
readonly root: number;
|
|
27
|
+
readonly surface: number;
|
|
28
|
+
};
|
|
29
|
+
export declare function createOverlayZIndex(layer?: number): {
|
|
30
|
+
readonly backdrop: number;
|
|
31
|
+
readonly surface: number;
|
|
32
|
+
};
|
|
33
|
+
export declare const zIndex: {
|
|
34
|
+
readonly base: 0;
|
|
35
|
+
readonly raised: 1;
|
|
36
|
+
readonly floating: 10;
|
|
37
|
+
readonly sticky: 20;
|
|
38
|
+
readonly dropdown: 40;
|
|
39
|
+
readonly dropdownSurface: 41;
|
|
40
|
+
readonly overlayBackdrop: 50;
|
|
41
|
+
readonly overlaySurface: 60;
|
|
42
|
+
readonly tooltip: 70;
|
|
43
|
+
readonly toast: 80;
|
|
44
|
+
readonly fullscreen: 90;
|
|
45
|
+
readonly fullscreenControl: 91;
|
|
46
|
+
readonly sheetHandle: 100;
|
|
47
|
+
readonly portalRoot: 999999;
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=z-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"z-index.d.ts","sourceRoot":"","sources":["../../../../src/styles/z-index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;CAeV,CAAC;AAEX,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAMrC,wBAAgB,oBAAoB,CAAC,KAAK,SAAI;;;EAM7C;AAED,wBAAgB,mBAAmB,CAAC,KAAK,SAAI;;;EAM5C;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;CAAU,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/tooltip/index.web.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/tooltip/index.web.tsx"],"names":[],"mappings":"AA0BA;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,2CAEA;yBANe,mBAAmB;;;AASnC,wBAAgB,KAAK,CAAC,EACpB,QAAQ,EACR,QAAmB,EACnB,OAAO,EACP,eAAe,GAChB,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAC7C,2CAkBA;AAED,wBAAgB,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,2CAMjE;AAED,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf,kDAgEA;AAED,eAAO,MAAM,UAAU;;6CAA4B,CAAC"}
|
package/package.json
CHANGED
package/src/avatar/Avatar.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import type { TextStyle } from 'react-native';
|
|
|
4
4
|
|
|
5
5
|
import { useTheme } from '../theme/use-theme';
|
|
6
6
|
import { useImageResolver } from '../image-resolver/context';
|
|
7
|
+
import { Z_INDEX } from '../styles/z-index';
|
|
7
8
|
import { lazyRequire } from '../utils/lazy-require';
|
|
8
9
|
import { useAvatarPlaceholder } from './placeholder-context';
|
|
9
10
|
import type { AvatarProps } from './types';
|
|
@@ -308,7 +309,7 @@ const styles = StyleSheet.create({
|
|
|
308
309
|
right: -2,
|
|
309
310
|
justifyContent: 'center',
|
|
310
311
|
alignItems: 'center',
|
|
311
|
-
zIndex:
|
|
312
|
+
zIndex: Z_INDEX.raised,
|
|
312
313
|
},
|
|
313
314
|
errorDetector: {
|
|
314
315
|
position: 'absolute',
|
package/src/badge/Badge.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import React, { memo, useMemo } from 'react';
|
|
|
2
2
|
import { View, Text, type ViewStyle, type TextStyle } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { useTheme } from '../theme/use-theme';
|
|
5
|
+
import { Z_INDEX } from '../styles/z-index';
|
|
5
6
|
import type { BadgeProps, BadgeColor, BadgeVariant } from './types';
|
|
6
7
|
|
|
7
8
|
const SIZE_CONFIG = {
|
|
@@ -135,7 +136,7 @@ const BadgeComponent: React.FC<BadgeProps> = ({
|
|
|
135
136
|
style={[
|
|
136
137
|
{
|
|
137
138
|
position: 'absolute',
|
|
138
|
-
zIndex:
|
|
139
|
+
zIndex: Z_INDEX.raised,
|
|
139
140
|
...PLACEMENT_CONFIG[placement],
|
|
140
141
|
},
|
|
141
142
|
badgeStyle,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type StyleProp,
|
|
12
12
|
} from 'react-native';
|
|
13
13
|
import { Gesture, GestureDetector, GestureHandlerRootView, type GestureType } from 'react-native-gesture-handler';
|
|
14
|
+
import { Z_INDEX } from '../styles/z-index';
|
|
14
15
|
import Animated, {
|
|
15
16
|
interpolate,
|
|
16
17
|
runOnJS,
|
|
@@ -764,7 +765,7 @@ const styles = StyleSheet.create({
|
|
|
764
765
|
width: 36,
|
|
765
766
|
height: 5,
|
|
766
767
|
borderRadius: 3,
|
|
767
|
-
zIndex:
|
|
768
|
+
zIndex: Z_INDEX.sheetHandle,
|
|
768
769
|
},
|
|
769
770
|
/**
|
|
770
771
|
* Hit area for the drag handle in `manualActivation` mode. Absolutely
|
|
@@ -781,7 +782,7 @@ const styles = StyleSheet.create({
|
|
|
781
782
|
alignItems: 'center',
|
|
782
783
|
justifyContent: 'flex-start',
|
|
783
784
|
paddingTop: 6,
|
|
784
|
-
zIndex:
|
|
785
|
+
zIndex: Z_INDEX.sheetHandle,
|
|
785
786
|
},
|
|
786
787
|
background: {
|
|
787
788
|
...StyleSheet.absoluteFill,
|
|
@@ -19,6 +19,7 @@ import { useTheme } from '../theme/use-theme';
|
|
|
19
19
|
import { Text } from '../typography';
|
|
20
20
|
import { Portal } from '../portal/index.web';
|
|
21
21
|
import { useInteractionState } from '../hooks/useInteractionState';
|
|
22
|
+
import { createOverlayZIndex } from '../styles/z-index';
|
|
22
23
|
import { ItemCtx, useItemContext } from './context';
|
|
23
24
|
import type {
|
|
24
25
|
ContextMenuContextValue,
|
|
@@ -31,6 +32,8 @@ import type {
|
|
|
31
32
|
TriggerProps,
|
|
32
33
|
} from './types';
|
|
33
34
|
|
|
35
|
+
const contextMenuZIndex = createOverlayZIndex();
|
|
36
|
+
|
|
34
37
|
// ---------------------------------------------------------------------------
|
|
35
38
|
// Web-specific context (extends base with position)
|
|
36
39
|
// ---------------------------------------------------------------------------
|
|
@@ -327,13 +330,13 @@ const styles = StyleSheet.create({
|
|
|
327
330
|
left: 0,
|
|
328
331
|
right: 0,
|
|
329
332
|
bottom: 0,
|
|
330
|
-
zIndex:
|
|
333
|
+
zIndex: contextMenuZIndex.backdrop,
|
|
331
334
|
// Opt back in from the Portal root's `pointer-events: none`.
|
|
332
335
|
pointerEvents: 'auto',
|
|
333
336
|
},
|
|
334
337
|
dropdown: {
|
|
335
338
|
position: 'fixed' as 'absolute',
|
|
336
|
-
zIndex:
|
|
339
|
+
zIndex: contextMenuZIndex.surface,
|
|
337
340
|
borderRadius: 8,
|
|
338
341
|
padding: 4,
|
|
339
342
|
borderWidth: 1,
|
|
@@ -5,6 +5,7 @@ import { RemoveScrollBar } from 'react-remove-scroll-bar';
|
|
|
5
5
|
import { useInteractionStates } from '../hooks/useInteractionState';
|
|
6
6
|
import { TimesLarge_Stroke2_Corner0_Rounded as CloseIcon } from '../icons/Times';
|
|
7
7
|
import { Portal } from '../portal/index.web';
|
|
8
|
+
import { createOverlayZIndex } from '../styles/z-index';
|
|
8
9
|
import { useTheme } from '../theme/use-theme';
|
|
9
10
|
import { Text } from '../typography';
|
|
10
11
|
import type { CenteredDialogProps } from './centered-dialog-types';
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
|
|
19
20
|
const FADE_OUT_DURATION = 150;
|
|
20
21
|
const CLOSE_HIT_SLOP = { top: 8, bottom: 8, left: 8, right: 8 } as const;
|
|
22
|
+
const dialogZIndex = createOverlayZIndex();
|
|
21
23
|
|
|
22
24
|
/** Stable testID for the dimmed backdrop. Overridden when a `testID` is set. */
|
|
23
25
|
export const CENTERED_DIALOG_BACKDROP_TESTID = 'bloom-centered-dialog-backdrop';
|
|
@@ -312,7 +314,7 @@ const styles = StyleSheet.create({
|
|
|
312
314
|
left: 0,
|
|
313
315
|
right: 0,
|
|
314
316
|
bottom: 0,
|
|
315
|
-
zIndex:
|
|
317
|
+
zIndex: dialogZIndex.backdrop,
|
|
316
318
|
alignItems: 'center',
|
|
317
319
|
justifyContent: 'center',
|
|
318
320
|
pointerEvents: 'auto',
|
|
@@ -333,7 +335,7 @@ const styles = StyleSheet.create({
|
|
|
333
335
|
shadowOpacity: 0.18,
|
|
334
336
|
shadowRadius: 24,
|
|
335
337
|
shadowOffset: { width: 0, height: 8 },
|
|
336
|
-
zIndex:
|
|
338
|
+
zIndex: dialogZIndex.surface,
|
|
337
339
|
},
|
|
338
340
|
header: {
|
|
339
341
|
flexDirection: 'row',
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
import { RemoveScrollBar } from 'react-remove-scroll-bar';
|
|
21
21
|
|
|
22
22
|
import { Portal } from '../portal/index.web';
|
|
23
|
+
import { createOverlayZIndex } from '../styles/z-index';
|
|
23
24
|
import type { ThemeColors } from '../theme/types';
|
|
24
25
|
import { useTheme } from '../theme/use-theme';
|
|
25
26
|
import { Context, useDialogContext, useDialogControl } from './context';
|
|
@@ -31,6 +32,7 @@ import type {
|
|
|
31
32
|
} from './types';
|
|
32
33
|
|
|
33
34
|
const FADE_OUT_DURATION = 150;
|
|
35
|
+
const dialogZIndex = createOverlayZIndex();
|
|
34
36
|
|
|
35
37
|
const stopPropagation = (e: { stopPropagation: () => void }) => e.stopPropagation();
|
|
36
38
|
|
|
@@ -143,7 +145,7 @@ export function Dialog({
|
|
|
143
145
|
left: 0,
|
|
144
146
|
right: 0,
|
|
145
147
|
bottom: 0,
|
|
146
|
-
zIndex:
|
|
148
|
+
zIndex: dialogZIndex.backdrop,
|
|
147
149
|
alignItems: 'center',
|
|
148
150
|
justifyContent: 'center',
|
|
149
151
|
paddingHorizontal: 20,
|
|
@@ -216,7 +218,7 @@ function DialogPanel({
|
|
|
216
218
|
shadowRadius: 30,
|
|
217
219
|
shadowOffset: { width: 0, height: 4 },
|
|
218
220
|
padding: 20,
|
|
219
|
-
zIndex:
|
|
221
|
+
zIndex: dialogZIndex.surface,
|
|
220
222
|
},
|
|
221
223
|
isClosing
|
|
222
224
|
? ({ animation: `bloomDialogZoomFadeOut ease-in ${FADE_OUT_DURATION}ms forwards` } as ViewStyle)
|
|
@@ -14,6 +14,7 @@ import React, { useCallback, useImperativeHandle, useMemo, useRef } from 'react'
|
|
|
14
14
|
import { Pressable, StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';
|
|
15
15
|
|
|
16
16
|
import { BottomSheet, type BottomSheetRef } from '../bottom-sheet';
|
|
17
|
+
import { Z_INDEX } from '../styles/z-index';
|
|
17
18
|
import { useTheme } from '../theme/use-theme';
|
|
18
19
|
import { Context } from './context';
|
|
19
20
|
import type { DialogControlProps } from './types';
|
|
@@ -135,7 +136,7 @@ const styles = StyleSheet.create({
|
|
|
135
136
|
position: 'absolute',
|
|
136
137
|
width: '100%',
|
|
137
138
|
alignItems: 'center',
|
|
138
|
-
zIndex:
|
|
139
|
+
zIndex: Z_INDEX.floating,
|
|
139
140
|
height: 20,
|
|
140
141
|
},
|
|
141
142
|
handleBar: {
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export * from './theme';
|
|
3
3
|
|
|
4
4
|
// Styles & Utilities
|
|
5
|
-
export { atoms, flatten } from './styles';
|
|
5
|
+
export { atoms, flatten, Z_INDEX, zIndex } from './styles';
|
|
6
6
|
export type { ViewStyleProp, TextStyleProp } from './styles';
|
|
7
7
|
export * as tokens from './styles/tokens';
|
|
8
8
|
export { web, native, ios, android, platform, select } from './styles/platform';
|
package/src/index.web.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
export * from './theme';
|
|
8
8
|
|
|
9
9
|
// Styles & Utilities
|
|
10
|
-
export { atoms, flatten } from './styles';
|
|
10
|
+
export { atoms, flatten, Z_INDEX, zIndex } from './styles';
|
|
11
11
|
export type { ViewStyleProp, TextStyleProp } from './styles';
|
|
12
12
|
export * as tokens from './styles/tokens';
|
|
13
13
|
export { web, native, ios, android, platform, select } from './styles/platform';
|
package/src/menu/index.web.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import React, {
|
|
|
5
5
|
useCallback,
|
|
6
6
|
useEffect,
|
|
7
7
|
useId,
|
|
8
|
+
useLayoutEffect,
|
|
8
9
|
useMemo,
|
|
9
10
|
useRef,
|
|
10
11
|
useState,
|
|
@@ -13,8 +14,9 @@ import { Pressable, StyleSheet, View, type StyleProp, type ViewStyle } from 'rea
|
|
|
13
14
|
|
|
14
15
|
import { useTheme } from '../theme/use-theme';
|
|
15
16
|
import { Text } from '../typography';
|
|
16
|
-
import { Portal } from '../portal/index.web';
|
|
17
17
|
import type { DialogControlProps } from '../dialog/types';
|
|
18
|
+
import { Portal } from '../portal/index.web';
|
|
19
|
+
import { createDropdownZIndex } from '../styles/z-index';
|
|
18
20
|
import {
|
|
19
21
|
MenuContext,
|
|
20
22
|
ItemContext,
|
|
@@ -32,6 +34,10 @@ import type {
|
|
|
32
34
|
|
|
33
35
|
export { useMenuContext };
|
|
34
36
|
|
|
37
|
+
const menuZIndex = createDropdownZIndex();
|
|
38
|
+
const VIEWPORT_GUTTER = 8;
|
|
39
|
+
const MENU_OFFSET = 6;
|
|
40
|
+
|
|
35
41
|
export type MenuControlProps = {
|
|
36
42
|
id: string;
|
|
37
43
|
isOpen: boolean;
|
|
@@ -95,25 +101,58 @@ export function Root({
|
|
|
95
101
|
const defaultControl = useMenuControl();
|
|
96
102
|
const activeControl = control ?? defaultControl;
|
|
97
103
|
const dialogControl = useMenuControlAsDialogControl(activeControl);
|
|
104
|
+
const rootRef = useRef<View>(null);
|
|
105
|
+
const triggerRef = useRef<unknown>(null);
|
|
106
|
+
const dropdownRef = useRef<unknown>(null);
|
|
98
107
|
|
|
99
108
|
const context = useMemo<MenuContextType>(
|
|
100
|
-
() => ({
|
|
109
|
+
() => ({
|
|
110
|
+
control: dialogControl,
|
|
111
|
+
isOpen: activeControl.isOpen,
|
|
112
|
+
triggerRef,
|
|
113
|
+
dropdownRef,
|
|
114
|
+
}),
|
|
101
115
|
[activeControl.isOpen, dialogControl],
|
|
102
116
|
);
|
|
103
117
|
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (!activeControl.isOpen || typeof document === 'undefined') {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const handlePointerDown = (event: PointerEvent) => {
|
|
124
|
+
const rootNode = rootRef.current as unknown as HTMLElement | null;
|
|
125
|
+
const dropdownNode = dropdownRef.current as HTMLElement | null;
|
|
126
|
+
const eventTarget = event.target as Node;
|
|
127
|
+
if (rootNode?.contains(eventTarget) || dropdownNode?.contains(eventTarget)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
activeControl.close();
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
134
|
+
if (event.key === 'Escape') {
|
|
135
|
+
activeControl.close();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
document.addEventListener('pointerdown', handlePointerDown, true);
|
|
140
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
141
|
+
|
|
142
|
+
return () => {
|
|
143
|
+
document.removeEventListener('pointerdown', handlePointerDown, true);
|
|
144
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
145
|
+
};
|
|
146
|
+
}, [activeControl]);
|
|
147
|
+
|
|
104
148
|
return (
|
|
105
149
|
<MenuContext.Provider value={context}>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
accessibilityLabel="Close menu"
|
|
113
|
-
/>
|
|
114
|
-
</Portal>
|
|
115
|
-
)}
|
|
116
|
-
{children}
|
|
150
|
+
<View
|
|
151
|
+
ref={rootRef}
|
|
152
|
+
style={[styles.root, activeControl.isOpen && styles.openRoot]}
|
|
153
|
+
>
|
|
154
|
+
{children}
|
|
155
|
+
</View>
|
|
117
156
|
</MenuContext.Provider>
|
|
118
157
|
);
|
|
119
158
|
}
|
|
@@ -124,7 +163,7 @@ export function Trigger({
|
|
|
124
163
|
role = 'button',
|
|
125
164
|
hint,
|
|
126
165
|
}: TriggerProps) {
|
|
127
|
-
const { control } = useMenuContext();
|
|
166
|
+
const { control, triggerRef } = useMenuContext();
|
|
128
167
|
const [hovered, setHovered] = useState(false);
|
|
129
168
|
const [focused, setFocused] = useState(false);
|
|
130
169
|
|
|
@@ -136,6 +175,7 @@ export function Trigger({
|
|
|
136
175
|
pressed: false,
|
|
137
176
|
},
|
|
138
177
|
props: {
|
|
178
|
+
ref: triggerRef,
|
|
139
179
|
onPress: () => control.open(),
|
|
140
180
|
onFocus: () => setFocused(true),
|
|
141
181
|
onBlur: () => setFocused(false),
|
|
@@ -155,29 +195,76 @@ export function Outer({
|
|
|
155
195
|
}>) {
|
|
156
196
|
const theme = useTheme();
|
|
157
197
|
const context = useMenuContext();
|
|
158
|
-
const
|
|
198
|
+
const [position, setPosition] = useState<ViewStyle | null>(null);
|
|
199
|
+
|
|
200
|
+
useLayoutEffect(() => {
|
|
201
|
+
if (!context.isOpen || typeof window === 'undefined') {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const triggerNode = context.triggerRef?.current as HTMLElement | null;
|
|
206
|
+
if (!triggerNode?.getBoundingClientRect) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const updatePosition = () => {
|
|
211
|
+
const rect = triggerNode.getBoundingClientRect();
|
|
212
|
+
const width = Math.max(180, rect.width);
|
|
213
|
+
const availableRight = window.innerWidth - VIEWPORT_GUTTER;
|
|
214
|
+
const left = Math.min(
|
|
215
|
+
Math.max(VIEWPORT_GUTTER, rect.right - width),
|
|
216
|
+
Math.max(VIEWPORT_GUTTER, availableRight - width),
|
|
217
|
+
);
|
|
218
|
+
const top = Math.min(
|
|
219
|
+
rect.bottom + MENU_OFFSET,
|
|
220
|
+
Math.max(VIEWPORT_GUTTER, window.innerHeight - VIEWPORT_GUTTER),
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
setPosition({
|
|
224
|
+
position: 'fixed' as 'absolute',
|
|
225
|
+
top,
|
|
226
|
+
left,
|
|
227
|
+
right: undefined,
|
|
228
|
+
bottom: undefined,
|
|
229
|
+
minWidth: width,
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
updatePosition();
|
|
234
|
+
window.addEventListener('resize', updatePosition);
|
|
235
|
+
window.addEventListener('scroll', updatePosition, true);
|
|
236
|
+
|
|
237
|
+
return () => {
|
|
238
|
+
window.removeEventListener('resize', updatePosition);
|
|
239
|
+
window.removeEventListener('scroll', updatePosition, true);
|
|
240
|
+
};
|
|
241
|
+
}, [context.isOpen, context.triggerRef]);
|
|
159
242
|
|
|
160
243
|
if (!context.isOpen) {
|
|
161
244
|
return null;
|
|
162
245
|
}
|
|
163
246
|
|
|
164
247
|
return (
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
248
|
+
<Portal>
|
|
249
|
+
<View
|
|
250
|
+
ref={context.dropdownRef as React.Ref<View>}
|
|
251
|
+
style={[
|
|
252
|
+
styles.dropdown,
|
|
253
|
+
{
|
|
254
|
+
backgroundColor: theme.isDark
|
|
255
|
+
? theme.colors.backgroundSecondary
|
|
256
|
+
: theme.colors.background,
|
|
257
|
+
borderColor: theme.colors.borderLight,
|
|
258
|
+
shadowColor: theme.colors.shadow,
|
|
259
|
+
},
|
|
260
|
+
style,
|
|
261
|
+
styles.portaledDropdown,
|
|
262
|
+
position,
|
|
263
|
+
]}
|
|
264
|
+
>
|
|
265
|
+
{children}
|
|
266
|
+
</View>
|
|
267
|
+
</Portal>
|
|
181
268
|
);
|
|
182
269
|
}
|
|
183
270
|
|
|
@@ -294,26 +381,26 @@ export function Divider() {
|
|
|
294
381
|
}
|
|
295
382
|
|
|
296
383
|
const styles = StyleSheet.create({
|
|
297
|
-
|
|
298
|
-
position: '
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
bottom: 0,
|
|
303
|
-
zIndex: 50,
|
|
304
|
-
// Opt back in from the Portal root's `pointer-events: none`.
|
|
305
|
-
pointerEvents: 'auto',
|
|
384
|
+
root: {
|
|
385
|
+
position: 'relative',
|
|
386
|
+
},
|
|
387
|
+
openRoot: {
|
|
388
|
+
zIndex: menuZIndex.root,
|
|
306
389
|
},
|
|
307
390
|
dropdown: {
|
|
308
391
|
borderRadius: 8,
|
|
309
392
|
padding: 4,
|
|
310
393
|
borderWidth: 1,
|
|
311
394
|
overflow: 'hidden',
|
|
395
|
+
zIndex: menuZIndex.surface,
|
|
312
396
|
shadowOpacity: 0.15,
|
|
313
397
|
shadowRadius: 12,
|
|
314
398
|
shadowOffset: { width: 0, height: 4 },
|
|
315
399
|
minWidth: 180,
|
|
316
400
|
},
|
|
401
|
+
portaledDropdown: {
|
|
402
|
+
pointerEvents: 'auto',
|
|
403
|
+
},
|
|
317
404
|
webItem: {
|
|
318
405
|
flexDirection: 'row',
|
|
319
406
|
alignItems: 'center',
|
package/src/menu/types.ts
CHANGED
|
@@ -10,9 +10,20 @@ import type {
|
|
|
10
10
|
import type { Props as SVGIconProps } from '../icons/common';
|
|
11
11
|
import type { DialogControlProps } from '../dialog/types';
|
|
12
12
|
|
|
13
|
+
export type MenuAnchorRect = {
|
|
14
|
+
top: number;
|
|
15
|
+
left: number;
|
|
16
|
+
right: number;
|
|
17
|
+
bottom: number;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
13
22
|
export type MenuContextType = {
|
|
14
23
|
control: DialogControlProps;
|
|
15
24
|
isOpen?: boolean;
|
|
25
|
+
triggerRef?: React.RefObject<unknown>;
|
|
26
|
+
dropdownRef?: React.RefObject<unknown>;
|
|
16
27
|
};
|
|
17
28
|
|
|
18
29
|
export type ItemContextType = {
|
|
@@ -27,6 +38,7 @@ export type TriggerChildProps = {
|
|
|
27
38
|
pressed: boolean;
|
|
28
39
|
};
|
|
29
40
|
props: {
|
|
41
|
+
ref?: React.Ref<unknown>;
|
|
30
42
|
onPress: () => void;
|
|
31
43
|
onFocus?: () => void;
|
|
32
44
|
onBlur?: () => void;
|
|
@@ -11,6 +11,7 @@ import { Pressable, StyleSheet, View } from 'react-native';
|
|
|
11
11
|
|
|
12
12
|
import { useTheme } from '../theme/use-theme';
|
|
13
13
|
import { Portal } from '../portal/index.web';
|
|
14
|
+
import { createOverlayZIndex } from '../styles/z-index';
|
|
14
15
|
import { PopoverContext, usePopoverContext } from './context';
|
|
15
16
|
import type {
|
|
16
17
|
PopoverContentProps,
|
|
@@ -32,6 +33,8 @@ export type {
|
|
|
32
33
|
|
|
33
34
|
export { usePopoverContext };
|
|
34
35
|
|
|
36
|
+
const popoverZIndex = createOverlayZIndex();
|
|
37
|
+
|
|
35
38
|
/**
|
|
36
39
|
* Web self-contained control. Unlike native (which proxies a `BottomSheet`
|
|
37
40
|
* via `useDialogControl`), the web popover owns its own open state and
|
|
@@ -307,12 +310,12 @@ const styles = StyleSheet.create({
|
|
|
307
310
|
left: 0,
|
|
308
311
|
right: 0,
|
|
309
312
|
bottom: 0,
|
|
310
|
-
zIndex:
|
|
313
|
+
zIndex: popoverZIndex.backdrop,
|
|
311
314
|
pointerEvents: 'auto',
|
|
312
315
|
},
|
|
313
316
|
panel: {
|
|
314
317
|
position: 'fixed' as 'absolute',
|
|
315
|
-
zIndex:
|
|
318
|
+
zIndex: popoverZIndex.surface,
|
|
316
319
|
borderRadius: 12,
|
|
317
320
|
borderWidth: 1,
|
|
318
321
|
paddingVertical: 4,
|
package/src/portal/index.tsx
CHANGED
|
@@ -11,6 +11,7 @@ import React, {
|
|
|
11
11
|
useState,
|
|
12
12
|
} from 'react';
|
|
13
13
|
import { View, StyleSheet, Platform } from 'react-native';
|
|
14
|
+
import { Z_INDEX } from '../styles/z-index';
|
|
14
15
|
|
|
15
16
|
type Component = React.ReactElement;
|
|
16
17
|
|
|
@@ -120,6 +121,6 @@ const styles = StyleSheet.create({
|
|
|
120
121
|
right: 0,
|
|
121
122
|
bottom: 0,
|
|
122
123
|
pointerEvents: 'box-none',
|
|
123
|
-
zIndex:
|
|
124
|
+
zIndex: Z_INDEX.portalRoot,
|
|
124
125
|
},
|
|
125
126
|
});
|
package/src/portal/index.web.tsx
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import React, { Fragment, memo, useLayoutEffect, useState } from 'react';
|
|
17
17
|
import { createPortal } from 'react-dom';
|
|
18
|
+
import { Z_INDEX } from '../styles/z-index';
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Render children into a stable container appended to `document.body`.
|
|
@@ -52,7 +53,7 @@ function getPortalRoot(): HTMLElement | null {
|
|
|
52
53
|
root.style.pointerEvents = 'none';
|
|
53
54
|
// Above the document flow; individual portaled components can use
|
|
54
55
|
// their own z-index for stacking among themselves.
|
|
55
|
-
root.style.zIndex =
|
|
56
|
+
root.style.zIndex = String(Z_INDEX.portalRoot);
|
|
56
57
|
document.body.appendChild(root);
|
|
57
58
|
}
|
|
58
59
|
return root;
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
|
|
12
12
|
import { useTheme } from '../theme/use-theme';
|
|
13
13
|
import { Portal } from '../portal';
|
|
14
|
+
import { Z_INDEX } from '../styles/z-index';
|
|
14
15
|
import { PromptInputContext, type Attachment } from './context';
|
|
15
16
|
import { PromptInputTextarea } from './Textarea';
|
|
16
17
|
import { PromptInputActions } from './Actions';
|
|
@@ -198,7 +199,7 @@ export function PromptInput({
|
|
|
198
199
|
position: 'absolute',
|
|
199
200
|
top: 8,
|
|
200
201
|
right: 8,
|
|
201
|
-
zIndex:
|
|
202
|
+
zIndex: Z_INDEX.floating,
|
|
202
203
|
backgroundColor: theme.colors.background,
|
|
203
204
|
borderRadius: 9999,
|
|
204
205
|
padding: 6,
|
|
@@ -233,7 +234,7 @@ export function PromptInput({
|
|
|
233
234
|
style={[
|
|
234
235
|
StyleSheet.absoluteFill,
|
|
235
236
|
{
|
|
236
|
-
zIndex:
|
|
237
|
+
zIndex: Z_INDEX.fullscreen,
|
|
237
238
|
backgroundColor: theme.colors.background,
|
|
238
239
|
// Opt back in from the Portal root's `pointer-events: none`
|
|
239
240
|
// (web only — harmless on native).
|
|
@@ -247,7 +248,7 @@ export function PromptInput({
|
|
|
247
248
|
position: 'absolute',
|
|
248
249
|
top: 16,
|
|
249
250
|
right: 16,
|
|
250
|
-
zIndex:
|
|
251
|
+
zIndex: Z_INDEX.fullscreenControl,
|
|
251
252
|
padding: 8,
|
|
252
253
|
backgroundColor: theme.colors.background,
|
|
253
254
|
borderRadius: 9999,
|
package/src/select/index.web.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { Pressable, StyleSheet, View } from 'react-native';
|
|
|
4
4
|
import { useTheme } from '../theme/use-theme';
|
|
5
5
|
import { Text } from '../typography';
|
|
6
6
|
import { Portal } from '../portal/index.web';
|
|
7
|
+
import { createOverlayZIndex } from '../styles/z-index';
|
|
7
8
|
import { RadioIndicator } from '../radio-indicator';
|
|
8
9
|
import { useInteractionState } from '../hooks/useInteractionState';
|
|
9
10
|
import {
|
|
@@ -25,6 +26,8 @@ import type {
|
|
|
25
26
|
|
|
26
27
|
export { useItemContext };
|
|
27
28
|
|
|
29
|
+
const selectZIndex = createOverlayZIndex();
|
|
30
|
+
|
|
28
31
|
// ---------------------------------------------------------------------------
|
|
29
32
|
// Context
|
|
30
33
|
// ---------------------------------------------------------------------------
|
|
@@ -337,13 +340,13 @@ const styles = StyleSheet.create({
|
|
|
337
340
|
left: 0,
|
|
338
341
|
right: 0,
|
|
339
342
|
bottom: 0,
|
|
340
|
-
zIndex:
|
|
343
|
+
zIndex: selectZIndex.backdrop,
|
|
341
344
|
// Opt back in from the Portal root's `pointer-events: none`.
|
|
342
345
|
pointerEvents: 'auto',
|
|
343
346
|
},
|
|
344
347
|
dropdown: {
|
|
345
348
|
position: 'absolute',
|
|
346
|
-
zIndex:
|
|
349
|
+
zIndex: selectZIndex.surface,
|
|
347
350
|
borderRadius: 8,
|
|
348
351
|
borderWidth: 1,
|
|
349
352
|
overflow: 'hidden',
|