@xaui/native 0.0.13 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -37,6 +37,7 @@ __export(src_exports, {
37
37
  Avatar: () => Avatar,
38
38
  AvatarGroup: () => AvatarGroup,
39
39
  Badge: () => Badge,
40
+ BottomSheet: () => BottomSheet,
40
41
  DatePicker: () => DatePicker,
41
42
  Divider: () => Divider,
42
43
  Typography: () => Typography
@@ -1441,7 +1442,7 @@ var useAutocompleteVariantStyles = (themeColor, variant, isInvalid) => {
1441
1442
  if ((variant === "outlined" || variant === "faded") && themeColor === "default") {
1442
1443
  borderColor = import_palette2.colors.gray[300];
1443
1444
  }
1444
- const styles12 = {
1445
+ const styles13 = {
1445
1446
  outlined: {
1446
1447
  backgroundColor: "transparent",
1447
1448
  borderWidth: theme.borderWidth.md,
@@ -1466,7 +1467,7 @@ var useAutocompleteVariantStyles = (themeColor, variant, isInvalid) => {
1466
1467
  borderColor
1467
1468
  }
1468
1469
  };
1469
- return styles12[variant];
1470
+ return styles13[variant];
1470
1471
  }, [variant, theme, colorScheme, isInvalid, themeColor]);
1471
1472
  };
1472
1473
  var useAutocompleteLabelStyle = (themeColor, isInvalid, labelSize) => {
@@ -4133,7 +4134,7 @@ var useTypographyVariantStyles = (variant) => {
4133
4134
  fontWeight: theme.fontWeights.normal
4134
4135
  };
4135
4136
  }
4136
- const styles12 = {
4137
+ const styles13 = {
4137
4138
  caption: {
4138
4139
  fontFamily: theme.fontFamilies.body,
4139
4140
  fontSize: theme.fontSizes.xs,
@@ -4185,7 +4186,7 @@ var useTypographyVariantStyles = (variant) => {
4185
4186
  fontWeight: theme.fontWeights.normal
4186
4187
  }
4187
4188
  };
4188
- return styles12[variant];
4189
+ return styles13[variant];
4189
4190
  }, [theme, variant]);
4190
4191
  return variantStyles;
4191
4192
  };
@@ -4224,6 +4225,377 @@ var Typography = ({
4224
4225
  children
4225
4226
  );
4226
4227
  };
4228
+
4229
+ // src/components/bottom-sheet/bottom-sheet.tsx
4230
+ var import_react44 = __toESM(require("react"), 1);
4231
+ var import_react_native41 = require("react-native");
4232
+
4233
+ // src/components/bottom-sheet/bottom-sheet.hook.ts
4234
+ var import_react43 = require("react");
4235
+ var import_react_native39 = require("react-native");
4236
+ var import_core32 = require("@xaui/core");
4237
+
4238
+ // src/components/bottom-sheet/bottom-sheet.animation.ts
4239
+ var import_react_native38 = require("react-native");
4240
+ var SPRING_CONFIG = {
4241
+ useNativeDriver: true,
4242
+ speed: 14,
4243
+ bounciness: 4
4244
+ };
4245
+ var TIMING_CONFIG = {
4246
+ duration: 280,
4247
+ easing: import_react_native38.Easing.bezier(0.2, 0, 0, 1),
4248
+ useNativeDriver: true
4249
+ };
4250
+ var runOpenAnimation = (translateY, backdropOpacity, targetTranslateY) => {
4251
+ const animation = import_react_native38.Animated.parallel([
4252
+ import_react_native38.Animated.spring(translateY, {
4253
+ ...SPRING_CONFIG,
4254
+ toValue: targetTranslateY
4255
+ }),
4256
+ import_react_native38.Animated.timing(backdropOpacity, {
4257
+ ...TIMING_CONFIG,
4258
+ toValue: 1
4259
+ })
4260
+ ]);
4261
+ animation.start();
4262
+ return animation;
4263
+ };
4264
+ var runCloseAnimation = (translateY, backdropOpacity, screenHeight, onComplete) => {
4265
+ const animation = import_react_native38.Animated.parallel([
4266
+ import_react_native38.Animated.timing(translateY, {
4267
+ ...TIMING_CONFIG,
4268
+ toValue: screenHeight
4269
+ }),
4270
+ import_react_native38.Animated.timing(backdropOpacity, {
4271
+ ...TIMING_CONFIG,
4272
+ toValue: 0
4273
+ })
4274
+ ]);
4275
+ animation.start(({ finished }) => {
4276
+ if (finished && onComplete) {
4277
+ onComplete();
4278
+ }
4279
+ });
4280
+ return animation;
4281
+ };
4282
+ var runSnapAnimation = (translateY, targetTranslateY) => {
4283
+ const animation = import_react_native38.Animated.spring(translateY, {
4284
+ ...SPRING_CONFIG,
4285
+ toValue: targetTranslateY
4286
+ });
4287
+ animation.start();
4288
+ return animation;
4289
+ };
4290
+
4291
+ // src/components/bottom-sheet/bottom-sheet.hook.ts
4292
+ var DISMISS_VELOCITY_THRESHOLD = 0.5;
4293
+ var DISMISS_DISTANCE_FRACTION = 0.3;
4294
+ var SCREEN_HEIGHT = import_react_native39.Dimensions.get("window").height;
4295
+ var getTranslateYForSnap = (snapFraction) => SCREEN_HEIGHT * (1 - snapFraction);
4296
+ var useBottomSheetAnimation = ({
4297
+ isOpen,
4298
+ snapPoints,
4299
+ initialSnapIndex,
4300
+ enableSwipeToDismiss,
4301
+ disableAnimation,
4302
+ onClose,
4303
+ onSnapChange
4304
+ }) => {
4305
+ const [shouldRender, setShouldRender] = (0, import_react43.useState)(false);
4306
+ const currentSnapIndex = (0, import_react43.useRef)(initialSnapIndex);
4307
+ const animationRef = (0, import_react43.useRef)(null);
4308
+ const translateY = (0, import_react43.useRef)(new import_react_native39.Animated.Value(SCREEN_HEIGHT)).current;
4309
+ const backdropOpacity = (0, import_react43.useRef)(new import_react_native39.Animated.Value(0)).current;
4310
+ const sortedSnapPoints = (0, import_react43.useMemo)(
4311
+ () => [...snapPoints].sort((a, b) => a - b),
4312
+ [snapPoints]
4313
+ );
4314
+ const snapTranslateValues = (0, import_react43.useMemo)(
4315
+ () => sortedSnapPoints.map(getTranslateYForSnap),
4316
+ [sortedSnapPoints]
4317
+ );
4318
+ const open = (0, import_react43.useCallback)(() => {
4319
+ setShouldRender(true);
4320
+ const targetIndex = Math.min(initialSnapIndex, sortedSnapPoints.length - 1);
4321
+ currentSnapIndex.current = targetIndex;
4322
+ if (disableAnimation) {
4323
+ translateY.setValue(snapTranslateValues[targetIndex]);
4324
+ backdropOpacity.setValue(1);
4325
+ return;
4326
+ }
4327
+ translateY.setValue(SCREEN_HEIGHT);
4328
+ backdropOpacity.setValue(0);
4329
+ animationRef.current?.stop();
4330
+ animationRef.current = runOpenAnimation(
4331
+ translateY,
4332
+ backdropOpacity,
4333
+ snapTranslateValues[targetIndex]
4334
+ );
4335
+ }, [
4336
+ initialSnapIndex,
4337
+ sortedSnapPoints,
4338
+ snapTranslateValues,
4339
+ disableAnimation,
4340
+ translateY,
4341
+ backdropOpacity
4342
+ ]);
4343
+ const close = (0, import_react43.useCallback)(() => {
4344
+ if (disableAnimation) {
4345
+ translateY.setValue(SCREEN_HEIGHT);
4346
+ backdropOpacity.setValue(0);
4347
+ setShouldRender(false);
4348
+ onClose?.();
4349
+ return;
4350
+ }
4351
+ animationRef.current?.stop();
4352
+ animationRef.current = runCloseAnimation(
4353
+ translateY,
4354
+ backdropOpacity,
4355
+ SCREEN_HEIGHT,
4356
+ () => {
4357
+ setShouldRender(false);
4358
+ onClose?.();
4359
+ }
4360
+ );
4361
+ }, [disableAnimation, translateY, backdropOpacity, onClose]);
4362
+ const snapTo = (0, import_react43.useCallback)(
4363
+ (index) => {
4364
+ const clampedIndex = Math.max(0, Math.min(index, sortedSnapPoints.length - 1));
4365
+ currentSnapIndex.current = clampedIndex;
4366
+ onSnapChange?.(clampedIndex);
4367
+ if (disableAnimation) {
4368
+ translateY.setValue(snapTranslateValues[clampedIndex]);
4369
+ return;
4370
+ }
4371
+ animationRef.current?.stop();
4372
+ animationRef.current = runSnapAnimation(
4373
+ translateY,
4374
+ snapTranslateValues[clampedIndex]
4375
+ );
4376
+ },
4377
+ [sortedSnapPoints, snapTranslateValues, disableAnimation, translateY, onSnapChange]
4378
+ );
4379
+ (0, import_react43.useEffect)(() => {
4380
+ if (isOpen) {
4381
+ open();
4382
+ } else if (shouldRender) {
4383
+ close();
4384
+ }
4385
+ }, [isOpen]);
4386
+ const panResponder = (0, import_react43.useMemo)(
4387
+ () => import_react_native39.PanResponder.create({
4388
+ onStartShouldSetPanResponder: () => true,
4389
+ onMoveShouldSetPanResponder: (_, gestureState) => Math.abs(gestureState.dy) > 5,
4390
+ onPanResponderMove: (_, gestureState) => {
4391
+ const currentTranslate = snapTranslateValues[currentSnapIndex.current];
4392
+ const newTranslateY = currentTranslate + gestureState.dy;
4393
+ const maxExpanded = snapTranslateValues[snapTranslateValues.length - 1];
4394
+ const clamped = Math.max(maxExpanded, newTranslateY);
4395
+ translateY.setValue(clamped);
4396
+ },
4397
+ onPanResponderRelease: (_, gestureState) => {
4398
+ const currentTranslate = snapTranslateValues[currentSnapIndex.current];
4399
+ const finalPosition = currentTranslate + gestureState.dy;
4400
+ if (enableSwipeToDismiss && (gestureState.vy > DISMISS_VELOCITY_THRESHOLD || finalPosition > SCREEN_HEIGHT * (1 - sortedSnapPoints[0] * DISMISS_DISTANCE_FRACTION))) {
4401
+ close();
4402
+ return;
4403
+ }
4404
+ if (gestureState.vy < -DISMISS_VELOCITY_THRESHOLD) {
4405
+ const nextIndex = Math.min(
4406
+ currentSnapIndex.current + 1,
4407
+ sortedSnapPoints.length - 1
4408
+ );
4409
+ snapTo(nextIndex);
4410
+ return;
4411
+ }
4412
+ if (gestureState.vy > DISMISS_VELOCITY_THRESHOLD) {
4413
+ const prevIndex = Math.max(currentSnapIndex.current - 1, 0);
4414
+ if (prevIndex === currentSnapIndex.current && enableSwipeToDismiss) {
4415
+ close();
4416
+ return;
4417
+ }
4418
+ snapTo(prevIndex);
4419
+ return;
4420
+ }
4421
+ let closestIndex = 0;
4422
+ let minDistance = Infinity;
4423
+ snapTranslateValues.forEach((snapVal, index) => {
4424
+ const distance = Math.abs(finalPosition - snapVal);
4425
+ if (distance < minDistance) {
4426
+ minDistance = distance;
4427
+ closestIndex = index;
4428
+ }
4429
+ });
4430
+ snapTo(closestIndex);
4431
+ }
4432
+ }),
4433
+ [
4434
+ snapTranslateValues,
4435
+ sortedSnapPoints,
4436
+ enableSwipeToDismiss,
4437
+ translateY,
4438
+ close,
4439
+ snapTo
4440
+ ]
4441
+ );
4442
+ return {
4443
+ shouldRender,
4444
+ translateY,
4445
+ backdropOpacity,
4446
+ panResponder,
4447
+ close,
4448
+ snapTo,
4449
+ screenHeight: SCREEN_HEIGHT
4450
+ };
4451
+ };
4452
+ var useBottomSheetStyles = (themeColor, radius) => {
4453
+ const theme = useXUITheme();
4454
+ const safeThemeColor = (0, import_core32.getSafeThemeColor)(themeColor);
4455
+ const colorScheme = theme.colors[safeThemeColor];
4456
+ const sheetStyles = (0, import_react43.useMemo)(
4457
+ () => ({
4458
+ backgroundColor: colorScheme.background ?? theme.colors.background ?? "#ffffff",
4459
+ borderTopLeftRadius: theme.borderRadius[radius],
4460
+ borderTopRightRadius: theme.borderRadius[radius]
4461
+ }),
4462
+ [theme, colorScheme, radius]
4463
+ );
4464
+ const handleIndicatorColor = (0, import_react43.useMemo)(
4465
+ () => theme.mode === "dark" ? `${colorScheme.main}60` : `${colorScheme.main}40`,
4466
+ [theme, colorScheme]
4467
+ );
4468
+ return { sheetStyles, handleIndicatorColor };
4469
+ };
4470
+
4471
+ // src/components/bottom-sheet/bottom-sheet.style.ts
4472
+ var import_react_native40 = require("react-native");
4473
+ var styles12 = import_react_native40.StyleSheet.create({
4474
+ backdrop: {
4475
+ position: "absolute",
4476
+ top: 0,
4477
+ left: 0,
4478
+ right: 0,
4479
+ bottom: 0,
4480
+ backgroundColor: "rgba(0, 0, 0, 0.5)"
4481
+ },
4482
+ container: {
4483
+ position: "absolute",
4484
+ left: 0,
4485
+ right: 0,
4486
+ bottom: 0
4487
+ },
4488
+ sheet: {
4489
+ overflow: "hidden"
4490
+ },
4491
+ handle: {
4492
+ alignItems: "center",
4493
+ justifyContent: "center",
4494
+ paddingVertical: 10
4495
+ },
4496
+ handleIndicator: {
4497
+ width: 36,
4498
+ height: 4,
4499
+ borderRadius: 2
4500
+ },
4501
+ content: {
4502
+ flex: 1
4503
+ }
4504
+ });
4505
+
4506
+ // src/components/bottom-sheet/bottom-sheet.tsx
4507
+ var BottomSheet = ({
4508
+ children,
4509
+ isOpen,
4510
+ snapPoints = [0.4, 0.9],
4511
+ initialSnapIndex = 0,
4512
+ themeColor = "default",
4513
+ radius = "lg",
4514
+ showBackdrop = true,
4515
+ closeOnBackdropPress = true,
4516
+ enableSwipeToDismiss = true,
4517
+ showHandle = true,
4518
+ disableAnimation = false,
4519
+ style,
4520
+ handleContent,
4521
+ onClose,
4522
+ onSnapChange
4523
+ }) => {
4524
+ const {
4525
+ shouldRender,
4526
+ translateY,
4527
+ backdropOpacity,
4528
+ panResponder,
4529
+ close,
4530
+ screenHeight
4531
+ } = useBottomSheetAnimation({
4532
+ isOpen,
4533
+ snapPoints,
4534
+ initialSnapIndex,
4535
+ enableSwipeToDismiss,
4536
+ disableAnimation,
4537
+ onClose,
4538
+ onSnapChange
4539
+ });
4540
+ const { sheetStyles, handleIndicatorColor } = useBottomSheetStyles(
4541
+ themeColor,
4542
+ radius
4543
+ );
4544
+ if (!shouldRender) {
4545
+ return null;
4546
+ }
4547
+ const handleBackdropPress = () => {
4548
+ if (closeOnBackdropPress) {
4549
+ close();
4550
+ }
4551
+ };
4552
+ return /* @__PURE__ */ import_react44.default.createElement(Portal, null, showBackdrop && /* @__PURE__ */ import_react44.default.createElement(
4553
+ import_react_native41.Animated.View,
4554
+ {
4555
+ style: [styles12.backdrop, { opacity: backdropOpacity }]
4556
+ },
4557
+ /* @__PURE__ */ import_react44.default.createElement(
4558
+ import_react_native41.Pressable,
4559
+ {
4560
+ style: styles12.backdrop,
4561
+ onPress: handleBackdropPress
4562
+ }
4563
+ )
4564
+ ), /* @__PURE__ */ import_react44.default.createElement(
4565
+ import_react_native41.Animated.View,
4566
+ {
4567
+ style: [
4568
+ styles12.container,
4569
+ {
4570
+ height: screenHeight,
4571
+ transform: [{ translateY }]
4572
+ }
4573
+ ]
4574
+ },
4575
+ /* @__PURE__ */ import_react44.default.createElement(
4576
+ import_react_native41.View,
4577
+ {
4578
+ style: [
4579
+ styles12.sheet,
4580
+ { height: screenHeight },
4581
+ sheetStyles,
4582
+ style
4583
+ ],
4584
+ ...panResponder.panHandlers
4585
+ },
4586
+ showHandle && /* @__PURE__ */ import_react44.default.createElement(import_react_native41.View, { style: styles12.handle }, handleContent ?? /* @__PURE__ */ import_react44.default.createElement(
4587
+ import_react_native41.View,
4588
+ {
4589
+ style: [
4590
+ styles12.handleIndicator,
4591
+ { backgroundColor: handleIndicatorColor }
4592
+ ]
4593
+ }
4594
+ )),
4595
+ /* @__PURE__ */ import_react44.default.createElement(import_react_native41.View, { style: styles12.content }, children)
4596
+ )
4597
+ ));
4598
+ };
4227
4599
  // Annotate the CommonJS export names for ESM import in node:
4228
4600
  0 && (module.exports = {
4229
4601
  ActivityIndicator,
@@ -4233,6 +4605,7 @@ var Typography = ({
4233
4605
  Avatar,
4234
4606
  AvatarGroup,
4235
4607
  Badge,
4608
+ BottomSheet,
4236
4609
  DatePicker,
4237
4610
  Divider,
4238
4611
  Typography
package/dist/index.d.cts CHANGED
@@ -6,6 +6,7 @@ export { Alert } from './alert/index.cjs';
6
6
  export { Autocomplete, AutocompleteItem } from './autocomplete/index.cjs';
7
7
  export { DatePicker } from './datepicker/index.cjs';
8
8
  export { Typography } from './typography/index.cjs';
9
+ export { BottomSheet } from './bottom-sheet/index.cjs';
9
10
  import 'react';
10
11
  import './index-BOw6tbkc.cjs';
11
12
  import 'react-native';
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export { Alert } from './alert/index.js';
6
6
  export { Autocomplete, AutocompleteItem } from './autocomplete/index.js';
7
7
  export { DatePicker } from './datepicker/index.js';
8
8
  export { Typography } from './typography/index.js';
9
+ export { BottomSheet } from './bottom-sheet/index.js';
9
10
  import 'react';
10
11
  import './index-BOw6tbkc.js';
11
12
  import 'react-native';
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ import {
2
+ BottomSheet
3
+ } from "./chunk-GAOI4KIV.js";
1
4
  import {
2
5
  Avatar,
3
6
  AvatarGroup
@@ -34,6 +37,7 @@ export {
34
37
  Avatar,
35
38
  AvatarGroup,
36
39
  Badge,
40
+ BottomSheet,
37
41
  DatePicker,
38
42
  Divider,
39
43
  Typography
@@ -251,7 +251,7 @@ var Menu = ({
251
251
  {
252
252
  top: isMeasured ? menuPosition.top : -9999,
253
253
  left: isMeasured ? menuPosition.left : -9999,
254
- backgroundColor: theme.colors.background,
254
+ backgroundColor: theme.mode === "dark" ? theme.colors.default.background : theme.colors.background,
255
255
  borderRadius: theme.borderRadius.md,
256
256
  opacity: isMeasured ? opacity : 0,
257
257
  transform: [{ scale }],
@@ -4,13 +4,7 @@ import {
4
4
 
5
5
  // src/components/menu/menu.tsx
6
6
  import React from "react";
7
- import {
8
- Animated as Animated2,
9
- Modal,
10
- Pressable,
11
- ScrollView,
12
- View
13
- } from "react-native";
7
+ import { Animated as Animated2, Modal, Pressable, ScrollView, View } from "react-native";
14
8
 
15
9
  // src/components/menu/menu.style.ts
16
10
  import { StyleSheet } from "react-native";
@@ -185,7 +179,7 @@ var Menu = ({
185
179
  {
186
180
  top: isMeasured ? menuPosition.top : -9999,
187
181
  left: isMeasured ? menuPosition.left : -9999,
188
- backgroundColor: theme.colors.background,
182
+ backgroundColor: theme.mode === "dark" ? theme.colors.default.background : theme.colors.background,
189
183
  borderRadius: theme.borderRadius.md,
190
184
  opacity: isMeasured ? opacity : 0,
191
185
  transform: [{ scale }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xaui/native",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "description": "Flutter-inspired React Native UI components with native animations powered by React Native Reanimated",
5
5
  "keywords": [
6
6
  "react-native",
@@ -102,6 +102,11 @@
102
102
  "import": "./dist/view/index.js",
103
103
  "require": "./dist/view/index.js"
104
104
  },
105
+ "./bottom-sheet": {
106
+ "types": "./dist/bottom-sheet/index.d.ts",
107
+ "import": "./dist/bottom-sheet/index.js",
108
+ "require": "./dist/bottom-sheet/index.js"
109
+ },
105
110
  "./menu": {
106
111
  "types": "./dist/menu/index.d.ts",
107
112
  "import": "./dist/menu/index.js",
@@ -119,7 +124,7 @@
119
124
  },
120
125
  "dependencies": {
121
126
  "@xaui/core": "0.1.7",
122
- "@xaui/icons": "0.0.2"
127
+ "@xaui/icons": "0.0.3"
123
128
  },
124
129
  "peerDependencies": {
125
130
  "react": "^18.0.0 || ^19.0.0",