-
Notifications
You must be signed in to change notification settings - Fork 5
/
Stack.tsx
130 lines (108 loc) · 3.48 KB
/
Stack.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import React, { useMemo } from 'react';
import { StyleProp, View, ViewStyle } from 'react-native';
import type { FlexProps } from './Flex';
import Flex from './Flex';
import type { SelectorProps } from './Selector';
import Selector, { notLastChild } from './Selector';
import Divider from './Divider';
import { useSpacing } from './SpacingFuncContext';
import { getValidChildren } from './utilities';
export interface StackProps extends FlexProps, SelectorProps {
/**
* The spacing between items in the stack.
*
* @default 0
*/
spacing?: number;
/**
* If `true`, each stack item will show a divider.
*
* @default false
*/
divider?: React.ReactElement | boolean;
/**
* A style object to apply to each divider.
*/
dividerStyle?: StyleProp<ViewStyle>;
/**
* If `true`, the children will be wrapped in a `Box` and the `Box` will take the spacing properties.
*
* @default false
*/
shouldWrapChildren?: boolean;
}
const Stack: React.FC<StackProps> = ({
spacing = 0,
divider = false,
dividerStyle,
shouldWrapChildren = false,
childrenStyle,
children,
...rest
}) => {
const spacingValue = useSpacing(spacing);
const direction = useMemo(() => {
return rest.inline ? 'row' : rest.direction || 'column';
}, [rest.inline, rest.direction]);
const spacingStyle = useMemo(() => {
switch (direction) {
case 'column':
return { marginBottom: spacingValue };
case 'row':
return { marginEnd: spacingValue };
case 'column-reverse':
return { marginTop: spacingValue };
case 'row-reverse':
return { marginStart: spacingValue };
}
}, [spacingValue, direction]);
const shouldUseChildren = !shouldWrapChildren && !divider;
const validChildren = getValidChildren(children);
const clones = shouldUseChildren
? validChildren
: validChildren.map((child, index) => {
const key = typeof child.key !== 'undefined' ? child.key : index;
const isLast = index + 1 === validChildren.length;
const wrappedChild = <StackItem key={key}>{child}</StackItem>;
const _child = shouldWrapChildren ? wrappedChild : child;
if (!divider) return _child;
const dividerElement = React.isValidElement(divider) ? (
divider
) : (
<Divider
orientation={
direction === 'row' || direction === 'row-reverse'
? 'vertical'
: 'horizontal'
}
/>
);
const clonedDivider = React.cloneElement(dividerElement, {
key: `${key}-divider`,
style: [dividerElement.props.style, dividerStyle],
});
const _divider = isLast ? null : clonedDivider;
return [_child, _divider];
});
return (
<Flex {...rest}>
<Selector childrenStyle={[notLastChild(spacingStyle), childrenStyle]}>
{clones}
</Selector>
</Flex>
);
};
export default Stack;
const StackItem: React.FC = (props) => <View {...props} />;
export interface HStackProps extends Omit<StackProps, 'inline' | 'direction'> {
reverse?: boolean;
}
export const HStack: React.FC<HStackProps> = ({ reverse, ...rest }) => {
return <Stack {...rest} direction={reverse ? 'row-reverse' : 'row'} />;
};
export interface VStackProps extends Omit<StackProps, 'inline' | 'direction'> {
reverse?: boolean;
}
export const VStack: React.FC<VStackProps> = ({ reverse, ...rest }) => {
return <Stack {...rest} direction={reverse ? 'column-reverse' : 'column'} />;
};