react-native-molecules 0.5.0-beta.14 → 0.5.0-beta.16
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/components/Accordion/Accordion.tsx +2 -6
- package/components/Accordion/AccordionItem.tsx +16 -12
- package/components/Accordion/AccordionItemContent.tsx +6 -1
- package/components/Accordion/AccordionItemHeader.tsx +1 -1
- package/components/Accordion/utils.ts +6 -0
- package/components/Appbar/AppbarBase.tsx +18 -13
- package/components/Button/index.tsx +1 -1
- package/components/DatePickerInput/DatePickerInput.tsx +69 -80
- package/components/DatePickerInput/index.tsx +2 -1
- package/components/DatePickerInput/utils.ts +9 -0
- package/components/Drawer/Drawer.tsx +17 -6
- package/components/FilePicker/FilePicker.tsx +12 -7
- package/components/FilePicker/index.tsx +2 -1
- package/components/FilePicker/utils.ts +9 -0
- package/components/NavigationRail/NavigationRail.tsx +15 -9
- package/components/Tabs/Tabs.tsx +3 -2
- package/components/TextInput/TextInput.tsx +2 -2
- package/components/Tooltip/Tooltip.tsx +42 -67
- package/components/Tooltip/TooltipContent.tsx +32 -5
- package/components/Tooltip/index.tsx +1 -1
- package/hooks/index.tsx +0 -1
- package/package.json +1 -1
- package/utils/extractSubcomponents.ts +89 -0
- package/hooks/useSubcomponents.tsx +0 -91
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from 'react';
|
|
10
10
|
import { View } from 'react-native';
|
|
11
11
|
|
|
12
|
-
import { useControlledValue
|
|
12
|
+
import { useControlledValue } from '../../hooks';
|
|
13
13
|
import { accordionStyles } from './utils';
|
|
14
14
|
|
|
15
15
|
export type Props = Omit<ComponentPropsWithRef<typeof View>, 'children'> & {
|
|
@@ -38,8 +38,6 @@ const Accordion = (
|
|
|
38
38
|
onChange: onChangeProp,
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
const { AccordionItem } = useSubcomponents({ children, allowedChildren: ['AccordionItem'] });
|
|
42
|
-
|
|
43
41
|
const onPressItem = useCallback(
|
|
44
42
|
(id: string) => {
|
|
45
43
|
const isSelected = Array.isArray(expandedItemIds)
|
|
@@ -70,9 +68,7 @@ const Accordion = (
|
|
|
70
68
|
|
|
71
69
|
return (
|
|
72
70
|
<View style={[accordionStyles.root, style]} {...rest} ref={ref}>
|
|
73
|
-
<AccordionContext.Provider value={contextValue}>
|
|
74
|
-
{AccordionItem}
|
|
75
|
-
</AccordionContext.Provider>
|
|
71
|
+
<AccordionContext.Provider value={contextValue}>{children}</AccordionContext.Provider>
|
|
76
72
|
</View>
|
|
77
73
|
);
|
|
78
74
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
createContext,
|
|
3
2
|
forwardRef,
|
|
4
3
|
memo,
|
|
5
4
|
type ReactElement,
|
|
@@ -10,10 +9,11 @@ import {
|
|
|
10
9
|
} from 'react';
|
|
11
10
|
import { View, type ViewProps } from 'react-native';
|
|
12
11
|
|
|
13
|
-
import { useControlledValue
|
|
12
|
+
import { useControlledValue } from '../../hooks';
|
|
14
13
|
import type { WithElements } from '../../types';
|
|
14
|
+
import { extractSubcomponents } from '../../utils/extractSubcomponents';
|
|
15
15
|
import { AccordionContext } from './Accordion';
|
|
16
|
-
import { accordionItemStyles } from './utils';
|
|
16
|
+
import { AccordionItemContext, accordionItemStyles } from './utils';
|
|
17
17
|
|
|
18
18
|
export type Props = Omit<ViewProps, 'children'> &
|
|
19
19
|
WithElements<ReactNode> & {
|
|
@@ -45,9 +45,17 @@ const AccordionItem = memo(
|
|
|
45
45
|
|
|
46
46
|
const groupContext = useContext(AccordionContext);
|
|
47
47
|
|
|
48
|
-
const {
|
|
48
|
+
const {
|
|
49
|
+
AccordionItem_Header,
|
|
50
|
+
AccordionItem_Content,
|
|
51
|
+
rest: restChildren,
|
|
52
|
+
} = extractSubcomponents({
|
|
49
53
|
children,
|
|
50
|
-
allowedChildren: [
|
|
54
|
+
allowedChildren: [
|
|
55
|
+
{ name: 'AccordionItem_Header', allowMultiple: false },
|
|
56
|
+
{ name: 'AccordionItem_Content', allowMultiple: false },
|
|
57
|
+
],
|
|
58
|
+
includeRest: true,
|
|
51
59
|
});
|
|
52
60
|
|
|
53
61
|
useEffect(() => {
|
|
@@ -76,8 +84,9 @@ const AccordionItem = memo(
|
|
|
76
84
|
return (
|
|
77
85
|
<View style={[accordionItemStyles.root, style]} {...rest} ref={ref}>
|
|
78
86
|
<AccordionItemContext.Provider value={contextValue}>
|
|
79
|
-
{AccordionItem_Header
|
|
80
|
-
{contextValue.expanded ? AccordionItem_Content
|
|
87
|
+
{AccordionItem_Header}
|
|
88
|
+
{contextValue.expanded ? AccordionItem_Content : null}
|
|
89
|
+
{restChildren}
|
|
81
90
|
</AccordionItemContext.Provider>
|
|
82
91
|
</View>
|
|
83
92
|
);
|
|
@@ -85,11 +94,6 @@ const AccordionItem = memo(
|
|
|
85
94
|
),
|
|
86
95
|
);
|
|
87
96
|
|
|
88
|
-
export const AccordionItemContext = createContext({
|
|
89
|
-
expanded: false,
|
|
90
|
-
onExpandedChange: (_expanded: boolean) => {},
|
|
91
|
-
});
|
|
92
|
-
|
|
93
97
|
AccordionItem.displayName = 'AccordionItem';
|
|
94
98
|
|
|
95
99
|
export default AccordionItem;
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { memo } from 'react';
|
|
1
|
+
import { memo, useContext } from 'react';
|
|
2
2
|
import { View, type ViewProps } from 'react-native';
|
|
3
3
|
|
|
4
|
+
import { AccordionItemContext } from './utils';
|
|
4
5
|
import { accordionItemContentStyles } from './utils';
|
|
5
6
|
|
|
6
7
|
export type Props = ViewProps & {};
|
|
7
8
|
|
|
8
9
|
const AccordionItemContent = memo(({ style, children, ...rest }: Props) => {
|
|
10
|
+
const { expanded } = useContext(AccordionItemContext);
|
|
11
|
+
|
|
12
|
+
if (!expanded) return null;
|
|
13
|
+
|
|
9
14
|
return (
|
|
10
15
|
<View style={[accordionItemContentStyles.root, style]} {...rest}>
|
|
11
16
|
{children}
|
|
@@ -6,7 +6,7 @@ import type { WithElements } from '../../types';
|
|
|
6
6
|
import { resolveStateVariant } from '../../utils';
|
|
7
7
|
import { Text } from '../Text';
|
|
8
8
|
import { TouchableRipple, type TouchableRippleProps } from '../TouchableRipple';
|
|
9
|
-
import { AccordionItemContext } from './
|
|
9
|
+
import { AccordionItemContext } from './utils';
|
|
10
10
|
import { accordionItemHeaderStyles } from './utils';
|
|
11
11
|
|
|
12
12
|
export type AccordionHeaderElementProps = {
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
1
2
|
import { StyleSheet } from 'react-native-unistyles';
|
|
2
3
|
|
|
3
4
|
import { getRegisteredComponentStylesWithFallback } from '../../core';
|
|
4
5
|
|
|
6
|
+
export const AccordionItemContext = createContext({
|
|
7
|
+
expanded: false,
|
|
8
|
+
onExpandedChange: (_expanded: boolean) => {},
|
|
9
|
+
});
|
|
10
|
+
|
|
5
11
|
const accordionStylesDefault = StyleSheet.create({
|
|
6
12
|
root: {},
|
|
7
13
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createContext, memo, useMemo } from 'react';
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { extractSubcomponents } from '../../utils/extractSubcomponents';
|
|
5
5
|
import { Surface } from '../Surface';
|
|
6
6
|
import type { AppbarBaseProps, AppbarType } from './types';
|
|
7
7
|
import { appbarBaseStyles } from './utils';
|
|
@@ -26,9 +26,19 @@ const AppbarBase = ({
|
|
|
26
26
|
};
|
|
27
27
|
}, [innerContainerStyleProp, scrolling, style]);
|
|
28
28
|
|
|
29
|
-
const {
|
|
29
|
+
const {
|
|
30
|
+
Appbar_Left,
|
|
31
|
+
Appbar_Right,
|
|
32
|
+
Appbar_Title,
|
|
33
|
+
rest: restChildren,
|
|
34
|
+
} = extractSubcomponents({
|
|
30
35
|
children,
|
|
31
|
-
allowedChildren: [
|
|
36
|
+
allowedChildren: [
|
|
37
|
+
{ name: 'Appbar_Left', allowMultiple: false },
|
|
38
|
+
{ name: 'Appbar_Right', allowMultiple: false },
|
|
39
|
+
{ name: 'Appbar_Title', allowMultiple: false },
|
|
40
|
+
],
|
|
41
|
+
includeRest: true,
|
|
32
42
|
});
|
|
33
43
|
|
|
34
44
|
const contextValue = useMemo(() => ({ type: _type }), [_type]);
|
|
@@ -37,17 +47,12 @@ const AppbarBase = ({
|
|
|
37
47
|
<Surface elevation={elevation} style={containerStyle} {...rest}>
|
|
38
48
|
<AppbarContext.Provider value={contextValue}>
|
|
39
49
|
<View style={innerContainerStyle}>
|
|
40
|
-
{Appbar_Left
|
|
41
|
-
<>
|
|
42
|
-
|
|
43
|
-
Appbar_Title[0]
|
|
44
|
-
) : (
|
|
45
|
-
<View />
|
|
46
|
-
)}
|
|
47
|
-
</>
|
|
48
|
-
{Appbar_Right[0]}
|
|
50
|
+
{Appbar_Left}
|
|
51
|
+
<>{_type === 'center-aligned' || _type === 'small' ? Appbar_Title : <View />}</>
|
|
52
|
+
{Appbar_Right}
|
|
49
53
|
</View>
|
|
50
|
-
|
|
54
|
+
{(_type === 'medium' || _type === 'large') && Appbar_Title}
|
|
55
|
+
{restChildren}
|
|
51
56
|
</AppbarContext.Provider>
|
|
52
57
|
</Surface>
|
|
53
58
|
);
|
|
@@ -10,4 +10,4 @@ export const Button = Object.assign(ButtonBase, {
|
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
export type { Props as ButtonProps } from './Button';
|
|
13
|
-
export { buttonIconStyles, buttonStyles, buttonTextStyles } from './utils';
|
|
13
|
+
export { ButtonContext, buttonIconStyles, buttonStyles, buttonTextStyles } from './utils';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { forwardRef, memo, useCallback, useMemo, useRef
|
|
1
|
+
import { forwardRef, memo, useCallback, useMemo, useRef } from 'react';
|
|
2
2
|
import { StyleSheet } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { useLatest, useToggle } from '../../hooks';
|
|
@@ -9,6 +9,7 @@ import { TextInput } from '../TextInput';
|
|
|
9
9
|
import DatePickerInputModal from './DatePickerInputModal';
|
|
10
10
|
import DatePickerInputWithoutModal from './DatePickerInputWithoutModal';
|
|
11
11
|
import type { DatePickerInputProps } from './types';
|
|
12
|
+
import { DatePickerInputContext } from './utils';
|
|
12
13
|
|
|
13
14
|
function DatePickerInput(
|
|
14
15
|
{
|
|
@@ -24,111 +25,99 @@ function DatePickerInput(
|
|
|
24
25
|
startYear,
|
|
25
26
|
endYear,
|
|
26
27
|
dockedPopoverContentProps,
|
|
28
|
+
children,
|
|
27
29
|
//locale = 'en',
|
|
28
30
|
...rest
|
|
29
31
|
}: DatePickerInputProps,
|
|
30
32
|
ref: any,
|
|
31
33
|
) {
|
|
32
34
|
const triggerRef = useRef(null);
|
|
33
|
-
const { state: isOpen, onToggle } = useToggle(false);
|
|
34
|
-
const [visible, setVisible] = useState<boolean>(false);
|
|
35
|
+
const { state: isOpen, onToggle, handleOpen, handleClose } = useToggle(false);
|
|
35
36
|
|
|
36
37
|
const onDismiss = useCallback(() => {
|
|
37
|
-
|
|
38
|
-
}, [
|
|
38
|
+
handleClose();
|
|
39
|
+
}, [handleClose]);
|
|
39
40
|
|
|
40
41
|
const onChangeRef = useLatest(onChange);
|
|
41
42
|
|
|
42
43
|
const onInnerConfirm = useCallback(
|
|
43
44
|
({ date }: any) => {
|
|
44
|
-
|
|
45
|
+
handleClose();
|
|
45
46
|
onChangeRef.current(date);
|
|
46
47
|
},
|
|
47
|
-
[
|
|
48
|
+
[handleClose, onChangeRef],
|
|
48
49
|
);
|
|
49
50
|
|
|
50
51
|
const onPressCalendarIcon = useCallback(() => {
|
|
51
52
|
if (pickerMode === 'docked') {
|
|
52
53
|
onToggle();
|
|
54
|
+
} else {
|
|
55
|
+
handleOpen();
|
|
53
56
|
}
|
|
54
|
-
|
|
55
|
-
}, [pickerMode, onToggle]);
|
|
57
|
+
}, [pickerMode, onToggle, handleOpen]);
|
|
56
58
|
|
|
57
|
-
const renderers =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
/>
|
|
69
|
-
),
|
|
70
|
-
docked: (
|
|
71
|
-
<DatePickerDocked
|
|
72
|
-
date={value}
|
|
73
|
-
locale={locale}
|
|
74
|
-
startYear={startYear}
|
|
75
|
-
endYear={endYear}
|
|
76
|
-
onChange={onInnerConfirm}
|
|
77
|
-
isOpen={isOpen}
|
|
78
|
-
onClose={onDismiss}
|
|
79
|
-
onToggle={onToggle}
|
|
80
|
-
triggerRef={triggerRef}
|
|
81
|
-
popoverContentProps={dockedPopoverContentProps}
|
|
82
|
-
/>
|
|
83
|
-
),
|
|
84
|
-
};
|
|
85
|
-
}, [
|
|
86
|
-
dockedPopoverContentProps,
|
|
87
|
-
endYear,
|
|
88
|
-
isOpen,
|
|
89
|
-
locale,
|
|
90
|
-
onDismiss,
|
|
91
|
-
onInnerConfirm,
|
|
92
|
-
onToggle,
|
|
93
|
-
startYear,
|
|
94
|
-
validRange,
|
|
95
|
-
value,
|
|
96
|
-
visible,
|
|
97
|
-
]);
|
|
98
|
-
|
|
99
|
-
const rightElement = useMemo(
|
|
100
|
-
() => (
|
|
101
|
-
<>
|
|
102
|
-
{withModal || pickerMode === 'docked' ? (
|
|
103
|
-
<>
|
|
104
|
-
<IconButton
|
|
105
|
-
ref={triggerRef}
|
|
106
|
-
style={styles.calendarButton}
|
|
107
|
-
name={calendarIcon}
|
|
108
|
-
onPress={onPressCalendarIcon}
|
|
109
|
-
disabled={disabled}
|
|
110
|
-
/>
|
|
111
|
-
{renderers[pickerMode]}
|
|
112
|
-
</>
|
|
113
|
-
) : null}
|
|
114
|
-
</>
|
|
59
|
+
const renderers = {
|
|
60
|
+
modal: (
|
|
61
|
+
<DatePickerInputModal
|
|
62
|
+
date={value}
|
|
63
|
+
mode="single"
|
|
64
|
+
isOpen={isOpen}
|
|
65
|
+
onClose={onDismiss}
|
|
66
|
+
onConfirm={onInnerConfirm}
|
|
67
|
+
locale={locale}
|
|
68
|
+
validRange={validRange}
|
|
69
|
+
/>
|
|
115
70
|
),
|
|
116
|
-
|
|
71
|
+
docked: (
|
|
72
|
+
<DatePickerDocked
|
|
73
|
+
date={value}
|
|
74
|
+
locale={locale}
|
|
75
|
+
startYear={startYear}
|
|
76
|
+
endYear={endYear}
|
|
77
|
+
onChange={onInnerConfirm}
|
|
78
|
+
isOpen={isOpen}
|
|
79
|
+
onClose={onDismiss}
|
|
80
|
+
onToggle={onToggle}
|
|
81
|
+
triggerRef={triggerRef}
|
|
82
|
+
popoverContentProps={dockedPopoverContentProps}
|
|
83
|
+
/>
|
|
84
|
+
),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const contextValue = useMemo(
|
|
88
|
+
() => ({ isOpen, onPressTrigger: onPressCalendarIcon }),
|
|
89
|
+
[isOpen, onPressCalendarIcon],
|
|
117
90
|
);
|
|
118
91
|
|
|
119
92
|
return (
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
93
|
+
<DatePickerInputContext value={contextValue}>
|
|
94
|
+
<DatePickerInputWithoutModal
|
|
95
|
+
ref={ref}
|
|
96
|
+
{...rest}
|
|
97
|
+
disabled={disabled}
|
|
98
|
+
value={value}
|
|
99
|
+
inputMode={inputMode}
|
|
100
|
+
validRange={validRange}
|
|
101
|
+
onChange={onChange}
|
|
102
|
+
// locale={locale}
|
|
103
|
+
>
|
|
104
|
+
<TextInput.Right>
|
|
105
|
+
{withModal || pickerMode === 'docked' ? (
|
|
106
|
+
<>
|
|
107
|
+
<IconButton
|
|
108
|
+
ref={triggerRef}
|
|
109
|
+
style={styles.calendarButton}
|
|
110
|
+
name={calendarIcon}
|
|
111
|
+
onPress={onPressCalendarIcon}
|
|
112
|
+
disabled={disabled}
|
|
113
|
+
/>
|
|
114
|
+
</>
|
|
115
|
+
) : null}
|
|
116
|
+
</TextInput.Right>
|
|
117
|
+
{children}
|
|
118
|
+
{renderers[pickerMode]}
|
|
119
|
+
</DatePickerInputWithoutModal>
|
|
120
|
+
</DatePickerInputContext>
|
|
132
121
|
);
|
|
133
122
|
}
|
|
134
123
|
|
|
@@ -7,4 +7,5 @@ export const DatePickerInput = getRegisteredComponentWithFallback(
|
|
|
7
7
|
);
|
|
8
8
|
|
|
9
9
|
export type { DatePickerInputProps } from './types';
|
|
10
|
-
export {
|
|
10
|
+
export type { DatePickerInputContextType } from './utils';
|
|
11
|
+
export { DatePickerInputContext, datePickerInputStyles } from './utils';
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
1
2
|
import { StyleSheet } from 'react-native-unistyles';
|
|
2
3
|
|
|
3
4
|
import { getRegisteredComponentStylesWithFallback } from '../../core';
|
|
4
5
|
|
|
6
|
+
export type DatePickerInputContextType = {
|
|
7
|
+
onPressTrigger: () => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const DatePickerInputContext = createContext<DatePickerInputContextType>({
|
|
11
|
+
onPressTrigger: () => {},
|
|
12
|
+
});
|
|
13
|
+
|
|
5
14
|
const datePickerInputStylesDefault = StyleSheet.create({
|
|
6
15
|
root: {
|
|
7
16
|
minWidth: 150,
|
|
@@ -3,25 +3,36 @@ import { View, type ViewProps } from 'react-native';
|
|
|
3
3
|
import { StyleSheet } from 'react-native-unistyles';
|
|
4
4
|
|
|
5
5
|
import { getRegisteredComponentStylesWithFallback } from '../../core';
|
|
6
|
-
import {
|
|
6
|
+
import { extractSubcomponents } from '../../utils/extractSubcomponents';
|
|
7
7
|
|
|
8
8
|
export type Props = Omit<ViewProps, 'children'> & {
|
|
9
9
|
children: ReactElement | ReactElement[];
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
const allowedChildren = [
|
|
12
|
+
const allowedChildren = [
|
|
13
|
+
{ name: 'Drawer_Footer', allowMultiple: false },
|
|
14
|
+
{ name: 'Drawer_Header', allowMultiple: false },
|
|
15
|
+
{ name: 'Drawer_Content', allowMultiple: false },
|
|
16
|
+
];
|
|
13
17
|
|
|
14
18
|
const Drawer = ({ style, children, ...rest }: Props) => {
|
|
15
|
-
const {
|
|
19
|
+
const {
|
|
20
|
+
Drawer_Header,
|
|
21
|
+
Drawer_Footer,
|
|
22
|
+
Drawer_Content,
|
|
23
|
+
rest: restChildren,
|
|
24
|
+
} = extractSubcomponents({
|
|
16
25
|
children,
|
|
17
26
|
allowedChildren,
|
|
27
|
+
includeRest: true,
|
|
18
28
|
});
|
|
19
29
|
|
|
20
30
|
return (
|
|
21
31
|
<View style={[drawerStyles.root, style]} {...rest}>
|
|
22
|
-
{Drawer_Header
|
|
23
|
-
{Drawer_Content
|
|
24
|
-
{Drawer_Footer
|
|
32
|
+
{Drawer_Header}
|
|
33
|
+
{Drawer_Content}
|
|
34
|
+
{Drawer_Footer}
|
|
35
|
+
{restChildren}
|
|
25
36
|
</View>
|
|
26
37
|
);
|
|
27
38
|
};
|
|
@@ -5,6 +5,7 @@ import useFilePicker from '../../hooks/useFilePicker';
|
|
|
5
5
|
import type { DocumentPickerOptions, DocumentResult } from '../../utils/DocumentPicker';
|
|
6
6
|
import { IconButton } from '../IconButton';
|
|
7
7
|
import { TextInput, type TextInputProps } from '../TextInput';
|
|
8
|
+
import { FilePickerContext } from './utils';
|
|
8
9
|
|
|
9
10
|
export type OmitProp =
|
|
10
11
|
| 'editable'
|
|
@@ -84,14 +85,18 @@ const FilePicker = ({
|
|
|
84
85
|
});
|
|
85
86
|
}, [onValueChange, openFilePicker]);
|
|
86
87
|
|
|
88
|
+
const contextValue = useMemo(() => ({ onPressTrigger: onPress }), [onPress]);
|
|
89
|
+
|
|
87
90
|
return (
|
|
88
|
-
<
|
|
89
|
-
<TextInput
|
|
90
|
-
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
<FilePickerContext value={contextValue}>
|
|
92
|
+
<TextInput value={displayText} {...rest} editable={false} ref={ref}>
|
|
93
|
+
<TextInput.Label>Choose file</TextInput.Label>
|
|
94
|
+
<TextInput.Right>
|
|
95
|
+
<IconButton type="material-community" name="upload" onPress={onPress} />
|
|
96
|
+
</TextInput.Right>
|
|
97
|
+
{children}
|
|
98
|
+
</TextInput>
|
|
99
|
+
</FilePickerContext>
|
|
95
100
|
);
|
|
96
101
|
};
|
|
97
102
|
|
|
@@ -4,4 +4,5 @@ import FilePickerDefault from './FilePicker';
|
|
|
4
4
|
export const FilePicker = getRegisteredComponentWithFallback('FilePicker', FilePickerDefault);
|
|
5
5
|
|
|
6
6
|
export type { Props as FilePickerProps } from './FilePicker';
|
|
7
|
-
export {
|
|
7
|
+
export type { FilePickerContextType } from './utils';
|
|
8
|
+
export { defaultStyles, FilePickerContext } from './utils';
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
1
2
|
import { StyleSheet } from 'react-native-unistyles';
|
|
2
3
|
|
|
3
4
|
import { getRegisteredComponentStylesWithFallback } from '../../core';
|
|
4
5
|
|
|
6
|
+
export type FilePickerContextType = {
|
|
7
|
+
onPressTrigger: () => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const FilePickerContext = createContext<FilePickerContextType>({
|
|
11
|
+
onPressTrigger: () => {},
|
|
12
|
+
});
|
|
13
|
+
|
|
5
14
|
const filePickerStylesDefault = StyleSheet.create({
|
|
6
15
|
root: {},
|
|
7
16
|
});
|
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
import { memo, type ReactElement } from 'react';
|
|
2
2
|
import { View, type ViewProps } from 'react-native';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { extractSubcomponents } from '../../utils/extractSubcomponents';
|
|
5
5
|
import { navigationRailStyles } from './utils';
|
|
6
6
|
|
|
7
7
|
export type Props = Omit<ViewProps, 'children'> & {
|
|
8
8
|
children: ReactElement | ReactElement[];
|
|
9
9
|
};
|
|
10
10
|
const allowedChildren = [
|
|
11
|
-
'NavigationRail_Header',
|
|
12
|
-
'NavigationRail_Content',
|
|
13
|
-
'NavigationRail_Footer',
|
|
11
|
+
{ name: 'NavigationRail_Header', allowMultiple: false },
|
|
12
|
+
{ name: 'NavigationRail_Content', allowMultiple: false },
|
|
13
|
+
{ name: 'NavigationRail_Footer', allowMultiple: false },
|
|
14
14
|
];
|
|
15
15
|
|
|
16
16
|
const NavigationRail = ({ children, style, ...rest }: Props) => {
|
|
17
|
-
const {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
const {
|
|
18
|
+
NavigationRail_Header,
|
|
19
|
+
NavigationRail_Content,
|
|
20
|
+
NavigationRail_Footer,
|
|
21
|
+
rest: restChildren,
|
|
22
|
+
} = extractSubcomponents({
|
|
23
|
+
children,
|
|
24
|
+
allowedChildren,
|
|
25
|
+
includeRest: true,
|
|
26
|
+
});
|
|
22
27
|
|
|
23
28
|
return (
|
|
24
29
|
<View style={[navigationRailStyles.root, style]} {...rest}>
|
|
25
30
|
{NavigationRail_Header[0]}
|
|
26
31
|
{NavigationRail_Content[0]}
|
|
27
32
|
{NavigationRail_Footer[0]}
|
|
33
|
+
{restChildren}
|
|
28
34
|
</View>
|
|
29
35
|
);
|
|
30
36
|
};
|
package/components/Tabs/Tabs.tsx
CHANGED
|
@@ -22,7 +22,8 @@ import {
|
|
|
22
22
|
} from 'react-native';
|
|
23
23
|
|
|
24
24
|
import { typedMemo } from '../../hocs';
|
|
25
|
-
import { useControlledValue
|
|
25
|
+
import { useControlledValue } from '../../hooks';
|
|
26
|
+
import { extractSubcomponents } from '../../utils/extractSubcomponents';
|
|
26
27
|
import { noop } from '../../utils/lodash';
|
|
27
28
|
import type { TabItemProps } from './TabItem';
|
|
28
29
|
import { tabsStyles } from './utils';
|
|
@@ -81,7 +82,7 @@ export const TabBase = <T extends string | number>({
|
|
|
81
82
|
variant,
|
|
82
83
|
});
|
|
83
84
|
|
|
84
|
-
const { Tabs_Item: tabItems } =
|
|
85
|
+
const { Tabs_Item: tabItems } = extractSubcomponents({
|
|
85
86
|
children,
|
|
86
87
|
allowedChildren: ['Tabs_Item'],
|
|
87
88
|
});
|
|
@@ -34,8 +34,8 @@ import {
|
|
|
34
34
|
import { useActionState } from '../../hooks/useActionState';
|
|
35
35
|
import useControlledValue from '../../hooks/useControlledValue';
|
|
36
36
|
import useLatest from '../../hooks/useLatest';
|
|
37
|
-
import useSubcomponents from '../../hooks/useSubcomponents';
|
|
38
37
|
import { createSyntheticEvent, resolveStateVariant } from '../../utils';
|
|
38
|
+
import { extractSubcomponents } from '../../utils/extractSubcomponents';
|
|
39
39
|
import { HelperText } from '../HelperText';
|
|
40
40
|
import { Icon } from '../Icon';
|
|
41
41
|
import { StateLayer } from '../StateLayer';
|
|
@@ -215,7 +215,7 @@ const TextInput = ({
|
|
|
215
215
|
TextInput_SupportingText,
|
|
216
216
|
TextInput_Outline,
|
|
217
217
|
rest: restChildren,
|
|
218
|
-
} =
|
|
218
|
+
} = extractSubcomponents({
|
|
219
219
|
children,
|
|
220
220
|
allowedChildren: [
|
|
221
221
|
{ name: 'TextInput_Label', allowMultiple: false },
|
|
@@ -2,41 +2,63 @@ import {
|
|
|
2
2
|
createContext,
|
|
3
3
|
memo,
|
|
4
4
|
type ReactElement,
|
|
5
|
+
type RefObject,
|
|
5
6
|
useCallback,
|
|
6
7
|
useEffect,
|
|
7
8
|
useMemo,
|
|
8
9
|
useRef,
|
|
9
10
|
} from 'react';
|
|
10
|
-
import { Text, type ViewProps, type ViewStyle } from 'react-native';
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { tooltipStyles } from './utils';
|
|
12
|
+
import { useToggle } from '../../hooks';
|
|
13
|
+
import { extractSubcomponents } from '../../utils/extractSubcomponents';
|
|
15
14
|
|
|
16
|
-
export type Props =
|
|
15
|
+
export type Props = {
|
|
17
16
|
fadeInDelay?: number;
|
|
18
17
|
fadeOutDelay?: number;
|
|
19
|
-
showArrow?: boolean;
|
|
20
|
-
style?: ViewStyle;
|
|
21
|
-
children: ReactElement | ReactElement[];
|
|
22
18
|
hoverableContent?: boolean;
|
|
19
|
+
children: ReactElement | ReactElement[];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type TooltipContextValue = {
|
|
23
|
+
isOpen: boolean;
|
|
24
|
+
triggerRef: RefObject<any>;
|
|
25
|
+
onOpen: () => void;
|
|
26
|
+
onClose: () => void;
|
|
27
|
+
onMouseEnter?: () => void;
|
|
28
|
+
onMouseLeave?: () => void;
|
|
23
29
|
};
|
|
24
30
|
|
|
31
|
+
export const TooltipContext = createContext<TooltipContextValue>({
|
|
32
|
+
isOpen: false,
|
|
33
|
+
onOpen: () => {},
|
|
34
|
+
onClose: () => {},
|
|
35
|
+
triggerRef: { current: null },
|
|
36
|
+
});
|
|
37
|
+
|
|
25
38
|
const Tooltip = ({
|
|
26
|
-
style,
|
|
27
39
|
children,
|
|
28
40
|
fadeInDelay = 100,
|
|
29
41
|
fadeOutDelay = 300,
|
|
30
|
-
showArrow = false,
|
|
31
42
|
hoverableContent = false,
|
|
32
|
-
...rest
|
|
33
43
|
}: Props) => {
|
|
34
44
|
const { state: isOpen, setState: setIsOpen } = useToggle(false);
|
|
35
45
|
const triggerRef = useRef(null);
|
|
36
46
|
const timeOutRef = useRef<NodeJS.Timeout>(undefined);
|
|
37
|
-
const popoverTimeoutRef = useRef<NodeJS.Timeout>(undefined);
|
|
38
47
|
const preventCloseRef = useRef(false);
|
|
39
48
|
|
|
49
|
+
const {
|
|
50
|
+
Tooltip_Trigger,
|
|
51
|
+
Tooltip_Content,
|
|
52
|
+
rest: restChildren,
|
|
53
|
+
} = extractSubcomponents({
|
|
54
|
+
children,
|
|
55
|
+
allowedChildren: [
|
|
56
|
+
{ name: 'Tooltip_Trigger', allowMultiple: false },
|
|
57
|
+
{ name: 'Tooltip_Content', allowMultiple: false },
|
|
58
|
+
],
|
|
59
|
+
includeRest: true,
|
|
60
|
+
});
|
|
61
|
+
|
|
40
62
|
const onClose = useCallback(() => {
|
|
41
63
|
if (preventCloseRef.current) return;
|
|
42
64
|
clearTimeout(timeOutRef.current);
|
|
@@ -48,34 +70,13 @@ const Tooltip = ({
|
|
|
48
70
|
timeOutRef.current = setTimeout(() => setIsOpen(true), fadeInDelay);
|
|
49
71
|
}, [fadeInDelay, setIsOpen]);
|
|
50
72
|
|
|
51
|
-
|
|
52
|
-
// (_isOpen: boolean) => {
|
|
53
|
-
// clearTimeout(popoverTimeoutRef.current);
|
|
54
|
-
// popoverTimeoutRef.current = setTimeout(
|
|
55
|
-
// () => setIsOpen(_isOpen),
|
|
56
|
-
// isOpen ? fadeInDelay : fadeOutDelay,
|
|
57
|
-
// );
|
|
58
|
-
// },
|
|
59
|
-
// [fadeInDelay, fadeOutDelay, isOpen, setIsOpen],
|
|
60
|
-
// );
|
|
61
|
-
|
|
62
|
-
const { Tooltip_Trigger, Tooltip_Content } = useSubcomponents({
|
|
63
|
-
children,
|
|
64
|
-
allowedChildren: ['Tooltip_Trigger', 'Tooltip_Content'],
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const contextValue = useMemo(
|
|
73
|
+
const contextValue = useMemo<TooltipContextValue>(
|
|
68
74
|
() => ({
|
|
75
|
+
isOpen,
|
|
69
76
|
triggerRef,
|
|
70
77
|
onOpen,
|
|
71
78
|
onClose,
|
|
72
|
-
|
|
73
|
-
[onClose, onOpen],
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
const { popoverContentProps, popoverStyle } = useMemo(
|
|
77
|
-
() => ({
|
|
78
|
-
popoverContentProps: (hoverableContent
|
|
79
|
+
...(hoverableContent
|
|
79
80
|
? {
|
|
80
81
|
onMouseEnter: () => {
|
|
81
82
|
preventCloseRef.current = true;
|
|
@@ -88,50 +89,24 @@ const Tooltip = ({
|
|
|
88
89
|
setIsOpen(false);
|
|
89
90
|
},
|
|
90
91
|
}
|
|
91
|
-
: {})
|
|
92
|
-
popoverStyle: [tooltipStyles.content, style],
|
|
92
|
+
: {}),
|
|
93
93
|
}),
|
|
94
|
-
[hoverableContent,
|
|
94
|
+
[hoverableContent, isOpen, onClose, onOpen, setIsOpen],
|
|
95
95
|
);
|
|
96
96
|
|
|
97
97
|
useEffect(() => {
|
|
98
|
-
const popoverTimeout = popoverTimeoutRef;
|
|
99
|
-
|
|
100
98
|
return () => {
|
|
101
99
|
clearTimeout(timeOutRef.current);
|
|
102
|
-
clearTimeout(popoverTimeout.current);
|
|
103
100
|
};
|
|
104
101
|
}, []);
|
|
105
102
|
|
|
106
103
|
return (
|
|
107
104
|
<TooltipContext.Provider value={contextValue}>
|
|
108
|
-
{Tooltip_Trigger
|
|
109
|
-
{
|
|
110
|
-
|
|
111
|
-
isOpen={isOpen}
|
|
112
|
-
inverted
|
|
113
|
-
// placement={placement}
|
|
114
|
-
showArrow={showArrow}
|
|
115
|
-
// backdropStyles={styles.backdrop}
|
|
116
|
-
triggerRef={triggerRef}
|
|
117
|
-
// setIsOpen={setPopoverOpen}
|
|
118
|
-
{...popoverContentProps}
|
|
119
|
-
{...rest}
|
|
120
|
-
style={popoverStyle}
|
|
121
|
-
// contentTextStyles={contentTextStyles}
|
|
122
|
-
// popoverContentProps={popoverContentProps}
|
|
123
|
-
onClose={onClose}>
|
|
124
|
-
<Text style={tooltipStyles.contentText}>{Tooltip_Content[0]}</Text>
|
|
125
|
-
</Popover>
|
|
126
|
-
)}
|
|
105
|
+
{Tooltip_Trigger}
|
|
106
|
+
{Tooltip_Content}
|
|
107
|
+
{restChildren}
|
|
127
108
|
</TooltipContext.Provider>
|
|
128
109
|
);
|
|
129
110
|
};
|
|
130
111
|
|
|
131
|
-
export const TooltipContext = createContext({
|
|
132
|
-
onOpen: () => {},
|
|
133
|
-
onClose: () => {},
|
|
134
|
-
triggerRef: null as any,
|
|
135
|
-
});
|
|
136
|
-
|
|
137
112
|
export default memo(Tooltip);
|
|
@@ -1,11 +1,38 @@
|
|
|
1
|
-
import { memo, type
|
|
1
|
+
import { memo, type ReactNode, useContext, useMemo } from 'react';
|
|
2
|
+
import { type StyleProp, Text, type ViewStyle } from 'react-native';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
import { Popover, type PopoverProps } from '../Popover';
|
|
5
|
+
import { TooltipContext } from './Tooltip';
|
|
6
|
+
import { tooltipStyles } from './utils';
|
|
7
|
+
|
|
8
|
+
export type Props = Omit<PopoverProps, 'isOpen' | 'triggerRef' | 'onClose' | 'children'> & {
|
|
9
|
+
children?: ReactNode;
|
|
5
10
|
};
|
|
6
11
|
|
|
7
|
-
const TooltipContent = memo(({ children }: Props) => {
|
|
8
|
-
|
|
12
|
+
const TooltipContent = memo(({ children, style, ...rest }: Props) => {
|
|
13
|
+
const { isOpen, triggerRef, onClose, onMouseEnter, onMouseLeave } = useContext(TooltipContext);
|
|
14
|
+
|
|
15
|
+
const popoverStyle = useMemo<StyleProp<ViewStyle>>(
|
|
16
|
+
() => [tooltipStyles.content, style],
|
|
17
|
+
[style],
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if (!isOpen) return null;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Popover
|
|
24
|
+
isOpen={isOpen}
|
|
25
|
+
inverted
|
|
26
|
+
triggerRef={triggerRef}
|
|
27
|
+
onClose={onClose}
|
|
28
|
+
{...rest}
|
|
29
|
+
style={popoverStyle}
|
|
30
|
+
// @ts-ignore — onMouseEnter/onMouseLeave spread onto the inner View via ...rest in Popover
|
|
31
|
+
onMouseEnter={onMouseEnter}
|
|
32
|
+
onMouseLeave={onMouseLeave}>
|
|
33
|
+
<Text style={tooltipStyles.contentText}>{children}</Text>
|
|
34
|
+
</Popover>
|
|
35
|
+
);
|
|
9
36
|
});
|
|
10
37
|
|
|
11
38
|
TooltipContent.displayName = 'Tooltip_Content';
|
|
@@ -10,7 +10,7 @@ export const TooltipDefault = Object.assign(TooltipComponent, {
|
|
|
10
10
|
|
|
11
11
|
export const Tooltip = getRegisteredComponentWithFallback('Tooltip', TooltipDefault);
|
|
12
12
|
|
|
13
|
-
export type { Props as TooltipProps } from './Tooltip';
|
|
13
|
+
export type { TooltipContextValue, Props as TooltipProps } from './Tooltip';
|
|
14
14
|
export type { Props as TooltipContentProps } from './TooltipContent';
|
|
15
15
|
export type { Props as TooltipTriggerProps } from './TooltipTrigger';
|
|
16
16
|
export { tooltipStyles } from './utils';
|
package/hooks/index.tsx
CHANGED
|
@@ -18,6 +18,5 @@ export { useMediaQuery } from './useMediaQuery';
|
|
|
18
18
|
export { useMergedRefs } from './useMergedRefs';
|
|
19
19
|
export { default as usePrevious } from './usePrevious';
|
|
20
20
|
export * from './useQueryFilter';
|
|
21
|
-
export { default as useSubcomponents, type UseSubcomponentsProps } from './useSubcomponents';
|
|
22
21
|
export * from './useTheme';
|
|
23
22
|
export { default as useToggle } from './useToggle';
|
package/package.json
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
2
|
+
import { Children, type FC, isValidElement } from 'react';
|
|
3
|
+
|
|
4
|
+
export type ExtractSubcomponentsArgs<T extends string> = {
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
/**
|
|
7
|
+
* array of displayName as string
|
|
8
|
+
* */
|
|
9
|
+
allowedChildren: (T | { name: T; allowMultiple?: boolean })[];
|
|
10
|
+
/**
|
|
11
|
+
* If true, also returns the remaining children that don't match any of the allowedChildren
|
|
12
|
+
* in a `rest` property
|
|
13
|
+
*/
|
|
14
|
+
includeRest?: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ExtractSubcomponentsResult<T extends string, IncludeRest extends boolean = false> = {
|
|
18
|
+
[key in T]: ReactElement[];
|
|
19
|
+
} & (IncludeRest extends true ? { rest: ReactNode[] } : {});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* This will return an object with the displayNames as the property names
|
|
23
|
+
* eg. allowedChildren: ['Drawer_Header', 'Drawer_Content', 'Drawer_Footer', 'DrawerItem'];
|
|
24
|
+
*
|
|
25
|
+
* return value -> {
|
|
26
|
+
* Drawer_Header: [],
|
|
27
|
+
* Drawer_Content: [],
|
|
28
|
+
* Drawer_Footer: [],
|
|
29
|
+
* DrawerItem: [],
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* If includeRest is true, also returns:
|
|
33
|
+
* {
|
|
34
|
+
* ...above,
|
|
35
|
+
* rest: [remaining children that don't match allowedChildren]
|
|
36
|
+
* }
|
|
37
|
+
* */
|
|
38
|
+
export function extractSubcomponents<
|
|
39
|
+
T extends string = string,
|
|
40
|
+
IncludeRest extends boolean = false,
|
|
41
|
+
>({
|
|
42
|
+
children,
|
|
43
|
+
allowedChildren,
|
|
44
|
+
includeRest,
|
|
45
|
+
}: ExtractSubcomponentsArgs<T> & { includeRest?: IncludeRest }): ExtractSubcomponentsResult<
|
|
46
|
+
T,
|
|
47
|
+
IncludeRest
|
|
48
|
+
> {
|
|
49
|
+
// Single map: name → allowMultiple
|
|
50
|
+
const allowedMap = new Map<T, boolean>();
|
|
51
|
+
for (const entry of allowedChildren) {
|
|
52
|
+
if (typeof entry === 'string') {
|
|
53
|
+
allowedMap.set(entry as T, true);
|
|
54
|
+
} else {
|
|
55
|
+
allowedMap.set(entry.name, entry.allowMultiple ?? true);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const result = (includeRest ? { rest: [] } : {}) as ExtractSubcomponentsResult<T, IncludeRest>;
|
|
60
|
+
for (const name of allowedMap.keys()) {
|
|
61
|
+
(result as any)[name] = [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
Children.forEach(children, child => {
|
|
65
|
+
if (!isValidElement(child)) {
|
|
66
|
+
if (includeRest) (result as any).rest.push(child);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const displayName = (child.type as FC)?.displayName as T | undefined;
|
|
71
|
+
if (!displayName) {
|
|
72
|
+
if (includeRest) (result as any).rest.push(child);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const allowMultiple = allowedMap.get(displayName);
|
|
77
|
+
if (allowMultiple !== undefined) {
|
|
78
|
+
if (allowMultiple) {
|
|
79
|
+
(result as any)[displayName].push(child);
|
|
80
|
+
} else {
|
|
81
|
+
(result as any)[displayName] = [child];
|
|
82
|
+
}
|
|
83
|
+
} else if (includeRest) {
|
|
84
|
+
(result as any).rest.push(child);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import type { ReactElement, ReactNode } from 'react';
|
|
2
|
-
import { Children, type FC, isValidElement, useMemo } from 'react';
|
|
3
|
-
|
|
4
|
-
export type UseSubcomponentsProps<T extends string> = {
|
|
5
|
-
children: ReactNode;
|
|
6
|
-
/**
|
|
7
|
-
* array of displayName as string
|
|
8
|
-
* */
|
|
9
|
-
allowedChildren: (T | { name: T; allowMultiple?: boolean })[];
|
|
10
|
-
/**
|
|
11
|
-
* If true, also returns the remaining children that don't match any of the allowedChildren
|
|
12
|
-
* in a `rest` property
|
|
13
|
-
*/
|
|
14
|
-
includeRest?: boolean;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export type UseSubcomponentsResult<T extends string, IncludeRest extends boolean = false> = {
|
|
18
|
-
[key in T]: ReactElement[];
|
|
19
|
-
} & (IncludeRest extends true ? { rest: ReactNode[] } : {});
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* This will return an object with the displayNames as the property names
|
|
23
|
-
* eg. allowedChildren: ['Drawer_Header', 'Drawer_Content', 'Drawer_Footer', 'DrawerItem'];
|
|
24
|
-
*
|
|
25
|
-
* return value -> {
|
|
26
|
-
* Drawer_Header: [],
|
|
27
|
-
* Drawer_Content: [],
|
|
28
|
-
* Drawer_Footer: [],
|
|
29
|
-
* DrawerItem: [],
|
|
30
|
-
* }
|
|
31
|
-
*
|
|
32
|
-
* If includeRest is true, also returns:
|
|
33
|
-
* {
|
|
34
|
-
* ...above,
|
|
35
|
-
* rest: [remaining children that don't match allowedChildren]
|
|
36
|
-
* }
|
|
37
|
-
* */
|
|
38
|
-
function useSubcomponents<T extends string = string, IncludeRest extends boolean = false>({
|
|
39
|
-
children,
|
|
40
|
-
allowedChildren,
|
|
41
|
-
includeRest,
|
|
42
|
-
}: UseSubcomponentsProps<T> & { includeRest?: IncludeRest }): UseSubcomponentsResult<
|
|
43
|
-
T,
|
|
44
|
-
IncludeRest
|
|
45
|
-
> {
|
|
46
|
-
return useMemo(() => {
|
|
47
|
-
const configs = allowedChildren.map(entry =>
|
|
48
|
-
typeof entry === 'string'
|
|
49
|
-
? { name: entry, allowMultiple: true as boolean }
|
|
50
|
-
: { name: entry.name, allowMultiple: entry.allowMultiple ?? true },
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
const nameSet = new Set(configs.map(c => c.name));
|
|
54
|
-
const allowMultipleMap = new Map(configs.map(c => [c.name, c.allowMultiple]));
|
|
55
|
-
|
|
56
|
-
const result = configs.reduce((acc, { name }) => {
|
|
57
|
-
(acc as any)[name] = [];
|
|
58
|
-
return acc;
|
|
59
|
-
}, (includeRest ? { rest: [] } : {}) as UseSubcomponentsResult<T, IncludeRest>);
|
|
60
|
-
|
|
61
|
-
Children.forEach(children, child => {
|
|
62
|
-
if (!isValidElement(child)) {
|
|
63
|
-
if (includeRest) {
|
|
64
|
-
(result as any).rest.push(child);
|
|
65
|
-
}
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const displayName = (child.type as FC)?.displayName as T | undefined;
|
|
70
|
-
|
|
71
|
-
if (displayName && nameSet.has(displayName)) {
|
|
72
|
-
if (allowMultipleMap.get(displayName)) {
|
|
73
|
-
(result as any)[displayName].push(child);
|
|
74
|
-
} else {
|
|
75
|
-
// Only keep the last matching child
|
|
76
|
-
(result as any)[displayName] = [child];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (includeRest) {
|
|
83
|
-
(result as any).rest.push(child);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
return result;
|
|
88
|
-
}, [allowedChildren, children, includeRest]);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export default useSubcomponents;
|