Skip to content

Commit 0758a3b

Browse files
author
梁朝飞
committed
feat: add vertical props
1 parent 658632e commit 0758a3b

File tree

6 files changed

+366
-62
lines changed

6 files changed

+366
-62
lines changed

assets/index.less

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
@segmented-prefix-cls: rc-segmented;
22

3+
@disabled-color: fade(#000, 25%);
4+
@selected-bg-color: white;
5+
@text-color: #262626;
6+
@transition-duration: 0.3s;
7+
@transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1);
8+
39
.segmented-disabled-item() {
410
&,
511
&:hover,
612
&:focus {
7-
color: fade(#000, 25%);
13+
color: @disabled-color;
814
cursor: not-allowed;
915
}
1016
}
1117

1218
.segmented-item-selected() {
13-
background-color: white;
19+
background-color: @selected-bg-color;
1420
}
1521

1622
.@{segmented-prefix-cls} {
@@ -21,9 +27,9 @@
2127
&-group {
2228
position: relative;
2329
display: flex;
30+
flex-direction: row;
2431
align-items: stretch;
25-
justify-items: flex-start;
26-
32+
justify-content: flex-start;
2733
width: 100%;
2834
border-radius: 2px;
2935
}
@@ -32,19 +38,18 @@
3238
position: relative;
3339
min-height: 28px;
3440
padding: 4px 10px;
35-
3641
color: fade(#000, 85%);
3742
text-align: center;
3843
cursor: pointer;
3944

4045
&-selected {
4146
.segmented-item-selected();
42-
color: #262626;
47+
color: @text-color;
4348
}
4449

4550
&:hover,
4651
&:focus {
47-
color: #262626;
52+
color: @text-color;
4853
}
4954

5055
&-disabled {
@@ -60,37 +65,51 @@
6065
position: absolute;
6166
top: 0;
6267
left: 0;
63-
6468
width: 0;
6569
height: 0;
6670
opacity: 0;
6771
pointer-events: none;
6872
}
6973
}
7074

71-
// disabled styles
72-
&-disabled &-item,
73-
&-disabled &-item:hover,
74-
&-disabled &-item:focus {
75-
.segmented-disabled-item();
76-
}
77-
7875
&-thumb {
7976
.segmented-item-selected();
80-
8177
position: absolute;
82-
// top: 0;
83-
// left: 0;
8478
width: 0;
8579
height: 100%;
8680
padding: 4px 0;
81+
transition: transform @transition-duration @transition-timing-function,
82+
width @transition-duration @transition-timing-function;
83+
}
84+
85+
&-vertical &-group {
86+
flex-direction: column;
87+
}
88+
89+
&-vertical &-item {
90+
width: 100%;
91+
text-align: left;
92+
}
93+
94+
&-vertical &-thumb {
95+
width: 100%;
96+
height: 0;
97+
padding: 0 4px;
98+
transition: transform @transition-duration @transition-timing-function,
99+
height @transition-duration @transition-timing-function;
100+
}
101+
102+
// disabled styles
103+
&-disabled &-item,
104+
&-disabled &-item:hover,
105+
&-disabled &-item:focus {
106+
.segmented-disabled-item();
87107
}
88108

89-
// transition effect when `enter-active`
90109
&-thumb-motion-appear-active,
91110
&-thumb-motion-enter-active {
92-
transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
93-
width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
111+
transition: transform @transition-duration @transition-timing-function,
112+
width @transition-duration @transition-timing-function;
94113
will-change: transform, width;
95114
}
96115

docs/demo/basic.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import '../../assets/style.less';
2-
import React from 'react';
31
import Segmented from 'rc-segmented';
2+
import React from 'react';
3+
import '../../assets/style.less';
44

55
export default function App() {
66
return (
@@ -11,6 +11,13 @@ export default function App() {
1111
onChange={(value) => console.log(value, typeof value)}
1212
/>
1313
</div>
14+
<div className="wrapper">
15+
<Segmented
16+
vertical
17+
options={['iOS', 'Android', 'Web']}
18+
onChange={(value) => console.log(value, typeof value)}
19+
/>
20+
</div>
1421
<div className="wrapper">
1522
<Segmented
1623
options={[13333333333, 157110000, 12110086]}

src/MotionThumb.tsx

Lines changed: 95 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ type ThumbReact = {
99
left: number;
1010
right: number;
1111
width: number;
12+
top: number;
13+
bottom: number;
14+
height: number;
1215
} | null;
1316

1417
export interface MotionThumbInterface {
@@ -20,23 +23,53 @@ export interface MotionThumbInterface {
2023
onMotionStart: VoidFunction;
2124
onMotionEnd: VoidFunction;
2225
direction?: 'ltr' | 'rtl';
26+
vertical?: boolean;
2327
}
2428

2529
const calcThumbStyle = (
2630
targetElement: HTMLElement | null | undefined,
27-
): ThumbReact =>
28-
targetElement
29-
? {
30-
left: targetElement.offsetLeft,
31-
right:
32-
(targetElement.parentElement!.clientWidth as number) -
33-
targetElement.clientWidth -
34-
targetElement.offsetLeft,
35-
width: targetElement.clientWidth,
36-
}
37-
: null;
31+
vertical?: boolean,
32+
): ThumbReact => {
33+
if (!targetElement) return null;
34+
35+
const style: ThumbReact = {
36+
left: targetElement.offsetLeft,
37+
right:
38+
(targetElement.parentElement!.clientWidth as number) -
39+
targetElement.clientWidth -
40+
targetElement.offsetLeft,
41+
width: targetElement.clientWidth,
42+
top: targetElement.offsetTop,
43+
bottom:
44+
(targetElement.parentElement!.clientHeight as number) -
45+
targetElement.clientHeight -
46+
targetElement.offsetTop,
47+
height: targetElement.clientHeight,
48+
};
49+
50+
if (vertical) {
51+
// Adjusts positioning and size for vertical layout by setting horizontal properties to 0 and using vertical properties from the style object.
52+
return {
53+
left: 0,
54+
right: 0,
55+
width: 0,
56+
top: style.top,
57+
bottom: style.bottom,
58+
height: style.height,
59+
};
60+
}
61+
62+
return {
63+
left: style.left,
64+
right: style.right,
65+
width: style.width,
66+
top: 0,
67+
bottom: 0,
68+
height: 0,
69+
};
70+
};
3871

39-
const toPX = (value: number) =>
72+
const toPX = (value: number | undefined): string | undefined =>
4073
value !== undefined ? `${value}px` : undefined;
4174

4275
export default function MotionThumb(props: MotionThumbInterface) {
@@ -49,6 +82,7 @@ export default function MotionThumb(props: MotionThumbInterface) {
4982
onMotionStart,
5083
onMotionEnd,
5184
direction,
85+
vertical = false,
5286
} = props;
5387

5488
const thumbRef = React.useRef<HTMLDivElement>(null);
@@ -57,11 +91,9 @@ export default function MotionThumb(props: MotionThumbInterface) {
5791
// =========================== Effect ===========================
5892
const findValueElement = (val: SegmentedValue) => {
5993
const index = getValueIndex(val);
60-
6194
const ele = containerRef.current?.querySelectorAll<HTMLDivElement>(
6295
`.${prefixCls}-item`,
6396
)[index];
64-
6597
return ele?.offsetParent && ele;
6698
};
6799

@@ -73,8 +105,8 @@ export default function MotionThumb(props: MotionThumbInterface) {
73105
const prev = findValueElement(prevValue);
74106
const next = findValueElement(value);
75107

76-
const calcPrevStyle = calcThumbStyle(prev);
77-
const calcNextStyle = calcThumbStyle(next);
108+
const calcPrevStyle = calcThumbStyle(prev, vertical);
109+
const calcNextStyle = calcThumbStyle(next, vertical);
78110

79111
setPrevValue(value);
80112
setPrevStyle(calcPrevStyle);
@@ -88,34 +120,59 @@ export default function MotionThumb(props: MotionThumbInterface) {
88120
}
89121
}, [value]);
90122

91-
const thumbStart = React.useMemo(
92-
() =>
93-
direction === 'rtl'
94-
? toPX(-(prevStyle?.right as number))
95-
: toPX(prevStyle?.left as number),
96-
[direction, prevStyle],
97-
);
98-
const thumbActive = React.useMemo(
99-
() =>
100-
direction === 'rtl'
101-
? toPX(-(nextStyle?.right as number))
102-
: toPX(nextStyle?.left as number),
103-
[direction, nextStyle],
104-
);
123+
const thumbStart = React.useMemo(() => {
124+
if (vertical) {
125+
return toPX(prevStyle?.top ?? 0);
126+
}
127+
128+
if (direction === 'rtl') {
129+
return toPX(-(prevStyle?.right as number));
130+
}
131+
132+
return toPX(prevStyle?.left as number);
133+
}, [vertical, direction, prevStyle]);
134+
135+
const thumbActive = React.useMemo(() => {
136+
if (vertical) {
137+
return toPX(nextStyle?.top ?? 0);
138+
}
139+
140+
if (direction === 'rtl') {
141+
return toPX(-(nextStyle?.right as number));
142+
}
143+
144+
return toPX(nextStyle?.left as number);
145+
}, [vertical, direction, nextStyle]);
105146

106147
// =========================== Motion ===========================
107148
const onAppearStart = () => {
149+
if (vertical) {
150+
return {
151+
transform: 'translateY(var(--thumb-start-top))',
152+
height: 'var(--thumb-start-height)',
153+
};
154+
}
155+
108156
return {
109-
transform: `translateX(var(--thumb-start-left))`,
110-
width: `var(--thumb-start-width)`,
157+
transform: 'translateX(var(--thumb-start-left))',
158+
width: 'var(--thumb-start-width)',
111159
};
112160
};
161+
113162
const onAppearActive = () => {
163+
if (vertical) {
164+
return {
165+
transform: 'translateY(var(--thumb-active-top))',
166+
height: 'var(--thumb-active-height)',
167+
};
168+
}
169+
114170
return {
115-
transform: `translateX(var(--thumb-active-left))`,
116-
width: `var(--thumb-active-width)`,
171+
transform: 'translateX(var(--thumb-active-left))',
172+
width: 'var(--thumb-active-width)',
117173
};
118174
};
175+
119176
const onVisibleChanged = () => {
120177
setPrevStyle(null);
121178
setNextStyle(null);
@@ -144,6 +201,10 @@ export default function MotionThumb(props: MotionThumbInterface) {
144201
'--thumb-start-width': toPX(prevStyle?.width),
145202
'--thumb-active-left': thumbActive,
146203
'--thumb-active-width': toPX(nextStyle?.width),
204+
'--thumb-start-top': thumbStart,
205+
'--thumb-start-height': toPX(prevStyle?.height),
206+
'--thumb-active-top': thumbActive,
207+
'--thumb-active-height': toPX(nextStyle?.height),
147208
} as React.CSSProperties;
148209

149210
// It's little ugly which should be refactor when @umi/test update to latest jsdom

0 commit comments

Comments
 (0)