@storybook/react-native-ui-lite 10.0.1 → 10.0.2-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +154 -50
- package/package.json +5 -4
- package/src/Layout.tsx +12 -8
- package/src/MobileAddonsPanel.tsx +78 -38
- package/src/MobileMenuDrawer.tsx +116 -41
- package/src/Search.tsx +14 -2
- package/src/TreeNode.tsx +4 -3
- package/src/useKeyboard.ts +63 -0
package/dist/index.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ import { Item, ExpandAction, CombinedDataset, Selection, SBUI } from '@storybook
|
|
|
7
7
|
import { State, StoriesHash } from 'storybook/manager-api';
|
|
8
8
|
import { API_LoadedRefData, API_IndexHash } from 'storybook/internal/types';
|
|
9
9
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
10
|
-
import { StoryContext, Args } from 'storybook/internal/csf';
|
|
11
10
|
import { ReactRenderer } from '@storybook/react';
|
|
11
|
+
import { StoryContext, Args } from 'storybook/internal/csf';
|
|
12
12
|
|
|
13
13
|
interface NodeProps$1 {
|
|
14
14
|
children: React.ReactNode | React.ReactNode[];
|
package/dist/index.js
CHANGED
|
@@ -391,7 +391,7 @@ var GroupNode = import_react2.default.memo(function GroupNode2({
|
|
|
391
391
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Wrapper, { children: [
|
|
392
392
|
isExpandable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CollapseIcon, { isExpanded }),
|
|
393
393
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GroupIcon, { width: 14, height: 14, color })
|
|
394
|
-
] }),
|
|
394
|
+
] }, `group-${props.id}-${color}`),
|
|
395
395
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BranchNodeText, { children })
|
|
396
396
|
] });
|
|
397
397
|
});
|
|
@@ -405,7 +405,7 @@ var ComponentNode = import_react2.default.memo(
|
|
|
405
405
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Wrapper, { children: [
|
|
406
406
|
isExpandable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CollapseIcon, { isExpanded }),
|
|
407
407
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ComponentIcon, { width: 12, height: 12, color })
|
|
408
|
-
] }),
|
|
408
|
+
] }, `component-${props.id}-${color}`),
|
|
409
409
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BranchNodeText, { children })
|
|
410
410
|
] });
|
|
411
411
|
}
|
|
@@ -417,7 +417,7 @@ var StoryNode = import_react2.default.memo(
|
|
|
417
417
|
return props.selected ? theme.color.lightest : theme.color.seafoam;
|
|
418
418
|
}, [props.selected, theme.color.lightest, theme.color.seafoam]);
|
|
419
419
|
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 }) }),
|
|
420
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Wrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StoryIcon, { width: 14, height: 14, color }) }, `story-${props.id}-${color}`),
|
|
421
421
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LeafNodeText, { selected: props.selected, children })
|
|
422
422
|
] });
|
|
423
423
|
})
|
|
@@ -919,9 +919,20 @@ var SearchField = import_react_native_theming5.styled.View({
|
|
|
919
919
|
flexDirection: "column",
|
|
920
920
|
position: "relative"
|
|
921
921
|
});
|
|
922
|
+
var inputPlatformSpecificStyles = import_react_native3.Platform.select({
|
|
923
|
+
macos: {
|
|
924
|
+
paddingVertical: 6
|
|
925
|
+
},
|
|
926
|
+
android: {
|
|
927
|
+
minHeight: 32
|
|
928
|
+
},
|
|
929
|
+
default: {
|
|
930
|
+
minHeight: 32,
|
|
931
|
+
height: 32
|
|
932
|
+
}
|
|
933
|
+
});
|
|
922
934
|
var Input = (0, import_react_native_theming5.styled)(import_react_native3.TextInput)(({ theme }) => ({
|
|
923
|
-
|
|
924
|
-
minHeight: 32,
|
|
935
|
+
...inputPlatformSpecificStyles,
|
|
925
936
|
paddingLeft: 28,
|
|
926
937
|
paddingRight: 28,
|
|
927
938
|
borderWidth: 1,
|
|
@@ -1354,10 +1365,12 @@ var Sidebar = import_react11.default.memo(function Sidebar2({
|
|
|
1354
1365
|
});
|
|
1355
1366
|
|
|
1356
1367
|
// src/Layout.tsx
|
|
1368
|
+
var import_portal = require("@gorhom/portal");
|
|
1357
1369
|
var import_react_native_theming10 = require("@storybook/react-native-theming");
|
|
1358
1370
|
var import_react_native_ui_common7 = require("@storybook/react-native-ui-common");
|
|
1359
1371
|
var import_react16 = require("react");
|
|
1360
1372
|
var import_react_native10 = require("react-native");
|
|
1373
|
+
var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
|
|
1361
1374
|
var import_core_events = require("storybook/internal/core-events");
|
|
1362
1375
|
var import_manager_api2 = require("storybook/manager-api");
|
|
1363
1376
|
|
|
@@ -1386,44 +1399,86 @@ var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
|
1386
1399
|
var MobileAddonsPanel = (0, import_react13.forwardRef)(
|
|
1387
1400
|
({ storyId }, ref) => {
|
|
1388
1401
|
const theme = (0, import_react_native_theming8.useTheme)();
|
|
1389
|
-
const [mobileMenuOpen, setMobileMenuOpen] = (0, import_react13.useState)(false);
|
|
1390
1402
|
const { height } = (0, import_react_native7.useWindowDimensions)();
|
|
1391
|
-
const panelHeight = useAnimatedValue(
|
|
1392
|
-
const positionBottomAnimation = useAnimatedValue(
|
|
1403
|
+
const panelHeight = useAnimatedValue(0);
|
|
1404
|
+
const positionBottomAnimation = useAnimatedValue(height / 2);
|
|
1405
|
+
const [isOpen, setIsOpen] = (0, import_react13.useState)(false);
|
|
1406
|
+
const setMobileMenuOpen = (0, import_react13.useCallback)(
|
|
1407
|
+
(open) => {
|
|
1408
|
+
setIsOpen(open);
|
|
1409
|
+
if (open) {
|
|
1410
|
+
import_react_native7.Animated.parallel([
|
|
1411
|
+
import_react_native7.Animated.timing(positionBottomAnimation, {
|
|
1412
|
+
toValue: 0,
|
|
1413
|
+
// Negative to move up
|
|
1414
|
+
duration: 350,
|
|
1415
|
+
useNativeDriver: false,
|
|
1416
|
+
easing: import_react_native7.Easing.inOut(import_react_native7.Easing.cubic)
|
|
1417
|
+
}),
|
|
1418
|
+
import_react_native7.Animated.timing(panelHeight, {
|
|
1419
|
+
toValue: height / 2,
|
|
1420
|
+
duration: 350,
|
|
1421
|
+
useNativeDriver: false,
|
|
1422
|
+
easing: import_react_native7.Easing.inOut(import_react_native7.Easing.cubic)
|
|
1423
|
+
})
|
|
1424
|
+
]).start();
|
|
1425
|
+
} else {
|
|
1426
|
+
import_react_native7.Animated.parallel([
|
|
1427
|
+
import_react_native7.Animated.timing(positionBottomAnimation, {
|
|
1428
|
+
toValue: height / 2,
|
|
1429
|
+
duration: 350,
|
|
1430
|
+
useNativeDriver: false,
|
|
1431
|
+
easing: import_react_native7.Easing.inOut(import_react_native7.Easing.cubic)
|
|
1432
|
+
}),
|
|
1433
|
+
import_react_native7.Animated.timing(panelHeight, {
|
|
1434
|
+
toValue: 0,
|
|
1435
|
+
duration: 350,
|
|
1436
|
+
useNativeDriver: false,
|
|
1437
|
+
easing: import_react_native7.Easing.inOut(import_react_native7.Easing.cubic)
|
|
1438
|
+
})
|
|
1439
|
+
]).start();
|
|
1440
|
+
}
|
|
1441
|
+
},
|
|
1442
|
+
[height, positionBottomAnimation, panelHeight]
|
|
1443
|
+
);
|
|
1393
1444
|
(0, import_react13.useEffect)(() => {
|
|
1394
1445
|
const handleKeyboardShow = ({ endCoordinates, duration, easing }) => {
|
|
1395
|
-
|
|
1396
|
-
import_react_native7.Animated.
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1446
|
+
if (isOpen) {
|
|
1447
|
+
import_react_native7.Animated.parallel([
|
|
1448
|
+
import_react_native7.Animated.timing(panelHeight, {
|
|
1449
|
+
toValue: (height - endCoordinates.height) / 2,
|
|
1450
|
+
duration,
|
|
1451
|
+
useNativeDriver: false,
|
|
1452
|
+
easing: import_react_native7.Easing[easing] || import_react_native7.Easing.out(import_react_native7.Easing.ease)
|
|
1453
|
+
}),
|
|
1454
|
+
import_react_native7.Animated.timing(positionBottomAnimation, {
|
|
1455
|
+
toValue: -endCoordinates.height,
|
|
1456
|
+
// Negative to move up
|
|
1457
|
+
duration,
|
|
1458
|
+
useNativeDriver: false,
|
|
1459
|
+
easing: import_react_native7.Easing[easing] || import_react_native7.Easing.out(import_react_native7.Easing.ease)
|
|
1460
|
+
})
|
|
1461
|
+
]).start();
|
|
1462
|
+
}
|
|
1410
1463
|
};
|
|
1411
1464
|
const handleKeyboardHide = ({ duration, easing }) => {
|
|
1412
|
-
|
|
1413
|
-
import_react_native7.Animated.
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1465
|
+
if (isOpen) {
|
|
1466
|
+
import_react_native7.Animated.parallel([
|
|
1467
|
+
import_react_native7.Animated.timing(positionBottomAnimation, {
|
|
1468
|
+
toValue: 0,
|
|
1469
|
+
// Back to original position
|
|
1470
|
+
duration,
|
|
1471
|
+
useNativeDriver: false,
|
|
1472
|
+
easing: import_react_native7.Easing[easing] || import_react_native7.Easing.out(import_react_native7.Easing.ease)
|
|
1473
|
+
}),
|
|
1474
|
+
import_react_native7.Animated.timing(panelHeight, {
|
|
1475
|
+
toValue: height / 2,
|
|
1476
|
+
duration,
|
|
1477
|
+
useNativeDriver: false,
|
|
1478
|
+
easing: import_react_native7.Easing[easing] || import_react_native7.Easing.out(import_react_native7.Easing.ease)
|
|
1479
|
+
})
|
|
1480
|
+
]).start();
|
|
1481
|
+
}
|
|
1427
1482
|
};
|
|
1428
1483
|
const showSubscription = import_react_native7.Keyboard.addListener("keyboardDidShow", handleKeyboardShow);
|
|
1429
1484
|
const willShowSubscription = import_react_native7.Keyboard.addListener("keyboardWillShow", handleKeyboardShow);
|
|
@@ -1435,7 +1490,7 @@ var MobileAddonsPanel = (0, import_react13.forwardRef)(
|
|
|
1435
1490
|
hideSubscription.remove();
|
|
1436
1491
|
didHideSubscription.remove();
|
|
1437
1492
|
};
|
|
1438
|
-
}, [height, panelHeight, positionBottomAnimation]);
|
|
1493
|
+
}, [height, panelHeight, positionBottomAnimation, isOpen]);
|
|
1439
1494
|
(0, import_react13.useImperativeHandle)(ref, () => ({
|
|
1440
1495
|
setAddonsPanelOpen: (open) => {
|
|
1441
1496
|
if (open) {
|
|
@@ -1445,9 +1500,6 @@ var MobileAddonsPanel = (0, import_react13.forwardRef)(
|
|
|
1445
1500
|
}
|
|
1446
1501
|
}
|
|
1447
1502
|
}));
|
|
1448
|
-
if (!mobileMenuOpen) {
|
|
1449
|
-
return null;
|
|
1450
|
-
}
|
|
1451
1503
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1452
1504
|
import_react_native7.Animated.View,
|
|
1453
1505
|
{
|
|
@@ -1576,7 +1628,8 @@ var AddonsTabs = ({ onClose, storyId }) => {
|
|
|
1576
1628
|
style: addonsScrollStyle,
|
|
1577
1629
|
contentContainerStyle: scrollContentContainerStyle,
|
|
1578
1630
|
children: panel
|
|
1579
|
-
}
|
|
1631
|
+
},
|
|
1632
|
+
`addons-scroll-${storyId}`
|
|
1580
1633
|
)
|
|
1581
1634
|
] });
|
|
1582
1635
|
};
|
|
@@ -1604,11 +1657,60 @@ var import_react_native_theming9 = require("@storybook/react-native-theming");
|
|
|
1604
1657
|
var import_react14 = require("react");
|
|
1605
1658
|
var import_react_native8 = require("react-native");
|
|
1606
1659
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1660
|
+
var useAnimatedModalHeight = () => {
|
|
1661
|
+
const { height } = (0, import_react_native8.useWindowDimensions)();
|
|
1662
|
+
const animatedHeight = useAnimatedValue(0.65 * height);
|
|
1663
|
+
(0, import_react14.useEffect)(() => {
|
|
1664
|
+
const modalHeight = 0.65 * height;
|
|
1665
|
+
const maxModalHeight = 0.85 * height;
|
|
1666
|
+
const expand = (duration = 250) => import_react_native8.Animated.timing(animatedHeight, {
|
|
1667
|
+
toValue: maxModalHeight,
|
|
1668
|
+
duration,
|
|
1669
|
+
useNativeDriver: false
|
|
1670
|
+
}).start();
|
|
1671
|
+
const collapse = (duration = 250) => import_react_native8.Animated.timing(animatedHeight, {
|
|
1672
|
+
toValue: modalHeight,
|
|
1673
|
+
duration,
|
|
1674
|
+
useNativeDriver: false
|
|
1675
|
+
}).start();
|
|
1676
|
+
const handleKeyboardWillShow = (e) => {
|
|
1677
|
+
if (import_react_native8.Platform.OS === "ios") {
|
|
1678
|
+
expand(e.duration);
|
|
1679
|
+
}
|
|
1680
|
+
};
|
|
1681
|
+
const handleKeyboardDidShow = (e) => {
|
|
1682
|
+
if (import_react_native8.Platform.OS === "android") {
|
|
1683
|
+
expand();
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
const handleKeyboardWillHide = (e) => {
|
|
1687
|
+
if (import_react_native8.Platform.OS === "ios") {
|
|
1688
|
+
collapse(e.duration);
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
const handleKeyboardDidHide = (e) => {
|
|
1692
|
+
if (import_react_native8.Platform.OS === "android") {
|
|
1693
|
+
collapse();
|
|
1694
|
+
}
|
|
1695
|
+
};
|
|
1696
|
+
const subscriptions = [
|
|
1697
|
+
import_react_native8.Keyboard.addListener("keyboardWillShow", handleKeyboardWillShow),
|
|
1698
|
+
import_react_native8.Keyboard.addListener("keyboardDidShow", handleKeyboardDidShow),
|
|
1699
|
+
import_react_native8.Keyboard.addListener("keyboardWillHide", handleKeyboardWillHide),
|
|
1700
|
+
import_react_native8.Keyboard.addListener("keyboardDidHide", handleKeyboardDidHide)
|
|
1701
|
+
];
|
|
1702
|
+
return () => {
|
|
1703
|
+
subscriptions.forEach((subscription) => subscription.remove());
|
|
1704
|
+
};
|
|
1705
|
+
}, [animatedHeight, height]);
|
|
1706
|
+
return animatedHeight;
|
|
1707
|
+
};
|
|
1607
1708
|
var MobileMenuDrawer = (0, import_react14.memo)(
|
|
1608
1709
|
(0, import_react14.forwardRef)(({ children }, ref) => {
|
|
1609
1710
|
const [mobileMenuOpen, setMobileMenuOpen] = (0, import_react14.useState)(false);
|
|
1610
1711
|
const { scrollToSelectedNode, scrollRef } = useSelectedNode();
|
|
1611
1712
|
const theme = (0, import_react_native_theming9.useTheme)();
|
|
1713
|
+
const animatedHeight = useAnimatedModalHeight();
|
|
1612
1714
|
const dragY = useAnimatedValue(0);
|
|
1613
1715
|
const panResponder = (0, import_react14.useRef)(
|
|
1614
1716
|
import_react_native8.PanResponder.create({
|
|
@@ -1666,14 +1768,14 @@ var MobileMenuDrawer = (0, import_react14.memo)(
|
|
|
1666
1768
|
transparent: true,
|
|
1667
1769
|
statusBarTranslucent: true,
|
|
1668
1770
|
onRequestClose: () => setMobileMenuOpen(false),
|
|
1669
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native8.
|
|
1771
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native8.Animated.View, { style: { flex: 1 }, children: [
|
|
1670
1772
|
/* @__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)(
|
|
1773
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native8.Animated.View, { style: { height: animatedHeight }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1672
1774
|
import_react_native8.Animated.View,
|
|
1673
1775
|
{
|
|
1674
1776
|
style: [
|
|
1675
1777
|
{
|
|
1676
|
-
|
|
1778
|
+
flex: 1,
|
|
1677
1779
|
borderTopColor: theme.appBorderColor,
|
|
1678
1780
|
borderTopWidth: 1,
|
|
1679
1781
|
borderStyle: "solid",
|
|
@@ -1712,7 +1814,7 @@ var MobileMenuDrawer = (0, import_react14.memo)(
|
|
|
1712
1814
|
)
|
|
1713
1815
|
]
|
|
1714
1816
|
}
|
|
1715
|
-
)
|
|
1817
|
+
) })
|
|
1716
1818
|
] })
|
|
1717
1819
|
}
|
|
1718
1820
|
);
|
|
@@ -1803,7 +1905,6 @@ var StorybookLogo = ({ theme }) => {
|
|
|
1803
1905
|
};
|
|
1804
1906
|
|
|
1805
1907
|
// src/Layout.tsx
|
|
1806
|
-
var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
|
|
1807
1908
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1808
1909
|
var desktopLogoContainer = {
|
|
1809
1910
|
flexDirection: "row",
|
|
@@ -1825,7 +1926,10 @@ var mobileMenuDrawerContentStyle = {
|
|
|
1825
1926
|
paddingTop: 4,
|
|
1826
1927
|
paddingBottom: 4
|
|
1827
1928
|
};
|
|
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.
|
|
1929
|
+
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: [
|
|
1930
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Layout, { storyHash, story, children }),
|
|
1931
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_portal.PortalHost, { name: "storybook-lite-ui-root" })
|
|
1932
|
+
] }) }) }) }) });
|
|
1829
1933
|
var Layout = ({
|
|
1830
1934
|
storyHash,
|
|
1831
1935
|
story,
|
|
@@ -1885,7 +1989,7 @@ var Layout = ({
|
|
|
1885
1989
|
const fullScreenButtonStyle = (0, import_react_native_ui_common7.useStyle)(
|
|
1886
1990
|
() => ({
|
|
1887
1991
|
position: "absolute",
|
|
1888
|
-
bottom: uiHidden ? 56 : 16,
|
|
1992
|
+
bottom: uiHidden ? insets.bottom + 56 : 16,
|
|
1889
1993
|
right: 16,
|
|
1890
1994
|
backgroundColor: theme.background.content,
|
|
1891
1995
|
padding: 4,
|
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.1",
|
|
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.1",
|
|
64
|
+
"@storybook/react-native-ui-common": "^10.0.2-alpha.1",
|
|
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": "
|
|
80
|
+
"gitHead": "14fb007dc99d435fd405054b84bedb38b96ab8c5"
|
|
80
81
|
}
|
package/src/Layout.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { PortalHost, PortalProvider } from '@gorhom/portal';
|
|
2
2
|
import type { ReactRenderer } from '@storybook/react';
|
|
3
3
|
import { styled, ThemeProvider, useTheme } from '@storybook/react-native-theming';
|
|
4
4
|
import {
|
|
@@ -11,10 +11,12 @@ import {
|
|
|
11
11
|
useStyle,
|
|
12
12
|
} from '@storybook/react-native-ui-common';
|
|
13
13
|
import { ReactElement, ReactNode, useCallback, useRef, useState } from 'react';
|
|
14
|
-
import {
|
|
14
|
+
import { ScrollView, Text, TouchableOpacity, View, ViewStyle } from 'react-native';
|
|
15
|
+
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
15
16
|
import { SET_CURRENT_STORY } from 'storybook/internal/core-events';
|
|
16
|
-
import {
|
|
17
|
+
import type { Args, StoryContext } from 'storybook/internal/csf';
|
|
17
18
|
import { type API_IndexHash } from 'storybook/internal/types';
|
|
19
|
+
import { addons } from 'storybook/manager-api';
|
|
18
20
|
import { AddonsTabs, MobileAddonsPanel, MobileAddonsPanelRef } from './MobileAddonsPanel';
|
|
19
21
|
import { MobileMenuDrawer, MobileMenuDrawerRef } from './MobileMenuDrawer';
|
|
20
22
|
import { SelectedNodeProvider } from './SelectedNodeProvider';
|
|
@@ -27,7 +29,6 @@ import {
|
|
|
27
29
|
FullscreenIcon,
|
|
28
30
|
MenuIcon,
|
|
29
31
|
} from './icon/iconDataUris';
|
|
30
|
-
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
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
|
-
<
|
|
66
|
-
{
|
|
67
|
-
|
|
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(
|
|
32
|
-
const positionBottomAnimation = useAnimatedValue(
|
|
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
|
-
|
|
38
|
-
Animated.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
57
|
-
Animated.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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}
|
package/src/MobileMenuDrawer.tsx
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import { useTheme } from '@storybook/react-native-theming';
|
|
2
|
-
import {
|
|
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
|
-
|
|
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,53 @@ export const MobileMenuDrawer = memo(
|
|
|
95
168
|
statusBarTranslucent
|
|
96
169
|
onRequestClose={() => setMobileMenuOpen(false)}
|
|
97
170
|
>
|
|
98
|
-
<
|
|
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
|
-
<Animated.View
|
|
104
|
-
|
|
105
|
-
{
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
}}
|
|
176
|
+
<Animated.View style={{ height: animatedHeight }}>
|
|
177
|
+
<Animated.View
|
|
178
|
+
style={[
|
|
179
|
+
{
|
|
180
|
+
flex: 1,
|
|
181
|
+
borderTopColor: theme.appBorderColor,
|
|
182
|
+
borderTopWidth: 1,
|
|
183
|
+
borderStyle: 'solid',
|
|
184
|
+
backgroundColor: theme.background.content,
|
|
185
|
+
elevation: 8,
|
|
186
|
+
},
|
|
187
|
+
{ transform: [{ translateY: dragY }] },
|
|
188
|
+
]}
|
|
138
189
|
>
|
|
139
|
-
{
|
|
140
|
-
|
|
190
|
+
{/* Drag handle */}
|
|
191
|
+
<View
|
|
192
|
+
{...panResponder.panHandlers}
|
|
193
|
+
style={{
|
|
194
|
+
alignItems: 'center',
|
|
195
|
+
justifyContent: 'center',
|
|
196
|
+
backgroundColor: theme.background.content,
|
|
197
|
+
}}
|
|
198
|
+
>
|
|
199
|
+
<View style={handleStyle} />
|
|
200
|
+
</View>
|
|
201
|
+
|
|
202
|
+
<ScrollView
|
|
203
|
+
ref={scrollRef}
|
|
204
|
+
keyboardShouldPersistTaps="handled"
|
|
205
|
+
style={{
|
|
206
|
+
flex: 1,
|
|
207
|
+
paddingBottom: 150,
|
|
208
|
+
alignSelf: 'flex-end',
|
|
209
|
+
width: '100%',
|
|
210
|
+
backgroundColor: theme.background.content,
|
|
211
|
+
}}
|
|
212
|
+
>
|
|
213
|
+
{children}
|
|
214
|
+
</ScrollView>
|
|
215
|
+
</Animated.View>
|
|
141
216
|
</Animated.View>
|
|
142
|
-
</
|
|
217
|
+
</Animated.View>
|
|
143
218
|
</Modal>
|
|
144
219
|
);
|
|
145
220
|
})
|
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
|
-
|
|
58
|
-
minHeight: 32,
|
|
70
|
+
...inputPlatformSpecificStyles,
|
|
59
71
|
paddingLeft: 28,
|
|
60
72
|
paddingRight: 28,
|
|
61
73
|
borderWidth: 1,
|
package/src/TreeNode.tsx
CHANGED
|
@@ -100,7 +100,7 @@ export const GroupNode: FC<
|
|
|
100
100
|
|
|
101
101
|
return (
|
|
102
102
|
<BranchNode isExpandable={isExpandable} {...props}>
|
|
103
|
-
<Wrapper>
|
|
103
|
+
<Wrapper key={`group-${props.id}-${color}`}>
|
|
104
104
|
{isExpandable && <CollapseIcon isExpanded={isExpanded} />}
|
|
105
105
|
<GroupIcon width={14} height={14} color={color} />
|
|
106
106
|
</Wrapper>
|
|
@@ -119,7 +119,8 @@ export const ComponentNode: FC<ComponentProps<typeof BranchNode>> = React.memo(
|
|
|
119
119
|
|
|
120
120
|
return (
|
|
121
121
|
<BranchNode isExpandable={isExpandable} {...props}>
|
|
122
|
-
|
|
122
|
+
{/* workaround for macos icon color bug */}
|
|
123
|
+
<Wrapper key={`component-${props.id}-${color}`}>
|
|
123
124
|
{isExpandable && <CollapseIcon isExpanded={isExpanded} />}
|
|
124
125
|
<ComponentIcon width={12} height={12} color={color} />
|
|
125
126
|
</Wrapper>
|
|
@@ -142,7 +143,7 @@ export const StoryNode = React.memo(
|
|
|
142
143
|
|
|
143
144
|
return (
|
|
144
145
|
<LeafNode {...props} ref={ref}>
|
|
145
|
-
<Wrapper>
|
|
146
|
+
<Wrapper key={`story-${props.id}-${color}`}>
|
|
146
147
|
<StoryIcon width={14} height={14} color={color} />
|
|
147
148
|
</Wrapper>
|
|
148
149
|
<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
|
+
}
|