Skip to content

Commit eedf6a2

Browse files
committed
feat: 分割线divider
1 parent 7240052 commit eedf6a2

File tree

14 files changed

+2346
-0
lines changed

14 files changed

+2346
-0
lines changed

components/divider/Divider.tsx

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import React, { useMemo } from 'react'
2+
import { StyleSheet, Text, View, ViewStyle } from 'react-native'
3+
import { useTheme, WithTheme, WithThemeStyles } from '../style'
4+
import Line from './Line'
5+
import { DimensionValue, DividerPropsType } from './PropsType'
6+
import dividerStyles, { DividerStyles } from './style/index'
7+
8+
interface DividerProps
9+
extends DividerPropsType,
10+
WithThemeStyles<DividerStyles> {}
11+
12+
const Divider = (props: DividerProps) => {
13+
const {
14+
orientation = 'horizontal',
15+
position = 'center',
16+
variant = 'solid',
17+
content,
18+
thickness = StyleSheet.hairlineWidth,
19+
color,
20+
pattern = [4, 2],
21+
innerPadding = 10,
22+
orientationMargin,
23+
style,
24+
} = props
25+
26+
const memoStyles = useTheme({
27+
themeStyles: dividerStyles,
28+
})
29+
const _thickness = thickness >= 0.1 ? thickness : StyleSheet.hairlineWidth
30+
const _innerPadding = typeof innerPadding === 'number' ? innerPadding : 10
31+
const isHorizontal = orientation === 'horizontal'
32+
const hasCustomMargin = orientationMargin != null
33+
34+
// ================== orientationStyle ==================
35+
const orientationStyle = useMemo(() => {
36+
return isHorizontal ? memoStyles.horizontal : memoStyles.vertical
37+
}, [isHorizontal, memoStyles])
38+
39+
// ================== contentStyle ==================
40+
const contentStyle = useMemo<ViewStyle>(() => {
41+
let paddingRecord: DimensionValue[] = [0, 0, 0, 0] // [left, top, right, bottom]
42+
let marginRecord: DimensionValue[] = [0, 0, 0, 0] // [left, top, right, bottom]
43+
44+
if (hasCustomMargin) {
45+
if (position === 'left') {
46+
paddingRecord = isHorizontal
47+
? [0, 0, _innerPadding, 0]
48+
: [0, 0, 0, _innerPadding]
49+
marginRecord = isHorizontal
50+
? [orientationMargin, 0, 0, 0]
51+
: [0, orientationMargin, 0, 0]
52+
} else if (position === 'right') {
53+
paddingRecord = isHorizontal
54+
? [_innerPadding, 0, 0, 0]
55+
: [0, _innerPadding, 0, 0]
56+
marginRecord = isHorizontal
57+
? [0, 0, orientationMargin, 0]
58+
: [0, 0, 0, orientationMargin]
59+
} else {
60+
paddingRecord = isHorizontal
61+
? [_innerPadding, 0, _innerPadding, 0]
62+
: [0, _innerPadding, 0, _innerPadding]
63+
}
64+
} else {
65+
paddingRecord = isHorizontal
66+
? [_innerPadding, 0, _innerPadding, 0]
67+
: [0, _innerPadding, 0, _innerPadding]
68+
}
69+
const [paddingLeft, paddingTop, paddingRight, paddingBottom] = paddingRecord
70+
const [marginLeft, marginTop, marginRight, marginBottom] = marginRecord
71+
72+
return {
73+
paddingLeft,
74+
paddingTop,
75+
paddingRight,
76+
paddingBottom,
77+
marginLeft,
78+
marginTop,
79+
marginRight,
80+
marginBottom,
81+
}
82+
}, [
83+
isHorizontal,
84+
hasCustomMargin,
85+
_innerPadding,
86+
position,
87+
orientationMargin,
88+
])
89+
90+
// ================== contentDom ==================
91+
const contentDom = useMemo(() => {
92+
if (typeof content === 'string') {
93+
return <Text style={memoStyles.content}>{content}</Text>
94+
}
95+
if (React.isValidElement(content)) {
96+
return content
97+
}
98+
return null
99+
}, [content, memoStyles])
100+
101+
// ================== lineDom ==================
102+
const lineDom = useMemo(() => {
103+
return (
104+
<Line
105+
orientation={orientation}
106+
color={color}
107+
variant={variant}
108+
pattern={pattern}
109+
thickness={_thickness}
110+
style={memoStyles.line}
111+
/>
112+
)
113+
}, [orientation, color, variant, pattern, _thickness, memoStyles])
114+
115+
// ================== lineStyle ==================
116+
const lineStyle = useMemo(() => {
117+
const _orientationStyle = isHorizontal
118+
? memoStyles.horizontal
119+
: memoStyles.vertical
120+
const shortLineStyle = isHorizontal
121+
? memoStyles.horizontal_short_line
122+
: memoStyles.vertical_short_line
123+
const autoStyle = isHorizontal
124+
? memoStyles.horizontal_auto
125+
: memoStyles.vertical_auto
126+
return {
127+
..._orientationStyle,
128+
...shortLineStyle,
129+
...autoStyle,
130+
}
131+
}, [isHorizontal, memoStyles])
132+
133+
// ================== firstLineDom, secondLineDom ==================
134+
const [firstLineDom, secondLineDom] = useMemo(() => {
135+
let _firstLineDom: React.ReactNode = null
136+
let _secondLineDom: React.ReactNode = null
137+
if (position === 'left') {
138+
_firstLineDom = hasCustomMargin ? null : (
139+
<View style={lineStyle}>{lineDom}</View>
140+
)
141+
_secondLineDom = lineDom
142+
} else if (position === 'right') {
143+
_firstLineDom = lineDom
144+
_secondLineDom = hasCustomMargin ? null : (
145+
<View style={lineStyle}>{lineDom}</View>
146+
)
147+
} else {
148+
_firstLineDom = lineDom
149+
_secondLineDom = lineDom
150+
}
151+
return [_firstLineDom, _secondLineDom]
152+
}, [position, hasCustomMargin, lineStyle, lineDom])
153+
154+
// ================== dividerDom ==================
155+
const dividerDom = useMemo(() => {
156+
if (content == null) {
157+
return lineDom
158+
}
159+
return (
160+
<>
161+
{firstLineDom}
162+
<View style={contentStyle}>{contentDom}</View>
163+
{secondLineDom}
164+
</>
165+
)
166+
}, [lineDom, firstLineDom, secondLineDom, contentDom, contentStyle, content])
167+
168+
return (
169+
<WithTheme themeStyles={dividerStyles}>
170+
{(_styles) => (
171+
<View style={[_styles.container, orientationStyle, style]}>
172+
{dividerDom}
173+
</View>
174+
)}
175+
</WithTheme>
176+
)
177+
}
178+
179+
Divider.displayName = 'Divider'
180+
export default React.memo(Divider)

components/divider/Line.tsx

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import React, { useMemo, useState } from 'react'
2+
import { LayoutChangeEvent, StyleSheet, View, ViewStyle } from 'react-native'
3+
import { LinePropsType } from './PropsType'
4+
5+
const Line: React.FC<LinePropsType> = ({
6+
orientation = 'horizontal',
7+
variant = 'dashed',
8+
thickness = StyleSheet.hairlineWidth,
9+
pattern,
10+
color,
11+
style,
12+
}) => {
13+
const [lineLength, setLineLength] = useState(1)
14+
const backgroundColor = color || style?.backgroundColor
15+
const _thickness = thickness >= 0.1 ? thickness : StyleSheet.hairlineWidth
16+
const isHorizontal = orientation === 'horizontal'
17+
18+
// ================== orientationStyle ==================
19+
const orientationStyle = useMemo<ViewStyle>(() => {
20+
const flexDirection = isHorizontal ? 'row' : 'column'
21+
return {
22+
flex: 1,
23+
flexDirection,
24+
alignItems: 'center',
25+
}
26+
}, [isHorizontal])
27+
28+
// ================== solidLineStyle ==================
29+
const solidLineStyle = useMemo(() => {
30+
const thicknessStyle = isHorizontal
31+
? { height: _thickness }
32+
: { width: _thickness }
33+
return { flex: 1, backgroundColor, ...thicknessStyle }
34+
}, [isHorizontal, _thickness, backgroundColor])
35+
36+
// ================== solidLineDom ==================
37+
const solidLineDom = useMemo(() => {
38+
return <View style={solidLineStyle} />
39+
}, [solidLineStyle])
40+
41+
// ================== dashedPattern ==================
42+
const dashedPattern = useMemo(() => {
43+
const [len, gap] =
44+
Array.isArray(pattern) && pattern.length >= 2
45+
? pattern.slice(0, 2)
46+
: [4, 2]
47+
const l = len >= 1 ? len : 1
48+
const g = gap >= 1 ? gap : 1
49+
return { l, g }
50+
}, [pattern])
51+
52+
// ================== dashedLineStyle ==================
53+
const dashedLineStyle = useMemo(() => {
54+
const { l, g } = dashedPattern
55+
const dashStyle = isHorizontal
56+
? { height: _thickness, width: l, marginHorizontal: g }
57+
: { width: _thickness, height: l, marginVertical: g }
58+
return { ...dashStyle, backgroundColor }
59+
}, [isHorizontal, _thickness, backgroundColor, dashedPattern])
60+
61+
// ================== dashedLineDom ==================
62+
const dashedLineDom = useMemo(() => {
63+
const { l, g } = dashedPattern
64+
const count = Math.floor(lineLength / (g * 2 + l))
65+
return (
66+
<View
67+
style={orientationStyle}
68+
onLayout={(e: LayoutChangeEvent) => {
69+
const { height, width } = e.nativeEvent.layout
70+
const curLength = isHorizontal ? width : height
71+
setLineLength(curLength)
72+
}}>
73+
{[...Array(count)].map((_, index) => {
74+
return <View key={index} style={dashedLineStyle} />
75+
})}
76+
</View>
77+
)
78+
}, [
79+
dashedPattern,
80+
lineLength,
81+
isHorizontal,
82+
orientationStyle,
83+
dashedLineStyle,
84+
])
85+
86+
// ================== lineDom ==================
87+
const lineDom = useMemo(() => {
88+
if (variant === 'dashed') {
89+
return dashedLineDom
90+
} else {
91+
return solidLineDom
92+
}
93+
}, [variant, dashedLineDom, solidLineDom])
94+
95+
return lineDom
96+
}
97+
98+
Line.displayName = 'Line'
99+
export default Line

components/divider/PropsType.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React from 'react'
2+
import { ColorValue, ViewStyle } from 'react-native'
3+
4+
export type DimensionValue = number | string | undefined
5+
type Orientation = 'horizontal' | 'vertical'
6+
type Position = 'center' | 'left' | 'right'
7+
type Variant = 'solid' | 'dashed'
8+
9+
export interface DividerPropsType {
10+
orientation?: Orientation
11+
position?: Position
12+
variant?: Variant
13+
content?: string | React.ReactNode
14+
thickness?: number
15+
pattern?: number[]
16+
color?: ColorValue
17+
orientationMargin?: DimensionValue
18+
innerPadding?: DimensionValue
19+
style?: ViewStyle
20+
}
21+
22+
export interface LinePropsType {
23+
orientation?: Orientation
24+
variant?: Variant
25+
thickness?: number
26+
color?: ColorValue
27+
pattern?: number[]
28+
style?: ViewStyle
29+
}

0 commit comments

Comments
 (0)