@impulse-ui-native/modal 0.0.0-stage → 7.0.1

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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -2
  3. package/dist/components/close.d.mts +11 -0
  4. package/dist/components/close.js +49 -0
  5. package/dist/components/close.js.map +1 -0
  6. package/dist/components/content.d.mts +11 -0
  7. package/dist/components/content.js +29 -0
  8. package/dist/components/content.js.map +1 -0
  9. package/dist/components/description.d.mts +11 -0
  10. package/dist/components/description.js +20 -0
  11. package/dist/components/description.js.map +1 -0
  12. package/dist/components/footer.d.mts +11 -0
  13. package/dist/components/footer.js +33 -0
  14. package/dist/components/footer.js.map +1 -0
  15. package/dist/components/header.d.mts +11 -0
  16. package/dist/components/header.js +32 -0
  17. package/dist/components/header.js.map +1 -0
  18. package/dist/components/index.d.mts +22 -0
  19. package/dist/components/index.js +23 -0
  20. package/dist/components/index.js.map +1 -0
  21. package/dist/components/modal.d.mts +11 -0
  22. package/dist/components/modal.js +56 -0
  23. package/dist/components/modal.js.map +1 -0
  24. package/dist/components/provider.d.mts +11 -0
  25. package/dist/components/provider.js +17 -0
  26. package/dist/components/provider.js.map +1 -0
  27. package/dist/components/root.d.mts +11 -0
  28. package/dist/components/root.js +96 -0
  29. package/dist/components/root.js.map +1 -0
  30. package/dist/components/surface.d.mts +11 -0
  31. package/dist/components/surface.js +37 -0
  32. package/dist/components/surface.js.map +1 -0
  33. package/dist/components/title.d.mts +11 -0
  34. package/dist/components/title.js +21 -0
  35. package/dist/components/title.js.map +1 -0
  36. package/dist/constants/modal.constants.d.mts +4 -0
  37. package/dist/constants/modal.constants.js +7 -0
  38. package/dist/constants/modal.constants.js.map +1 -0
  39. package/dist/contexts/modal.context.d.mts +10 -0
  40. package/dist/contexts/modal.context.js +8 -0
  41. package/dist/contexts/modal.context.js.map +1 -0
  42. package/dist/hooks/use-modal-context.hook.d.mts +10 -0
  43. package/dist/hooks/use-modal-context.hook.js +13 -0
  44. package/dist/hooks/use-modal-context.hook.js.map +1 -0
  45. package/dist/hooks/use-modal-lifecycle.hook.d.mts +17 -0
  46. package/dist/hooks/use-modal-lifecycle.hook.js +46 -0
  47. package/dist/hooks/use-modal-lifecycle.hook.js.map +1 -0
  48. package/dist/index.d.mts +9 -0
  49. package/dist/index.js +7 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/types/index.d.mts +6 -0
  52. package/dist/types/index.js +2 -0
  53. package/dist/types/index.js.map +1 -0
  54. package/dist/types/modal.types.d.mts +36 -0
  55. package/dist/types/modal.types.js +1 -0
  56. package/dist/types/modal.types.js.map +1 -0
  57. package/package.json +64 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ImpulseUI Native contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,144 @@
1
- # Temporary Holding Version
1
+ # @impulse-ui-native/modal
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A compact, themed native dialog with compound presentation parts and an overlay
4
+ lifecycle. Use it locally inside a Portal or register it globally with OverlayHost.
5
+
6
+ ## Installation
7
+
8
+ ```sh
9
+ pnpm add @impulse-ui-native/modal react-native-reanimated react-native-safe-area-context react-native-worklets
10
+ ```
11
+
12
+ Complete the native setup for Reanimated and Worklets, and wrap the application
13
+ in ThemeProvider and SafeAreaProvider. Local portal usage additionally requires
14
+ @impulse-ui-native/portal with PortalProvider and PortalsHost. Global usage requires
15
+ @impulse-ui-native/overlay with OverlayProvider and OverlayHost. The toolkit exports Modal.
16
+
17
+ ## Local composition
18
+
19
+ Keep Portal mounted and control open from the nearby trigger:
20
+
21
+ ```tsx
22
+ import { Modal } from "@impulse-ui-native/modal";
23
+ import { Portal } from "@impulse-ui-native/portal";
24
+ import { Button } from "@impulse-ui-native/primitives";
25
+
26
+ <>
27
+ <Button onPress={() => setOpen(true)}>Project details</Button>
28
+ <Portal>
29
+ <Modal
30
+ id="project-details"
31
+ open={open}
32
+ title="Project details"
33
+ onClose={() => setOpen(false)}
34
+ footer={<Button onPress={() => setOpen(false)}>Done</Button>}
35
+ >
36
+ <Modal.Description>Review the project details.</Modal.Description>
37
+ </Modal>
38
+ </Portal>
39
+ </>;
40
+ ```
41
+
42
+ Modal composes Provider, Root, Header, Close, Content, and Footer, with an X close button in the
43
+ header by default, using the neutral text.tertiary color to match the Popover close icon. Set hideClose to omit it. A truthy header replaces the
44
+ generated title beside the close button; a truthy footer renders the footer. Root supplies the
45
+ centered surface, safe-area padding, backdrop, Android back dismissal, and animations.
46
+ Provider owns the lifecycle and shares state with Root and Close. Root renders the container. Neither installs a Portal; put Provider inside the local Portal so its context reaches the dialog without bridging.
47
+
48
+ ## Compound composition
49
+
50
+ ```tsx
51
+ <Portal>
52
+ <Modal.Provider
53
+ id="project-details"
54
+ open={open}
55
+ onClose={() => setOpen(false)}
56
+ >
57
+ <Modal.Root size="medium">
58
+ <Modal.Header>
59
+ <Modal.Title>Project details</Modal.Title>
60
+ <Modal.Close marginLeft="auto">
61
+ <Icon icon={XIcon} size="small" color={colors.text.tertiary} />
62
+ </Modal.Close>
63
+ </Modal.Header>
64
+ <Modal.Content>
65
+ <Modal.Description>Review the project details.</Modal.Description>
66
+ </Modal.Content>
67
+ <Modal.Footer>
68
+ <Modal.Close>
69
+ <Typography.Label>Done</Typography.Label>
70
+ </Modal.Close>
71
+ </Modal.Footer>
72
+ </Modal.Root>
73
+ </Modal.Provider>
74
+ </Portal>
75
+ ```
76
+
77
+ Header, Content, Footer, Title, and Description render supplied children and use
78
+ independent theme styling. They accept the corresponding View or Text primitive
79
+ props. Content is a regular View with no built-in scrolling. Keep dialog content short.
80
+ Root's View props and style customize the surface.
81
+
82
+ Modal.Close renders supplied children and dismisses through Provider's context.
83
+ It accepts Pressable props, including disabled and loading, and preserves the
84
+ consumer onPress callback. Closing or closed dialogs block Close interaction.
85
+ It does not generate an icon or impose minimum dimensions.
86
+
87
+ Import Icon from @impulse-ui-native/icon, XIcon from @impulse-ui-native/icon/icons/x,
88
+ and Typography from @impulse-ui-native/primitives. Use useColors from
89
+ @impulse-ui-native/theme to obtain colors.
90
+
91
+ Custom composable actions can read close, status, and interactive through
92
+ useModalContext inside Modal.Provider. No children callback is needed.
93
+
94
+ ## Global OverlayHost usage
95
+
96
+ Register Modal directly with the store provided to OverlayProvider:
97
+
98
+ ```tsx
99
+ const detailsModal = store.register({
100
+ id: "project-details",
101
+ unique: true,
102
+ Component: Modal,
103
+ Content: ProjectDetails,
104
+ title: "Project details",
105
+ onClose: (id) => store.close(id),
106
+ });
107
+
108
+ detailsModal.open();
109
+ detailsModal.close();
110
+ ```
111
+
112
+ OverlayHost supplies id, open, layer, title, and lifecycle callbacks. It removes
113
+ the entry after onCloseFinished. Do not wrap each registered modal in another
114
+ Portal; mount OverlayHost at the application overlay level.
115
+
116
+ ## Lifecycle
117
+
118
+ Provider accepts lifecycle props and layer from OverlayComponentProps. The
119
+ convenience Modal accepts the full contract and uses title to generate its header. id is required, open defaults to
120
+ false, and layer defaults to zero.
121
+
122
+ Opening calls onOpen(id), then onOpenFinished(id) after entry. Header X presses, backdrop presses,
123
+ Android back presses, and changing open to false begin dismissal and call
124
+ onClose(id). The modal stays mounted for the exit animation, then calls
125
+ onCloseFinished(id). For local usage, update open in onClose and keep Provider and Root mounted
126
+ through the exit. Reopening during exit cancels the stale closing completion.
127
+
128
+ The backdrop accepts presses without pressed-state visual feedback. The fade
129
+ and scale animations use Reanimated. Animations are canceled when Provider unmounts.
130
+
131
+ ## Tokens and sizes
132
+
133
+ Maximum widths are 320, 440, and 640 for small, medium, and large; medium is the
134
+ default. Available width accounts for safe-area insets and themed viewport padding.
135
+ Root uses the elevated surface, subtle border, 16-point radius, and 16-point inner
136
+ padding. Header is horizontal; Footer aligns trailing actions and wraps.
137
+
138
+ Customize through primitive style props, native style, or ThemeProvider's
139
+ components.modal tokens. Overlay tokens control backdrop color and opacity,
140
+ base z-index, viewport padding, and the closed animation scale. Content does not
141
+ scroll, and keyboard avoidance is not built in.
142
+
143
+ Provider uses the shared useOverlayLifecycle hook from the overlay package. Observe
144
+ local status with onStatusChange(id, status), or hosted status with useOverlayStatus(id).
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ModalCloseProps } from '../types/modal.types.mjs';
4
+ import 'react-native-reanimated';
5
+ import '@impulse-ui-native/overlay';
6
+ import '@impulse-ui-native/primitives';
7
+ import '@impulse-ui-native/theme';
8
+
9
+ declare const ModalClose: react.MemoExoticComponent<({ disabled, loading, style, onPress, ...props }: ModalCloseProps) => react_jsx_runtime.JSX.Element>;
10
+
11
+ export { ModalClose };
@@ -0,0 +1,49 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { memo, useCallback } from "react";
3
+ import { StyleSheet } from "react-native";
4
+ import { useEventCallback } from "@impulse-ui-native/core";
5
+ import { Pressable } from "@impulse-ui-native/primitives";
6
+ import { useModalContext } from "../hooks/use-modal-context.hook";
7
+ const ModalClose = memo(function ModalClose2({
8
+ disabled,
9
+ loading,
10
+ style,
11
+ onPress,
12
+ ...props
13
+ }) {
14
+ const { close, interactive } = useModalContext();
15
+ const blocked = disabled === true || loading === true || !interactive;
16
+ const closeStyle = useCallback(
17
+ (state) => [
18
+ styles.close,
19
+ typeof style === "function" ? style(state) : style
20
+ ],
21
+ [styles.close, style]
22
+ );
23
+ const handlePress = useEventCallback((event) => {
24
+ if (blocked) {
25
+ return;
26
+ }
27
+ close();
28
+ onPress?.(event);
29
+ });
30
+ return /* @__PURE__ */ jsx(
31
+ Pressable,
32
+ {
33
+ ...props,
34
+ disabled: blocked,
35
+ style: closeStyle,
36
+ onPress: handlePress
37
+ }
38
+ );
39
+ });
40
+ const styles = StyleSheet.create({
41
+ close: {
42
+ alignItems: "center",
43
+ justifyContent: "center"
44
+ }
45
+ });
46
+ export {
47
+ ModalClose
48
+ };
49
+ //# sourceMappingURL=close.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/close.tsx"],"sourcesContent":["import type {\n GestureResponderEvent,\n PressableStateCallbackType,\n} from \"react-native\";\nimport { memo, useCallback } from \"react\";\nimport { StyleSheet } from \"react-native\";\n\nimport { useEventCallback } from \"@impulse-ui-native/core\";\nimport { Pressable } from \"@impulse-ui-native/primitives\";\n\nimport type { ModalCloseProps } from \"../types\";\nimport { useModalContext } from \"../hooks/use-modal-context.hook\";\n\nexport const ModalClose = memo(function ModalClose({\n disabled,\n loading,\n style,\n onPress,\n ...props\n}: ModalCloseProps) {\n const { close, interactive } = useModalContext();\n const blocked = disabled === true || loading === true || !interactive;\n\n const closeStyle = useCallback(\n (state: PressableStateCallbackType) => [\n styles.close,\n typeof style === \"function\" ? style(state) : style,\n ],\n [styles.close, style],\n );\n\n const handlePress = useEventCallback((event: GestureResponderEvent) => {\n if (blocked) {\n return;\n }\n\n close();\n onPress?.(event);\n });\n\n return (\n <Pressable\n {...props}\n disabled={blocked}\n style={closeStyle}\n onPress={handlePress}\n />\n );\n});\n\nconst styles = StyleSheet.create({\n close: {\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n});\n"],"mappings":"AAyCI;AArCJ,SAAS,MAAM,mBAAmB;AAClC,SAAS,kBAAkB;AAE3B,SAAS,wBAAwB;AACjC,SAAS,iBAAiB;AAG1B,SAAS,uBAAuB;AAEzB,MAAM,aAAa,KAAK,SAASA,YAAW;AAAA,EACjD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,QAAM,EAAE,OAAO,YAAY,IAAI,gBAAgB;AAC/C,QAAM,UAAU,aAAa,QAAQ,YAAY,QAAQ,CAAC;AAE1D,QAAM,aAAa;AAAA,IACjB,CAAC,UAAsC;AAAA,MACrC,OAAO;AAAA,MACP,OAAO,UAAU,aAAa,MAAM,KAAK,IAAI;AAAA,IAC/C;AAAA,IACA,CAAC,OAAO,OAAO,KAAK;AAAA,EACtB;AAEA,QAAM,cAAc,iBAAiB,CAAC,UAAiC;AACrE,QAAI,SAAS;AACX;AAAA,IACF;AAEA,UAAM;AACN,cAAU,KAAK;AAAA,EACjB,CAAC;AAED,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,UAAU;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA;AAAA,EACX;AAEJ,CAAC;AAED,MAAM,SAAS,WAAW,OAAO;AAAA,EAC/B,OAAO;AAAA,IACL,YAAY;AAAA,IACZ,gBAAgB;AAAA,EAClB;AACF,CAAC;","names":["ModalClose"]}
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ModalContentProps } from '../types/modal.types.mjs';
4
+ import 'react-native-reanimated';
5
+ import '@impulse-ui-native/overlay';
6
+ import '@impulse-ui-native/primitives';
7
+ import '@impulse-ui-native/theme';
8
+
9
+ declare const ModalContent: react.MemoExoticComponent<({ style, ...props }: ModalContentProps) => react_jsx_runtime.JSX.Element>;
10
+
11
+ export { ModalContent };
@@ -0,0 +1,29 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { memo, useMemo } from "react";
3
+ import { StyleSheet } from "react-native";
4
+ import { View } from "@impulse-ui-native/primitives";
5
+ import { useStyleProps, useThemedStyles } from "@impulse-ui-native/theme";
6
+ const ModalContent = memo(function ModalContent2({
7
+ style,
8
+ ...props
9
+ }) {
10
+ const extractedStyleProps = useStyleProps(props);
11
+ const styles = useThemedStyles(themedStyles);
12
+ const partStyle = useMemo(
13
+ () => [styles.part, extractedStyleProps, style],
14
+ [styles.part, extractedStyleProps, style]
15
+ );
16
+ return /* @__PURE__ */ jsx(View, { ...props, style: partStyle });
17
+ });
18
+ function themedStyles(theme) {
19
+ const tokens = theme.components.modal;
20
+ return StyleSheet.create({
21
+ part: {
22
+ gap: tokens.gap
23
+ }
24
+ });
25
+ }
26
+ export {
27
+ ModalContent
28
+ };
29
+ //# sourceMappingURL=content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/content.tsx"],"sourcesContent":["import { memo, useMemo } from \"react\";\nimport { StyleSheet } from \"react-native\";\n\nimport type { AppTheme } from \"@impulse-ui-native/theme\";\nimport { View } from \"@impulse-ui-native/primitives\";\nimport { useStyleProps, useThemedStyles } from \"@impulse-ui-native/theme\";\n\nimport type { ModalContentProps } from \"../types\";\n\nexport const ModalContent = memo(function ModalContent({\n style,\n ...props\n}: ModalContentProps) {\n const extractedStyleProps = useStyleProps(props);\n const styles = useThemedStyles(themedStyles);\n\n const partStyle = useMemo(\n () => [styles.part, extractedStyleProps, style],\n [styles.part, extractedStyleProps, style],\n );\n\n return <View {...props} style={partStyle} />;\n});\n\nfunction themedStyles(theme: AppTheme) {\n const tokens = theme.components.modal;\n\n return StyleSheet.create({\n part: {\n gap: tokens.gap,\n },\n });\n}\n"],"mappings":"AAqBS;AArBT,SAAS,MAAM,eAAe;AAC9B,SAAS,kBAAkB;AAG3B,SAAS,YAAY;AACrB,SAAS,eAAe,uBAAuB;AAIxC,MAAM,eAAe,KAAK,SAASA,cAAa;AAAA,EACrD;AAAA,EACA,GAAG;AACL,GAAsB;AACpB,QAAM,sBAAsB,cAAc,KAAK;AAC/C,QAAM,SAAS,gBAAgB,YAAY;AAE3C,QAAM,YAAY;AAAA,IAChB,MAAM,CAAC,OAAO,MAAM,qBAAqB,KAAK;AAAA,IAC9C,CAAC,OAAO,MAAM,qBAAqB,KAAK;AAAA,EAC1C;AAEA,SAAO,oBAAC,QAAM,GAAG,OAAO,OAAO,WAAW;AAC5C,CAAC;AAED,SAAS,aAAa,OAAiB;AACrC,QAAM,SAAS,MAAM,WAAW;AAEhC,SAAO,WAAW,OAAO;AAAA,IACvB,MAAM;AAAA,MACJ,KAAK,OAAO;AAAA,IACd;AAAA,EACF,CAAC;AACH;","names":["ModalContent"]}
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ModalDescriptionProps } from '../types/modal.types.mjs';
4
+ import 'react-native-reanimated';
5
+ import '@impulse-ui-native/overlay';
6
+ import '@impulse-ui-native/primitives';
7
+ import '@impulse-ui-native/theme';
8
+
9
+ declare const ModalDescription: react.MemoExoticComponent<(props: ModalDescriptionProps) => react_jsx_runtime.JSX.Element>;
10
+
11
+ export { ModalDescription };
@@ -0,0 +1,20 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { memo } from "react";
3
+ import { Typography } from "@impulse-ui-native/primitives";
4
+ import { useComponentsTokens } from "@impulse-ui-native/theme";
5
+ const ModalDescription = memo(function ModalDescription2(props) {
6
+ const tokens = useComponentsTokens().modal;
7
+ return /* @__PURE__ */ jsx(
8
+ Typography.BodySmall,
9
+ {
10
+ color: tokens.descriptionColor,
11
+ fontSize: tokens.descriptionFontSize,
12
+ lineHeight: tokens.descriptionLineHeight,
13
+ ...props
14
+ }
15
+ );
16
+ });
17
+ export {
18
+ ModalDescription
19
+ };
20
+ //# sourceMappingURL=description.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/description.tsx"],"sourcesContent":["import { memo } from \"react\";\n\nimport { Typography } from \"@impulse-ui-native/primitives\";\nimport { useComponentsTokens } from \"@impulse-ui-native/theme\";\n\nimport type { ModalDescriptionProps } from \"../types\";\n\nexport const ModalDescription = memo(function ModalDescription(\n props: ModalDescriptionProps,\n) {\n const tokens = useComponentsTokens().modal;\n\n return (\n <Typography.BodySmall\n color={tokens.descriptionColor}\n fontSize={tokens.descriptionFontSize}\n lineHeight={tokens.descriptionLineHeight}\n {...props}\n />\n );\n});\n"],"mappings":"AAaI;AAbJ,SAAS,YAAY;AAErB,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;AAI7B,MAAM,mBAAmB,KAAK,SAASA,kBAC5C,OACA;AACA,QAAM,SAAS,oBAAoB,EAAE;AAErC,SACE;AAAA,IAAC,WAAW;AAAA,IAAX;AAAA,MACC,OAAO,OAAO;AAAA,MACd,UAAU,OAAO;AAAA,MACjB,YAAY,OAAO;AAAA,MAClB,GAAG;AAAA;AAAA,EACN;AAEJ,CAAC;","names":["ModalDescription"]}
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ModalFooterProps } from '../types/modal.types.mjs';
4
+ import 'react-native-reanimated';
5
+ import '@impulse-ui-native/overlay';
6
+ import '@impulse-ui-native/primitives';
7
+ import '@impulse-ui-native/theme';
8
+
9
+ declare const ModalFooter: react.MemoExoticComponent<({ style, ...props }: ModalFooterProps) => react_jsx_runtime.JSX.Element>;
10
+
11
+ export { ModalFooter };
@@ -0,0 +1,33 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { memo, useMemo } from "react";
3
+ import { StyleSheet } from "react-native";
4
+ import { View } from "@impulse-ui-native/primitives";
5
+ import { useStyleProps, useThemedStyles } from "@impulse-ui-native/theme";
6
+ const ModalFooter = memo(function ModalFooter2({
7
+ style,
8
+ ...props
9
+ }) {
10
+ const extractedStyleProps = useStyleProps(props);
11
+ const styles = useThemedStyles(themedStyles);
12
+ const partStyle = useMemo(
13
+ () => [styles.part, extractedStyleProps, style],
14
+ [styles.part, extractedStyleProps, style]
15
+ );
16
+ return /* @__PURE__ */ jsx(View, { ...props, style: partStyle });
17
+ });
18
+ function themedStyles(theme) {
19
+ const tokens = theme.components.modal;
20
+ return StyleSheet.create({
21
+ part: {
22
+ flexDirection: "row",
23
+ alignItems: "center",
24
+ justifyContent: "flex-end",
25
+ flexWrap: "wrap",
26
+ gap: tokens.footerGap
27
+ }
28
+ });
29
+ }
30
+ export {
31
+ ModalFooter
32
+ };
33
+ //# sourceMappingURL=footer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/footer.tsx"],"sourcesContent":["import { memo, useMemo } from \"react\";\nimport { StyleSheet } from \"react-native\";\n\nimport type { AppTheme } from \"@impulse-ui-native/theme\";\nimport { View } from \"@impulse-ui-native/primitives\";\nimport { useStyleProps, useThemedStyles } from \"@impulse-ui-native/theme\";\n\nimport type { ModalFooterProps } from \"../types\";\n\nexport const ModalFooter = memo(function ModalFooter({\n style,\n ...props\n}: ModalFooterProps) {\n const extractedStyleProps = useStyleProps(props);\n const styles = useThemedStyles(themedStyles);\n\n const partStyle = useMemo(\n () => [styles.part, extractedStyleProps, style],\n [styles.part, extractedStyleProps, style],\n );\n\n return <View {...props} style={partStyle} />;\n});\n\nfunction themedStyles(theme: AppTheme) {\n const tokens = theme.components.modal;\n\n return StyleSheet.create({\n part: {\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"flex-end\",\n flexWrap: \"wrap\",\n gap: tokens.footerGap,\n },\n });\n}\n"],"mappings":"AAqBS;AArBT,SAAS,MAAM,eAAe;AAC9B,SAAS,kBAAkB;AAG3B,SAAS,YAAY;AACrB,SAAS,eAAe,uBAAuB;AAIxC,MAAM,cAAc,KAAK,SAASA,aAAY;AAAA,EACnD;AAAA,EACA,GAAG;AACL,GAAqB;AACnB,QAAM,sBAAsB,cAAc,KAAK;AAC/C,QAAM,SAAS,gBAAgB,YAAY;AAE3C,QAAM,YAAY;AAAA,IAChB,MAAM,CAAC,OAAO,MAAM,qBAAqB,KAAK;AAAA,IAC9C,CAAC,OAAO,MAAM,qBAAqB,KAAK;AAAA,EAC1C;AAEA,SAAO,oBAAC,QAAM,GAAG,OAAO,OAAO,WAAW;AAC5C,CAAC;AAED,SAAS,aAAa,OAAiB;AACrC,QAAM,SAAS,MAAM,WAAW;AAEhC,SAAO,WAAW,OAAO;AAAA,IACvB,MAAM;AAAA,MACJ,eAAe;AAAA,MACf,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,UAAU;AAAA,MACV,KAAK,OAAO;AAAA,IACd;AAAA,EACF,CAAC;AACH;","names":["ModalFooter"]}
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ModalHeaderProps } from '../types/modal.types.mjs';
4
+ import 'react-native-reanimated';
5
+ import '@impulse-ui-native/overlay';
6
+ import '@impulse-ui-native/primitives';
7
+ import '@impulse-ui-native/theme';
8
+
9
+ declare const ModalHeader: react.MemoExoticComponent<({ style, ...props }: ModalHeaderProps) => react_jsx_runtime.JSX.Element>;
10
+
11
+ export { ModalHeader };
@@ -0,0 +1,32 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { memo, useMemo } from "react";
3
+ import { StyleSheet } from "react-native";
4
+ import { View } from "@impulse-ui-native/primitives";
5
+ import { useStyleProps, useThemedStyles } from "@impulse-ui-native/theme";
6
+ const ModalHeader = memo(function ModalHeader2({
7
+ style,
8
+ ...props
9
+ }) {
10
+ const extractedStyleProps = useStyleProps(props);
11
+ const styles = useThemedStyles(themedStyles);
12
+ const partStyle = useMemo(
13
+ () => [styles.part, extractedStyleProps, style],
14
+ [styles.part, extractedStyleProps, style]
15
+ );
16
+ return /* @__PURE__ */ jsx(View, { ...props, style: partStyle });
17
+ });
18
+ function themedStyles(theme) {
19
+ const tokens = theme.components.modal;
20
+ return StyleSheet.create({
21
+ part: {
22
+ flexDirection: "row",
23
+ alignItems: "center",
24
+ justifyContent: "space-between",
25
+ gap: tokens.headerGap
26
+ }
27
+ });
28
+ }
29
+ export {
30
+ ModalHeader
31
+ };
32
+ //# sourceMappingURL=header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/header.tsx"],"sourcesContent":["import { memo, useMemo } from \"react\";\nimport { StyleSheet } from \"react-native\";\n\nimport type { AppTheme } from \"@impulse-ui-native/theme\";\nimport { View } from \"@impulse-ui-native/primitives\";\nimport { useStyleProps, useThemedStyles } from \"@impulse-ui-native/theme\";\n\nimport type { ModalHeaderProps } from \"../types\";\n\nexport const ModalHeader = memo(function ModalHeader({\n style,\n ...props\n}: ModalHeaderProps) {\n const extractedStyleProps = useStyleProps(props);\n const styles = useThemedStyles(themedStyles);\n\n const partStyle = useMemo(\n () => [styles.part, extractedStyleProps, style],\n [styles.part, extractedStyleProps, style],\n );\n\n return <View {...props} style={partStyle} />;\n});\n\nfunction themedStyles(theme: AppTheme) {\n const tokens = theme.components.modal;\n\n return StyleSheet.create({\n part: {\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: tokens.headerGap,\n },\n });\n}\n"],"mappings":"AAqBS;AArBT,SAAS,MAAM,eAAe;AAC9B,SAAS,kBAAkB;AAG3B,SAAS,YAAY;AACrB,SAAS,eAAe,uBAAuB;AAIxC,MAAM,cAAc,KAAK,SAASA,aAAY;AAAA,EACnD;AAAA,EACA,GAAG;AACL,GAAqB;AACnB,QAAM,sBAAsB,cAAc,KAAK;AAC/C,QAAM,SAAS,gBAAgB,YAAY;AAE3C,QAAM,YAAY;AAAA,IAChB,MAAM,CAAC,OAAO,MAAM,qBAAqB,KAAK;AAAA,IAC9C,CAAC,OAAO,MAAM,qBAAqB,KAAK;AAAA,EAC1C;AAEA,SAAO,oBAAC,QAAM,GAAG,OAAO,OAAO,WAAW;AAC5C,CAAC;AAED,SAAS,aAAa,OAAiB;AACrC,QAAM,SAAS,MAAM,WAAW;AAEhC,SAAO,WAAW,OAAO;AAAA,IACvB,MAAM;AAAA,MACJ,eAAe;AAAA,MACf,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,KAAK,OAAO;AAAA,IACd;AAAA,EACF,CAAC;AACH;","names":["ModalHeader"]}
@@ -0,0 +1,22 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ModalProps, ModalProviderProps, ModalRootProps, ModalHeaderProps, ModalCloseProps, ModalContentProps, ModalFooterProps, ModalTitleProps, ModalDescriptionProps } from '../types/modal.types.mjs';
4
+ import 'react-native-reanimated';
5
+ import '@impulse-ui-native/overlay';
6
+ import '@impulse-ui-native/primitives';
7
+ import '@impulse-ui-native/theme';
8
+
9
+ declare const Modal: react.NamedExoticComponent<ModalProps> & {
10
+ readonly type: ({ id, open, layer, onOpen, onOpenFinished, onStatusChange, onClose, onCloseFinished, header, title, footer, hideClose, children, ...props }: ModalProps) => react_jsx_runtime.JSX.Element;
11
+ } & {
12
+ Provider: react.MemoExoticComponent<({ children, layer, ...props }: ModalProviderProps) => react_jsx_runtime.JSX.Element>;
13
+ Root: react.MemoExoticComponent<({ children, ...props }: ModalRootProps) => react_jsx_runtime.JSX.Element | null>;
14
+ Header: react.MemoExoticComponent<({ style, ...props }: ModalHeaderProps) => react_jsx_runtime.JSX.Element>;
15
+ Close: react.MemoExoticComponent<({ disabled, loading, style, onPress, ...props }: ModalCloseProps) => react_jsx_runtime.JSX.Element>;
16
+ Content: react.MemoExoticComponent<({ style, ...props }: ModalContentProps) => react_jsx_runtime.JSX.Element>;
17
+ Footer: react.MemoExoticComponent<({ style, ...props }: ModalFooterProps) => react_jsx_runtime.JSX.Element>;
18
+ Title: react.MemoExoticComponent<(props: ModalTitleProps) => react_jsx_runtime.JSX.Element>;
19
+ Description: react.MemoExoticComponent<(props: ModalDescriptionProps) => react_jsx_runtime.JSX.Element>;
20
+ };
21
+
22
+ export { Modal };
@@ -0,0 +1,23 @@
1
+ import { ModalClose } from "./close";
2
+ import { ModalContent } from "./content";
3
+ import { ModalDescription } from "./description";
4
+ import { ModalFooter } from "./footer";
5
+ import { ModalHeader } from "./header";
6
+ import { ModalComponent } from "./modal";
7
+ import { ModalProvider } from "./provider";
8
+ import { ModalRoot } from "./root";
9
+ import { ModalTitle } from "./title";
10
+ const Modal = Object.assign(ModalComponent, {
11
+ Provider: ModalProvider,
12
+ Root: ModalRoot,
13
+ Header: ModalHeader,
14
+ Close: ModalClose,
15
+ Content: ModalContent,
16
+ Footer: ModalFooter,
17
+ Title: ModalTitle,
18
+ Description: ModalDescription
19
+ });
20
+ export {
21
+ Modal
22
+ };
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/index.ts"],"sourcesContent":["import { ModalClose } from \"./close\";\nimport { ModalContent } from \"./content\";\nimport { ModalDescription } from \"./description\";\nimport { ModalFooter } from \"./footer\";\nimport { ModalHeader } from \"./header\";\nimport { ModalComponent } from \"./modal\";\nimport { ModalProvider } from \"./provider\";\nimport { ModalRoot } from \"./root\";\nimport { ModalTitle } from \"./title\";\n\nexport const Modal = Object.assign(ModalComponent, {\n Provider: ModalProvider,\n Root: ModalRoot,\n Header: ModalHeader,\n Close: ModalClose,\n Content: ModalContent,\n Footer: ModalFooter,\n Title: ModalTitle,\n Description: ModalDescription,\n});\n"],"mappings":"AAAA,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AACjC,SAAS,mBAAmB;AAC5B,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAEpB,MAAM,QAAQ,OAAO,OAAO,gBAAgB;AAAA,EACjD,UAAU;AAAA,EACV,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,aAAa;AACf,CAAC;","names":[]}
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ModalProps } from '../types/modal.types.mjs';
4
+ import 'react-native-reanimated';
5
+ import '@impulse-ui-native/overlay';
6
+ import '@impulse-ui-native/primitives';
7
+ import '@impulse-ui-native/theme';
8
+
9
+ declare const ModalComponent: react.MemoExoticComponent<({ id, open, layer, onOpen, onOpenFinished, onStatusChange, onClose, onCloseFinished, header, title, footer, hideClose, children, ...props }: ModalProps) => react_jsx_runtime.JSX.Element>;
10
+
11
+ export { ModalComponent };
@@ -0,0 +1,56 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { memo } from "react";
3
+ import { Icon } from "@impulse-ui-native/icon";
4
+ import { XIcon } from "@impulse-ui-native/icon/icons/x";
5
+ import { useColors } from "@impulse-ui-native/theme";
6
+ import { ModalClose } from "./close";
7
+ import { ModalContent } from "./content";
8
+ import { ModalFooter } from "./footer";
9
+ import { ModalHeader } from "./header";
10
+ import { ModalProvider } from "./provider";
11
+ import { ModalRoot } from "./root";
12
+ import { ModalTitle } from "./title";
13
+ const ModalComponent = memo(function Modal({
14
+ id,
15
+ open,
16
+ layer,
17
+ onOpen,
18
+ onOpenFinished,
19
+ onStatusChange,
20
+ onClose,
21
+ onCloseFinished,
22
+ header,
23
+ title,
24
+ footer,
25
+ hideClose = false,
26
+ children,
27
+ ...props
28
+ }) {
29
+ const colors = useColors();
30
+ return /* @__PURE__ */ jsx(
31
+ ModalProvider,
32
+ {
33
+ id,
34
+ open,
35
+ layer,
36
+ onOpen,
37
+ onOpenFinished,
38
+ onStatusChange,
39
+ onClose,
40
+ onCloseFinished,
41
+ children: /* @__PURE__ */ jsxs(ModalRoot, { ...props, children: [
42
+ header || title || !hideClose ? /* @__PURE__ */ jsxs(ModalHeader, { children: [
43
+ header,
44
+ !header && title ? /* @__PURE__ */ jsx(ModalTitle, { children: title }) : null,
45
+ !hideClose ? /* @__PURE__ */ jsx(ModalClose, { marginLeft: "auto", children: /* @__PURE__ */ jsx(Icon, { icon: XIcon, size: "small", color: colors.text.tertiary }) }) : null
46
+ ] }) : null,
47
+ /* @__PURE__ */ jsx(ModalContent, { children }),
48
+ footer ? /* @__PURE__ */ jsx(ModalFooter, { children: footer }) : null
49
+ ] })
50
+ }
51
+ );
52
+ });
53
+ export {
54
+ ModalComponent
55
+ };
56
+ //# sourceMappingURL=modal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/modal.tsx"],"sourcesContent":["import { memo } from \"react\";\n\nimport { Icon } from \"@impulse-ui-native/icon\";\nimport { XIcon } from \"@impulse-ui-native/icon/icons/x\";\nimport { useColors } from \"@impulse-ui-native/theme\";\n\nimport type { ModalProps } from \"../types\";\nimport { ModalClose } from \"./close\";\nimport { ModalContent } from \"./content\";\nimport { ModalFooter } from \"./footer\";\nimport { ModalHeader } from \"./header\";\nimport { ModalProvider } from \"./provider\";\nimport { ModalRoot } from \"./root\";\nimport { ModalTitle } from \"./title\";\n\nexport const ModalComponent = memo(function Modal({\n id,\n open,\n layer,\n onOpen,\n onOpenFinished,\n onStatusChange,\n onClose,\n onCloseFinished,\n header,\n title,\n footer,\n hideClose = false,\n children,\n ...props\n}: ModalProps) {\n const colors = useColors();\n\n return (\n <ModalProvider\n id={id}\n open={open}\n layer={layer}\n onOpen={onOpen}\n onOpenFinished={onOpenFinished}\n onStatusChange={onStatusChange}\n onClose={onClose}\n onCloseFinished={onCloseFinished}\n >\n <ModalRoot {...props}>\n {header || title || !hideClose ? (\n <ModalHeader>\n {header}\n {!header && title ? <ModalTitle>{title}</ModalTitle> : null}\n {!hideClose ? (\n <ModalClose marginLeft=\"auto\">\n <Icon icon={XIcon} size=\"small\" color={colors.text.tertiary} />\n </ModalClose>\n ) : null}\n </ModalHeader>\n ) : null}\n <ModalContent>{children}</ModalContent>\n {footer ? <ModalFooter>{footer}</ModalFooter> : null}\n </ModalRoot>\n </ModalProvider>\n );\n});\n"],"mappings":"AA8CU,SAEsB,KAFtB;AA9CV,SAAS,YAAY;AAErB,SAAS,YAAY;AACrB,SAAS,aAAa;AACtB,SAAS,iBAAiB;AAG1B,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAC5B,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAEpB,MAAM,iBAAiB,KAAK,SAAS,MAAM;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,GAAG;AACL,GAAe;AACb,QAAM,SAAS,UAAU;AAEzB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MAEA,+BAAC,aAAW,GAAG,OACZ;AAAA,kBAAU,SAAS,CAAC,YACnB,qBAAC,eACE;AAAA;AAAA,UACA,CAAC,UAAU,QAAQ,oBAAC,cAAY,iBAAM,IAAgB;AAAA,UACtD,CAAC,YACA,oBAAC,cAAW,YAAW,QACrB,8BAAC,QAAK,MAAM,OAAO,MAAK,SAAQ,OAAO,OAAO,KAAK,UAAU,GAC/D,IACE;AAAA,WACN,IACE;AAAA,QACJ,oBAAC,gBAAc,UAAS;AAAA,QACvB,SAAS,oBAAC,eAAa,kBAAO,IAAiB;AAAA,SAClD;AAAA;AAAA,EACF;AAEJ,CAAC;","names":[]}
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ModalProviderProps } from '../types/modal.types.mjs';
4
+ import 'react-native-reanimated';
5
+ import '@impulse-ui-native/overlay';
6
+ import '@impulse-ui-native/primitives';
7
+ import '@impulse-ui-native/theme';
8
+
9
+ declare const ModalProvider: react.MemoExoticComponent<({ children, layer, ...props }: ModalProviderProps) => react_jsx_runtime.JSX.Element>;
10
+
11
+ export { ModalProvider };
@@ -0,0 +1,17 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { memo, useMemo } from "react";
3
+ import { ModalContext } from "../contexts/modal.context";
4
+ import { useModalLifecycle } from "../hooks/use-modal-lifecycle.hook";
5
+ const ModalProvider = memo(function ModalProvider2({
6
+ children,
7
+ layer = 0,
8
+ ...props
9
+ }) {
10
+ const lifecycle = useModalLifecycle(props);
11
+ const context = useMemo(() => ({ ...lifecycle, layer }), [lifecycle, layer]);
12
+ return /* @__PURE__ */ jsx(ModalContext.Provider, { value: context, children });
13
+ });
14
+ export {
15
+ ModalProvider
16
+ };
17
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/provider.tsx"],"sourcesContent":["import { memo, useMemo } from \"react\";\n\nimport type { ModalProviderProps } from \"../types\";\nimport { ModalContext } from \"../contexts/modal.context\";\nimport { useModalLifecycle } from \"../hooks/use-modal-lifecycle.hook\";\n\nexport const ModalProvider = memo(function ModalProvider({\n children,\n layer = 0,\n ...props\n}: ModalProviderProps) {\n const lifecycle = useModalLifecycle(props);\n\n const context = useMemo(() => ({ ...lifecycle, layer }), [lifecycle, layer]);\n\n return (\n <ModalContext.Provider value={context}>{children}</ModalContext.Provider>\n );\n});\n"],"mappings":"AAgBI;AAhBJ,SAAS,MAAM,eAAe;AAG9B,SAAS,oBAAoB;AAC7B,SAAS,yBAAyB;AAE3B,MAAM,gBAAgB,KAAK,SAASA,eAAc;AAAA,EACvD;AAAA,EACA,QAAQ;AAAA,EACR,GAAG;AACL,GAAuB;AACrB,QAAM,YAAY,kBAAkB,KAAK;AAEzC,QAAM,UAAU,QAAQ,OAAO,EAAE,GAAG,WAAW,MAAM,IAAI,CAAC,WAAW,KAAK,CAAC;AAE3E,SACE,oBAAC,aAAa,UAAb,EAAsB,OAAO,SAAU,UAAS;AAErD,CAAC;","names":["ModalProvider"]}
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ModalRootProps } from '../types/modal.types.mjs';
4
+ import 'react-native-reanimated';
5
+ import '@impulse-ui-native/overlay';
6
+ import '@impulse-ui-native/primitives';
7
+ import '@impulse-ui-native/theme';
8
+
9
+ declare const ModalRoot: react.MemoExoticComponent<({ children, ...props }: ModalRootProps) => react_jsx_runtime.JSX.Element | null>;
10
+
11
+ export { ModalRoot };
@@ -0,0 +1,96 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { memo, useEffect, useMemo } from "react";
3
+ import { BackHandler, Pressable, StyleSheet } from "react-native";
4
+ import Animated, { useAnimatedStyle } from "react-native-reanimated";
5
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
6
+ import { useComponentsTokens, useThemedStyles } from "@impulse-ui-native/theme";
7
+ import { useModalContext } from "../hooks/use-modal-context.hook";
8
+ import { ModalSurface } from "./surface";
9
+ const ModalRoot = memo(function ModalRoot2({
10
+ children,
11
+ ...props
12
+ }) {
13
+ const { mounted, interactive, progress, close, layer } = useModalContext();
14
+ const insets = useSafeAreaInsets();
15
+ const tokens = useComponentsTokens().modal;
16
+ const styles = useThemedStyles(themedStyles, { insets, layer }, [
17
+ insets,
18
+ layer
19
+ ]);
20
+ const overlayStyle = useAnimatedStyle(() => ({ opacity: progress.value }));
21
+ const surfaceStyle = useAnimatedStyle(() => ({
22
+ transform: [
23
+ { scale: tokens.closedScale + (1 - tokens.closedScale) * progress.value }
24
+ ]
25
+ }));
26
+ const containerStyle = useMemo(
27
+ () => [styles.container, overlayStyle],
28
+ [styles.container, overlayStyle]
29
+ );
30
+ const surfaceContainerStyle = useMemo(
31
+ () => [styles.surfaceContainer, surfaceStyle],
32
+ [styles.surfaceContainer, surfaceStyle]
33
+ );
34
+ useEffect(() => {
35
+ if (!mounted) {
36
+ return;
37
+ }
38
+ const subscription = BackHandler.addEventListener(
39
+ "hardwareBackPress",
40
+ () => {
41
+ close();
42
+ return true;
43
+ }
44
+ );
45
+ return () => subscription.remove();
46
+ }, [mounted, close]);
47
+ return mounted ? /* @__PURE__ */ jsxs(Animated.View, { style: containerStyle, children: [
48
+ /* @__PURE__ */ jsx(
49
+ Pressable,
50
+ {
51
+ style: styles.backdrop,
52
+ onPress: close,
53
+ disabled: !interactive
54
+ }
55
+ ),
56
+ /* @__PURE__ */ jsx(
57
+ Animated.View,
58
+ {
59
+ style: surfaceContainerStyle,
60
+ pointerEvents: interactive ? "box-none" : "none",
61
+ children: /* @__PURE__ */ jsx(ModalSurface, { ...props, children })
62
+ }
63
+ )
64
+ ] }) : null;
65
+ });
66
+ function themedStyles(theme, {
67
+ insets,
68
+ layer
69
+ }) {
70
+ const tokens = theme.components.modal;
71
+ return StyleSheet.create({
72
+ container: {
73
+ ...StyleSheet.absoluteFill,
74
+ zIndex: tokens.zIndexBase + layer,
75
+ alignItems: "center",
76
+ justifyContent: "center",
77
+ paddingTop: insets.top + tokens.viewportPadding,
78
+ paddingRight: insets.right + tokens.viewportPadding,
79
+ paddingBottom: insets.bottom + tokens.viewportPadding,
80
+ paddingLeft: insets.left + tokens.viewportPadding
81
+ },
82
+ backdrop: {
83
+ ...StyleSheet.absoluteFill,
84
+ backgroundColor: tokens.overlayColor,
85
+ opacity: tokens.overlayVisibleOpacity
86
+ },
87
+ surfaceContainer: {
88
+ width: "100%",
89
+ alignItems: "center"
90
+ }
91
+ });
92
+ }
93
+ export {
94
+ ModalRoot
95
+ };
96
+ //# sourceMappingURL=root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/root.tsx"],"sourcesContent":["import { memo, useEffect, useMemo } from \"react\";\nimport { BackHandler, Pressable, StyleSheet } from \"react-native\";\nimport Animated, { useAnimatedStyle } from \"react-native-reanimated\";\nimport { useSafeAreaInsets } from \"react-native-safe-area-context\";\n\nimport type { AppTheme } from \"@impulse-ui-native/theme\";\nimport { useComponentsTokens, useThemedStyles } from \"@impulse-ui-native/theme\";\n\nimport type { ModalRootProps } from \"../types\";\nimport { useModalContext } from \"../hooks/use-modal-context.hook\";\nimport { ModalSurface } from \"./surface\";\n\nexport const ModalRoot = memo(function ModalRoot({\n children,\n ...props\n}: ModalRootProps) {\n const { mounted, interactive, progress, close, layer } = useModalContext();\n const insets = useSafeAreaInsets();\n const tokens = useComponentsTokens().modal;\n const styles = useThemedStyles(themedStyles, { insets, layer }, [\n insets,\n layer,\n ]);\n\n const overlayStyle = useAnimatedStyle(() => ({ opacity: progress.value }));\n const surfaceStyle = useAnimatedStyle(() => ({\n transform: [\n { scale: tokens.closedScale + (1 - tokens.closedScale) * progress.value },\n ],\n }));\n\n const containerStyle = useMemo(\n () => [styles.container, overlayStyle],\n [styles.container, overlayStyle],\n );\n const surfaceContainerStyle = useMemo(\n () => [styles.surfaceContainer, surfaceStyle],\n [styles.surfaceContainer, surfaceStyle],\n );\n\n useEffect(() => {\n if (!mounted) {\n return;\n }\n\n const subscription = BackHandler.addEventListener(\n \"hardwareBackPress\",\n () => {\n close();\n\n return true;\n },\n );\n\n return () => subscription.remove();\n }, [mounted, close]);\n\n return mounted ? (\n <Animated.View style={containerStyle}>\n <Pressable\n style={styles.backdrop}\n onPress={close}\n disabled={!interactive}\n />\n <Animated.View\n style={surfaceContainerStyle}\n pointerEvents={interactive ? \"box-none\" : \"none\"}\n >\n <ModalSurface {...props}>{children}</ModalSurface>\n </Animated.View>\n </Animated.View>\n ) : null;\n});\n\nfunction themedStyles(\n theme: AppTheme,\n {\n insets,\n layer,\n }: {\n insets: { top: number; right: number; bottom: number; left: number };\n layer: number;\n },\n) {\n const tokens = theme.components.modal;\n\n return StyleSheet.create({\n container: {\n ...StyleSheet.absoluteFill,\n zIndex: tokens.zIndexBase + layer,\n alignItems: \"center\",\n justifyContent: \"center\",\n paddingTop: insets.top + tokens.viewportPadding,\n paddingRight: insets.right + tokens.viewportPadding,\n paddingBottom: insets.bottom + tokens.viewportPadding,\n paddingLeft: insets.left + tokens.viewportPadding,\n },\n backdrop: {\n ...StyleSheet.absoluteFill,\n backgroundColor: tokens.overlayColor,\n opacity: tokens.overlayVisibleOpacity,\n },\n surfaceContainer: {\n width: \"100%\",\n alignItems: \"center\",\n },\n });\n}\n"],"mappings":"AA0DI,SACE,KADF;AA1DJ,SAAS,MAAM,WAAW,eAAe;AACzC,SAAS,aAAa,WAAW,kBAAkB;AACnD,OAAO,YAAY,wBAAwB;AAC3C,SAAS,yBAAyB;AAGlC,SAAS,qBAAqB,uBAAuB;AAGrD,SAAS,uBAAuB;AAChC,SAAS,oBAAoB;AAEtB,MAAM,YAAY,KAAK,SAASA,WAAU;AAAA,EAC/C;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,QAAM,EAAE,SAAS,aAAa,UAAU,OAAO,MAAM,IAAI,gBAAgB;AACzE,QAAM,SAAS,kBAAkB;AACjC,QAAM,SAAS,oBAAoB,EAAE;AACrC,QAAM,SAAS,gBAAgB,cAAc,EAAE,QAAQ,MAAM,GAAG;AAAA,IAC9D;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,eAAe,iBAAiB,OAAO,EAAE,SAAS,SAAS,MAAM,EAAE;AACzE,QAAM,eAAe,iBAAiB,OAAO;AAAA,IAC3C,WAAW;AAAA,MACT,EAAE,OAAO,OAAO,eAAe,IAAI,OAAO,eAAe,SAAS,MAAM;AAAA,IAC1E;AAAA,EACF,EAAE;AAEF,QAAM,iBAAiB;AAAA,IACrB,MAAM,CAAC,OAAO,WAAW,YAAY;AAAA,IACrC,CAAC,OAAO,WAAW,YAAY;AAAA,EACjC;AACA,QAAM,wBAAwB;AAAA,IAC5B,MAAM,CAAC,OAAO,kBAAkB,YAAY;AAAA,IAC5C,CAAC,OAAO,kBAAkB,YAAY;AAAA,EACxC;AAEA,YAAU,MAAM;AACd,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AAEA,UAAM,eAAe,YAAY;AAAA,MAC/B;AAAA,MACA,MAAM;AACJ,cAAM;AAEN,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO,MAAM,aAAa,OAAO;AAAA,EACnC,GAAG,CAAC,SAAS,KAAK,CAAC;AAEnB,SAAO,UACL,qBAAC,SAAS,MAAT,EAAc,OAAO,gBACpB;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,OAAO;AAAA,QACd,SAAS;AAAA,QACT,UAAU,CAAC;AAAA;AAAA,IACb;AAAA,IACA;AAAA,MAAC,SAAS;AAAA,MAAT;AAAA,QACC,OAAO;AAAA,QACP,eAAe,cAAc,aAAa;AAAA,QAE1C,8BAAC,gBAAc,GAAG,OAAQ,UAAS;AAAA;AAAA,IACrC;AAAA,KACF,IACE;AACN,CAAC;AAED,SAAS,aACP,OACA;AAAA,EACE;AAAA,EACA;AACF,GAIA;AACA,QAAM,SAAS,MAAM,WAAW;AAEhC,SAAO,WAAW,OAAO;AAAA,IACvB,WAAW;AAAA,MACT,GAAG,WAAW;AAAA,MACd,QAAQ,OAAO,aAAa;AAAA,MAC5B,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,YAAY,OAAO,MAAM,OAAO;AAAA,MAChC,cAAc,OAAO,QAAQ,OAAO;AAAA,MACpC,eAAe,OAAO,SAAS,OAAO;AAAA,MACtC,aAAa,OAAO,OAAO,OAAO;AAAA,IACpC;AAAA,IACA,UAAU;AAAA,MACR,GAAG,WAAW;AAAA,MACd,iBAAiB,OAAO;AAAA,MACxB,SAAS,OAAO;AAAA,IAClB;AAAA,IACA,kBAAkB;AAAA,MAChB,OAAO;AAAA,MACP,YAAY;AAAA,IACd;AAAA,EACF,CAAC;AACH;","names":["ModalRoot"]}
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ModalSurfaceProps } from '../types/modal.types.mjs';
4
+ import 'react-native-reanimated';
5
+ import '@impulse-ui-native/overlay';
6
+ import '@impulse-ui-native/primitives';
7
+ import '@impulse-ui-native/theme';
8
+
9
+ declare const ModalSurface: react.MemoExoticComponent<({ size, style, ...props }: ModalSurfaceProps) => react_jsx_runtime.JSX.Element>;
10
+
11
+ export { ModalSurface };
@@ -0,0 +1,37 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { memo, useMemo } from "react";
3
+ import { StyleSheet } from "react-native";
4
+ import { View } from "@impulse-ui-native/primitives";
5
+ import { useStyleProps, useThemedStyles } from "@impulse-ui-native/theme";
6
+ const ModalSurface = memo(function ModalSurface2({
7
+ size = "medium",
8
+ style,
9
+ ...props
10
+ }) {
11
+ const extractedStyleProps = useStyleProps(props);
12
+ const styles = useThemedStyles(themedStyles, { size }, [size]);
13
+ const rootStyle = useMemo(
14
+ () => [styles.root, extractedStyleProps, style],
15
+ [styles.root, extractedStyleProps, style]
16
+ );
17
+ return /* @__PURE__ */ jsx(View, { ...props, style: rootStyle });
18
+ });
19
+ function themedStyles(theme, { size }) {
20
+ const tokens = theme.components.modal;
21
+ return StyleSheet.create({
22
+ root: {
23
+ width: "100%",
24
+ maxWidth: tokens.sizes[size].maxWidth,
25
+ padding: tokens.padding,
26
+ gap: tokens.gap,
27
+ backgroundColor: tokens.backgroundColor,
28
+ borderColor: tokens.borderColor,
29
+ borderWidth: tokens.borderWidth,
30
+ borderRadius: tokens.borderRadius
31
+ }
32
+ });
33
+ }
34
+ export {
35
+ ModalSurface
36
+ };
37
+ //# sourceMappingURL=surface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/surface.tsx"],"sourcesContent":["import { memo, useMemo } from \"react\";\nimport { StyleSheet } from \"react-native\";\n\nimport type { AppTheme, ComponentSize } from \"@impulse-ui-native/theme\";\nimport { View } from \"@impulse-ui-native/primitives\";\nimport { useStyleProps, useThemedStyles } from \"@impulse-ui-native/theme\";\n\nimport type { ModalSurfaceProps } from \"../types\";\n\nexport const ModalSurface = memo(function ModalSurface({\n size = \"medium\",\n style,\n ...props\n}: ModalSurfaceProps) {\n const extractedStyleProps = useStyleProps(props);\n const styles = useThemedStyles(themedStyles, { size }, [size]);\n\n const rootStyle = useMemo(\n () => [styles.root, extractedStyleProps, style],\n [styles.root, extractedStyleProps, style],\n );\n\n return <View {...props} style={rootStyle} />;\n});\n\nfunction themedStyles(theme: AppTheme, { size }: { size: ComponentSize }) {\n const tokens = theme.components.modal;\n\n return StyleSheet.create({\n root: {\n width: \"100%\",\n maxWidth: tokens.sizes[size].maxWidth,\n padding: tokens.padding,\n gap: tokens.gap,\n backgroundColor: tokens.backgroundColor,\n borderColor: tokens.borderColor,\n borderWidth: tokens.borderWidth,\n borderRadius: tokens.borderRadius,\n },\n });\n}\n"],"mappings":"AAsBS;AAtBT,SAAS,MAAM,eAAe;AAC9B,SAAS,kBAAkB;AAG3B,SAAS,YAAY;AACrB,SAAS,eAAe,uBAAuB;AAIxC,MAAM,eAAe,KAAK,SAASA,cAAa;AAAA,EACrD,OAAO;AAAA,EACP;AAAA,EACA,GAAG;AACL,GAAsB;AACpB,QAAM,sBAAsB,cAAc,KAAK;AAC/C,QAAM,SAAS,gBAAgB,cAAc,EAAE,KAAK,GAAG,CAAC,IAAI,CAAC;AAE7D,QAAM,YAAY;AAAA,IAChB,MAAM,CAAC,OAAO,MAAM,qBAAqB,KAAK;AAAA,IAC9C,CAAC,OAAO,MAAM,qBAAqB,KAAK;AAAA,EAC1C;AAEA,SAAO,oBAAC,QAAM,GAAG,OAAO,OAAO,WAAW;AAC5C,CAAC;AAED,SAAS,aAAa,OAAiB,EAAE,KAAK,GAA4B;AACxE,QAAM,SAAS,MAAM,WAAW;AAEhC,SAAO,WAAW,OAAO;AAAA,IACvB,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,UAAU,OAAO,MAAM,IAAI,EAAE;AAAA,MAC7B,SAAS,OAAO;AAAA,MAChB,KAAK,OAAO;AAAA,MACZ,iBAAiB,OAAO;AAAA,MACxB,aAAa,OAAO;AAAA,MACpB,aAAa,OAAO;AAAA,MACpB,cAAc,OAAO;AAAA,IACvB;AAAA,EACF,CAAC;AACH;","names":["ModalSurface"]}
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ModalTitleProps } from '../types/modal.types.mjs';
4
+ import 'react-native-reanimated';
5
+ import '@impulse-ui-native/overlay';
6
+ import '@impulse-ui-native/primitives';
7
+ import '@impulse-ui-native/theme';
8
+
9
+ declare const ModalTitle: react.MemoExoticComponent<(props: ModalTitleProps) => react_jsx_runtime.JSX.Element>;
10
+
11
+ export { ModalTitle };
@@ -0,0 +1,21 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { memo } from "react";
3
+ import { Typography } from "@impulse-ui-native/primitives";
4
+ import { useComponentsTokens } from "@impulse-ui-native/theme";
5
+ const ModalTitle = memo(function ModalTitle2(props) {
6
+ const tokens = useComponentsTokens().modal;
7
+ return /* @__PURE__ */ jsx(
8
+ Typography.Title4,
9
+ {
10
+ color: tokens.titleColor,
11
+ fontSize: tokens.titleFontSize,
12
+ lineHeight: tokens.titleLineHeight,
13
+ flexShrink: 1,
14
+ ...props
15
+ }
16
+ );
17
+ });
18
+ export {
19
+ ModalTitle
20
+ };
21
+ //# sourceMappingURL=title.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/title.tsx"],"sourcesContent":["import { memo } from \"react\";\n\nimport { Typography } from \"@impulse-ui-native/primitives\";\nimport { useComponentsTokens } from \"@impulse-ui-native/theme\";\n\nimport type { ModalTitleProps } from \"../types\";\n\nexport const ModalTitle = memo(function ModalTitle(props: ModalTitleProps) {\n const tokens = useComponentsTokens().modal;\n\n return (\n <Typography.Title4\n color={tokens.titleColor}\n fontSize={tokens.titleFontSize}\n lineHeight={tokens.titleLineHeight}\n flexShrink={1}\n {...props}\n />\n );\n});\n"],"mappings":"AAWI;AAXJ,SAAS,YAAY;AAErB,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;AAI7B,MAAM,aAAa,KAAK,SAASA,YAAW,OAAwB;AACzE,QAAM,SAAS,oBAAoB,EAAE;AAErC,SACE;AAAA,IAAC,WAAW;AAAA,IAAX;AAAA,MACC,OAAO,OAAO;AAAA,MACd,UAAU,OAAO;AAAA,MACjB,YAAY,OAAO;AAAA,MACnB,YAAY;AAAA,MACX,GAAG;AAAA;AAAA,EACN;AAEJ,CAAC;","names":["ModalTitle"]}
@@ -0,0 +1,4 @@
1
+ declare const EnterDuration = 180;
2
+ declare const ExitDuration = 150;
3
+
4
+ export { EnterDuration, ExitDuration };
@@ -0,0 +1,7 @@
1
+ const EnterDuration = 180;
2
+ const ExitDuration = 150;
3
+ export {
4
+ EnterDuration,
5
+ ExitDuration
6
+ };
7
+ //# sourceMappingURL=modal.constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/constants/modal.constants.ts"],"sourcesContent":["export const EnterDuration = 180;\nexport const ExitDuration = 150;\n"],"mappings":"AAAO,MAAM,gBAAgB;AACtB,MAAM,eAAe;","names":[]}
@@ -0,0 +1,10 @@
1
+ import * as react from 'react';
2
+ import { ModalContextValue } from '../types/modal.types.mjs';
3
+ import 'react-native-reanimated';
4
+ import '@impulse-ui-native/overlay';
5
+ import '@impulse-ui-native/primitives';
6
+ import '@impulse-ui-native/theme';
7
+
8
+ declare const ModalContext: react.Context<ModalContextValue | undefined>;
9
+
10
+ export { ModalContext };
@@ -0,0 +1,8 @@
1
+ import { createContext } from "react";
2
+ const ModalContext = createContext(
3
+ void 0
4
+ );
5
+ export {
6
+ ModalContext
7
+ };
8
+ //# sourceMappingURL=modal.context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/contexts/modal.context.ts"],"sourcesContent":["import { createContext } from \"react\";\n\nimport type { ModalContextValue } from \"../types\";\n\nexport const ModalContext = createContext<ModalContextValue | undefined>(\n undefined,\n);\n"],"mappings":"AAAA,SAAS,qBAAqB;AAIvB,MAAM,eAAe;AAAA,EAC1B;AACF;","names":[]}
@@ -0,0 +1,10 @@
1
+ import { ModalContextValue } from '../types/modal.types.mjs';
2
+ import 'react';
3
+ import 'react-native-reanimated';
4
+ import '@impulse-ui-native/overlay';
5
+ import '@impulse-ui-native/primitives';
6
+ import '@impulse-ui-native/theme';
7
+
8
+ declare function useModalContext(): ModalContextValue;
9
+
10
+ export { useModalContext };
@@ -0,0 +1,13 @@
1
+ import { useContext } from "react";
2
+ import { ModalContext } from "../contexts/modal.context";
3
+ function useModalContext() {
4
+ const context = useContext(ModalContext);
5
+ if (!context) {
6
+ throw new Error("Modal components must be rendered inside Modal.Provider");
7
+ }
8
+ return context;
9
+ }
10
+ export {
11
+ useModalContext
12
+ };
13
+ //# sourceMappingURL=use-modal-context.hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/use-modal-context.hook.ts"],"sourcesContent":["import { useContext } from \"react\";\n\nimport { ModalContext } from \"../contexts/modal.context\";\n\nexport function useModalContext() {\n const context = useContext(ModalContext);\n\n if (!context) {\n throw new Error(\"Modal components must be rendered inside Modal.Provider\");\n }\n\n return context;\n}\n"],"mappings":"AAAA,SAAS,kBAAkB;AAE3B,SAAS,oBAAoB;AAEtB,SAAS,kBAAkB;AAChC,QAAM,UAAU,WAAW,YAAY;AAEvC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AAEA,SAAO;AACT;","names":[]}
@@ -0,0 +1,17 @@
1
+ import * as _impulse_ui_native_overlay from '@impulse-ui-native/overlay';
2
+ import * as react_native_reanimated from 'react-native-reanimated';
3
+ import { ModalLifecycleProps } from '../types/modal.types.mjs';
4
+ import 'react';
5
+ import '@impulse-ui-native/primitives';
6
+ import '@impulse-ui-native/theme';
7
+
8
+ declare function useModalLifecycle(props: ModalLifecycleProps): {
9
+ progress: react_native_reanimated.SharedValue<number>;
10
+ status: _impulse_ui_native_overlay.OverlayLifecycleStatus;
11
+ mounted: boolean;
12
+ interactive: boolean;
13
+ close: () => void;
14
+ completeTransition: _impulse_ui_native_overlay.OverlayTransitionCompletion;
15
+ };
16
+
17
+ export { useModalLifecycle };
@@ -0,0 +1,46 @@
1
+ import { useEffect, useMemo } from "react";
2
+ import {
3
+ cancelAnimation,
4
+ useSharedValue,
5
+ withTiming
6
+ } from "react-native-reanimated";
7
+ import { scheduleOnRN } from "react-native-worklets";
8
+ import { useEventCallback } from "@impulse-ui-native/core";
9
+ import { useOverlayLifecycle } from "@impulse-ui-native/overlay";
10
+ import { EnterDuration, ExitDuration } from "../constants/modal.constants";
11
+ function useModalLifecycle(props) {
12
+ const progress = useSharedValue(0);
13
+ const enter = useEventCallback(
14
+ (transitionId, complete) => {
15
+ progress.set(
16
+ withTiming(1, { duration: EnterDuration }, (finished) => {
17
+ if (finished) {
18
+ scheduleOnRN(complete, transitionId);
19
+ }
20
+ })
21
+ );
22
+ }
23
+ );
24
+ const exit = useEventCallback(
25
+ (transitionId, complete) => {
26
+ progress.set(
27
+ withTiming(0, { duration: ExitDuration }, (finished) => {
28
+ if (finished) {
29
+ scheduleOnRN(complete, transitionId);
30
+ }
31
+ })
32
+ );
33
+ }
34
+ );
35
+ const options = useMemo(
36
+ () => ({ onEnter: enter, onExit: exit }),
37
+ [enter, exit]
38
+ );
39
+ const lifecycle = useOverlayLifecycle(props, options);
40
+ useEffect(() => () => cancelAnimation(progress), [progress]);
41
+ return useMemo(() => ({ ...lifecycle, progress }), [lifecycle, progress]);
42
+ }
43
+ export {
44
+ useModalLifecycle
45
+ };
46
+ //# sourceMappingURL=use-modal-lifecycle.hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/use-modal-lifecycle.hook.ts"],"sourcesContent":["import { useEffect, useMemo } from \"react\";\nimport {\n cancelAnimation,\n useSharedValue,\n withTiming,\n} from \"react-native-reanimated\";\nimport { scheduleOnRN } from \"react-native-worklets\";\n\nimport type { OverlayTransitionHandler } from \"@impulse-ui-native/overlay\";\nimport { useEventCallback } from \"@impulse-ui-native/core\";\nimport { useOverlayLifecycle } from \"@impulse-ui-native/overlay\";\n\nimport type { ModalLifecycleProps } from \"../types\";\nimport { EnterDuration, ExitDuration } from \"../constants/modal.constants\";\n\nexport function useModalLifecycle(props: ModalLifecycleProps) {\n const progress = useSharedValue(0);\n\n const enter = useEventCallback<OverlayTransitionHandler>(\n (transitionId, complete) => {\n progress.set(\n withTiming(1, { duration: EnterDuration }, (finished) => {\n if (finished) {\n scheduleOnRN(complete, transitionId);\n }\n }),\n );\n },\n );\n const exit = useEventCallback<OverlayTransitionHandler>(\n (transitionId, complete) => {\n progress.set(\n withTiming(0, { duration: ExitDuration }, (finished) => {\n if (finished) {\n scheduleOnRN(complete, transitionId);\n }\n }),\n );\n },\n );\n\n const options = useMemo(\n () => ({ onEnter: enter, onExit: exit }),\n [enter, exit],\n );\n const lifecycle = useOverlayLifecycle(props, options);\n\n useEffect(() => () => cancelAnimation(progress), [progress]);\n\n return useMemo(() => ({ ...lifecycle, progress }), [lifecycle, progress]);\n}\n"],"mappings":"AAAA,SAAS,WAAW,eAAe;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAG7B,SAAS,wBAAwB;AACjC,SAAS,2BAA2B;AAGpC,SAAS,eAAe,oBAAoB;AAErC,SAAS,kBAAkB,OAA4B;AAC5D,QAAM,WAAW,eAAe,CAAC;AAEjC,QAAM,QAAQ;AAAA,IACZ,CAAC,cAAc,aAAa;AAC1B,eAAS;AAAA,QACP,WAAW,GAAG,EAAE,UAAU,cAAc,GAAG,CAAC,aAAa;AACvD,cAAI,UAAU;AACZ,yBAAa,UAAU,YAAY;AAAA,UACrC;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,QAAM,OAAO;AAAA,IACX,CAAC,cAAc,aAAa;AAC1B,eAAS;AAAA,QACP,WAAW,GAAG,EAAE,UAAU,aAAa,GAAG,CAAC,aAAa;AACtD,cAAI,UAAU;AACZ,yBAAa,UAAU,YAAY;AAAA,UACrC;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU;AAAA,IACd,OAAO,EAAE,SAAS,OAAO,QAAQ,KAAK;AAAA,IACtC,CAAC,OAAO,IAAI;AAAA,EACd;AACA,QAAM,YAAY,oBAAoB,OAAO,OAAO;AAEpD,YAAU,MAAM,MAAM,gBAAgB,QAAQ,GAAG,CAAC,QAAQ,CAAC;AAE3D,SAAO,QAAQ,OAAO,EAAE,GAAG,WAAW,SAAS,IAAI,CAAC,WAAW,QAAQ,CAAC;AAC1E;","names":[]}
@@ -0,0 +1,9 @@
1
+ export { Modal } from './components/index.mjs';
2
+ export { ModalCloseProps, ModalContentProps, ModalContextValue, ModalDescriptionProps, ModalFooterProps, ModalHeaderProps, ModalLifecycleProps, ModalProps, ModalProviderProps, ModalRootProps, ModalSurfaceProps, ModalTitleProps } from './types/modal.types.mjs';
3
+ export { useModalContext } from './hooks/use-modal-context.hook.mjs';
4
+ import 'react/jsx-runtime';
5
+ import 'react';
6
+ import 'react-native-reanimated';
7
+ import '@impulse-ui-native/overlay';
8
+ import '@impulse-ui-native/primitives';
9
+ import '@impulse-ui-native/theme';
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./components";
2
+ export * from "./types";
3
+ import { useModalContext } from "./hooks/use-modal-context.hook";
4
+ export {
5
+ useModalContext
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from \"./components\";\nexport * from \"./types\";\nexport { useModalContext } from \"./hooks/use-modal-context.hook\";\n"],"mappings":"AAAA,cAAc;AACd,cAAc;AACd,SAAS,uBAAuB;","names":[]}
@@ -0,0 +1,6 @@
1
+ export { ModalCloseProps, ModalContentProps, ModalContextValue, ModalDescriptionProps, ModalFooterProps, ModalHeaderProps, ModalLifecycleProps, ModalProps, ModalProviderProps, ModalRootProps, ModalSurfaceProps, ModalTitleProps } from './modal.types.mjs';
2
+ import 'react';
3
+ import 'react-native-reanimated';
4
+ import '@impulse-ui-native/overlay';
5
+ import '@impulse-ui-native/primitives';
6
+ import '@impulse-ui-native/theme';
@@ -0,0 +1,2 @@
1
+ export * from "./modal.types";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types/index.ts"],"sourcesContent":["export * from \"./modal.types\";\n"],"mappings":"AAAA,cAAc;","names":[]}
@@ -0,0 +1,36 @@
1
+ import { PropsWithChildren, ReactNode } from 'react';
2
+ import { SharedValue } from 'react-native-reanimated';
3
+ import { OverlayLifecycleProps, OverlayComponentProps, OverlayLifecycleStatus } from '@impulse-ui-native/overlay';
4
+ import { ViewProps, PressableCoreProps, TextProps } from '@impulse-ui-native/primitives';
5
+ import { ComponentSize } from '@impulse-ui-native/theme';
6
+
7
+ interface ModalSurfaceProps extends ViewProps {
8
+ size?: ComponentSize;
9
+ }
10
+ interface ModalProviderProps extends OverlayLifecycleProps, PropsWithChildren {
11
+ layer?: number;
12
+ }
13
+ type ModalRootProps = ModalSurfaceProps;
14
+ interface ModalContextValue {
15
+ status: OverlayLifecycleStatus;
16
+ mounted: boolean;
17
+ interactive: boolean;
18
+ progress: SharedValue<number>;
19
+ layer: number;
20
+ close: () => void;
21
+ }
22
+ interface ModalProps extends Omit<ModalRootProps, "id" | "children">, ModalProviderProps {
23
+ title?: OverlayComponentProps["title"];
24
+ header?: ReactNode;
25
+ footer?: ReactNode;
26
+ hideClose?: boolean;
27
+ }
28
+ type ModalHeaderProps = ViewProps;
29
+ type ModalCloseProps = PropsWithChildren<Omit<PressableCoreProps, "children">>;
30
+ type ModalContentProps = ViewProps;
31
+ type ModalFooterProps = ViewProps;
32
+ type ModalTitleProps = TextProps;
33
+ type ModalDescriptionProps = TextProps;
34
+ type ModalLifecycleProps = OverlayLifecycleProps;
35
+
36
+ export type { ModalCloseProps, ModalContentProps, ModalContextValue, ModalDescriptionProps, ModalFooterProps, ModalHeaderProps, ModalLifecycleProps, ModalProps, ModalProviderProps, ModalRootProps, ModalSurfaceProps, ModalTitleProps };
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=modal.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,6 +1,67 @@
1
1
  {
2
2
  "name": "@impulse-ui-native/modal",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "7.0.1",
4
+ "description": "Compound modal dialogs with local and OverlayHost lifecycle support.",
5
+ "keywords": [
6
+ "react-native",
7
+ "modal",
8
+ "dialog",
9
+ "components"
10
+ ],
11
+ "license": "MIT",
12
+ "source": "src/index.ts",
13
+ "module": "dist/index.js",
14
+ "types": "dist/index.d.mts",
15
+ "react-native": "src/index.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.mts",
19
+ "react-native": "./src/index.ts",
20
+ "import": "./dist/index.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "sideEffects": false,
29
+ "dependencies": {
30
+ "@impulse-ui-native/theme": "7.0.1",
31
+ "@impulse-ui-native/primitives": "7.0.1",
32
+ "@impulse-ui-native/overlay": "7.0.1",
33
+ "@impulse-ui-native/icon": "7.0.1",
34
+ "@impulse-ui-native/core": "7.0.1"
35
+ },
36
+ "devDependencies": {
37
+ "@types/react": "~19.2.14",
38
+ "eslint": "^9.38.0",
39
+ "prettier": "^3.6.2",
40
+ "react": "19.2.3",
41
+ "react-native": "0.86.2",
42
+ "typescript": "~6.0.3",
43
+ "react-native-reanimated": "4.5.1",
44
+ "react-native-safe-area-context": "~5.7.0",
45
+ "react-native-worklets": "0.10.1",
46
+ "@impulse-ui-native/eslint-config": "1.0.0",
47
+ "@impulse-ui-native/prettier-config": "1.0.0",
48
+ "@impulse-ui-native/tsconfig": "1.0.0"
49
+ },
50
+ "peerDependencies": {
51
+ "react": ">=18",
52
+ "react-native": ">=0.73",
53
+ "react-native-reanimated": ">=4",
54
+ "react-native-safe-area-context": ">=5.0.0",
55
+ "react-native-worklets": ">=0.5"
56
+ },
57
+ "publishConfig": {
58
+ "access": "public"
59
+ },
60
+ "prettier": "@impulse-ui-native/prettier-config",
61
+ "scripts": {
62
+ "build": "tsup --config tsup.config.ts",
63
+ "typecheck": "tsc -p tsconfig.json --noEmit",
64
+ "lint": "pnpm exec eslint . --fix",
65
+ "format": "prettier --write \"./**/*.{ts,tsx,js,jsx,json,md}\" --ignore-path ../../.prettierignore"
66
+ }
6
67
  }