react-native-bottom-sheet-stack 0.1.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.
Files changed (56) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +82 -0
  3. package/lib/commonjs/BottomSheet.context.js +14 -0
  4. package/lib/commonjs/BottomSheet.context.js.map +1 -0
  5. package/lib/commonjs/BottomSheetHost.js +73 -0
  6. package/lib/commonjs/BottomSheetHost.js.map +1 -0
  7. package/lib/commonjs/BottomSheetManaged.js +72 -0
  8. package/lib/commonjs/BottomSheetManaged.js.map +1 -0
  9. package/lib/commonjs/BottomSheetManager.provider.js +39 -0
  10. package/lib/commonjs/BottomSheetManager.provider.js.map +1 -0
  11. package/lib/commonjs/bottomSheet.store.js +101 -0
  12. package/lib/commonjs/bottomSheet.store.js.map +1 -0
  13. package/lib/commonjs/bottomSheetCoordinator.js +43 -0
  14. package/lib/commonjs/bottomSheetCoordinator.js.map +1 -0
  15. package/lib/commonjs/index.js +41 -0
  16. package/lib/commonjs/index.js.map +1 -0
  17. package/lib/commonjs/package.json +1 -0
  18. package/lib/commonjs/refsMap.js +8 -0
  19. package/lib/commonjs/refsMap.js.map +1 -0
  20. package/lib/commonjs/useBottomSheetManager.js +78 -0
  21. package/lib/commonjs/useBottomSheetManager.js.map +1 -0
  22. package/lib/commonjs/useBottomSheetState.js +27 -0
  23. package/lib/commonjs/useBottomSheetState.js.map +1 -0
  24. package/lib/typescript/example/src/App.d.ts +4 -0
  25. package/lib/typescript/example/src/App.d.ts.map +1 -0
  26. package/lib/typescript/src/BottomSheet.context.d.ts +7 -0
  27. package/lib/typescript/src/BottomSheet.context.d.ts.map +1 -0
  28. package/lib/typescript/src/BottomSheetHost.d.ts +5 -0
  29. package/lib/typescript/src/BottomSheetHost.d.ts.map +1 -0
  30. package/lib/typescript/src/BottomSheetManaged.d.ts +10 -0
  31. package/lib/typescript/src/BottomSheetManaged.d.ts.map +1 -0
  32. package/lib/typescript/src/BottomSheetManager.provider.d.ts +14 -0
  33. package/lib/typescript/src/BottomSheetManager.provider.d.ts.map +1 -0
  34. package/lib/typescript/src/bottomSheet.store.d.ts +32 -0
  35. package/lib/typescript/src/bottomSheet.store.d.ts.map +1 -0
  36. package/lib/typescript/src/bottomSheetCoordinator.d.ts +2 -0
  37. package/lib/typescript/src/bottomSheetCoordinator.d.ts.map +1 -0
  38. package/lib/typescript/src/index.d.ts +6 -0
  39. package/lib/typescript/src/index.d.ts.map +1 -0
  40. package/lib/typescript/src/refsMap.d.ts +3 -0
  41. package/lib/typescript/src/refsMap.d.ts.map +1 -0
  42. package/lib/typescript/src/useBottomSheetManager.d.ts +22 -0
  43. package/lib/typescript/src/useBottomSheetManager.d.ts.map +1 -0
  44. package/lib/typescript/src/useBottomSheetState.d.ts +6 -0
  45. package/lib/typescript/src/useBottomSheetState.d.ts.map +1 -0
  46. package/package.json +130 -0
  47. package/src/BottomSheet.context.ts +15 -0
  48. package/src/BottomSheetHost.tsx +70 -0
  49. package/src/BottomSheetManaged.tsx +89 -0
  50. package/src/BottomSheetManager.provider.tsx +39 -0
  51. package/src/bottomSheet.store.ts +107 -0
  52. package/src/bottomSheetCoordinator.ts +34 -0
  53. package/src/index.tsx +5 -0
  54. package/src/refsMap.ts +6 -0
  55. package/src/useBottomSheetManager.tsx +87 -0
  56. package/src/useBottomSheetState.ts +31 -0
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useBottomSheetState = useBottomSheetState;
7
+ var _BottomSheet = require("./BottomSheet.context");
8
+ var _bottomSheet = require("./bottomSheet.store");
9
+ function useBottomSheetState() {
10
+ const context = (0, _BottomSheet.useMaybeBottomSheetContext)();
11
+ const {
12
+ bottomSheetsStack,
13
+ startClosing
14
+ } = (0, _bottomSheet.useBottomSheetStore)(store => ({
15
+ bottomSheetsStack: store.stack,
16
+ startClosing: store.startClosing
17
+ }));
18
+ const bottomSheetState = bottomSheetsStack.find(bottomSheet => bottomSheet.id === context?.id);
19
+ if (!bottomSheetState) {
20
+ throw new Error('useBottomSheetState must be used within a BottomSheetProvider');
21
+ }
22
+ return {
23
+ bottomSheetState,
24
+ close: () => startClosing(bottomSheetState.id)
25
+ };
26
+ }
27
+ //# sourceMappingURL=useBottomSheetState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_BottomSheet","require","_bottomSheet","useBottomSheetState","context","useMaybeBottomSheetContext","bottomSheetsStack","startClosing","useBottomSheetStore","store","stack","bottomSheetState","find","bottomSheet","id","Error","close"],"sourceRoot":"../../src","sources":["useBottomSheetState.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAKO,SAASE,mBAAmBA,CAAA,EAGjC;EACA,MAAMC,OAAO,GAAG,IAAAC,uCAA0B,EAAC,CAAC;EAC5C,MAAM;IAAEC,iBAAiB;IAAEC;EAAa,CAAC,GAAG,IAAAC,gCAAmB,EAAEC,KAAK,KAAM;IAC1EH,iBAAiB,EAAEG,KAAK,CAACC,KAAK;IAC9BH,YAAY,EAAEE,KAAK,CAACF;EACtB,CAAC,CAAC,CAAC;EAEH,MAAMI,gBAAgB,GAAGL,iBAAiB,CAACM,IAAI,CAC5CC,WAAW,IAAKA,WAAW,CAACC,EAAE,KAAKV,OAAO,EAAEU,EAC/C,CAAC;EAED,IAAI,CAACH,gBAAgB,EAAE;IACrB,MAAM,IAAII,KAAK,CACb,+DACF,CAAC;EACH;EAEA,OAAO;IACLJ,gBAAgB;IAChBK,KAAK,EAAEA,CAAA,KAAMT,YAAY,CAACI,gBAAgB,CAACG,EAAE;EAC/C,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ import { type PropsWithChildren } from 'react';
2
+ export default function App(): import("react/jsx-runtime").JSX.Element;
3
+ export declare const Content: ({}: PropsWithChildren) => import("react/jsx-runtime").JSX.Element;
4
+ //# sourceMappingURL=App.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../../example/src/App.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAc,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAO3D,MAAM,CAAC,OAAO,UAAU,GAAG,4CAY1B;AAmBD,eAAO,MAAM,OAAO,GAAI,IAAI,iBAAiB,4CAU5C,CAAC"}
@@ -0,0 +1,7 @@
1
+ interface BottomSheetContextValue {
2
+ id: string;
3
+ }
4
+ export declare const BottomSheetContext: import("react").Context<BottomSheetContextValue | undefined>;
5
+ export declare function useMaybeBottomSheetContext(): BottomSheetContextValue | undefined;
6
+ export {};
7
+ //# sourceMappingURL=BottomSheet.context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BottomSheet.context.d.ts","sourceRoot":"","sources":["../../../src/BottomSheet.context.ts"],"names":[],"mappings":"AAEA,UAAU,uBAAuB;IAC/B,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,eAAO,MAAM,kBAAkB,8DAEnB,CAAC;AAEb,wBAAgB,0BAA0B,wCAIzC"}
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ declare function BottomSheetHostComp(): import("react/jsx-runtime").JSX.Element;
3
+ export declare const BottomSheetHost: React.MemoExoticComponent<typeof BottomSheetHostComp>;
4
+ export {};
5
+ //# sourceMappingURL=BottomSheetHost.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BottomSheetHost.d.ts","sourceRoot":"","sources":["../../../src/BottomSheetHost.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqD,MAAM,OAAO,CAAC;AAU1E,iBAAS,mBAAmB,4CA2C3B;AASD,eAAO,MAAM,eAAe,uDAAkC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { type BottomSheetProps } from '@gorhom/bottom-sheet';
2
+ import { type BottomSheetMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
3
+ import React from 'react';
4
+ export interface BottomSheetRef extends BottomSheetMethods {
5
+ }
6
+ interface BottomSheetManagedProps extends BottomSheetProps {
7
+ }
8
+ export declare const BottomSheetManaged: React.ForwardRefExoticComponent<BottomSheetManagedProps & React.RefAttributes<BottomSheetRef>>;
9
+ export {};
10
+ //# sourceMappingURL=BottomSheetManaged.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BottomSheetManaged.d.ts","sourceRoot":"","sources":["../../../src/BottomSheetManaged.tsx"],"names":[],"mappings":"AAAA,OAA4B,EAG1B,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AACpF,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,MAAM,WAAW,cAAe,SAAQ,kBAAkB;CAAG;AAE7D,UAAU,uBAAwB,SAAQ,gBAAgB;CAAG;AAE7D,eAAO,MAAM,kBAAkB,gGAsE9B,CAAC"}
@@ -0,0 +1,14 @@
1
+ import React, { type PropsWithChildren } from 'react';
2
+ interface Props extends PropsWithChildren {
3
+ id: string;
4
+ }
5
+ declare function BottomSheetManagerProviderComp({ id, children }: Props): import("react/jsx-runtime").JSX.Element;
6
+ export declare const BottomSheetManagerProvider: React.MemoExoticComponent<typeof BottomSheetManagerProviderComp>;
7
+ export declare const useBottomSheetManagerContext: () => {
8
+ groupId: string;
9
+ };
10
+ export declare const useMaybeBottomSheetManagerContext: () => {
11
+ groupId: string;
12
+ } | null;
13
+ export {};
14
+ //# sourceMappingURL=BottomSheetManager.provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BottomSheetManager.provider.d.ts","sourceRoot":"","sources":["../../../src/BottomSheetManager.provider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAItD,UAAU,KAAM,SAAQ,iBAAiB;IACvC,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,iBAAS,8BAA8B,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,KAAK,2CAM9D;AAED,eAAO,MAAM,0BAA0B,kEAEtC,CAAC;AAEF,eAAO,MAAM,4BAA4B;aAlBM,MAAM;CA2BpD,CAAC;AAEF,eAAO,MAAM,iCAAiC;aA7BC,MAAM;QAoCpD,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { type ReactNode } from 'react';
2
+ export type BottomSheetStatus = 'opening' | 'open' | 'closing' | 'hidden';
3
+ export type OpenMode = 'push' | 'switch' | 'replace';
4
+ export interface BottomSheetState {
5
+ groupId: string;
6
+ id: string;
7
+ content: ReactNode;
8
+ status: BottomSheetStatus;
9
+ }
10
+ type TriggerState = Omit<BottomSheetState, 'status'>;
11
+ interface BottomSheetStoreState {
12
+ stack: BottomSheetState[];
13
+ }
14
+ interface BottomSheetStoreActions {
15
+ push(sheet: TriggerState): void;
16
+ switch(sheet: TriggerState): void;
17
+ replace(sheet: TriggerState): void;
18
+ startClosing(id: string): void;
19
+ finishClosing(id: string): void;
20
+ clearAll(): void;
21
+ }
22
+ export declare const useBottomSheetStore: import("zustand/traditional").UseBoundStoreWithEqualityFn<Omit<import("zustand/vanilla").StoreApi<BottomSheetStoreState & BottomSheetStoreActions>, "subscribe"> & {
23
+ subscribe: {
24
+ (listener: (selectedState: BottomSheetStoreState & BottomSheetStoreActions, previousSelectedState: BottomSheetStoreState & BottomSheetStoreActions) => void): () => void;
25
+ <U>(selector: (state: BottomSheetStoreState & BottomSheetStoreActions) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: {
26
+ equalityFn?: ((a: U, b: U) => boolean) | undefined;
27
+ fireImmediately?: boolean;
28
+ } | undefined): () => void;
29
+ };
30
+ }>;
31
+ export {};
32
+ //# sourceMappingURL=bottomSheet.store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottomSheet.store.d.ts","sourceRoot":"","sources":["../../../src/bottomSheet.store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAC1E,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAErD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,SAAS,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAED,KAAK,YAAY,GAAG,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;AAErD,UAAU,qBAAqB;IAC7B,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,UAAU,uBAAuB;IAC/B,IAAI,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAChC,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED,eAAO,MAAM,mBAAmB;;;;;;;;EA6E/B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function initBottomSheetCoordinator(): void;
2
+ //# sourceMappingURL=bottomSheetCoordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottomSheetCoordinator.d.ts","sourceRoot":"","sources":["../../../src/bottomSheetCoordinator.ts"],"names":[],"mappings":"AAGA,wBAAgB,0BAA0B,SA8BzC"}
@@ -0,0 +1,6 @@
1
+ export { BottomSheetHost } from './BottomSheetHost';
2
+ export { BottomSheetManagerProvider } from './BottomSheetManager.provider';
3
+ export { useBottomSheetManager } from './useBottomSheetManager';
4
+ export { useBottomSheetState } from './useBottomSheetState';
5
+ export { BottomSheetManaged } from './BottomSheetManaged';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { BottomSheetMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
2
+ export declare const sheetRefs: Record<string, React.RefObject<BottomSheetMethods | null>>;
3
+ //# sourceMappingURL=refsMap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refsMap.d.ts","sourceRoot":"","sources":["../../../src/refsMap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAEpF,eAAO,MAAM,SAAS,EAAE,MAAM,CAC5B,MAAM,EACN,KAAK,CAAC,SAAS,CAAC,kBAAkB,GAAG,IAAI,CAAC,CACtC,CAAC"}
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import { type OpenMode } from './bottomSheet.store';
3
+ export declare const useBottomSheetManager: () => {
4
+ clearAll: () => void;
5
+ closeTop: () => void;
6
+ openBottomSheet: (content: React.ReactElement, options?: {
7
+ id?: string;
8
+ groupId?: string;
9
+ mode?: OpenMode;
10
+ }) => string;
11
+ pushBottomSheet: (sheet: {
12
+ groupId: string;
13
+ id: string;
14
+ content: React.ReactNode;
15
+ }) => void;
16
+ replaceBottomSheet: (sheet: {
17
+ groupId: string;
18
+ id: string;
19
+ content: React.ReactNode;
20
+ }) => void;
21
+ };
22
+ //# sourceMappingURL=useBottomSheetManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBottomSheetManager.d.ts","sourceRoot":"","sources":["../../../src/useBottomSheetManager.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAuB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAKzE,eAAO,MAAM,qBAAqB;;;+BAoBrB,KAAK,CAAC,YAAY,YAClB;QACP,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,QAAQ,CAAC;KACjB;;;;;;;;;;;CAsDJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { type BottomSheetState } from './bottomSheet.store';
2
+ export declare function useBottomSheetState(): {
3
+ bottomSheetState: BottomSheetState;
4
+ close: () => void;
5
+ };
6
+ //# sourceMappingURL=useBottomSheetState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBottomSheetState.d.ts","sourceRoot":"","sources":["../../../src/useBottomSheetState.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAE7B,wBAAgB,mBAAmB,IAAI;IACrC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAqBA"}
package/package.json ADDED
@@ -0,0 +1,130 @@
1
+ {
2
+ "name": "react-native-bottom-sheet-stack",
3
+ "version": "0.1.0",
4
+ "description": "Bottom Sheet Stack Manager",
5
+ "source": "./src/index.tsx",
6
+ "main": "lib/commonjs/index.js",
7
+ "types": "lib/typescript/src/index.d.ts",
8
+ "files": [
9
+ "src",
10
+ "lib/typescript",
11
+ "lib/commonjs"
12
+ ],
13
+ "scripts": {
14
+ "example": "yarn workspace react-native-bottom-sheet-stack-example",
15
+ "test": "jest",
16
+ "typecheck": "tsc",
17
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
18
+ "clean": "del-cli lib",
19
+ "prepare": "bob build",
20
+ "release": "release-it"
21
+ },
22
+ "keywords": [
23
+ "react-native",
24
+ "ios",
25
+ "android"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/arekkubaczkowski/react-native-bottom-sheet-stack.git"
30
+ },
31
+ "author": "Arek Kubaczkowski <arek.kubaczkowski@gmail.com> (https://github.com/arekkubaczkowski)",
32
+ "license": "MIT",
33
+ "bugs": {
34
+ "url": "https://github.com/arekkubaczkowski/react-native-bottom-sheet-stack/issues"
35
+ },
36
+ "homepage": "https://github.com/arekkubaczkowski/react-native-bottom-sheet-stack#readme",
37
+ "publishConfig": {
38
+ "registry": "https://registry.npmjs.org/"
39
+ },
40
+ "devDependencies": {
41
+ "@commitlint/config-conventional": "^19.6.0",
42
+ "@eslint/compat": "^1.2.7",
43
+ "@eslint/eslintrc": "^3.3.0",
44
+ "@eslint/js": "^9.22.0",
45
+ "@evilmartians/lefthook": "^1.5.0",
46
+ "@gorhom/bottom-sheet": "^5.1.2",
47
+ "@react-native/eslint-config": "^0.78.0",
48
+ "@release-it/conventional-changelog": "^9.0.2",
49
+ "@types/jest": "^29.5.5",
50
+ "@types/react": "^19.0.12",
51
+ "commitlint": "^19.6.1",
52
+ "del-cli": "^5.1.0",
53
+ "eslint": "^9.22.0",
54
+ "eslint-config-prettier": "^10.1.1",
55
+ "eslint-plugin-prettier": "^5.2.3",
56
+ "jest": "^29.7.0",
57
+ "prettier": "^3.0.3",
58
+ "react": "18.3.1",
59
+ "react-native": "0.76.9",
60
+ "react-native-builder-bob": "^0.40.6",
61
+ "react-native-gesture-handler": "^2.25.0",
62
+ "react-native-reanimated": "^3.17.4",
63
+ "release-it": "^17.10.0",
64
+ "typescript": "^5.2.2",
65
+ "zustand": "^5.0.3"
66
+ },
67
+ "peerDependencies": {
68
+ "@gorhom/bottom-sheet": ">=5.0.0",
69
+ "react": "*",
70
+ "react-native": "*",
71
+ "react-native-gesture-handler": ">=2.0.0",
72
+ "react-native-reanimated": ">=3.0.0",
73
+ "zustand": ">=5.0.0"
74
+ },
75
+ "workspaces": [
76
+ "example"
77
+ ],
78
+ "packageManager": "yarn@3.6.1",
79
+ "jest": {
80
+ "preset": "react-native",
81
+ "modulePathIgnorePatterns": [
82
+ "<rootDir>/example/node_modules",
83
+ "<rootDir>/lib/"
84
+ ]
85
+ },
86
+ "commitlint": {
87
+ "extends": [
88
+ "@commitlint/config-conventional"
89
+ ]
90
+ },
91
+ "release-it": {
92
+ "git": {
93
+ "commitMessage": "chore: release ${version}",
94
+ "tagName": "v${version}"
95
+ },
96
+ "npm": {
97
+ "publish": true
98
+ },
99
+ "github": {
100
+ "release": true
101
+ },
102
+ "plugins": {
103
+ "@release-it/conventional-changelog": {
104
+ "preset": {
105
+ "name": "angular"
106
+ }
107
+ }
108
+ }
109
+ },
110
+ "prettier": {
111
+ "quoteProps": "consistent",
112
+ "singleQuote": true,
113
+ "tabWidth": 2,
114
+ "trailingComma": "es5",
115
+ "useTabs": false
116
+ },
117
+ "react-native-builder-bob": {
118
+ "source": "src",
119
+ "output": "lib",
120
+ "targets": [
121
+ "commonjs",
122
+ "typescript"
123
+ ]
124
+ },
125
+ "create-react-native-library": {
126
+ "languages": "js",
127
+ "type": "library",
128
+ "version": "0.49.8"
129
+ }
130
+ }
@@ -0,0 +1,15 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ interface BottomSheetContextValue {
4
+ id: string;
5
+ }
6
+
7
+ export const BottomSheetContext = createContext<
8
+ BottomSheetContextValue | undefined
9
+ >(undefined);
10
+
11
+ export function useMaybeBottomSheetContext() {
12
+ const context = useContext(BottomSheetContext);
13
+
14
+ return context;
15
+ }
@@ -0,0 +1,70 @@
1
+ import React, { useEffect, useMemo, type PropsWithChildren } from 'react';
2
+ import { StyleSheet, useWindowDimensions, View } from 'react-native';
3
+
4
+ import { BottomSheetContext } from './BottomSheet.context';
5
+ import { useBottomSheetStore } from './bottomSheet.store';
6
+ import { initBottomSheetCoordinator } from './bottomSheetCoordinator';
7
+ import { useBottomSheetManagerContext } from './BottomSheetManager.provider';
8
+
9
+ initBottomSheetCoordinator();
10
+
11
+ function BottomSheetHostComp() {
12
+ const { bottomSheetsStack, clearAll } = useBottomSheetStore((store) => ({
13
+ bottomSheetsStack: store.stack,
14
+ clearAll: store.clearAll,
15
+ }));
16
+
17
+ const { width, height } = useWindowDimensions();
18
+ const { groupId } = useBottomSheetManagerContext();
19
+
20
+ const filteredQueue = useMemo(
21
+ () =>
22
+ bottomSheetsStack.filter(
23
+ (bottomSheet) => bottomSheet.groupId === groupId
24
+ ),
25
+ [bottomSheetsStack, groupId]
26
+ );
27
+
28
+ useEffect(() => {
29
+ return () => {
30
+ clearAll();
31
+ };
32
+ }, [clearAll]);
33
+
34
+ return (
35
+ <>
36
+ {filteredQueue.map(({ id, content }) => (
37
+ <BottomSheetContext.Provider key={id} value={{ id }}>
38
+ <View
39
+ style={[
40
+ StyleSheet.absoluteFillObject,
41
+ styles.container,
42
+ {
43
+ width,
44
+ height,
45
+ },
46
+ ]}
47
+ >
48
+ <MemoizedContent id={id}>{content}</MemoizedContent>
49
+ </View>
50
+ </BottomSheetContext.Provider>
51
+ ))}
52
+ </>
53
+ );
54
+ }
55
+
56
+ const MemoizedContent = React.memo(
57
+ ({ children }: PropsWithChildren<{ id: string }>) => <>{children}</>,
58
+ (prevProps, nextProps) => {
59
+ return prevProps.id === nextProps.id;
60
+ }
61
+ );
62
+
63
+ export const BottomSheetHost = React.memo(BottomSheetHostComp);
64
+
65
+ const styles = StyleSheet.create({
66
+ container: {
67
+ zIndex: 100_000_000,
68
+ pointerEvents: 'box-none',
69
+ },
70
+ });
@@ -0,0 +1,89 @@
1
+ import BottomSheetOriginal, {
2
+ BottomSheetBackdrop,
3
+ useBottomSheetSpringConfigs,
4
+ type BottomSheetProps,
5
+ } from '@gorhom/bottom-sheet';
6
+ import { type BottomSheetMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
7
+ import React from 'react';
8
+
9
+ import {
10
+ useBottomSheetStore,
11
+ type BottomSheetStatus,
12
+ } from './bottomSheet.store';
13
+ import { useBottomSheetState } from './useBottomSheetState';
14
+
15
+ export interface BottomSheetRef extends BottomSheetMethods {}
16
+
17
+ interface BottomSheetManagedProps extends BottomSheetProps {}
18
+
19
+ export const BottomSheetManaged = React.forwardRef<
20
+ BottomSheetRef,
21
+ BottomSheetManagedProps
22
+ >(
23
+ (
24
+ { children, onAnimate, onClose, enablePanDownToClose = true, ...props },
25
+ ref
26
+ ) => {
27
+ const { startClosing, finishClosing } = useBottomSheetStore.getState();
28
+ const { bottomSheetState } = useBottomSheetState();
29
+
30
+ const handleOnAnimate: BottomSheetProps['onAnimate'] = (
31
+ from: number,
32
+ to: number
33
+ ) => {
34
+ if (to === -1) {
35
+ if (
36
+ bottomSheetState.status === 'open' ||
37
+ bottomSheetState.status === 'opening'
38
+ ) {
39
+ startClosing(bottomSheetState.id);
40
+ }
41
+ }
42
+ if (from === -1 && to >= 0) {
43
+ const currentState = useBottomSheetStore.getState();
44
+ useBottomSheetStore.setState({
45
+ stack: currentState.stack.map((s) =>
46
+ s.id === bottomSheetState.id
47
+ ? { ...s, status: 'open' as BottomSheetStatus }
48
+ : s
49
+ ),
50
+ });
51
+ }
52
+ onAnimate?.(from, to);
53
+ };
54
+
55
+ const config = useBottomSheetSpringConfigs({
56
+ stiffness: 400,
57
+ damping: 80,
58
+ mass: 0.7,
59
+ });
60
+
61
+ const handleClose = () => {
62
+ onClose?.();
63
+
64
+ if (bottomSheetState.status !== 'hidden') {
65
+ finishClosing(bottomSheetState.id);
66
+ }
67
+ };
68
+
69
+ return (
70
+ <BottomSheetOriginal
71
+ animationConfigs={config}
72
+ ref={ref}
73
+ {...props}
74
+ onClose={handleClose}
75
+ onAnimate={handleOnAnimate}
76
+ enablePanDownToClose={enablePanDownToClose}
77
+ backdropComponent={(props) => (
78
+ <BottomSheetBackdrop
79
+ {...props}
80
+ disappearsOnIndex={-1}
81
+ appearsOnIndex={0}
82
+ />
83
+ )}
84
+ >
85
+ {children}
86
+ </BottomSheetOriginal>
87
+ );
88
+ }
89
+ );
@@ -0,0 +1,39 @@
1
+ import React, { type PropsWithChildren } from 'react';
2
+
3
+ const Context = React.createContext<{ groupId: string } | null>(null);
4
+
5
+ interface Props extends PropsWithChildren {
6
+ id: string;
7
+ }
8
+
9
+ function BottomSheetManagerProviderComp({ id, children }: Props) {
10
+ return (
11
+ <Context.Provider key={id} value={{ groupId: id }}>
12
+ {children}
13
+ </Context.Provider>
14
+ );
15
+ }
16
+
17
+ export const BottomSheetManagerProvider = React.memo(
18
+ BottomSheetManagerProviderComp,
19
+ );
20
+
21
+ export const useBottomSheetManagerContext = () => {
22
+ const context = React.useContext(Context);
23
+
24
+ if (!context) {
25
+ throw new Error(
26
+ 'useBottomSheetManagerContext must be used within a BottomSheetManagerProvider',
27
+ );
28
+ }
29
+ return context;
30
+ };
31
+
32
+ export const useMaybeBottomSheetManagerContext = () => {
33
+ const context = React.useContext(Context);
34
+
35
+ if (!context) {
36
+ return null;
37
+ }
38
+ return context;
39
+ };
@@ -0,0 +1,107 @@
1
+ import { type ReactNode } from 'react';
2
+ import { subscribeWithSelector } from 'zustand/middleware';
3
+ import { createWithEqualityFn as create } from 'zustand/traditional';
4
+
5
+ export type BottomSheetStatus = 'opening' | 'open' | 'closing' | 'hidden';
6
+ export type OpenMode = 'push' | 'switch' | 'replace';
7
+
8
+ export interface BottomSheetState {
9
+ groupId: string;
10
+ id: string;
11
+ content: ReactNode;
12
+ status: BottomSheetStatus;
13
+ }
14
+
15
+ type TriggerState = Omit<BottomSheetState, 'status'>;
16
+
17
+ interface BottomSheetStoreState {
18
+ stack: BottomSheetState[];
19
+ }
20
+
21
+ interface BottomSheetStoreActions {
22
+ push(sheet: TriggerState): void;
23
+ switch(sheet: TriggerState): void;
24
+ replace(sheet: TriggerState): void;
25
+ startClosing(id: string): void;
26
+ finishClosing(id: string): void;
27
+ clearAll(): void;
28
+ }
29
+
30
+ export const useBottomSheetStore = create(
31
+ subscribeWithSelector<BottomSheetStoreState & BottomSheetStoreActions>(
32
+ (set) => ({
33
+ stack: [],
34
+
35
+ push: (sheet) =>
36
+ set((state) => ({
37
+ stack: [...state.stack, { ...sheet, status: 'opening' }],
38
+ })),
39
+ switch: (sheet) =>
40
+ set((state) => {
41
+ const stack = [...state.stack];
42
+
43
+ if (stack.length) {
44
+ const topIndex = stack.length - 1;
45
+ if (stack[topIndex]) {
46
+ stack[topIndex] = { ...stack[topIndex], status: 'hidden' };
47
+ }
48
+ }
49
+
50
+ stack.push({ ...sheet, status: 'opening' });
51
+
52
+ return { stack };
53
+ }),
54
+
55
+ replace: (sheet) =>
56
+ set((state) => {
57
+ const stack = [...state.stack];
58
+ const prevTop = stack.pop();
59
+
60
+ if (prevTop) {
61
+ stack.push({ ...prevTop, status: 'closing' });
62
+ }
63
+ stack.push({ ...sheet, status: 'opening' });
64
+
65
+ return { stack };
66
+ }),
67
+ startClosing: (id) =>
68
+ set((state) => {
69
+ const stack = [...state.stack];
70
+ const index = stack.findIndex((s) => s.id === id);
71
+ if (index === -1) {
72
+ return { stack };
73
+ }
74
+
75
+ const closing = stack[index];
76
+ if (closing?.status === 'hidden') {
77
+ return { stack };
78
+ }
79
+
80
+ if (closing) {
81
+ stack[index] = { ...closing, status: 'closing' };
82
+ }
83
+
84
+ const below = stack[index - 1];
85
+ if (below && below.status === 'hidden') {
86
+ stack[index - 1] = { ...below, status: 'opening' };
87
+ }
88
+
89
+ return { stack };
90
+ }),
91
+
92
+ finishClosing: (id) =>
93
+ set((state) => {
94
+ const stack = state.stack.filter((s) => s.id !== id);
95
+
96
+ const topIndex = stack.length - 1;
97
+ if (topIndex >= 0 && stack[topIndex]?.status === 'hidden') {
98
+ stack[topIndex] = { ...stack[topIndex], status: 'opening' };
99
+ }
100
+
101
+ return { stack };
102
+ }),
103
+
104
+ clearAll: () => set(() => ({ stack: [] })),
105
+ })
106
+ )
107
+ );