@storybook/react-native-ui-lite 10.0.1 → 10.0.2-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -49,6 +49,7 @@ module.exports = __toCommonJS(index_exports);
49
49
  var import_react_native_theming2 = require("@storybook/react-native-theming");
50
50
  var import_react2 = __toESM(require("react"));
51
51
  var import_polished = require("polished");
52
+ var import_react_native2 = require("react-native");
52
53
 
53
54
  // src/icon/iconDataUris.tsx
54
55
  var import_react = require("react");
@@ -387,8 +388,9 @@ var GroupNode = import_react2.default.memo(function GroupNode2({
387
388
  const color = (0, import_react2.useMemo)(() => {
388
389
  return theme.base === "dark" ? theme.color.primary : theme.color.ultraviolet;
389
390
  }, [theme.base, theme.color.primary, theme.color.ultraviolet]);
391
+ const wrapperProps = import_react_native2.Platform.OS === "macos" ? { key: `${props.id}-${color}` } : {};
390
392
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(BranchNode, { isExpandable, ...props, children: [
391
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Wrapper, { children: [
393
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Wrapper, { ...wrapperProps, children: [
392
394
  isExpandable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CollapseIcon, { isExpanded }),
393
395
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GroupIcon, { width: 14, height: 14, color })
394
396
  ] }),
@@ -401,8 +403,9 @@ var ComponentNode = import_react2.default.memo(
401
403
  const color = (0, import_react2.useMemo)(() => {
402
404
  return theme.color.secondary;
403
405
  }, [theme.color.secondary]);
406
+ const wrapperProps = import_react_native2.Platform.OS === "macos" ? { key: `${props.id}-${color}` } : {};
404
407
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(BranchNode, { isExpandable, ...props, children: [
405
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Wrapper, { children: [
408
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Wrapper, { ...wrapperProps, children: [
406
409
  isExpandable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CollapseIcon, { isExpanded }),
407
410
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ComponentIcon, { width: 12, height: 12, color })
408
411
  ] }),
@@ -416,8 +419,9 @@ var StoryNode = import_react2.default.memo(
416
419
  const color = (0, import_react2.useMemo)(() => {
417
420
  return props.selected ? theme.color.lightest : theme.color.seafoam;
418
421
  }, [props.selected, theme.color.lightest, theme.color.seafoam]);
422
+ const wrapperProps = import_react_native2.Platform.OS === "macos" ? { key: `${props.id}-${color}` } : {};
419
423
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(LeafNode, { ...props, ref, children: [
420
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Wrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StoryIcon, { width: 14, height: 14, color }) }),
424
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Wrapper, { ...wrapperProps, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StoryIcon, { width: 14, height: 14, color }) }),
421
425
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LeafNodeText, { selected: props.selected, children })
422
426
  ] });
423
427
  })
@@ -844,7 +848,7 @@ var Ref = import_react5.default.memo(
844
848
  );
845
849
 
846
850
  // src/Explorer.tsx
847
- var import_react_native2 = require("react-native");
851
+ var import_react_native3 = require("react-native");
848
852
  var import_jsx_runtime6 = require("react/jsx-runtime");
849
853
  var import_react7 = require("react");
850
854
  var Explorer = import_react6.default.memo(function Explorer2({
@@ -855,7 +859,7 @@ var Explorer = import_react6.default.memo(function Explorer2({
855
859
  setSelection
856
860
  }) {
857
861
  const containerRef = (0, import_react6.useRef)(null);
858
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.View, { ref: containerRef, id: "storybook-explorer-tree", children: dataset.entries.map(([refId, ref]) => /* @__PURE__ */ (0, import_react7.createElement)(
862
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native3.View, { ref: containerRef, id: "storybook-explorer-tree", children: dataset.entries.map(([refId, ref]) => /* @__PURE__ */ (0, import_react7.createElement)(
859
863
  Ref,
860
864
  {
861
865
  ...ref,
@@ -872,7 +876,7 @@ var Explorer = import_react6.default.memo(function Explorer2({
872
876
  var import_react_native_theming7 = require("@storybook/react-native-theming");
873
877
  var import_react_native_ui_common5 = require("@storybook/react-native-ui-common");
874
878
  var import_react11 = __toESM(require("react"));
875
- var import_react_native5 = require("react-native");
879
+ var import_react_native6 = require("react-native");
876
880
 
877
881
  // src/constants.ts
878
882
  var BREAKPOINT = 1e3;
@@ -883,7 +887,7 @@ var DEFAULT_REF_ID = "storybook_internal";
883
887
  var import_react_native_theming5 = require("@storybook/react-native-theming");
884
888
  var import_fuse = __toESM(require("fuse.js"));
885
889
  var import_react8 = __toESM(require("react"));
886
- var import_react_native3 = require("react-native");
890
+ var import_react_native4 = require("react-native");
887
891
  var import_react_native_ui_common3 = require("@storybook/react-native-ui-common");
888
892
  var import_jsx_runtime7 = require("react/jsx-runtime");
889
893
  var DEFAULT_MAX_SEARCH_RESULTS = 50;
@@ -919,9 +923,20 @@ var SearchField = import_react_native_theming5.styled.View({
919
923
  flexDirection: "column",
920
924
  position: "relative"
921
925
  });
922
- var Input = (0, import_react_native_theming5.styled)(import_react_native3.TextInput)(({ theme }) => ({
923
- height: import_react_native3.Platform.OS === "android" ? "auto" : 32,
924
- minHeight: 32,
926
+ var inputPlatformSpecificStyles = import_react_native4.Platform.select({
927
+ macos: {
928
+ paddingVertical: 6
929
+ },
930
+ android: {
931
+ minHeight: 32
932
+ },
933
+ default: {
934
+ minHeight: 32,
935
+ height: 32
936
+ }
937
+ });
938
+ var Input = (0, import_react_native_theming5.styled)(import_react_native4.TextInput)(({ theme }) => ({
939
+ ...inputPlatformSpecificStyles,
925
940
  paddingLeft: 28,
926
941
  paddingRight: 28,
927
942
  borderWidth: 1,
@@ -1041,7 +1056,7 @@ var Search = import_react8.default.memo(function Search2({ children, dataset, se
1041
1056
  const deferredQuery = (0, import_react8.useDeferredValue)(inputValue);
1042
1057
  const input = deferredQuery ? deferredQuery.trim() : "";
1043
1058
  const results = input ? getResults(input) : [];
1044
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native3.View, { style: { flex: 1 }, children: [
1059
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native4.View, { style: { flex: 1 }, children: [
1045
1060
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(SearchField, { children: [
1046
1061
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SearchIconWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SearchIcon, {}) }),
1047
1062
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
@@ -1081,7 +1096,7 @@ var import_react_native_theming6 = require("@storybook/react-native-theming");
1081
1096
  var import_react_native_ui_common4 = require("@storybook/react-native-ui-common");
1082
1097
  var import_polished2 = require("polished");
1083
1098
  var import_react9 = __toESM(require("react"));
1084
- var import_react_native4 = require("react-native");
1099
+ var import_react_native5 = require("react-native");
1085
1100
  var import_jsx_runtime8 = require("react/jsx-runtime");
1086
1101
  var import_react10 = require("react");
1087
1102
  var ResultsList = import_react_native_theming6.styled.View({
@@ -1208,7 +1223,7 @@ var Result = import_react9.default.memo(function Result2({
1208
1223
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Title, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Highlight, { match: nameMatch, children: item.name }, "search-result-item--label-highlight") }),
1209
1224
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Path, { children: item.path.map((group, index) => {
1210
1225
  const pathSeparator = index === item.path.length - 1 ? "" : "/";
1211
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native4.View, { style: { flexShrink: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PathText, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1226
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native5.View, { style: { flexShrink: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PathText, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1212
1227
  Highlight,
1213
1228
  {
1214
1229
  match: pathMatches.find((match) => match.refIndex === index),
@@ -1239,7 +1254,7 @@ var SearchResults = import_react9.default.memo(function SearchResults2({
1239
1254
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: "Recently opened" }),
1240
1255
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_ui_common4.IconButton, { onPress: handleClearLastViewed })
1241
1256
  ] }) : null,
1242
- results.length === 0 && query ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native4.View, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(NoResults, { children: [
1257
+ results.length === 0 && query ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native5.View, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(NoResults, { children: [
1243
1258
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { style: { marginBottom: 8 }, children: "No components found" }),
1244
1259
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { children: "Find components by name or path." })
1245
1260
  ] }) }) : null,
@@ -1292,8 +1307,8 @@ var Swap = import_react11.default.memo(function Swap2({
1292
1307
  }) {
1293
1308
  const [a, b] = import_react11.default.Children.toArray(children);
1294
1309
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1295
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native5.View, { style: { display: condition ? "flex" : "none" }, children: a }),
1296
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native5.View, { style: { display: condition ? "none" : "flex" }, children: b })
1310
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native6.View, { style: { display: condition ? "flex" : "none" }, children: a }),
1311
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native6.View, { style: { display: condition ? "none" : "flex" }, children: b })
1297
1312
  ] });
1298
1313
  });
1299
1314
  var useCombination = (index, indexError, previewInitialized, status, refs) => {
@@ -1357,7 +1372,7 @@ var Sidebar = import_react11.default.memo(function Sidebar2({
1357
1372
  var import_react_native_theming10 = require("@storybook/react-native-theming");
1358
1373
  var import_react_native_ui_common7 = require("@storybook/react-native-ui-common");
1359
1374
  var import_react16 = require("react");
1360
- var import_react_native10 = require("react-native");
1375
+ var import_react_native11 = require("react-native");
1361
1376
  var import_core_events = require("storybook/internal/core-events");
1362
1377
  var import_manager_api2 = require("storybook/manager-api");
1363
1378
 
@@ -1365,17 +1380,17 @@ var import_manager_api2 = require("storybook/manager-api");
1365
1380
  var import_react_native_theming8 = require("@storybook/react-native-theming");
1366
1381
  var import_react_native_ui_common6 = require("@storybook/react-native-ui-common");
1367
1382
  var import_react13 = require("react");
1368
- var import_react_native7 = require("react-native");
1383
+ var import_react_native8 = require("react-native");
1369
1384
  var import_manager_api = require("storybook/manager-api");
1370
1385
  var import_types = require("storybook/internal/types");
1371
1386
 
1372
1387
  // src/useAnimatedValue.ts
1373
1388
  var import_react12 = require("react");
1374
- var import_react_native6 = require("react-native");
1389
+ var import_react_native7 = require("react-native");
1375
1390
  function useAnimatedValue(initialValue, config) {
1376
1391
  const ref = (0, import_react12.useRef)(null);
1377
1392
  if (ref.current == null) {
1378
- ref.current = new import_react_native6.Animated.Value(initialValue, config);
1393
+ ref.current = new import_react_native7.Animated.Value(initialValue, config);
1379
1394
  }
1380
1395
  return ref.current;
1381
1396
  }
@@ -1386,56 +1401,98 @@ var import_jsx_runtime10 = require("react/jsx-runtime");
1386
1401
  var MobileAddonsPanel = (0, import_react13.forwardRef)(
1387
1402
  ({ storyId }, ref) => {
1388
1403
  const theme = (0, import_react_native_theming8.useTheme)();
1389
- const [mobileMenuOpen, setMobileMenuOpen] = (0, import_react13.useState)(false);
1390
- const { height } = (0, import_react_native7.useWindowDimensions)();
1391
- const panelHeight = useAnimatedValue(height / 2);
1392
- const positionBottomAnimation = useAnimatedValue(0);
1404
+ const { height } = (0, import_react_native8.useWindowDimensions)();
1405
+ const panelHeight = useAnimatedValue(0);
1406
+ const positionBottomAnimation = useAnimatedValue(height / 2);
1407
+ const [isOpen, setIsOpen] = (0, import_react13.useState)(false);
1408
+ const setMobileMenuOpen = (0, import_react13.useCallback)(
1409
+ (open) => {
1410
+ setIsOpen(open);
1411
+ if (open) {
1412
+ import_react_native8.Animated.parallel([
1413
+ import_react_native8.Animated.timing(positionBottomAnimation, {
1414
+ toValue: 0,
1415
+ // Negative to move up
1416
+ duration: 350,
1417
+ useNativeDriver: false,
1418
+ easing: import_react_native8.Easing.inOut(import_react_native8.Easing.cubic)
1419
+ }),
1420
+ import_react_native8.Animated.timing(panelHeight, {
1421
+ toValue: height / 2,
1422
+ duration: 350,
1423
+ useNativeDriver: false,
1424
+ easing: import_react_native8.Easing.inOut(import_react_native8.Easing.cubic)
1425
+ })
1426
+ ]).start();
1427
+ } else {
1428
+ import_react_native8.Animated.parallel([
1429
+ import_react_native8.Animated.timing(positionBottomAnimation, {
1430
+ toValue: height / 2,
1431
+ duration: 350,
1432
+ useNativeDriver: false,
1433
+ easing: import_react_native8.Easing.inOut(import_react_native8.Easing.cubic)
1434
+ }),
1435
+ import_react_native8.Animated.timing(panelHeight, {
1436
+ toValue: 0,
1437
+ duration: 350,
1438
+ useNativeDriver: false,
1439
+ easing: import_react_native8.Easing.inOut(import_react_native8.Easing.cubic)
1440
+ })
1441
+ ]).start();
1442
+ }
1443
+ },
1444
+ [height, positionBottomAnimation, panelHeight]
1445
+ );
1393
1446
  (0, import_react13.useEffect)(() => {
1394
1447
  const handleKeyboardShow = ({ endCoordinates, duration, easing }) => {
1395
- import_react_native7.Animated.parallel([
1396
- import_react_native7.Animated.timing(positionBottomAnimation, {
1397
- toValue: -endCoordinates.height,
1398
- // Negative to move up
1399
- duration,
1400
- useNativeDriver: false,
1401
- easing: import_react_native7.Easing[easing] || import_react_native7.Easing.out(import_react_native7.Easing.ease)
1402
- }),
1403
- import_react_native7.Animated.timing(panelHeight, {
1404
- toValue: (height - endCoordinates.height) / 2,
1405
- duration: duration + 250,
1406
- useNativeDriver: false,
1407
- easing: import_react_native7.Easing[easing] || import_react_native7.Easing.out(import_react_native7.Easing.ease)
1408
- })
1409
- ]).start();
1448
+ if (isOpen) {
1449
+ import_react_native8.Animated.parallel([
1450
+ import_react_native8.Animated.timing(panelHeight, {
1451
+ toValue: (height - endCoordinates.height) / 2,
1452
+ duration,
1453
+ useNativeDriver: false,
1454
+ easing: import_react_native8.Easing[easing] || import_react_native8.Easing.out(import_react_native8.Easing.ease)
1455
+ }),
1456
+ import_react_native8.Animated.timing(positionBottomAnimation, {
1457
+ toValue: -endCoordinates.height,
1458
+ // Negative to move up
1459
+ duration,
1460
+ useNativeDriver: false,
1461
+ easing: import_react_native8.Easing[easing] || import_react_native8.Easing.out(import_react_native8.Easing.ease)
1462
+ })
1463
+ ]).start();
1464
+ }
1410
1465
  };
1411
1466
  const handleKeyboardHide = ({ duration, easing }) => {
1412
- import_react_native7.Animated.parallel([
1413
- import_react_native7.Animated.timing(positionBottomAnimation, {
1414
- toValue: 0,
1415
- // Back to original position
1416
- duration,
1417
- useNativeDriver: false,
1418
- easing: import_react_native7.Easing[easing] || import_react_native7.Easing.out(import_react_native7.Easing.ease)
1419
- }),
1420
- import_react_native7.Animated.timing(panelHeight, {
1421
- toValue: height / 2,
1422
- duration,
1423
- useNativeDriver: false,
1424
- easing: import_react_native7.Easing[easing] || import_react_native7.Easing.out(import_react_native7.Easing.ease)
1425
- })
1426
- ]).start();
1467
+ if (isOpen) {
1468
+ import_react_native8.Animated.parallel([
1469
+ import_react_native8.Animated.timing(positionBottomAnimation, {
1470
+ toValue: 0,
1471
+ // Back to original position
1472
+ duration,
1473
+ useNativeDriver: false,
1474
+ easing: import_react_native8.Easing[easing] || import_react_native8.Easing.out(import_react_native8.Easing.ease)
1475
+ }),
1476
+ import_react_native8.Animated.timing(panelHeight, {
1477
+ toValue: height / 2,
1478
+ duration,
1479
+ useNativeDriver: false,
1480
+ easing: import_react_native8.Easing[easing] || import_react_native8.Easing.out(import_react_native8.Easing.ease)
1481
+ })
1482
+ ]).start();
1483
+ }
1427
1484
  };
1428
- const showSubscription = import_react_native7.Keyboard.addListener("keyboardDidShow", handleKeyboardShow);
1429
- const willShowSubscription = import_react_native7.Keyboard.addListener("keyboardWillShow", handleKeyboardShow);
1430
- const hideSubscription = import_react_native7.Keyboard.addListener("keyboardWillHide", handleKeyboardHide);
1431
- const didHideSubscription = import_react_native7.Keyboard.addListener("keyboardDidHide", handleKeyboardHide);
1485
+ const showSubscription = import_react_native8.Keyboard.addListener("keyboardDidShow", handleKeyboardShow);
1486
+ const willShowSubscription = import_react_native8.Keyboard.addListener("keyboardWillShow", handleKeyboardShow);
1487
+ const hideSubscription = import_react_native8.Keyboard.addListener("keyboardWillHide", handleKeyboardHide);
1488
+ const didHideSubscription = import_react_native8.Keyboard.addListener("keyboardDidHide", handleKeyboardHide);
1432
1489
  return () => {
1433
1490
  showSubscription.remove();
1434
1491
  willShowSubscription.remove();
1435
1492
  hideSubscription.remove();
1436
1493
  didHideSubscription.remove();
1437
1494
  };
1438
- }, [height, panelHeight, positionBottomAnimation]);
1495
+ }, [height, panelHeight, positionBottomAnimation, isOpen]);
1439
1496
  (0, import_react13.useImperativeHandle)(ref, () => ({
1440
1497
  setAddonsPanelOpen: (open) => {
1441
1498
  if (open) {
@@ -1445,11 +1502,8 @@ var MobileAddonsPanel = (0, import_react13.forwardRef)(
1445
1502
  }
1446
1503
  }
1447
1504
  }));
1448
- if (!mobileMenuOpen) {
1449
- return null;
1450
- }
1451
1505
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1452
- import_react_native7.Animated.View,
1506
+ import_react_native8.Animated.View,
1453
1507
  {
1454
1508
  style: {
1455
1509
  position: "absolute",
@@ -1460,13 +1514,13 @@ var MobileAddonsPanel = (0, import_react13.forwardRef)(
1460
1514
  transform: [{ translateY: positionBottomAnimation }]
1461
1515
  },
1462
1516
  children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1463
- import_react_native7.View,
1517
+ import_react_native8.View,
1464
1518
  {
1465
1519
  style: {
1466
1520
  justifyContent: "flex-end"
1467
1521
  },
1468
1522
  children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1469
- import_react_native7.View,
1523
+ import_react_native8.View,
1470
1524
  {
1471
1525
  style: {
1472
1526
  height: "100%",
@@ -1474,7 +1528,7 @@ var MobileAddonsPanel = (0, import_react13.forwardRef)(
1474
1528
  paddingTop: 10,
1475
1529
  borderTopColor: theme.appBorderColor,
1476
1530
  borderTopWidth: 1,
1477
- paddingBottom: import_react_native7.Platform.OS === "android" ? 16 : 0
1531
+ paddingBottom: import_react_native8.Platform.OS === "android" ? 16 : 0
1478
1532
  },
1479
1533
  children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1480
1534
  AddonsTabs,
@@ -1525,10 +1579,10 @@ var AddonsTabs = ({ onClose, storyId }) => {
1525
1579
  const [addonSelected, setAddonSelected] = (0, import_react13.useState)(Object.keys(panels)[0]);
1526
1580
  const panel = (0, import_react13.useMemo)(() => {
1527
1581
  if (!storyId) {
1528
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native7.View, { style: centeredStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native7.Text, { children: "No Story Selected" }) });
1582
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native8.View, { style: centeredStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native8.Text, { children: "No Story Selected" }) });
1529
1583
  }
1530
1584
  if (Object.keys(panels).length === 0) {
1531
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native7.View, { style: centeredStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native7.Text, { children: "No addons loaded." }) });
1585
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native8.View, { style: centeredStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native8.Text, { children: "No addons loaded." }) });
1532
1586
  }
1533
1587
  return panels[addonSelected].render({ active: true });
1534
1588
  }, [addonSelected, panels, storyId]);
@@ -1538,10 +1592,10 @@ var AddonsTabs = ({ onClose, storyId }) => {
1538
1592
  }),
1539
1593
  [insets]
1540
1594
  );
1541
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native7.View, { style: addonsTabsContainerStyle, children: [
1542
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native7.View, { style: addonsTabsStyle, children: [
1595
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native8.View, { style: addonsTabsContainerStyle, children: [
1596
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native8.View, { style: addonsTabsStyle, children: [
1543
1597
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1544
- import_react_native7.ScrollView,
1598
+ import_react_native8.ScrollView,
1545
1599
  {
1546
1600
  horizontal: true,
1547
1601
  showsHorizontalScrollIndicator: false,
@@ -1571,12 +1625,13 @@ var AddonsTabs = ({ onClose, storyId }) => {
1571
1625
  )
1572
1626
  ] }),
1573
1627
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1574
- import_react_native7.ScrollView,
1628
+ import_react_native8.ScrollView,
1575
1629
  {
1576
1630
  style: addonsScrollStyle,
1577
1631
  contentContainerStyle: scrollContentContainerStyle,
1578
1632
  children: panel
1579
- }
1633
+ },
1634
+ `addons-scroll-${storyId}`
1580
1635
  )
1581
1636
  ] });
1582
1637
  };
@@ -1602,16 +1657,65 @@ var TabText = import_react_native_theming8.styled.Text(({ theme, active }) => ({
1602
1657
  // src/MobileMenuDrawer.tsx
1603
1658
  var import_react_native_theming9 = require("@storybook/react-native-theming");
1604
1659
  var import_react14 = require("react");
1605
- var import_react_native8 = require("react-native");
1660
+ var import_react_native9 = require("react-native");
1606
1661
  var import_jsx_runtime11 = require("react/jsx-runtime");
1662
+ var useAnimatedModalHeight = () => {
1663
+ const { height } = (0, import_react_native9.useWindowDimensions)();
1664
+ const animatedHeight = useAnimatedValue(0.65 * height);
1665
+ (0, import_react14.useEffect)(() => {
1666
+ const modalHeight = 0.65 * height;
1667
+ const maxModalHeight = 0.85 * height;
1668
+ const expand = (duration = 250) => import_react_native9.Animated.timing(animatedHeight, {
1669
+ toValue: maxModalHeight,
1670
+ duration,
1671
+ useNativeDriver: false
1672
+ }).start();
1673
+ const collapse = (duration = 250) => import_react_native9.Animated.timing(animatedHeight, {
1674
+ toValue: modalHeight,
1675
+ duration,
1676
+ useNativeDriver: false
1677
+ }).start();
1678
+ const handleKeyboardWillShow = (e) => {
1679
+ if (import_react_native9.Platform.OS === "ios") {
1680
+ expand(e.duration);
1681
+ }
1682
+ };
1683
+ const handleKeyboardDidShow = (e) => {
1684
+ if (import_react_native9.Platform.OS === "android") {
1685
+ expand();
1686
+ }
1687
+ };
1688
+ const handleKeyboardWillHide = (e) => {
1689
+ if (import_react_native9.Platform.OS === "ios") {
1690
+ collapse(e.duration);
1691
+ }
1692
+ };
1693
+ const handleKeyboardDidHide = (e) => {
1694
+ if (import_react_native9.Platform.OS === "android") {
1695
+ collapse();
1696
+ }
1697
+ };
1698
+ const subscriptions = [
1699
+ import_react_native9.Keyboard.addListener("keyboardWillShow", handleKeyboardWillShow),
1700
+ import_react_native9.Keyboard.addListener("keyboardDidShow", handleKeyboardDidShow),
1701
+ import_react_native9.Keyboard.addListener("keyboardWillHide", handleKeyboardWillHide),
1702
+ import_react_native9.Keyboard.addListener("keyboardDidHide", handleKeyboardDidHide)
1703
+ ];
1704
+ return () => {
1705
+ subscriptions.forEach((subscription) => subscription.remove());
1706
+ };
1707
+ }, [animatedHeight, height]);
1708
+ return animatedHeight;
1709
+ };
1607
1710
  var MobileMenuDrawer = (0, import_react14.memo)(
1608
1711
  (0, import_react14.forwardRef)(({ children }, ref) => {
1609
1712
  const [mobileMenuOpen, setMobileMenuOpen] = (0, import_react14.useState)(false);
1610
1713
  const { scrollToSelectedNode, scrollRef } = useSelectedNode();
1611
1714
  const theme = (0, import_react_native_theming9.useTheme)();
1715
+ const animatedHeight = useAnimatedModalHeight();
1612
1716
  const dragY = useAnimatedValue(0);
1613
1717
  const panResponder = (0, import_react14.useRef)(
1614
- import_react_native8.PanResponder.create({
1718
+ import_react_native9.PanResponder.create({
1615
1719
  onStartShouldSetPanResponder: () => true,
1616
1720
  onMoveShouldSetPanResponder: (_, gestureState) => {
1617
1721
  return gestureState.dy > 0;
@@ -1623,10 +1727,10 @@ var MobileMenuDrawer = (0, import_react14.memo)(
1623
1727
  },
1624
1728
  onPanResponderRelease: (_, gestureState) => {
1625
1729
  if (gestureState.dy > 50) {
1626
- import_react_native8.Keyboard.dismiss();
1730
+ import_react_native9.Keyboard.dismiss();
1627
1731
  setMobileMenuOpen(false);
1628
1732
  }
1629
- import_react_native8.Animated.timing(dragY, {
1733
+ import_react_native9.Animated.timing(dragY, {
1630
1734
  toValue: 0,
1631
1735
  duration: 300,
1632
1736
  useNativeDriver: true
@@ -1641,7 +1745,7 @@ var MobileMenuDrawer = (0, import_react14.memo)(
1641
1745
  scrollToSelectedNode();
1642
1746
  setMobileMenuOpen(true);
1643
1747
  } else {
1644
- import_react_native8.Keyboard.dismiss();
1748
+ import_react_native9.Keyboard.dismiss();
1645
1749
  setMobileMenuOpen(false);
1646
1750
  }
1647
1751
  }
@@ -1659,58 +1763,64 @@ var MobileMenuDrawer = (0, import_react14.memo)(
1659
1763
  [theme.color.mediumdark]
1660
1764
  );
1661
1765
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1662
- import_react_native8.Modal,
1766
+ import_react_native9.Modal,
1663
1767
  {
1664
1768
  visible: mobileMenuOpen,
1665
1769
  animationType: "slide",
1666
1770
  transparent: true,
1667
1771
  statusBarTranslucent: true,
1668
1772
  onRequestClose: () => setMobileMenuOpen(false),
1669
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native8.KeyboardAvoidingView, { behavior: "height", style: { flex: 1 }, children: [
1670
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native8.View, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native8.Pressable, { style: { flex: 1 }, onPress: () => setMobileMenuOpen(false) }) }),
1671
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1672
- import_react_native8.Animated.View,
1773
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native9.Animated.View, { style: { flex: 1 }, children: [
1774
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native9.View, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native9.Pressable, { style: { flex: 1 }, onPress: () => setMobileMenuOpen(false) }) }),
1775
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1776
+ import_react_native9.Animated.View,
1673
1777
  {
1674
- style: [
1778
+ style: { backgroundColor: theme.background.content, height: animatedHeight },
1779
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1780
+ import_react_native9.Animated.View,
1675
1781
  {
1676
- height: "65%",
1677
- borderTopColor: theme.appBorderColor,
1678
- borderTopWidth: 1,
1679
- borderStyle: "solid",
1680
- backgroundColor: theme.background.content,
1681
- elevation: 8
1682
- },
1683
- { transform: [{ translateY: dragY }] }
1684
- ],
1685
- children: [
1686
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1687
- import_react_native8.View,
1688
- {
1689
- ...panResponder.panHandlers,
1690
- style: {
1691
- alignItems: "center",
1692
- justifyContent: "center",
1693
- backgroundColor: theme.background.content
1694
- },
1695
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native8.View, { style: handleStyle })
1696
- }
1697
- ),
1698
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1699
- import_react_native8.ScrollView,
1700
- {
1701
- ref: scrollRef,
1702
- keyboardShouldPersistTaps: "handled",
1703
- style: {
1782
+ style: [
1783
+ {
1704
1784
  flex: 1,
1705
- paddingBottom: 150,
1706
- alignSelf: "flex-end",
1707
- width: "100%",
1708
- backgroundColor: theme.background.content
1785
+ borderTopColor: theme.appBorderColor,
1786
+ borderTopWidth: 1,
1787
+ borderStyle: "solid",
1788
+ backgroundColor: theme.background.content,
1789
+ elevation: 8
1709
1790
  },
1710
- children
1711
- }
1712
- )
1713
- ]
1791
+ { transform: [{ translateY: dragY }] }
1792
+ ],
1793
+ children: [
1794
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1795
+ import_react_native9.View,
1796
+ {
1797
+ ...panResponder.panHandlers,
1798
+ style: {
1799
+ alignItems: "center",
1800
+ justifyContent: "center",
1801
+ backgroundColor: theme.background.content
1802
+ },
1803
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native9.View, { style: handleStyle })
1804
+ }
1805
+ ),
1806
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1807
+ import_react_native9.ScrollView,
1808
+ {
1809
+ ref: scrollRef,
1810
+ keyboardShouldPersistTaps: "handled",
1811
+ style: {
1812
+ flex: 1,
1813
+ paddingBottom: 150,
1814
+ alignSelf: "flex-end",
1815
+ width: "100%",
1816
+ backgroundColor: theme.background.content
1817
+ },
1818
+ children
1819
+ }
1820
+ )
1821
+ ]
1822
+ }
1823
+ )
1714
1824
  }
1715
1825
  )
1716
1826
  ] })
@@ -1721,7 +1831,7 @@ var MobileMenuDrawer = (0, import_react14.memo)(
1721
1831
 
1722
1832
  // src/StorybookLogo.tsx
1723
1833
  var import_react15 = require("react");
1724
- var import_react_native9 = require("react-native");
1834
+ var import_react_native10 = require("react-native");
1725
1835
  var import_jsx_runtime12 = require("react/jsx-runtime");
1726
1836
  var WIDTH = 125;
1727
1837
  var HEIGHT = 25;
@@ -1745,7 +1855,7 @@ var BrandLogo = ({ theme }) => {
1745
1855
  return theme.brand.image;
1746
1856
  }
1747
1857
  const image = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1748
- import_react_native9.Image,
1858
+ import_react_native10.Image,
1749
1859
  {
1750
1860
  source: theme.brand.image,
1751
1861
  resizeMode: theme.brand.resizeMode ?? "contain",
@@ -1754,10 +1864,10 @@ var BrandLogo = ({ theme }) => {
1754
1864
  );
1755
1865
  if (theme.brand.url) {
1756
1866
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1757
- import_react_native9.TouchableOpacity,
1867
+ import_react_native10.TouchableOpacity,
1758
1868
  {
1759
1869
  onPress: () => {
1760
- if (theme.brand.url) import_react_native9.Linking.openURL(theme.brand.url);
1870
+ if (theme.brand.url) import_react_native10.Linking.openURL(theme.brand.url);
1761
1871
  },
1762
1872
  children: image
1763
1873
  }
@@ -1775,13 +1885,13 @@ var BrandTitle = ({ theme }) => {
1775
1885
  fontSize: theme.typography.size.m1
1776
1886
  };
1777
1887
  }, [theme]);
1778
- const title = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native9.Text, { style: brandTitleStyle, numberOfLines: 1, ellipsizeMode: "tail", children: theme.brand.title });
1888
+ const title = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native10.Text, { style: brandTitleStyle, numberOfLines: 1, ellipsizeMode: "tail", children: theme.brand.title });
1779
1889
  if (theme.brand.url) {
1780
1890
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1781
- import_react_native9.TouchableOpacity,
1891
+ import_react_native10.TouchableOpacity,
1782
1892
  {
1783
1893
  onPress: () => {
1784
- if (theme.brand.url) import_react_native9.Linking.openURL(theme.brand.url);
1894
+ if (theme.brand.url) import_react_native10.Linking.openURL(theme.brand.url);
1785
1895
  },
1786
1896
  children: title
1787
1897
  }
@@ -1804,6 +1914,7 @@ var StorybookLogo = ({ theme }) => {
1804
1914
 
1805
1915
  // src/Layout.tsx
1806
1916
  var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
1917
+ var import_portal = require("@gorhom/portal");
1807
1918
  var import_jsx_runtime13 = require("react/jsx-runtime");
1808
1919
  var desktopLogoContainer = {
1809
1920
  flexDirection: "row",
@@ -1825,7 +1936,10 @@ var mobileMenuDrawerContentStyle = {
1825
1936
  paddingTop: 4,
1826
1937
  paddingBottom: 4
1827
1938
  };
1828
- var LiteUI = ({ storage, theme, storyHash, story, children }) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_safe_area_context2.SafeAreaProvider, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_theming10.ThemeProvider, { theme, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_ui_common7.StorageProvider, { storage, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_ui_common7.LayoutProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Layout, { storyHash, story, children }) }) }) }) });
1939
+ var LiteUI = ({ storage, theme, storyHash, story, children }) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_safe_area_context2.SafeAreaProvider, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_theming10.ThemeProvider, { theme, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_ui_common7.StorageProvider, { storage, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_ui_common7.LayoutProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_portal.PortalProvider, { shouldAddRootHost: false, children: [
1940
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Layout, { storyHash, story, children }),
1941
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_portal.PortalHost, { name: "storybook-lite-ui-root" })
1942
+ ] }) }) }) }) });
1829
1943
  var Layout = ({
1830
1944
  storyHash,
1831
1945
  story,
@@ -1885,7 +1999,7 @@ var Layout = ({
1885
1999
  const fullScreenButtonStyle = (0, import_react_native_ui_common7.useStyle)(
1886
2000
  () => ({
1887
2001
  position: "absolute",
1888
- bottom: uiHidden ? 56 : 16,
2002
+ bottom: uiHidden ? insets.bottom + 56 : 16,
1889
2003
  right: 16,
1890
2004
  backgroundColor: theme.background.content,
1891
2005
  padding: 4,
@@ -1908,9 +2022,9 @@ var Layout = ({
1908
2022
  const channel = import_manager_api2.addons.getChannel();
1909
2023
  channel.emit(import_core_events.SET_CURRENT_STORY, { storyId: newStoryId });
1910
2024
  }, []);
1911
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native10.View, { style: containerStyle, children: [
1912
- isDesktop ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native10.View, { style: desktopSidebarStyle, children: desktopSidebarOpen ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native10.ScrollView, { keyboardShouldPersistTaps: "handled", children: [
1913
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native10.View, { style: desktopLogoContainer, children: [
2025
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native11.View, { style: containerStyle, children: [
2026
+ isDesktop ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native11.View, { style: desktopSidebarStyle, children: desktopSidebarOpen ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native11.ScrollView, { keyboardShouldPersistTaps: "handled", children: [
2027
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native11.View, { style: desktopLogoContainer, children: [
1914
2028
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StorybookLogo, { theme }),
1915
2029
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_ui_common7.IconButton, { onPress: () => setDesktopSidebarOpen(false), Icon: MenuIcon })
1916
2030
  ] }),
@@ -1928,17 +2042,17 @@ var Layout = ({
1928
2042
  }
1929
2043
  )
1930
2044
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_ui_common7.IconButton, { onPress: () => setDesktopSidebarOpen(true), Icon: MenuIcon }) }) : null,
1931
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native10.View, { style: mobileContentStyle, children: [
1932
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native10.View, { style: contentContainerStyle, children }),
2045
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native11.View, { style: mobileContentStyle, children: [
2046
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native11.View, { style: contentContainerStyle, children }),
1933
2047
  story?.parameters?.hideFullScreenButton || isDesktop ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1934
- import_react_native10.TouchableOpacity,
2048
+ import_react_native11.TouchableOpacity,
1935
2049
  {
1936
2050
  style: fullScreenButtonStyle,
1937
2051
  onPress: () => setUiHidden((prev) => !prev),
1938
2052
  children: uiHidden ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CloseFullscreenIcon, { color: theme.color.mediumdark }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FullscreenIcon, { color: theme.color.mediumdark })
1939
2053
  }
1940
2054
  ),
1941
- isDesktop ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native10.View, { style: desktopAddonsPanelStyle, children: desktopAddonsPanelOpen ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AddonsTabs, { storyId: story?.id, onClose: () => setDesktopAddonsPanelOpen(false) }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2055
+ isDesktop ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native11.View, { style: desktopAddonsPanelStyle, children: desktopAddonsPanelOpen ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AddonsTabs, { storyId: story?.id, onClose: () => setDesktopAddonsPanelOpen(false) }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1942
2056
  import_react_native_ui_common7.IconButton,
1943
2057
  {
1944
2058
  style: iconFloatRightStyle,
@@ -1957,7 +2071,7 @@ var Layout = ({
1957
2071
  onPress: () => mobileMenuDrawerRef.current?.setMobileMenuOpen(true),
1958
2072
  children: [
1959
2073
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(MenuIcon, { color: theme.color.mediumdark }),
1960
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native10.Text, { style: navButtonTextStyle, numberOfLines: 1, children: [
2074
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native11.Text, { style: navButtonTextStyle, numberOfLines: 1, children: [
1961
2075
  story?.title,
1962
2076
  "/",
1963
2077
  story?.name
@@ -1975,7 +2089,7 @@ var Layout = ({
1975
2089
  )
1976
2090
  ] }) }) : null,
1977
2091
  isDesktop ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SelectedNodeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(MobileMenuDrawer, { ref: mobileMenuDrawerRef, children: [
1978
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native10.View, { style: mobileMenuDrawerContentStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StorybookLogo, { theme }) }),
2092
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native11.View, { style: mobileMenuDrawerContentStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StorybookLogo, { theme }) }),
1979
2093
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1980
2094
  Sidebar,
1981
2095
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storybook/react-native-ui-lite",
3
- "version": "10.0.1",
3
+ "version": "10.0.2-alpha.0",
4
4
  "description": "lightweight ui components for react native storybook",
5
5
  "keywords": [
6
6
  "react",
@@ -58,9 +58,10 @@
58
58
  "typescript": "~5.9.3"
59
59
  },
60
60
  "dependencies": {
61
+ "@gorhom/portal": "^1.0.14",
61
62
  "@storybook/react": "^10.0.1",
62
- "@storybook/react-native-theming": "^10.0.1",
63
- "@storybook/react-native-ui-common": "^10.0.1",
63
+ "@storybook/react-native-theming": "^10.0.2-alpha.0",
64
+ "@storybook/react-native-ui-common": "^10.0.2-alpha.0",
64
65
  "fuse.js": "^7.1.0",
65
66
  "polished": "^4.3.1",
66
67
  "react-native-safe-area-context": "^5"
@@ -76,5 +77,5 @@
76
77
  "publishConfig": {
77
78
  "access": "public"
78
79
  },
79
- "gitHead": "7604d1c21f15767940d30d9c5dcf9da73d0a9fe7"
80
+ "gitHead": "f88cc47d0ddc9958f4a8b029f8366de9184bfc8e"
80
81
  }
package/src/Layout.tsx CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  useStyle,
12
12
  } from '@storybook/react-native-ui-common';
13
13
  import { ReactElement, ReactNode, useCallback, useRef, useState } from 'react';
14
- import { Platform, ScrollView, Text, TouchableOpacity, View, ViewStyle } from 'react-native';
14
+ import { ScrollView, Text, TouchableOpacity, View, ViewStyle } from 'react-native';
15
15
  import { SET_CURRENT_STORY } from 'storybook/internal/core-events';
16
16
  import { addons } from 'storybook/manager-api';
17
17
  import { type API_IndexHash } from 'storybook/internal/types';
@@ -28,6 +28,7 @@ import {
28
28
  MenuIcon,
29
29
  } from './icon/iconDataUris';
30
30
  import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
31
+ import { Portal, PortalHost, PortalProvider } from '@gorhom/portal';
31
32
 
32
33
  const desktopLogoContainer = {
33
34
  flexDirection: 'row',
@@ -62,9 +63,12 @@ export const LiteUI: SBUI = ({ storage, theme, storyHash, story, children }): Re
62
63
  <ThemeProvider theme={theme}>
63
64
  <StorageProvider storage={storage}>
64
65
  <LayoutProvider>
65
- <Layout storyHash={storyHash} story={story}>
66
- {children}
67
- </Layout>
66
+ <PortalProvider shouldAddRootHost={false}>
67
+ <Layout storyHash={storyHash} story={story}>
68
+ {children}
69
+ </Layout>
70
+ <PortalHost name="storybook-lite-ui-root" />
71
+ </PortalProvider>
68
72
  </LayoutProvider>
69
73
  </StorageProvider>
70
74
  </ThemeProvider>
@@ -145,7 +149,7 @@ export const Layout = ({
145
149
  const fullScreenButtonStyle = useStyle(
146
150
  () => ({
147
151
  position: 'absolute',
148
- bottom: uiHidden ? 56 : 16,
152
+ bottom: uiHidden ? insets.bottom + 56 : 16,
149
153
  right: 16,
150
154
  backgroundColor: theme.background.content,
151
155
  padding: 4,
@@ -1,6 +1,6 @@
1
1
  import { styled, useTheme } from '@storybook/react-native-theming';
2
2
  import { IconButton, useStyle } from '@storybook/react-native-ui-common';
3
- import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from 'react';
3
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react';
4
4
  import {
5
5
  Animated,
6
6
  Easing,
@@ -26,48 +26,91 @@ export interface MobileAddonsPanelRef {
26
26
  export const MobileAddonsPanel = forwardRef<MobileAddonsPanelRef, { storyId?: string }>(
27
27
  ({ storyId }, ref) => {
28
28
  const theme = useTheme();
29
- const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
30
29
  const { height } = useWindowDimensions();
31
- const panelHeight = useAnimatedValue(height / 2);
32
- const positionBottomAnimation = useAnimatedValue(0);
30
+ const panelHeight = useAnimatedValue(0);
31
+ const positionBottomAnimation = useAnimatedValue(height / 2);
32
+ const [isOpen, setIsOpen] = useState(false);
33
+
34
+ const setMobileMenuOpen = useCallback(
35
+ (open: boolean) => {
36
+ setIsOpen(open);
37
+
38
+ if (open) {
39
+ Animated.parallel([
40
+ Animated.timing(positionBottomAnimation, {
41
+ toValue: 0, // Negative to move up
42
+ duration: 350,
43
+ useNativeDriver: false,
44
+ easing: Easing.inOut(Easing.cubic),
45
+ }),
46
+
47
+ Animated.timing(panelHeight, {
48
+ toValue: height / 2,
49
+ duration: 350,
50
+ useNativeDriver: false,
51
+ easing: Easing.inOut(Easing.cubic),
52
+ }),
53
+ ]).start();
54
+ } else {
55
+ Animated.parallel([
56
+ Animated.timing(positionBottomAnimation, {
57
+ toValue: height / 2,
58
+ duration: 350,
59
+ useNativeDriver: false,
60
+ easing: Easing.inOut(Easing.cubic),
61
+ }),
62
+ Animated.timing(panelHeight, {
63
+ toValue: 0,
64
+ duration: 350,
65
+ useNativeDriver: false,
66
+ easing: Easing.inOut(Easing.cubic),
67
+ }),
68
+ ]).start();
69
+ }
70
+ },
71
+ [height, positionBottomAnimation, panelHeight]
72
+ );
33
73
 
34
74
  useEffect(() => {
35
75
  // Define keyboard show handler
36
76
  const handleKeyboardShow = ({ endCoordinates, duration, easing }) => {
37
- Animated.parallel([
38
- Animated.timing(positionBottomAnimation, {
39
- toValue: -endCoordinates.height, // Negative to move up
40
- duration,
41
- useNativeDriver: false,
42
- easing: Easing[easing] || Easing.out(Easing.ease),
43
- }),
44
-
45
- Animated.timing(panelHeight, {
46
- toValue: (height - endCoordinates.height) / 2,
47
- duration: duration + 250,
48
- useNativeDriver: false,
49
- easing: Easing[easing] || Easing.out(Easing.ease),
50
- }),
51
- ]).start();
77
+ if (isOpen) {
78
+ Animated.parallel([
79
+ Animated.timing(panelHeight, {
80
+ toValue: (height - endCoordinates.height) / 2,
81
+ duration,
82
+ useNativeDriver: false,
83
+ easing: Easing[easing] || Easing.out(Easing.ease),
84
+ }),
85
+ Animated.timing(positionBottomAnimation, {
86
+ toValue: -endCoordinates.height, // Negative to move up
87
+ duration,
88
+ useNativeDriver: false,
89
+ easing: Easing[easing] || Easing.out(Easing.ease),
90
+ }),
91
+ ]).start();
92
+ }
52
93
  };
53
94
 
54
95
  // Define keyboard hide handler
55
96
  const handleKeyboardHide = ({ duration, easing }) => {
56
- Animated.parallel([
57
- Animated.timing(positionBottomAnimation, {
58
- toValue: 0, // Back to original position
59
- duration,
60
- useNativeDriver: false,
61
- easing: Easing[easing] || Easing.out(Easing.ease),
62
- }),
97
+ if (isOpen) {
98
+ Animated.parallel([
99
+ Animated.timing(positionBottomAnimation, {
100
+ toValue: 0, // Back to original position
101
+ duration,
102
+ useNativeDriver: false,
103
+ easing: Easing[easing] || Easing.out(Easing.ease),
104
+ }),
63
105
 
64
- Animated.timing(panelHeight, {
65
- toValue: height / 2,
66
- duration,
67
- useNativeDriver: false,
68
- easing: Easing[easing] || Easing.out(Easing.ease),
69
- }),
70
- ]).start();
106
+ Animated.timing(panelHeight, {
107
+ toValue: height / 2,
108
+ duration,
109
+ useNativeDriver: false,
110
+ easing: Easing[easing] || Easing.out(Easing.ease),
111
+ }),
112
+ ]).start();
113
+ }
71
114
  };
72
115
 
73
116
  // Add keyboard event listeners
@@ -83,7 +126,7 @@ export const MobileAddonsPanel = forwardRef<MobileAddonsPanelRef, { storyId?: st
83
126
  hideSubscription.remove();
84
127
  didHideSubscription.remove();
85
128
  };
86
- }, [height, panelHeight, positionBottomAnimation]);
129
+ }, [height, panelHeight, positionBottomAnimation, isOpen]);
87
130
 
88
131
  useImperativeHandle(ref, () => ({
89
132
  setAddonsPanelOpen: (open: boolean) => {
@@ -95,10 +138,6 @@ export const MobileAddonsPanel = forwardRef<MobileAddonsPanelRef, { storyId?: st
95
138
  },
96
139
  }));
97
140
 
98
- if (!mobileMenuOpen) {
99
- return null;
100
- }
101
-
102
141
  return (
103
142
  <Animated.View
104
143
  style={{
@@ -234,6 +273,7 @@ export const AddonsTabs = ({ onClose, storyId }: { onClose?: () => void; storyId
234
273
  />
235
274
  </View>
236
275
  <ScrollView
276
+ key={`addons-scroll-${storyId}`}
237
277
  style={addonsScrollStyle}
238
278
  // keyboardShouldPersistTaps="handled"
239
279
  contentContainerStyle={scrollContentContainerStyle}
@@ -1,15 +1,26 @@
1
1
  import { useTheme } from '@storybook/react-native-theming';
2
- import { forwardRef, memo, ReactNode, useImperativeHandle, useMemo, useRef, useState } from 'react';
2
+ import {
3
+ forwardRef,
4
+ memo,
5
+ ReactNode,
6
+ useEffect,
7
+ useImperativeHandle,
8
+ useMemo,
9
+ useRef,
10
+ useState,
11
+ } from 'react';
3
12
  import {
4
13
  Animated,
5
14
  Keyboard,
6
- KeyboardAvoidingView,
15
+ useWindowDimensions,
7
16
  Modal,
8
17
  PanResponder,
9
18
  PanResponderInstance,
10
19
  Pressable,
11
20
  ScrollView,
12
21
  View,
22
+ KeyboardEventListener,
23
+ Platform,
13
24
  } from 'react-native';
14
25
  import { useSelectedNode } from './SelectedNodeProvider';
15
26
  import useAnimatedValue from './useAnimatedValue';
@@ -22,11 +33,73 @@ export interface MobileMenuDrawerRef {
22
33
  setMobileMenuOpen: (isOpen: boolean) => void;
23
34
  }
24
35
 
36
+ export const useAnimatedModalHeight = () => {
37
+ const { height } = useWindowDimensions();
38
+ const animatedHeight = useAnimatedValue(0.65 * height);
39
+
40
+ useEffect(() => {
41
+ const modalHeight = 0.65 * height;
42
+ const maxModalHeight = 0.85 * height;
43
+
44
+ const expand = (duration: number = 250) =>
45
+ Animated.timing(animatedHeight, {
46
+ toValue: maxModalHeight,
47
+ duration,
48
+ useNativeDriver: false,
49
+ }).start();
50
+
51
+ const collapse = (duration: number = 250) =>
52
+ Animated.timing(animatedHeight, {
53
+ toValue: modalHeight,
54
+ duration,
55
+ useNativeDriver: false,
56
+ }).start();
57
+
58
+ const handleKeyboardWillShow: KeyboardEventListener = (e) => {
59
+ if (Platform.OS === 'ios') {
60
+ expand(e.duration);
61
+ }
62
+ };
63
+
64
+ const handleKeyboardDidShow: KeyboardEventListener = (e) => {
65
+ if (Platform.OS === 'android') {
66
+ expand();
67
+ }
68
+ };
69
+
70
+ const handleKeyboardWillHide: KeyboardEventListener = (e) => {
71
+ if (Platform.OS === 'ios') {
72
+ collapse(e.duration);
73
+ }
74
+ };
75
+
76
+ const handleKeyboardDidHide: KeyboardEventListener = (e) => {
77
+ if (Platform.OS === 'android') {
78
+ collapse();
79
+ }
80
+ };
81
+
82
+ const subscriptions = [
83
+ Keyboard.addListener('keyboardWillShow', handleKeyboardWillShow),
84
+ Keyboard.addListener('keyboardDidShow', handleKeyboardDidShow),
85
+ Keyboard.addListener('keyboardWillHide', handleKeyboardWillHide),
86
+ Keyboard.addListener('keyboardDidHide', handleKeyboardDidHide),
87
+ ];
88
+
89
+ return () => {
90
+ subscriptions.forEach((subscription) => subscription.remove());
91
+ };
92
+ }, [animatedHeight, height]);
93
+
94
+ return animatedHeight;
95
+ };
96
+
25
97
  export const MobileMenuDrawer = memo(
26
98
  forwardRef<MobileMenuDrawerRef, MobileMenuDrawerProps>(({ children }, ref) => {
27
99
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
28
100
  const { scrollToSelectedNode, scrollRef } = useSelectedNode();
29
101
  const theme = useTheme();
102
+ const animatedHeight = useAnimatedModalHeight();
30
103
 
31
104
  // Create a reference for the drag handle animation
32
105
  const dragY = useAnimatedValue(0);
@@ -95,51 +168,55 @@ export const MobileMenuDrawer = memo(
95
168
  statusBarTranslucent
96
169
  onRequestClose={() => setMobileMenuOpen(false)}
97
170
  >
98
- <KeyboardAvoidingView behavior="height" style={{ flex: 1 }}>
171
+ <Animated.View style={{ flex: 1 }}>
99
172
  <View style={{ flex: 1 }}>
100
173
  <Pressable style={{ flex: 1 }} onPress={() => setMobileMenuOpen(false)}></Pressable>
101
174
  </View>
102
175
 
103
176
  <Animated.View
104
- style={[
105
- {
106
- height: '65%',
107
- borderTopColor: theme.appBorderColor,
108
- borderTopWidth: 1,
109
- borderStyle: 'solid',
110
- backgroundColor: theme.background.content,
111
- elevation: 8,
112
- },
113
- { transform: [{ translateY: dragY }] },
114
- ]}
177
+ style={{ backgroundColor: theme.background.content, height: animatedHeight }}
115
178
  >
116
- {/* Drag handle */}
117
- <View
118
- {...panResponder.panHandlers}
119
- style={{
120
- alignItems: 'center',
121
- justifyContent: 'center',
122
- backgroundColor: theme.background.content,
123
- }}
124
- >
125
- <View style={handleStyle} />
126
- </View>
127
-
128
- <ScrollView
129
- ref={scrollRef}
130
- keyboardShouldPersistTaps="handled"
131
- style={{
132
- flex: 1,
133
- paddingBottom: 150,
134
- alignSelf: 'flex-end',
135
- width: '100%',
136
- backgroundColor: theme.background.content,
137
- }}
179
+ <Animated.View
180
+ style={[
181
+ {
182
+ flex: 1,
183
+ borderTopColor: theme.appBorderColor,
184
+ borderTopWidth: 1,
185
+ borderStyle: 'solid',
186
+ backgroundColor: theme.background.content,
187
+ elevation: 8,
188
+ },
189
+ { transform: [{ translateY: dragY }] },
190
+ ]}
138
191
  >
139
- {children}
140
- </ScrollView>
192
+ {/* Drag handle */}
193
+ <View
194
+ {...panResponder.panHandlers}
195
+ style={{
196
+ alignItems: 'center',
197
+ justifyContent: 'center',
198
+ backgroundColor: theme.background.content,
199
+ }}
200
+ >
201
+ <View style={handleStyle} />
202
+ </View>
203
+
204
+ <ScrollView
205
+ ref={scrollRef}
206
+ keyboardShouldPersistTaps="handled"
207
+ style={{
208
+ flex: 1,
209
+ paddingBottom: 150,
210
+ alignSelf: 'flex-end',
211
+ width: '100%',
212
+ backgroundColor: theme.background.content,
213
+ }}
214
+ >
215
+ {children}
216
+ </ScrollView>
217
+ </Animated.View>
141
218
  </Animated.View>
142
- </KeyboardAvoidingView>
219
+ </Animated.View>
143
220
  </Modal>
144
221
  );
145
222
  })
package/src/Search.tsx CHANGED
@@ -53,9 +53,21 @@ const SearchField = styled.View({
53
53
  position: 'relative',
54
54
  });
55
55
 
56
+ const inputPlatformSpecificStyles = Platform.select({
57
+ macos: {
58
+ paddingVertical: 6,
59
+ },
60
+ android: {
61
+ minHeight: 32,
62
+ },
63
+ default: {
64
+ minHeight: 32,
65
+ height: 32,
66
+ },
67
+ });
68
+
56
69
  const Input = styled(TextInput)(({ theme }) => ({
57
- height: Platform.OS === 'android' ? 'auto' : 32,
58
- minHeight: 32,
70
+ ...inputPlatformSpecificStyles,
59
71
  paddingLeft: 28,
60
72
  paddingRight: 28,
61
73
  borderWidth: 1,
package/src/TreeNode.tsx CHANGED
@@ -2,7 +2,7 @@ import { styled, useTheme } from '@storybook/react-native-theming';
2
2
 
3
3
  import React, { ComponentProps, FC, forwardRef, useMemo } from 'react';
4
4
  import { transparentize } from 'polished';
5
- import { View } from 'react-native';
5
+ import { Platform, View } from 'react-native';
6
6
  import { CollapseIcon, ComponentIcon, GroupIcon, StoryIcon } from './icon/iconDataUris';
7
7
 
8
8
  export interface NodeProps {
@@ -98,9 +98,12 @@ export const GroupNode: FC<
98
98
  return theme.base === 'dark' ? theme.color.primary : theme.color.ultraviolet;
99
99
  }, [theme.base, theme.color.primary, theme.color.ultraviolet]);
100
100
 
101
+ const wrapperProps = Platform.OS === 'macos' ? { key: `${props.id}-${color}` } : {};
102
+
101
103
  return (
102
104
  <BranchNode isExpandable={isExpandable} {...props}>
103
- <Wrapper>
105
+ {/* workaround for macos icon color bug */}
106
+ <Wrapper {...wrapperProps}>
104
107
  {isExpandable && <CollapseIcon isExpanded={isExpanded} />}
105
108
  <GroupIcon width={14} height={14} color={color} />
106
109
  </Wrapper>
@@ -117,9 +120,12 @@ export const ComponentNode: FC<ComponentProps<typeof BranchNode>> = React.memo(
117
120
  return theme.color.secondary;
118
121
  }, [theme.color.secondary]);
119
122
 
123
+ const wrapperProps = Platform.OS === 'macos' ? { key: `${props.id}-${color}` } : {};
124
+
120
125
  return (
121
126
  <BranchNode isExpandable={isExpandable} {...props}>
122
- <Wrapper>
127
+ {/* workaround for macos icon color bug */}
128
+ <Wrapper {...wrapperProps}>
123
129
  {isExpandable && <CollapseIcon isExpanded={isExpanded} />}
124
130
  <ComponentIcon width={12} height={12} color={color} />
125
131
  </Wrapper>
@@ -140,9 +146,12 @@ export const StoryNode = React.memo(
140
146
  return props.selected ? theme.color.lightest : theme.color.seafoam;
141
147
  }, [props.selected, theme.color.lightest, theme.color.seafoam]);
142
148
 
149
+ const wrapperProps = Platform.OS === 'macos' ? { key: `${props.id}-${color}` } : {};
150
+
143
151
  return (
144
152
  <LeafNode {...props} ref={ref}>
145
- <Wrapper>
153
+ {/* workaround for macos icon color bug */}
154
+ <Wrapper {...wrapperProps}>
146
155
  <StoryIcon width={14} height={14} color={color} />
147
156
  </Wrapper>
148
157
  <LeafNodeText selected={props.selected}>{children}</LeafNodeText>
@@ -0,0 +1,63 @@
1
+ // taken from https://github.com/react-native-community/hooks/blob/main/src/useKeyboard.ts
2
+ import { useEffect, useState } from 'react';
3
+ import { Keyboard, KeyboardEventListener, KeyboardMetrics } from 'react-native';
4
+
5
+ const emptyCoordinates = Object.freeze({
6
+ screenX: 0,
7
+ screenY: 0,
8
+ width: 0,
9
+ height: 0,
10
+ });
11
+ const initialValue = {
12
+ start: emptyCoordinates,
13
+ end: emptyCoordinates,
14
+ };
15
+
16
+ export function useKeyboard() {
17
+ const [shown, setShown] = useState(false);
18
+ const [coordinates, setCoordinates] = useState<{
19
+ start: undefined | KeyboardMetrics;
20
+ end: KeyboardMetrics;
21
+ }>(initialValue);
22
+ const [keyboardHeight, setKeyboardHeight] = useState<number>(0);
23
+
24
+ const handleKeyboardWillShow: KeyboardEventListener = (e) => {
25
+ setCoordinates({ start: e.startCoordinates, end: e.endCoordinates });
26
+ };
27
+ const handleKeyboardDidShow: KeyboardEventListener = (e) => {
28
+ setShown(true);
29
+ setCoordinates({ start: e.startCoordinates, end: e.endCoordinates });
30
+ setKeyboardHeight(e.endCoordinates.height);
31
+ };
32
+ const handleKeyboardWillHide: KeyboardEventListener = (e) => {
33
+ setCoordinates({ start: e.startCoordinates, end: e.endCoordinates });
34
+ };
35
+ const handleKeyboardDidHide: KeyboardEventListener = (e) => {
36
+ setShown(false);
37
+ if (e) {
38
+ setCoordinates({ start: e.startCoordinates, end: e.endCoordinates });
39
+ } else {
40
+ setCoordinates(initialValue);
41
+ setKeyboardHeight(0);
42
+ }
43
+ };
44
+
45
+ useEffect(() => {
46
+ const subscriptions = [
47
+ Keyboard.addListener('keyboardWillShow', handleKeyboardWillShow),
48
+ Keyboard.addListener('keyboardDidShow', handleKeyboardDidShow),
49
+ Keyboard.addListener('keyboardWillHide', handleKeyboardWillHide),
50
+ Keyboard.addListener('keyboardDidHide', handleKeyboardDidHide),
51
+ ];
52
+
53
+ return () => {
54
+ subscriptions.forEach((subscription) => subscription.remove());
55
+ };
56
+ }, []);
57
+
58
+ return {
59
+ keyboardShown: shown,
60
+ coordinates,
61
+ keyboardHeight,
62
+ };
63
+ }