flet-box 0.0.0-stage → 1.0.2

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 (178) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +190 -2
  3. package/bin/cli.js +394 -0
  4. package/bin/commands/buildAndroid.js +200 -0
  5. package/bin/commands/buildIOS.js +237 -0
  6. package/bin/commands/create.js +406 -0
  7. package/bin/commands/createBundle.js +375 -0
  8. package/bin/commands/createComponent.js +115 -0
  9. package/bin/commands/createScreen.js +161 -0
  10. package/bin/commands/killServer.js +107 -0
  11. package/bin/commands/package.js +131 -0
  12. package/bin/commands/release.js +189 -0
  13. package/bin/commands/runBundle.js +62 -0
  14. package/bin/commands/runCloudflare.js +89 -0
  15. package/bin/commands/runNative.js +109 -0
  16. package/bin/commands/runServer.js +390 -0
  17. package/bin/ota/loader.js +361 -0
  18. package/bin/utils/bundleConfig.js +178 -0
  19. package/bin/utils/capacitor.js +137 -0
  20. package/bin/utils/colors.js +215 -0
  21. package/bin/utils/helpers.js +80 -0
  22. package/bin/utils/spinner.js +96 -0
  23. package/bin/utils/templates.js +1824 -0
  24. package/bin/utils/version.js +108 -0
  25. package/package.json +58 -4
  26. package/src/animations/AnimatedBox.js +607 -0
  27. package/src/animations/AnimatedText.js +105 -0
  28. package/src/animations/MatrixRain.js +220 -0
  29. package/src/animations/ParallaxBox.js +369 -0
  30. package/src/animations/animate.js +332 -0
  31. package/src/animations/index.js +25 -0
  32. package/src/app.js +41 -0
  33. package/src/assets/icon-1024-maskable.png +0 -0
  34. package/src/assets/icon-192-maskable.png +0 -0
  35. package/src/assets/icon-192.png +0 -0
  36. package/src/assets/icon-512-maskable.png +0 -0
  37. package/src/assets/icon-512.png +0 -0
  38. package/src/assets/logo.png +0 -0
  39. package/src/core/App.js +44 -0
  40. package/src/core/hmr-client.js +142 -0
  41. package/src/core/index.js +34 -0
  42. package/src/core/pwa.js +100 -0
  43. package/src/core/runApp.js +430 -0
  44. package/src/create-index.sh +237 -0
  45. package/src/fonts/MaterialIcons-Regular.woff2 +0 -0
  46. package/src/fonts/icons.css +23 -0
  47. package/src/index.d.ts +1930 -0
  48. package/src/index.js +288 -0
  49. package/src/navigations/AdaptiveScaffold.js +149 -0
  50. package/src/navigations/AppBar.js +331 -0
  51. package/src/navigations/BottomNavigation.js +298 -0
  52. package/src/navigations/CollapsibleSideBar.js +139 -0
  53. package/src/navigations/Drawer.js +333 -0
  54. package/src/navigations/DrawerItem.js +260 -0
  55. package/src/navigations/Router.js +475 -0
  56. package/src/navigations/Scaffold.js +537 -0
  57. package/src/navigations/Tabs.js +384 -0
  58. package/src/navigations/index.js +57 -0
  59. package/src/services/RamStore.js +213 -0
  60. package/src/services/Session.js +310 -0
  61. package/src/services/Storage.js +309 -0
  62. package/src/services/http.js +350 -0
  63. package/src/services/index.js +57 -0
  64. package/src/tools/animation.js +42 -0
  65. package/src/tools/array.js +95 -0
  66. package/src/tools/border.js +28 -0
  67. package/src/tools/clipboard.js +104 -0
  68. package/src/tools/color.js +329 -0
  69. package/src/tools/createList.js +215 -0
  70. package/src/tools/delay.js +55 -0
  71. package/src/tools/device.js +138 -0
  72. package/src/tools/dict.js +297 -0
  73. package/src/tools/dimensions.js +118 -0
  74. package/src/tools/filter.js +41 -0
  75. package/src/tools/flex.js +26 -0
  76. package/src/tools/gradient.js +34 -0
  77. package/src/tools/grid.js +57 -0
  78. package/src/tools/index.js +38 -0
  79. package/src/tools/mapList.js +107 -0
  80. package/src/tools/margin.js +93 -0
  81. package/src/tools/memo.js +60 -0
  82. package/src/tools/os.js +104 -0
  83. package/src/tools/padding.js +93 -0
  84. package/src/tools/print.js +69 -0
  85. package/src/tools/random.js +299 -0
  86. package/src/tools/ref.js +70 -0
  87. package/src/tools/rgba.js +4 -0
  88. package/src/tools/shadow.js +23 -0
  89. package/src/tools/string.js +85 -0
  90. package/src/tools/time.js +674 -0
  91. package/src/tools/transform.js +68 -0
  92. package/src/tools/transition.js +48 -0
  93. package/src/tools/useState.js +268 -0
  94. package/src/tools/uuid.js +42 -0
  95. package/src/utils/TextInputValidator.js +256 -0
  96. package/src/utils/composeUpdate.js +65 -0
  97. package/src/utils/getWidgetProps.js +35 -0
  98. package/src/utils/index.js +76 -0
  99. package/src/utils/markdownParser.js +368 -0
  100. package/src/utils/mediaTime.js +79 -0
  101. package/src/utils/navigation.js +263 -0
  102. package/src/utils/scriptInjector.js +73 -0
  103. package/src/utils/stackPosition.js +31 -0
  104. package/src/utils/stopWebRefresh.js +77 -0
  105. package/src/utils/styleInjector.js +38 -0
  106. package/src/utils/syntaxHighlight.js +339 -0
  107. package/src/utils/themes.js +347 -0
  108. package/src/utils/units.js +87 -0
  109. package/src/utils/visualEffects.js +169 -0
  110. package/src/widget-factory/addChildren.js +114 -0
  111. package/src/widget-factory/assignProps.js +256 -0
  112. package/src/widget-factory/createWidget.js +28 -0
  113. package/src/widget-factory/effects.js +132 -0
  114. package/src/widget-factory/index.js +7 -0
  115. package/src/widget-factory/lifecycle.js +210 -0
  116. package/src/widget-factory/makeParentable.js +89 -0
  117. package/src/widget-factory/pixTool.js +40 -0
  118. package/src/widget-factory/processProps.js +122 -0
  119. package/src/widget-factory/reactivity.js +98 -0
  120. package/src/widget-factory/remTool.js +34 -0
  121. package/src/widget-factory/stackPosition.js +87 -0
  122. package/src/widget-factory/stringTool.js +26 -0
  123. package/src/widget-factory/tools.js +249 -0
  124. package/src/widget-factory/translateProps.js +1049 -0
  125. package/src/widget-factory/warnings.js +284 -0
  126. package/src/widget-factory/widgetFactory.js +200 -0
  127. package/src/widgets/Accordion.js +562 -0
  128. package/src/widgets/AdMob.js +315 -0
  129. package/src/widgets/AdSense.js +268 -0
  130. package/src/widgets/AlertDialog.js +162 -0
  131. package/src/widgets/Audio.js +311 -0
  132. package/src/widgets/Avatar.js +210 -0
  133. package/src/widgets/Badge.js +189 -0
  134. package/src/widgets/BottomSheet.js +287 -0
  135. package/src/widgets/Button.js +247 -0
  136. package/src/widgets/Card.js +33 -0
  137. package/src/widgets/Carousel.js +301 -0
  138. package/src/widgets/Chart.js +632 -0
  139. package/src/widgets/Checkbox.js +106 -0
  140. package/src/widgets/Chip.js +153 -0
  141. package/src/widgets/CircularBar.js +352 -0
  142. package/src/widgets/CircularChart.js +549 -0
  143. package/src/widgets/CodeViewer.js +236 -0
  144. package/src/widgets/Column.js +29 -0
  145. package/src/widgets/Container.js +31 -0
  146. package/src/widgets/DataTable.js +257 -0
  147. package/src/widgets/Divider.js +93 -0
  148. package/src/widgets/DraggBox.js +115 -0
  149. package/src/widgets/Dropdown.js +505 -0
  150. package/src/widgets/DroppBox.js +180 -0
  151. package/src/widgets/FloatingActionButton.js +91 -0
  152. package/src/widgets/GridView.js +20 -0
  153. package/src/widgets/Icon.js +33 -0
  154. package/src/widgets/Image.js +15 -0
  155. package/src/widgets/Input.js +565 -0
  156. package/src/widgets/Inspector.js +254 -0
  157. package/src/widgets/InstallButton.js +110 -0
  158. package/src/widgets/ListTile.js +224 -0
  159. package/src/widgets/ListView.js +503 -0
  160. package/src/widgets/Markdown.js +1236 -0
  161. package/src/widgets/Modal.js +496 -0
  162. package/src/widgets/Pagination.js +195 -0
  163. package/src/widgets/ProgressBar.js +178 -0
  164. package/src/widgets/QRCode.js +467 -0
  165. package/src/widgets/Radio.js +132 -0
  166. package/src/widgets/Rating.js +261 -0
  167. package/src/widgets/Row.js +29 -0
  168. package/src/widgets/Skeleton.js +243 -0
  169. package/src/widgets/Slider.js +527 -0
  170. package/src/widgets/SnackBar.js +227 -0
  171. package/src/widgets/Stack.js +29 -0
  172. package/src/widgets/Stepper.js +296 -0
  173. package/src/widgets/Switch.js +102 -0
  174. package/src/widgets/Text.js +98 -0
  175. package/src/widgets/Tooltip.js +267 -0
  176. package/src/widgets/TreeView.js +370 -0
  177. package/src/widgets/Video.js +92 -0
  178. package/src/widgets/index.js +52 -0
@@ -0,0 +1,189 @@
1
+ /**
2
+ * @file Badge.js
3
+ * @description An overlay badge that renders a small indicator on top of a
4
+ * child element. Commonly used for notification counts, status dots, or
5
+ * "new item" markers on icons and avatars.
6
+ *
7
+ * Returns the child element directly when the badge should not be displayed
8
+ * (value is zero and `showZero` is false, or value is absent). Otherwise
9
+ * wraps the child in a Stack container with the badge positioned absolutely.
10
+ */
11
+
12
+ import { WidgetFactory } from "../widget-factory/index.js";
13
+ import { Stack } from "./Stack.js";
14
+ import { Text } from "./Text.js";
15
+ import { colors, subscribeTheme } from "../utils/themes.js";
16
+
17
+ /**
18
+ * Creates a Badge widget that overlays a small count or status indicator on
19
+ * top of any child element.
20
+ *
21
+ * When `value` is falsy (and `showZero` is false), the child is returned
22
+ * unwrapped — no DOM overhead is added for hidden badges.
23
+ *
24
+ * @param {Object} props - Configuration for the badge.
25
+ * @param {number|string} props.value - The value to display. Numbers are formatted; strings are shown verbatim.
26
+ * @param {HTMLElement} props.child - The element the badge is anchored to.
27
+ * @param {string} [props.bgColor=colors.secondary] - Badge background color.
28
+ * @param {string} [props.color=colors.text] - Badge text/icon color.
29
+ * @param {number} [props.size=20] - Badge diameter in pixels; also controls font size proportionally.
30
+ * @param {'top-right'|'top-left'|'bottom-right'|'bottom-left'} [props.position='top-right'] - Where to anchor the badge relative to the child.
31
+ * @param {number} [props.borderWidth=2] - Width of the contrasting border around the badge in pixels.
32
+ * @param {string} [props.borderColor=colors.surface] - Color of the border around the badge (typically matches the parent background).
33
+ * @param {boolean} [props.showZero=false] - When true, the badge is visible even when `value` is 0.
34
+ * @param {number} [props.max=99] - When `value` exceeds this, the badge shows `"{max}+"` instead of the raw number.
35
+ * @param {number} [props.offset=0] - Additional pixel offset applied to the badge position for fine-tuning.
36
+ * @returns {HTMLElement} The wrapped Stack element, or the child element directly if the badge is hidden.
37
+ */
38
+ export const Badge = (props) => {
39
+ const {
40
+ value,
41
+ child,
42
+ bgColor = colors.secondary,
43
+ color = colors.text,
44
+ size = 20,
45
+ position = "top-right",
46
+ borderWidth = 2,
47
+ borderColor = colors.surface,
48
+ showZero = false,
49
+ max = 99,
50
+ offset = 0,
51
+ ...rest
52
+ } = props;
53
+
54
+ // Use the supplied bgColor; fall back to danger red if neither bgColor nor color is set
55
+ const finalBgColor = bgColor || color || colors.danger;
56
+
57
+ let unsubscribeTheme = null;
58
+
59
+ // Decide whether to show the badge at all
60
+ const hasValue = value !== undefined && value !== null && value !== "";
61
+ const showBadge =
62
+ hasValue &&
63
+ (showZero || (typeof value === "number" ? value > 0 : value !== ""));
64
+
65
+ // Return the child unwrapped to avoid unnecessary DOM nesting
66
+ if (!showBadge) {
67
+ return child || null;
68
+ }
69
+
70
+ // Truncate numbers that exceed the `max` cap
71
+ let displayValue = value;
72
+ if (typeof value === "number" && max && value > max) {
73
+ displayValue = `${max}+`;
74
+ }
75
+
76
+ // Pixel offsets for each anchor position — the transform handles the corner alignment
77
+ const positions = {
78
+ "top-right": {
79
+ top: -offset,
80
+ right: -offset,
81
+ transform: "translate(25%, -25%)",
82
+ },
83
+ "top-left": {
84
+ top: -offset,
85
+ left: -offset,
86
+ transform: "translate(-25%, -25%)",
87
+ },
88
+ "bottom-right": {
89
+ bottom: -offset,
90
+ right: -offset,
91
+ transform: "translate(25%, 25%)",
92
+ },
93
+ "bottom-left": {
94
+ bottom: -offset,
95
+ left: -offset,
96
+ transform: "translate(-25%, 25%)",
97
+ },
98
+ };
99
+
100
+ const pos = positions[position] || positions["top-right"];
101
+
102
+ // The badge pill element itself
103
+ const badgeElement = WidgetFactory({
104
+ widgetName: "Badge",
105
+ position: "absolute",
106
+ top: pos.top,
107
+ right: pos.right,
108
+ bottom: pos.bottom,
109
+ left: pos.left,
110
+ transform: pos.transform,
111
+ backgroundColor: finalBgColor,
112
+ borderRadius: size,
113
+ minWidth: size,
114
+ height: size,
115
+ // Extra horizontal padding for two-digit numbers so they don't clip
116
+ padding: size > 20 ? `0 ${size / 3}px` : 0,
117
+ display: "flex",
118
+ alignItems: "center",
119
+ justifyContent: "center",
120
+ borderWidth: borderWidth,
121
+ borderColor: borderColor,
122
+ zIndex: 1,
123
+ boxSizing: "border-box",
124
+ boxShadow: "0 1px 3px rgba(0,0,0,0.2)",
125
+ child: Text({
126
+ text: String(displayValue),
127
+ size: size * 0.55,
128
+ color: color,
129
+ weight: "bold",
130
+ align: "center",
131
+ lineHeight: 1,
132
+ }),
133
+ });
134
+
135
+ // Subscribe to theme changes to keep the badge color in sync when no explicit
136
+ // bgColor/color was provided by the caller
137
+ if (!bgColor && !color) {
138
+ unsubscribeTheme = subscribeTheme(() => {
139
+ badgeElement.style.backgroundColor = colors.danger;
140
+ });
141
+ }
142
+
143
+ const validChild = child instanceof HTMLElement ? child : null;
144
+
145
+ if (!validChild) {
146
+ console.warn("Badge: child must be an HTMLElement");
147
+ if (unsubscribeTheme) unsubscribeTheme();
148
+ return badgeElement;
149
+ }
150
+
151
+ // Wrap the child and the badge in a Stack so the badge is positioned relative to the child
152
+ const badgeStack = Stack({
153
+ position: "relative",
154
+ display: "inline-block",
155
+ children: [validChild, badgeElement],
156
+ ...rest,
157
+ });
158
+
159
+ // Unsubscribe from theme updates when the component is removed from the DOM
160
+ badgeStack.onUnmount(() => {
161
+ if (unsubscribeTheme) unsubscribeTheme();
162
+ });
163
+
164
+ /**
165
+ * Updates the displayed badge value without re-rendering the component.
166
+ * Hides the badge automatically when the new value is zero (unless `showZero` is true).
167
+ *
168
+ * @param {number|string} newValue - The new badge value.
169
+ */
170
+ badgeStack.updateValue = (newValue) => {
171
+ let newDisplayValue = newValue;
172
+ if (typeof newValue === "number" && max && newValue > max) {
173
+ newDisplayValue = `${max}+`;
174
+ }
175
+ const textElement = badgeElement.querySelector("span:not(.material-icons)");
176
+ if (textElement) {
177
+ textElement.textContent = String(newDisplayValue);
178
+ }
179
+ const shouldShow =
180
+ newValue &&
181
+ (showZero ||
182
+ (typeof newValue === "number" ? newValue > 0 : newValue !== ""));
183
+ badgeElement.style.display = shouldShow ? "flex" : "none";
184
+ };
185
+
186
+ return badgeStack;
187
+ };
188
+
189
+ export default Badge;
@@ -0,0 +1,287 @@
1
+ /**
2
+ * @file BottomSheet.js
3
+ * @description Bottom sheet controller returned to callers (`open`/`close`/`toggle`/`destroy`).
4
+ */
5
+ import { WidgetFactory } from "../widget-factory/index.js";
6
+ import { colors } from "../utils/themes.js";
7
+ import { Row } from "./Row.js";
8
+ import { Column } from "./Column.js";
9
+ import { Text } from "./Text.js";
10
+ import { Icon } from "./Icon.js";
11
+ import { Container } from "./Container.js";
12
+
13
+ export const BottomSheet = (props) => {
14
+ const {
15
+ content,
16
+ title,
17
+ actions = [],
18
+ height = "auto",
19
+ maxHeight = "80%",
20
+ showDragHandle = true,
21
+ closeOnOverlayClick = true,
22
+ closeOnDragDown = true,
23
+ showCloseButton = true,
24
+
25
+ // Colors
26
+ backgroundColor = colors.surface,
27
+ overlayColor = "rgba(0, 0, 0, 0.5)",
28
+ dragHandleColor = colors.border,
29
+ headerTextColor = colors.text,
30
+ headerBorderColor = colors.border,
31
+ actionBorderColor = colors.border,
32
+
33
+ // Borders and shadows
34
+ borderRadius = 24,
35
+ shadow = "0 -4px 12px rgba(0,0,0,0.1)",
36
+
37
+ // Spacing
38
+ headerPadding = "0 16px 8px 16px",
39
+ contentPadding = "0 16px",
40
+ actionPadding = "12px 16px",
41
+ dragHandlePadding = "14px 0 8px 0",
42
+ dragHandleWidth = 40,
43
+ dragHandleHeight = 4,
44
+
45
+ // Animation
46
+ animationDuration = 300,
47
+
48
+ // Z-index
49
+ zIndex = 9999,
50
+ overlayZIndex = 9998,
51
+
52
+ // Callbacks
53
+ onOpen,
54
+ onClose,
55
+
56
+ ...rest
57
+ } = props;
58
+
59
+ let isOpen = false;
60
+ let overlay = null;
61
+ let sheetContainer = null;
62
+ let startY = 0;
63
+ let currentY = 0;
64
+
65
+ // ✅ Overlay with onclick
66
+ overlay = WidgetFactory({
67
+ position: "fixed",
68
+ top: 0,
69
+ left: 0,
70
+ width: "100%",
71
+ height: "100%",
72
+ backgroundColor: overlayColor,
73
+ zIndex: overlayZIndex,
74
+ opacity: 0,
75
+ visibility: "hidden",
76
+ transition: `opacity ${animationDuration}ms ease, visibility ${animationDuration}ms ease`,
77
+ // onclick: (e) => {
78
+ // // ✅ Only close if the click was directly on the overlay (not on the sheet)
79
+ // if (e.target === overlay && closeOnOverlayClick) {
80
+ // close();
81
+ // }
82
+ // },
83
+ });
84
+
85
+ // ✅ Drag handle with stopPropagation
86
+ const dragHandle = showDragHandle
87
+ ? WidgetFactory({
88
+ display: "flex",
89
+ justifyContent: "center",
90
+ padding: dragHandlePadding,
91
+ cursor: "grab",
92
+ onclick: (e) => e.stopPropagation(), // ✅ Prevents the click from reaching the overlay
93
+ child: WidgetFactory({
94
+ width: dragHandleWidth,
95
+ height: dragHandleHeight,
96
+ backgroundColor: dragHandleColor,
97
+ borderRadius: dragHandleHeight / 2,
98
+ }),
99
+ })
100
+ : null;
101
+
102
+ // Header
103
+ const headerChildren = [];
104
+ if (title) {
105
+ headerChildren.push(
106
+ Text({
107
+ text: title,
108
+ size: 18,
109
+ weight: "bold",
110
+ color: headerTextColor,
111
+ flex: 1,
112
+ }),
113
+ );
114
+ }
115
+ if (showCloseButton) {
116
+ const closeBtn = Icon({
117
+ name: "close",
118
+ size: 22,
119
+ color: colors.textSecondary,
120
+ cursor: "pointer",
121
+ });
122
+ closeBtn.onclick = () => close();
123
+ headerChildren.push(closeBtn);
124
+ }
125
+
126
+ const header =
127
+ title || showCloseButton
128
+ ? Row({
129
+ alignItems: "center",
130
+ justifyContent: "space-between",
131
+ padding: headerPadding,
132
+ borderBottom: title ? `1px solid ${headerBorderColor}` : "none",
133
+ children: headerChildren,
134
+ })
135
+ : null;
136
+
137
+ // Actions footer
138
+ const actionsElement =
139
+ actions.length > 0
140
+ ? Row({
141
+ alignItems: "center",
142
+ justifyContent: "flex-end",
143
+ gap: 8,
144
+ padding: actionPadding,
145
+ borderTop: `1px solid ${actionBorderColor}`,
146
+ marginTop: "auto",
147
+ children: actions,
148
+ })
149
+ : null;
150
+
151
+ // Content wrapper
152
+ const contentWrapper = Container({
153
+ flex: 1,
154
+ overflow: "auto",
155
+ padding: contentPadding,
156
+ child: content,
157
+ });
158
+
159
+ // ✅ SheetContainer with stopPropagation
160
+ sheetContainer = WidgetFactory({
161
+ position: "fixed",
162
+ bottom: 0,
163
+ left: 0,
164
+ right: 0,
165
+ paddingBottom: 12,
166
+ backgroundColor: backgroundColor,
167
+ borderRadius: `${borderRadius}px ${borderRadius}px 0 0`,
168
+ boxShadow: shadow,
169
+ transform: "translateY(100%)",
170
+ transition: `transform ${animationDuration}ms ease`,
171
+ maxHeight: maxHeight,
172
+ height: height === "auto" ? "auto" : height,
173
+ display: "flex",
174
+ flexDirection: "column",
175
+ zIndex: zIndex,
176
+ overflow: "hidden",
177
+ // onclick: (e) => e.stopPropagation(), // ✅ Prevents the click from reaching the overlay
178
+ });
179
+
180
+ // Body
181
+ const sheetBody = Column({
182
+ height: "100%",
183
+ gap: 12,
184
+ children: [dragHandle, header, contentWrapper, actionsElement].filter(
185
+ Boolean,
186
+ ),
187
+ });
188
+
189
+ sheetContainer.appendChild(sheetBody);
190
+ overlay.appendChild(sheetContainer);
191
+ document.body.appendChild(overlay);
192
+
193
+ // Drag handlers
194
+ const onTouchStart = (e) => {
195
+ // ✅ Prevent the drag event from propagating to the overlay
196
+ e.stopPropagation();
197
+ startY = e.touches ? e.touches[0].clientY : e.clientY;
198
+ currentY = startY;
199
+ sheetContainer.style.transition = "none";
200
+ };
201
+
202
+ const onTouchMove = (e) => {
203
+ if (!closeOnDragDown) return;
204
+ e.preventDefault();
205
+ const moveY = e.touches ? e.touches[0].clientY : e.clientY;
206
+ const delta = moveY - startY;
207
+ if (delta > 0) {
208
+ currentY = moveY;
209
+ sheetContainer.style.transform = `translateY(${delta}px)`;
210
+ const opacity = 1 - delta / sheetContainer.offsetHeight;
211
+ overlay.style.opacity = Math.max(0, Math.min(1, opacity));
212
+ }
213
+ };
214
+
215
+ const onTouchEnd = (e) => {
216
+ e.stopPropagation();
217
+ sheetContainer.style.transition = `transform ${animationDuration}ms ease`;
218
+ const delta = currentY - startY;
219
+ if (delta > 100) {
220
+ close();
221
+ } else {
222
+ sheetContainer.style.transform = "translateY(0)";
223
+ overlay.style.opacity = "1";
224
+ }
225
+ startY = 0;
226
+ currentY = 0;
227
+ };
228
+
229
+ const open = () => {
230
+ if (isOpen) return;
231
+ isOpen = true;
232
+ overlay.style.visibility = "visible";
233
+ overlay.style.opacity = "1";
234
+ sheetContainer.style.transform = "translateY(0)";
235
+ if (onOpen) onOpen();
236
+ document.addEventListener("keydown", handleKeyDown);
237
+
238
+ if (closeOnDragDown && dragHandle) {
239
+ dragHandle.addEventListener("mousedown", onTouchStart);
240
+ // dragHandle.addEventListener("mousemove", onTouchMove);
241
+ dragHandle.addEventListener("mouseup", onTouchEnd);
242
+ dragHandle.addEventListener("touchstart", onTouchStart);
243
+ dragHandle.addEventListener("touchmove", onTouchMove);
244
+ dragHandle.addEventListener("touchend", onTouchEnd);
245
+ }
246
+ };
247
+
248
+ const close = () => {
249
+ if (!isOpen) return;
250
+ isOpen = false;
251
+ sheetContainer.style.transform = "translateY(100%)";
252
+ overlay.style.opacity = "0";
253
+ setTimeout(() => {
254
+ overlay.style.visibility = "hidden";
255
+ }, animationDuration);
256
+ if (onClose) onClose();
257
+ document.removeEventListener("keydown", handleKeyDown);
258
+
259
+ if (closeOnDragDown && dragHandle) {
260
+ dragHandle.removeEventListener("mousedown", onTouchStart);
261
+ // dragHandle.removeEventListener("mousemove", onTouchMove);
262
+ dragHandle.removeEventListener("mouseup", onTouchEnd);
263
+ dragHandle.removeEventListener("touchstart", onTouchStart);
264
+ dragHandle.removeEventListener("touchmove", onTouchMove);
265
+ dragHandle.removeEventListener("touchend", onTouchEnd);
266
+ }
267
+ };
268
+
269
+ const toggle = () => {
270
+ if (isOpen) close();
271
+ else open();
272
+ };
273
+
274
+ const destroy = () => {
275
+ if (overlay && overlay.parentNode) overlay.parentNode.removeChild(overlay);
276
+ document.removeEventListener("keydown", handleKeyDown);
277
+ };
278
+
279
+ const handleKeyDown = (e) => {
280
+ if (e.key === "Escape" && isOpen) close();
281
+ };
282
+
283
+ const bottomSheet = { open, close, toggle, destroy };
284
+ return bottomSheet;
285
+ };
286
+
287
+ export default BottomSheet;