@storybook/react-native-ui 8.5.5-alpha.2 → 8.5.5-alpha.4
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 +10 -11
- package/dist/index.js +344 -285
- package/package.json +3 -3
- package/src/Layout.tsx +20 -17
- package/src/MobileMenuDrawer.tsx +7 -5
- package/src/Search.tsx +5 -1
- package/src/SelectedNodeProvider.tsx +58 -0
- package/src/Tree.tsx +15 -4
- package/src/TreeNode.tsx +24 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storybook/react-native-ui",
|
|
3
|
-
"version": "8.5.5-alpha.
|
|
3
|
+
"version": "8.5.5-alpha.4",
|
|
4
4
|
"description": "ui components for react native storybook",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@storybook/core": "^8.5.1",
|
|
62
62
|
"@storybook/react": "^8.5.1",
|
|
63
|
-
"@storybook/react-native-theming": "^8.5.5-alpha.
|
|
63
|
+
"@storybook/react-native-theming": "^8.5.5-alpha.4",
|
|
64
64
|
"fuse.js": "^7.0.0",
|
|
65
65
|
"memoizerific": "^1.11.3",
|
|
66
66
|
"polished": "^4.3.1",
|
|
@@ -81,5 +81,5 @@
|
|
|
81
81
|
"publishConfig": {
|
|
82
82
|
"access": "public"
|
|
83
83
|
},
|
|
84
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "1d84b44bf483612cdb27f8bf9b36dfd931776511"
|
|
85
85
|
}
|
package/src/Layout.tsx
CHANGED
|
@@ -19,6 +19,7 @@ import { CloseFullscreenIcon } from './icon/CloseFullscreenIcon';
|
|
|
19
19
|
import { FullscreenIcon } from './icon/FullscreenIcon';
|
|
20
20
|
import { MenuIcon } from './icon/MenuIcon';
|
|
21
21
|
import { useStyle } from './util/useStyle';
|
|
22
|
+
import { SelectedNodeProvider } from './SelectedNodeProvider';
|
|
22
23
|
|
|
23
24
|
const desktopLogoContainer = {
|
|
24
25
|
flexDirection: 'row',
|
|
@@ -249,23 +250,25 @@ export const Layout = ({
|
|
|
249
250
|
) : null}
|
|
250
251
|
|
|
251
252
|
{isDesktop ? null : (
|
|
252
|
-
<
|
|
253
|
-
<
|
|
254
|
-
<
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
253
|
+
<SelectedNodeProvider>
|
|
254
|
+
<MobileMenuDrawer ref={mobileMenuDrawerRef}>
|
|
255
|
+
<View style={mobileMenuDrawerContentStyle}>
|
|
256
|
+
<StorybookLogo theme={theme} />
|
|
257
|
+
</View>
|
|
258
|
+
|
|
259
|
+
<Sidebar
|
|
260
|
+
extra={placeholderArray}
|
|
261
|
+
previewInitialized
|
|
262
|
+
indexError={undefined}
|
|
263
|
+
refs={placeholderObject}
|
|
264
|
+
setSelection={setSelection}
|
|
265
|
+
status={placeholderObject}
|
|
266
|
+
index={storyHash}
|
|
267
|
+
storyId={story?.id}
|
|
268
|
+
refId={DEFAULT_REF_ID}
|
|
269
|
+
/>
|
|
270
|
+
</MobileMenuDrawer>
|
|
271
|
+
</SelectedNodeProvider>
|
|
269
272
|
)}
|
|
270
273
|
|
|
271
274
|
{isDesktop ? null : <MobileAddonsPanel ref={addonPanelRef} storyId={story?.id} />}
|
package/src/MobileMenuDrawer.tsx
CHANGED
|
@@ -3,11 +3,12 @@ import BottomSheet, {
|
|
|
3
3
|
BottomSheetBackdropProps,
|
|
4
4
|
BottomSheetScrollView,
|
|
5
5
|
} from '@gorhom/bottom-sheet';
|
|
6
|
-
import {
|
|
6
|
+
import { useTheme } from '@storybook/react-native-theming';
|
|
7
|
+
import { forwardRef, memo, ReactNode, useImperativeHandle, useMemo, useRef } from 'react';
|
|
7
8
|
import { Keyboard } from 'react-native';
|
|
8
9
|
import { useReducedMotion } from 'react-native-reanimated';
|
|
9
10
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
10
|
-
import {
|
|
11
|
+
import { useSelectedNode } from './SelectedNodeProvider';
|
|
11
12
|
|
|
12
13
|
interface MobileMenuDrawerProps {
|
|
13
14
|
children: ReactNode | ReactNode[];
|
|
@@ -34,18 +35,18 @@ export const MobileMenuDrawer = memo(
|
|
|
34
35
|
const reducedMotion = useReducedMotion();
|
|
35
36
|
const insets = useSafeAreaInsets();
|
|
36
37
|
const theme = useTheme();
|
|
37
|
-
|
|
38
38
|
const menuBottomSheetRef = useRef<BottomSheet>(null);
|
|
39
|
+
const { scrollToSelectedNode, scrollRef } = useSelectedNode();
|
|
39
40
|
|
|
40
41
|
useImperativeHandle(ref, () => ({
|
|
41
42
|
setMobileMenuOpen: (open: boolean) => {
|
|
42
43
|
if (open) {
|
|
43
|
-
// menuBottomSheetRef.current?.present();
|
|
44
44
|
menuBottomSheetRef.current?.snapToIndex(1);
|
|
45
|
+
|
|
46
|
+
scrollToSelectedNode();
|
|
45
47
|
} else {
|
|
46
48
|
Keyboard.dismiss();
|
|
47
49
|
|
|
48
|
-
// menuBottomSheetRef.current?.dismiss();
|
|
49
50
|
menuBottomSheetRef.current?.close();
|
|
50
51
|
}
|
|
51
52
|
},
|
|
@@ -82,6 +83,7 @@ export const MobileMenuDrawer = memo(
|
|
|
82
83
|
handleIndicatorStyle={handleIndicatorStyle}
|
|
83
84
|
>
|
|
84
85
|
<BottomSheetScrollView
|
|
86
|
+
ref={scrollRef}
|
|
85
87
|
keyboardShouldPersistTaps="handled"
|
|
86
88
|
contentContainerStyle={contentContainerStyle}
|
|
87
89
|
>
|
package/src/Search.tsx
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from './types';
|
|
19
19
|
import { getGroupStatus, getHighestStatus } from './util/status';
|
|
20
20
|
import { searchItem } from './util/tree';
|
|
21
|
+
import { useSelectedNode } from './SelectedNodeProvider';
|
|
21
22
|
|
|
22
23
|
const DEFAULT_MAX_SEARCH_RESULTS = 50;
|
|
23
24
|
|
|
@@ -111,6 +112,7 @@ export const Search = React.memo<{
|
|
|
111
112
|
const [isOpen, setIsOpen] = useState(false);
|
|
112
113
|
const [allComponents, showAllComponents] = useState(false);
|
|
113
114
|
const { isMobile } = useLayout();
|
|
115
|
+
const { scrollToSelectedNode } = useSelectedNode();
|
|
114
116
|
|
|
115
117
|
const selectStory = useCallback(
|
|
116
118
|
(id: string, refId: string) => {
|
|
@@ -121,8 +123,10 @@ export const Search = React.memo<{
|
|
|
121
123
|
setIsOpen(false);
|
|
122
124
|
|
|
123
125
|
showAllComponents(false);
|
|
126
|
+
|
|
127
|
+
scrollToSelectedNode();
|
|
124
128
|
},
|
|
125
|
-
[setSelection]
|
|
129
|
+
[scrollToSelectedNode, setSelection]
|
|
126
130
|
);
|
|
127
131
|
|
|
128
132
|
const getItemProps: GetSearchItemProps = useCallback(
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { BottomSheetScrollViewMethods } from '@gorhom/bottom-sheet/lib/typescript/components/bottomSheetScrollable/types';
|
|
2
|
+
import type { FC, PropsWithChildren } from 'react';
|
|
3
|
+
import { createContext, useCallback, useContext, useRef } from 'react';
|
|
4
|
+
import type { View } from 'react-native';
|
|
5
|
+
|
|
6
|
+
type SelectedNodeContextType = {
|
|
7
|
+
nodeRef: React.RefObject<View>;
|
|
8
|
+
setNodeRef: (node: View | null) => void;
|
|
9
|
+
scrollToSelectedNode: () => void;
|
|
10
|
+
scrollRef: React.RefObject<BottomSheetScrollViewMethods>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const SelectedNodeContext = createContext<SelectedNodeContextType>({
|
|
14
|
+
nodeRef: { current: null },
|
|
15
|
+
setNodeRef: () => {},
|
|
16
|
+
scrollToSelectedNode: () => {},
|
|
17
|
+
scrollRef: null,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const SelectedNodeProvider: FC<PropsWithChildren> = ({ children }) => {
|
|
21
|
+
const nodeRef = useRef<View | null>(null);
|
|
22
|
+
|
|
23
|
+
const setNodeRef = useCallback((node: View | null) => {
|
|
24
|
+
nodeRef.current = node;
|
|
25
|
+
}, []);
|
|
26
|
+
|
|
27
|
+
const scrollRef = useRef<BottomSheetScrollViewMethods>(null);
|
|
28
|
+
|
|
29
|
+
const scrollToSelectedNode = useCallback(() => {
|
|
30
|
+
// maybe later we can improve on this to not use setTimeout but right now it seems like the simplest solution
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
if (nodeRef?.current && scrollRef?.current) {
|
|
33
|
+
// im just not sure if older versions would error here,
|
|
34
|
+
// since measure layout probably changed since new arch
|
|
35
|
+
try {
|
|
36
|
+
nodeRef.current.measureLayout?.(scrollRef.current as any, (_x, y) => {
|
|
37
|
+
scrollRef.current?.scrollTo({ y: y - 100, animated: true });
|
|
38
|
+
});
|
|
39
|
+
} catch (error) {}
|
|
40
|
+
}
|
|
41
|
+
}, 500);
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<SelectedNodeContext.Provider
|
|
46
|
+
value={{
|
|
47
|
+
nodeRef,
|
|
48
|
+
setNodeRef,
|
|
49
|
+
scrollToSelectedNode,
|
|
50
|
+
scrollRef,
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
{children}
|
|
54
|
+
</SelectedNodeContext.Provider>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const useSelectedNode = () => useContext(SelectedNodeContext);
|
package/src/Tree.tsx
CHANGED
|
@@ -18,6 +18,7 @@ import type { ExpandAction, ExpandedState } from './hooks/useExpanded';
|
|
|
18
18
|
import { useExpanded } from './hooks/useExpanded';
|
|
19
19
|
import { getGroupStatus, statusMapping } from './util/status';
|
|
20
20
|
import { createId, getAncestorIds, getDescendantIds, isStoryHoistable } from './util/tree';
|
|
21
|
+
import { useSelectedNode } from './SelectedNodeProvider';
|
|
21
22
|
|
|
22
23
|
interface NodeProps {
|
|
23
24
|
item: Item;
|
|
@@ -52,6 +53,17 @@ export const Node = React.memo<NodeProps>(function Node({
|
|
|
52
53
|
setExpanded,
|
|
53
54
|
onSelectStoryId,
|
|
54
55
|
}) {
|
|
56
|
+
const { setNodeRef } = useSelectedNode();
|
|
57
|
+
|
|
58
|
+
const setRef = useCallback(
|
|
59
|
+
(node: View | null) => {
|
|
60
|
+
if (isSelected && node) {
|
|
61
|
+
setNodeRef(node);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
[isSelected, setNodeRef]
|
|
65
|
+
);
|
|
66
|
+
|
|
55
67
|
if (!isDisplayed) {
|
|
56
68
|
return null;
|
|
57
69
|
}
|
|
@@ -59,11 +71,10 @@ export const Node = React.memo<NodeProps>(function Node({
|
|
|
59
71
|
const id = createId(item.id, refId);
|
|
60
72
|
|
|
61
73
|
if (item.type === 'story') {
|
|
62
|
-
const LeafNode = StoryNode;
|
|
63
|
-
|
|
64
74
|
return (
|
|
65
75
|
<LeafNodeStyleWrapper>
|
|
66
|
-
<
|
|
76
|
+
<StoryNode
|
|
77
|
+
ref={setRef}
|
|
67
78
|
selected={isSelected}
|
|
68
79
|
key={id}
|
|
69
80
|
id={id}
|
|
@@ -73,7 +84,7 @@ export const Node = React.memo<NodeProps>(function Node({
|
|
|
73
84
|
}}
|
|
74
85
|
>
|
|
75
86
|
{(item.renderLabel as (i: typeof item) => React.ReactNode)?.(item) || item.name}
|
|
76
|
-
</
|
|
87
|
+
</StoryNode>
|
|
77
88
|
</LeafNodeStyleWrapper>
|
|
78
89
|
);
|
|
79
90
|
}
|
package/src/TreeNode.tsx
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { styled, useTheme } from '@storybook/react-native-theming';
|
|
2
2
|
import { ComponentIcon } from './icon/ComponentIcon';
|
|
3
3
|
import { GroupIcon } from './icon/GroupIcon';
|
|
4
|
-
|
|
5
4
|
import { StoryIcon } from './icon/StoryIcon';
|
|
6
5
|
import { CollapseIcon } from './icon/CollapseIcon';
|
|
7
|
-
import
|
|
6
|
+
import { ComponentProps, FC, forwardRef, useMemo } from 'react';
|
|
8
7
|
import { transparentize } from 'polished';
|
|
8
|
+
import { View } from 'react-native';
|
|
9
|
+
import React from 'react';
|
|
9
10
|
|
|
10
11
|
export interface NodeProps {
|
|
11
12
|
children: React.ReactNode | React.ReactNode[];
|
|
@@ -131,22 +132,26 @@ export const ComponentNode: FC<ComponentProps<typeof BranchNode>> = React.memo(
|
|
|
131
132
|
}
|
|
132
133
|
);
|
|
133
134
|
|
|
134
|
-
export const StoryNode
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
export const StoryNode = React.memo(
|
|
136
|
+
forwardRef<View, ComponentProps<typeof LeafNode>>(function StoryNode(
|
|
137
|
+
{ children, ...props },
|
|
138
|
+
ref
|
|
139
|
+
) {
|
|
140
|
+
const theme = useTheme();
|
|
139
141
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
142
|
+
const color = useMemo(() => {
|
|
143
|
+
return props.selected ? theme.color.lightest : theme.color.seafoam;
|
|
144
|
+
}, [props.selected, theme.color.lightest, theme.color.seafoam]);
|
|
143
145
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
})
|
|
146
|
+
return (
|
|
147
|
+
<LeafNode {...props} ref={ref}>
|
|
148
|
+
<Wrapper>
|
|
149
|
+
<StoryIcon width={14} height={14} color={color} />
|
|
150
|
+
</Wrapper>
|
|
151
|
+
<LeafNodeText selected={props.selected}>{children}</LeafNodeText>
|
|
152
|
+
</LeafNode>
|
|
153
|
+
);
|
|
154
|
+
})
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
StoryNode.displayName = 'StoryNode';
|